Enable secchan, ofp-discover to update /etc/resolv.conf.
[openvswitch] / lib / dhcp-client.c
index f6cc67bf8e41bc81b80d2257853ba687eb7fd49b..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. */
 
@@ -439,11 +521,12 @@ dhcp_receive(struct dhclient *cli, unsigned int msgs, struct dhcp_msg *msg)
         if (msg->type < 0 || msg->type > 31 || !((1u << msg->type) & msgs)) {
             VLOG_DBG("received unexpected %s in %s state: %s",
                      dhcp_type_name(msg->type), state_name(cli->state),
-                     dhcp_msg_to_string(msg, &cli->s));
+                     dhcp_msg_to_string(msg, false, &cli->s));
         } else if (msg->xid != cli->xid) {
             VLOG_DBG("ignoring %s with xid != %08"PRIx32" in %s state: %s",
                      dhcp_type_name(msg->type), msg->xid,
-                     state_name(cli->state), dhcp_msg_to_string(msg, &cli->s));
+                     state_name(cli->state),
+                     dhcp_msg_to_string(msg, false, &cli->s));
         } else {
             return true;
         }
@@ -457,18 +540,18 @@ validate_offered_options(struct dhclient *cli, const struct dhcp_msg *msg)
 {
     uint32_t lease, netmask;
     if (!dhcp_msg_get_secs(msg, DHCP_CODE_LEASE_TIME, 0, &lease)) {
-        VLOG_WARN("%s lacks lease time: %s",
-                  dhcp_type_name(msg->type), dhcp_msg_to_string(msg, &cli->s));
+        VLOG_WARN("%s lacks lease time: %s", dhcp_type_name(msg->type),
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (!dhcp_msg_get_ip(msg, DHCP_CODE_SUBNET_MASK, 0, &netmask)) {
-        VLOG_WARN("%s lacks netmask: %s",
-                  dhcp_type_name(msg->type), dhcp_msg_to_string(msg, &cli->s));
+        VLOG_WARN("%s lacks netmask: %s", dhcp_type_name(msg->type),
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (lease < MIN_ACCEPTABLE_LEASE) {
         VLOG_WARN("Ignoring %s with %"PRIu32"-second lease time: %s",
                   dhcp_type_name(msg->type), lease,
-                  dhcp_msg_to_string(msg, &cli->s));
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (cli->validate_offer && !cli->validate_offer(msg, cli->aux)) {
         VLOG_DBG("client validation hook refused offer: %s",
-                 dhcp_msg_to_string(msg, &cli->s));
+                 dhcp_msg_to_string(msg, false, &cli->s));
     } else {
         return true;
     }
@@ -492,11 +575,12 @@ dhclient_run_SELECTING(struct dhclient *cli)
         if (!dhcp_msg_get_ip(&msg, DHCP_CODE_SERVER_IDENTIFIER,
                              0, &cli->server_ip)) {
             VLOG_WARN("DHCPOFFER lacks server identifier: %s",
-                      dhcp_msg_to_string(&msg, &cli->s));
+                      dhcp_msg_to_string(&msg, false, &cli->s));
             continue;
         }
 
-        VLOG_DBG("accepting DHCPOFFER: %s", dhcp_msg_to_string(&msg, &cli->s));
+        VLOG_DBG("accepting DHCPOFFER: %s",
+                 dhcp_msg_to_string(&msg, false, &cli->s));
         cli->ipaddr = msg.yiaddr;
         state_transition(cli, S_REQUESTING);
         break;
@@ -550,7 +634,7 @@ receive_ack(struct dhclient *cli)
             cli->router = INADDR_ANY;
         }
         state_transition(cli, S_BOUND);
-        VLOG_DBG("Bound: %s", dhcp_msg_to_string(&msg, &cli->s));
+        VLOG_DBG("Bound: %s", dhcp_msg_to_string(&msg, false, &cli->s));
         return true;
     }
 }
@@ -640,8 +724,10 @@ state_transition(struct dhclient *cli, enum dhclient_state state)
 {
     bool was_bound = dhclient_is_bound(cli);
     bool am_bound;
-    VLOG_DBG("entering %s", state_name(state));
-    cli->state = state;
+    if (cli->state != state) {
+        VLOG_DBG("entering %s", state_name(state)); 
+        cli->state = state;
+    }
     cli->state_entered = time(0);
     cli->retransmit = cli->delay = 0;
     am_bound = dhclient_is_bound(cli);
@@ -698,7 +784,6 @@ dhclient_msg_init(struct dhclient *cli, enum dhcp_msg_type type,
     msg->secs = cli->secs;
     msg->type = type;
     memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
-    
 }
 
 static unsigned int
@@ -729,7 +814,7 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
         buffer_clear(&b);
         error = netdev_recv(cli->netdev, &b);
         if (error) {
-            break;
+            goto drained;
         }
 
         flow_extract(&b, 0, &flow);
@@ -758,12 +843,13 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
         buffer_pull(&b, b.l7 - b.data);
         error = dhcp_parse(msg, &b);
         if (!error) {
-            VLOG_DBG("received %s", dhcp_msg_to_string(msg, &cli->s));
+            VLOG_DBG("received %s", dhcp_msg_to_string(msg, false, &cli->s));
             buffer_uninit(&b);
             return true;
         }
     }
     netdev_drain(cli->netdev);
+drained:
     buffer_uninit(&b);
     return false;
 }
@@ -832,7 +918,7 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg)
      * frame to have to be discarded or fragmented if it travels over a regular
      * Ethernet at some point.  1500 bytes should be enough for anyone. */
     if (b.size <= ETH_TOTAL_MAX) {
-        VLOG_DBG("sending %s", dhcp_msg_to_string(msg, &cli->s));
+        VLOG_DBG("sending %s", dhcp_msg_to_string(msg, false, &cli->s));
         error = netdev_send(cli->netdev, &b);
         if (error) {
             VLOG_ERR("send failed on %s: %s",