Ver Fonte

dhcpclient: allow specifying hostname, vendor class id, and client id

ambrop7 há 14 anos atrás
pai
commit
62715f6974

+ 18 - 2
dhcpclient/BDHCPClient.c

@@ -166,7 +166,7 @@ fail0:
     return 0;
 }
 
-int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDHCPClient_handler handler, void *user)
+int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BDHCPClient_handler handler, void *user)
 {
     // init arguments
     o->reactor = reactor;
@@ -259,8 +259,24 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
         goto fail3;
     }
     
+    // init options
+    struct BDHCPClientCore_opts core_opts;
+    core_opts.hostname = opts.hostname;
+    core_opts.vendorclassid = opts.vendorclassid;
+    core_opts.clientid = opts.clientid;
+    core_opts.clientid_len = opts.clientid_len;
+    
+    // auto-generate clientid from MAC if requested
+    uint8_t mac_cid[7];
+    if (opts.auto_clientid) {
+        mac_cid[0] = DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET;
+        memcpy(mac_cid + 1, if_mac, 6);
+        core_opts.clientid = mac_cid;
+        core_opts.clientid_len = sizeof(mac_cid);
+    }
+    
     // init dhcp
-    if (!BDHCPClientCore_Init(&o->dhcp, PacketCopier_GetInput(&o->send_copier), PacketCopier_GetOutput(&o->recv_copier), if_mac, o->reactor, o,
+    if (!BDHCPClientCore_Init(&o->dhcp, PacketCopier_GetInput(&o->send_copier), PacketCopier_GetOutput(&o->recv_copier), if_mac, core_opts, o->reactor, o,
                               (BDHCPClientCore_func_getsendermac)dhcp_func_getsendermac,
                               (BDHCPClientCore_handler)dhcp_handler
     )) {

+ 9 - 1
dhcpclient/BDHCPClient.h

@@ -60,7 +60,15 @@ typedef struct {
     DebugObject d_obj;
 } BDHCPClient;
 
-int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDHCPClient_handler handler, void *user);
+struct BDHCPClient_opts {
+    const char *hostname;
+    const char *vendorclassid;
+    const uint8_t *clientid;
+    size_t clientid_len;
+    int auto_clientid;
+};
+
+int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BDHCPClient_handler handler, void *user);
 void BDHCPClient_Free (BDHCPClient *o);
 int BDHCPClient_IsUp (BDHCPClient *o);
 void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip);

+ 81 - 1
dhcpclient/BDHCPClientCore.c

@@ -26,6 +26,7 @@
 #include <misc/byteorder.h>
 #include <misc/minmax.h>
 #include <misc/balloc.h>
+#include <misc/bsize.h>
 #include <security/BRandom.h>
 #include <base/BLog.h>
 
@@ -128,6 +129,30 @@ static void send_message (
     ((uint8_t *)(out + 1))[3] = DHCP_OPTION_IP_ADDRESS_LEASE_TIME;
     out = (void *)((uint8_t *)(out + 1) + ntoh8(out->len));
     
+    if (o->hostname) {
+        // host name
+        out->type = hton8(DHCP_OPTION_HOST_NAME);
+        out->len = hton8(strlen(o->hostname));
+        memcpy(out + 1, o->hostname, strlen(o->hostname));
+        out = (void *)((uint8_t *)(out + 1) + ntoh8(out->len));
+    }
+    
+    if (o->vendorclassid) {
+        // vendor class identifier
+        out->type = hton8(DHCP_OPTION_VENDOR_CLASS_IDENTIFIER);
+        out->len = hton8(strlen(o->vendorclassid));
+        memcpy(out + 1, o->vendorclassid, strlen(o->vendorclassid));
+        out = (void *)((uint8_t *)(out + 1) + ntoh8(out->len));
+    }
+    
+    if (o->clientid) {
+        // client identifier
+        out->type = hton8(DHCP_OPTION_CLIENT_IDENTIFIER);
+        out->len = hton8(o->clientid_len);
+        memcpy(out + 1, o->clientid, o->clientid_len);
+        out = (void *)((uint8_t *)(out + 1) + ntoh8(out->len));
+    }
+    
     // end option
     *((uint8_t *)out) = 0xFF;
     out = (void *)((uint8_t *)out + 1);
@@ -603,7 +628,12 @@ static void lease_timer_handler (BDHCPClientCore *o)
     return;
 }
 
-int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, uint8_t *client_mac_addr, BReactor *reactor, void *user,
+static bsize_t maybe_len (const char *str)
+{
+    return bsize_fromsize(str ? strlen(str) : 0);
+}
+
+int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, void *user,
                           BDHCPClientCore_func_getsendermac func_getsendermac,
                           BDHCPClientCore_handler handler)
 {
@@ -621,6 +651,48 @@ int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, Pack
     o->func_getsendermac = func_getsendermac;
     o->handler = handler;
     
+    o->hostname = NULL;
+    o->vendorclassid = NULL;
+    o->clientid = NULL;
+    o->clientid_len = 0;
+    
+    // copy options
+    if (opts.hostname && !(o->hostname = strdup(opts.hostname))) {
+        BLog(BLOG_ERROR, "strdup failed");
+        goto fail0;
+    }
+    if (opts.vendorclassid && !(o->vendorclassid = strdup(opts.vendorclassid))) {
+        BLog(BLOG_ERROR, "strdup failed");
+        goto fail0;
+    }
+    if (opts.clientid) {
+        if (!(o->clientid = BAlloc(opts.clientid_len))) {
+            BLog(BLOG_ERROR, "BAlloc failed");
+            goto fail0;
+        }
+        memcpy(o->clientid, opts.clientid, opts.clientid_len);
+        o->clientid_len = opts.clientid_len;
+    }
+    
+    // make sure options aren't too long
+    bsize_t opts_size = bsize_add(maybe_len(o->hostname), bsize_add(maybe_len(o->vendorclassid), bsize_fromsize(o->clientid_len)));
+    if (opts_size.is_overflow || opts_size.value > 100) {
+        BLog(BLOG_ERROR, "options too long together");
+        goto fail0;
+    }
+    if (o->hostname && strlen(o->hostname) > 255) {
+        BLog(BLOG_ERROR, "hostname too long");
+        goto fail0;
+    }
+    if (o->vendorclassid && strlen(o->vendorclassid) > 255) {
+        BLog(BLOG_ERROR, "vendorclassid too long");
+        goto fail0;
+    }
+    if (o->clientid && o->clientid_len > 255) {
+        BLog(BLOG_ERROR, "clientid too long");
+        goto fail0;
+    }
+    
     // allocate buffers
     if (!(o->send_buf = BAlloc(PacketPassInterface_GetMTU(send_if)))) {
         BLog(BLOG_ERROR, "BAlloc send buf failed");
@@ -660,6 +732,9 @@ int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, Pack
 fail1:
     BFree(o->send_buf);
 fail0:
+    BFree(o->clientid);
+    free(o->vendorclassid);
+    free(o->hostname);
     return 0;
 }
 
@@ -677,6 +752,11 @@ void BDHCPClientCore_Free (BDHCPClientCore *o)
     // free buffers
     BFree(o->recv_buf);
     BFree(o->send_buf);
+    
+    // free options
+    BFree(o->clientid);
+    free(o->vendorclassid);
+    free(o->hostname);
 }
 
 void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip)

+ 12 - 1
dhcpclient/BDHCPClientCore.h

@@ -44,6 +44,13 @@
 typedef void (*BDHCPClientCore_func_getsendermac) (void *user, uint8_t *out_mac);
 typedef void (*BDHCPClientCore_handler) (void *user, int event);
 
+struct BDHCPClientCore_opts {
+    const char *hostname;
+    const char *vendorclassid;
+    const uint8_t *clientid;
+    size_t clientid_len;
+};
+
 typedef struct {
     PacketPassInterface *send_if;
     PacketRecvInterface *recv_if;
@@ -52,6 +59,10 @@ typedef struct {
     void *user;
     BDHCPClientCore_func_getsendermac func_getsendermac;
     BDHCPClientCore_handler handler;
+    char *hostname;
+    char *vendorclassid;
+    uint8_t *clientid;
+    size_t clientid_len;
     struct dhcp_header *send_buf;
     struct dhcp_header *recv_buf;
     int sending;
@@ -80,7 +91,7 @@ typedef struct {
     DebugObject d_obj;
 } BDHCPClientCore;
 
-int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, uint8_t *client_mac_addr, BReactor *reactor, void *user,
+int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, void *user,
                           BDHCPClientCore_func_getsendermac func_getsendermac,
                           BDHCPClientCore_handler handler);
 void BDHCPClientCore_Free (BDHCPClientCore *o);

+ 3 - 1
examples/dhcpclient_test.c

@@ -69,7 +69,9 @@ int main (int argc, char **argv)
         goto fail2;
     }
     
-    if (!BDHCPClient_Init(&dhcp, ifname, &reactor, dhcp_handler, NULL)) {
+    struct BDHCPClient_opts opts = {};
+    
+    if (!BDHCPClient_Init(&dhcp, ifname, opts, &reactor, dhcp_handler, NULL)) {
         DEBUG("BDHCPClient_Init failed");
         goto fail3;
     }

+ 1 - 0
misc/dhcp_proto.h

@@ -52,6 +52,7 @@
 #define DHCP_OPTION_RENEWAL_TIME_VALUE 58
 #define DHCP_OPTION_REBINDING_TIME_VALUE 59
 #define DHCP_OPTION_VENDOR_CLASS_IDENTIFIER 60
+#define DHCP_OPTION_CLIENT_IDENTIFIER 61
 
 #define DHCP_MESSAGE_TYPE_DISCOVER 1
 #define DHCP_MESSAGE_TYPE_OFFER 2

+ 52 - 6
ncd/modules/net_ipv4_dhcp.c

@@ -23,12 +23,16 @@
  * 
  * DHCP client module.
  * 
- * Synopsis: net.ipv4.dhcp(string ifname)
+ * Synopsis: net.ipv4.dhcp(string ifname, [list opts])
  * Description:
  *   Runs a DHCP client on a network interface. When an address is obtained,
  *   transitions up (but does not assign anything). If the lease times out,
  *   transitions down.
  *   The interface must already be up.
+ *   Supported options (in the opts argument):
+ *   - "hostname", (string value): send this hostname to the DHCP server
+ *   - "vendorclassid", (string value): send this vendor class identifier
+ *   - "auto_clientid": send a client identifier generated from the MAC address
  * Variables:
  *   string addr - assigned IP address ("A.B.C.D")
  *   string prefix - address prefix length ("N")
@@ -98,19 +102,61 @@ static void func_new (NCDModuleInst *i)
     o->i = i;
     
     // check arguments
-    NCDValue *arg;
-    if (!NCDValue_ListRead(o->i->args, 1, &arg)) {
+    NCDValue *ifname_arg;
+    NCDValue *opts_arg = NULL;
+    if (!NCDValue_ListRead(i->args, 1, &ifname_arg) && !NCDValue_ListRead(i->args, 2, &ifname_arg, &opts_arg)) {
         ModuleLog(o->i, BLOG_ERROR, "wrong arity");
         goto fail1;
     }
-    if (NCDValue_Type(arg) != NCDVALUE_STRING) {
+    if (NCDValue_Type(ifname_arg) != NCDVALUE_STRING || (opts_arg && NCDValue_Type(opts_arg) != NCDVALUE_LIST)) {
         ModuleLog(o->i, BLOG_ERROR, "wrong type");
         goto fail1;
     }
-    char *ifname = NCDValue_StringValue(arg);
+    char *ifname = NCDValue_StringValue(ifname_arg);
+    
+    struct BDHCPClient_opts opts = {};
+    
+    // read options
+    for (NCDValue *opt = (opts_arg ? NCDValue_ListFirst(opts_arg) : NULL); opt; opt = NCDValue_ListNext(opts_arg, opt)) {
+        // read name
+        if (NCDValue_Type(opt) != NCDVALUE_STRING) {
+            ModuleLog(o->i, BLOG_ERROR, "wrong option name type");
+            goto fail1;
+        }
+        char *optname = NCDValue_StringValue(opt);
+        
+        if (!strcmp(optname, "hostname") || !strcmp(optname, "vendorclassid")) {
+            // read value
+            NCDValue *val = NCDValue_ListNext(opts_arg, opt);
+            if (!val) {
+                ModuleLog(o->i, BLOG_ERROR, "option value missing");
+                goto fail1;
+            }
+            if (NCDValue_Type(val) != NCDVALUE_STRING) {
+                ModuleLog(o->i, BLOG_ERROR, "wrong option value type");
+                goto fail1;
+            }
+            char *optval = NCDValue_StringValue(val);
+            
+            if (!strcmp(optname, "hostname")) {
+                opts.hostname = optval;
+            } else {
+                opts.vendorclassid = optval;
+            }
+            
+            opt = val;
+        }
+        else if (!strcmp(optname, "auto_clientid")) {
+            opts.auto_clientid = 1;
+        }
+        else {
+            ModuleLog(o->i, BLOG_ERROR, "unknown option name");
+            goto fail1;
+        }
+    }
     
     // init DHCP
-    if (!BDHCPClient_Init(&o->dhcp, ifname, o->i->reactor, (BDHCPClient_handler)dhcp_handler, o)) {
+    if (!BDHCPClient_Init(&o->dhcp, ifname, opts, o->i->reactor, (BDHCPClient_handler)dhcp_handler, o)) {
         ModuleLog(o->i, BLOG_ERROR, "BDHCPClient_Init failed");
         goto fail1;
     }