From: Ben Pfaff Date: Wed, 23 Jul 2008 21:30:59 +0000 (-0700) Subject: Enable secchan, ofp-discover to update /etc/resolv.conf. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5fed6531b9146c9d30222062b83d9403ee254e03;p=openvswitch Enable secchan, ofp-discover to update /etc/resolv.conf. This way it becomes possible to more reliably refer to the controller and the PKI server using hostnames. --- diff --git a/include/dhcp-client.h b/include/dhcp-client.h index 2039d23a..9019ff11 100644 --- a/include/dhcp-client.h +++ b/include/dhcp-client.h @@ -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 *); diff --git a/lib/dhcp-client.c b/lib/dhcp-client.c index 436e7545..1eee5e57 100644 --- a/lib/dhcp-client.c +++ b/lib/dhcp-client.c @@ -40,7 +40,9 @@ #include #include #include +#include #include +#include #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; +} /* DHCP protocol. */ diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index 7cf17695..03c516cf 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -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 diff --git a/secchan/secchan.c b/secchan/secchan.c index 0f8ac987..fb0eb41c 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -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" diff --git a/tests/test-dhcp-client.c b/tests/test-dhcp-client.c index 815ecfd8..285d99fb 100644 --- a/tests/test-dhcp-client.c +++ b/tests/test-dhcp-client.c @@ -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" diff --git a/utilities/ofp-discover.c b/utilities/ofp-discover.c index 5c78d439..7dfc56af 100644 --- a/utilities/ofp-discover.c +++ b/utilities/ofp-discover.c @@ -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) {