Enable secchan, ofp-discover to update /etc/resolv.conf.
authorBen Pfaff <blp@nicira.com>
Wed, 23 Jul 2008 21:30:59 +0000 (14:30 -0700)
committerBen Pfaff <blp@nicira.com>
Wed, 23 Jul 2008 21:30:59 +0000 (14:30 -0700)
This way it becomes possible to more reliably refer to the controller
and the PKI server using hostnames.

include/dhcp-client.h
lib/dhcp-client.c
secchan/secchan.8.in
secchan/secchan.c
tests/test-dhcp-client.c
utilities/ofp-discover.c

index 2039d23a31767bcf517bcf6621f5bda1d26aa7da..9019ff116255de4b44ab7e219f9cc535ab810aa2 100644 (file)
@@ -58,6 +58,7 @@ uint32_t dhclient_get_router(const struct dhclient *);
 const struct dhcp_msg *dhclient_get_config(const struct dhclient *);
 
 int dhclient_configure_netdev(struct dhclient *);
+int dhclient_update_resolv_conf(struct dhclient *);
 
 void dhclient_run(struct dhclient *);
 void dhclient_wait(struct dhclient *);
index 436e75455ec5e4b807bc01b4f6a9daf90d31e16f..1eee5e577c40d3f4d83181a27e9de1048dac1082 100644 (file)
@@ -40,7 +40,9 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 #include <time.h>
+#include <unistd.h>
 #include "buffer.h"
 #include "csum.h"
 #include "dhcp.h"
@@ -376,6 +378,86 @@ dhclient_configure_netdev(struct dhclient *cli)
 
     return error;
 }
+
+/* If 'cli' is bound and the binding includes DNS domain parameters, updates
+ * /etc/resolv.conf will be updated to match the received parameters.  Returns
+ * 0 if successful, otherwise a positive errno value. */
+int
+dhclient_update_resolv_conf(struct dhclient *cli)
+{
+    uint32_t dns_server;
+    char *domain_name;
+    bool has_domain_name;
+    char new_name[128];
+    FILE *old, *new;
+    int i;
+
+    if (!dhclient_is_bound(cli)) {
+        return 0;
+    }
+    if (!dhcp_msg_get_ip(cli->binding, DHCP_CODE_DNS_SERVER, 0, &dns_server)) {
+        VLOG_DBG("binding does not include any DNS servers");
+        return 0;
+    }
+
+    sprintf(new_name, "/etc/resolv.conf.tmp%ld", (long int) getpid());
+    new = fopen(new_name, "w");
+    if (!new) {
+        VLOG_WARN("%s: create: %s", new_name, strerror(errno));
+        return errno;
+    }
+
+    domain_name = dhcp_msg_get_string(cli->binding, DHCP_CODE_DOMAIN_NAME);
+    has_domain_name = domain_name != NULL;
+    if (domain_name) {
+        if (strspn(domain_name, "-_.0123456789abcdefghijklmnopqrstuvwxyz"
+                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(domain_name)) {
+            fprintf(new, "domain %s\n", domain_name);
+        } else {
+            VLOG_WARN("ignoring invalid domain name %s", domain_name);
+            has_domain_name = false;
+        }
+    } else {
+        VLOG_DBG("binding does not include domain name");
+    }
+    free(domain_name);
+
+    for (i = 0; dhcp_msg_get_ip(cli->binding, DHCP_CODE_DNS_SERVER,
+                                i, &dns_server); i++) {
+        fprintf(new, "nameserver "IP_FMT"\n", IP_ARGS(&dns_server));
+    }
+
+    old = fopen("/etc/resolv.conf", "r");
+    if (old) {
+        char line[128];
+
+        while (fgets(line, sizeof line, old)) {
+            char *kw = xmemdup0(line, strcspn(line, " \t\r\n"));
+            if (strcmp(kw, "nameserver")
+                && (!has_domain_name
+                    || (strcmp(kw, "domain") && strcmp(kw, "search")))) {
+                fputs(line, new);
+            }
+            free(kw);
+        }
+        fclose(old);
+    } else {
+        VLOG_DBG("/etc/resolv.conf: open: %s", strerror(errno));
+    }
+
+    if (fclose(new) < 0) {
+        VLOG_WARN("%s: close: %s", new_name, strerror(errno));
+        return errno;
+    }
+
+    if (rename(new_name, "/etc/resolv.conf") < 0) {
+        VLOG_WARN("failed to rename %s to /etc/resolv.conf: %s",
+                  new_name, strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
 \f
 /* DHCP protocol. */
 
index 7cf17695e19bb55b4fbd203dac9188dcaeda1da8..03c516cf7e726204ec594e18837ffac006134452 100644 (file)
@@ -184,6 +184,24 @@ controller location string, as if it begins with \fB^\fR.
 
 When controller discovery is not performed, this option has no effect.
 
+.TP
+\fB--no-resolv-conf\fR
+When \fBsecchan\fR performs controller discovery (see \fBCONTACTING
+THE CONTROLLER\fR, above, for more information about controller
+discovery), by default it overwrites the system's
+\fB/etc/resolv.conf\fR with domain information and DNS servers
+obtained via DHCP.  If the location of the controller is specified
+using a hostname, rather than an IP address, and the network's DNS
+servers ever change, this behavior is essential.  But because it also
+interferes with any administrator or process that manages
+\fB/etc/resolv.conf\fR, when this option is specified, \fBsecchan\fR
+will not modify \fB/etc/resolv.conf\fR.
+
+\fBsecchan\fR will only modify \fBresolv.conf\fR if the DHCP response
+that it receives specifies one or more DNS servers.
+
+When controller discovery is not performed, this option has no effect.
+
 .TP
 \fB-f\fR, \fB--fail=\fR[\fBopen\fR|\fBclosed\fR]
 The controller is, ordinarily, responsible for setting up all flows on
index 0f8ac9870b4ce5c506f2c7287ff086e8618e4969..fb0eb41cd661bba968bd80f2e83ec19140075164 100644 (file)
@@ -133,6 +133,10 @@ static struct dhclient *dhcp;
 static const char *accept_controller_re;
 static regex_t accept_controller_regex;
 
+/* --no-resolv-conf: Update resolv.conf upon successful controller
+ * discovery? */
+static bool update_resolv_conf = true;
+
 static void parse_options(int argc, char *argv[]);
 static void usage(void) NO_RETURN;
 
@@ -298,8 +302,12 @@ main(int argc, char *argv[])
             dhclient_run(dhcp);
             if (dhclient_changed(dhcp)) {
                 dhclient_configure_netdev(dhcp);
-                free(controller_name);
+                if (update_resolv_conf) {
+                    dhclient_update_resolv_conf(dhcp);
+                }
+
                 if (dhclient_is_bound(dhcp)) {
+                    free(controller_name);
                     controller_name = dhcp_msg_get_string(
                         dhclient_get_config(dhcp),
                         DHCP_CODE_OFP_CONTROLLER_VCONN);
@@ -309,6 +317,7 @@ main(int argc, char *argv[])
                 } else if (controller_name) {
                     VLOG_WARN("%s: discover controller no longer available",
                               controller_name);
+                    free(controller_name);
                     controller_name = NULL;
                     rconn_disconnect(remote_rconn);
                 }
@@ -679,12 +688,14 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
+        OPT_NO_RESOLV_CONF,
         OPT_INACTIVITY_PROBE,
         OPT_MAX_IDLE,
         OPT_MAX_BACKOFF
     };
     static struct option long_options[] = {
         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
+        {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
         {"fail",        required_argument, 0, 'f'},
         {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
@@ -715,6 +726,10 @@ parse_options(int argc, char *argv[])
                                     : xasprintf("^%s", optarg));
             break;
 
+        case OPT_NO_RESOLV_CONF:
+            update_resolv_conf = false;
+            break;
+
         case 'f':
             if (!strcmp(optarg, "open")) {
                 fail_mode = FAIL_OPEN;
@@ -802,8 +817,10 @@ usage(void)
            "omitted, then secchan performs controller autodiscovery.\n",
            program_name, program_name);
     vconn_usage(true, true);
-    printf("\nNetworking options:\n"
+    printf("\nController discovery options:\n"
            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
+           "  --no-resolv-conf        do not update /etc/resolv.conf\n"
+           "\nNetworking options:\n"
            "  -f, --fail=open|closed  when controller connection fails:\n"
            "                            closed: drop all packets\n"
            "                            open (default): act as learning switch\n"
index 815ecfd838bfd662e465638233845d60d63c5739..285d99fb8eff417a34c33e03d5ba2a648dbb7d19 100644 (file)
@@ -53,6 +53,9 @@ static struct in_addr request_ip;
  * vendor class string is included. */
 static const char *vendor_class;
 
+/* --no-resolv-conf: Update /etc/resolv.conf to match DHCP reply? */
+static bool update_resolv_conf = true;
+
 static void parse_options(int argc, char *argv[]);
 static void usage(void);
 static void release(void *cli_);
@@ -88,6 +91,9 @@ main(int argc, char *argv[])
         dhclient_run(cli);
         if (dhclient_changed(cli)) {
             dhclient_configure_netdev(cli);
+            if (update_resolv_conf) {
+                dhclient_update_resolv_conf(cli);
+            }
         }
         dhclient_wait(cli);
         fatal_signal_unblock();
@@ -118,11 +124,13 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_REQUEST_IP = UCHAR_MAX + 1,
-        OPT_VENDOR_CLASS
+        OPT_VENDOR_CLASS,
+        OPT_NO_RESOLV_CONF
     };
     static struct option long_options[] = {
         {"request-ip",  required_argument, 0, OPT_REQUEST_IP },
         {"vendor-class", required_argument, 0, OPT_VENDOR_CLASS },
+        {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
@@ -149,6 +157,10 @@ parse_options(int argc, char *argv[])
             vendor_class = optarg;
             break;
 
+        case OPT_NO_RESOLV_CONF:
+            update_resolv_conf = false;
+            break;
+
         case 'h':
             usage();
 
@@ -181,6 +193,7 @@ usage(void)
            "                          do not request a specific IP)\n"
            "  --vendor-class=STRING   use STRING as vendor class (default:\n"
            "                          none); use OpenFlow to imitate secchan\n"
+           "  --no-resolv-conf        do not update /etc/resolv.conf\n"
            "\nOther options:\n"
            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
            "  -v, --verbose           set maximum verbosity level\n"
index 5c78d439baa67f20629894bb034fbfa417b60632..7dfc56af06c4a8653353da19ccd26e99e42826d4 100644 (file)
@@ -144,6 +144,7 @@ main(int argc, char *argv[])
                 /* Configure network device. */
                 if (!exit_without_bind) {
                     dhclient_configure_netdev(iface->dhcp);
+                    dhclient_update_resolv_conf(iface->dhcp);
                 }
 
                 if (is_bound) {