summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2013-02-01 01:33:45 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2013-02-01 01:33:45 +0000
commit19a793606451fcc605c05ca20bbd8662b0ec2fd9 (patch)
treeaaf459c9cb906b086c01cd38f0767035cb58c8d4 /sbin
parent8fb1ab30611285831c4c363f655a50adb5798f2f (diff)
Write out resolv.conf only if the default route is under the control
of the process binding the lease. Re-check the default route whenever a routing message arrives that might mean the default route has changed, and write out resolv.conf if appropriate. Reduces the chances that the name servers in resolv.conf are unreachable. Problem most eloquently explained, and solution suggested by beck@.
Diffstat (limited to 'sbin')
-rw-r--r--sbin/dhclient/dhclient.c28
-rw-r--r--sbin/dhclient/kroute.c86
2 files changed, 104 insertions, 10 deletions
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index e3779d07128..c2d0a5209e4 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhclient.c,v 1.217 2013/01/27 02:45:46 krw Exp $ */
+/* $OpenBSD: dhclient.c,v 1.218 2013/02/01 01:33:44 krw Exp $ */
/*
* Copyright 2004 Henning Brauer <henning@openbsd.org>
@@ -93,6 +93,7 @@ struct imsgbuf *unpriv_ibuf;
void sighdlr(int);
int findproto(char *, int);
struct sockaddr *get_ifa(char *, int);
+int resolv_conf_priority(int);
void usage(void);
int res_hnok(const char *dn);
char *option_as_string(unsigned int code, unsigned char *data, int len);
@@ -288,6 +289,15 @@ routehandler(void)
default:
break;
}
+
+ /* Something has happened. Try to write out the resolv.conf. */
+ if (client->active && client->active->resolv_conf &&
+ resolv_conf_priority(ifi->rdomain))
+ write_file("/etc/resolv.conf",
+ O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_EXLOCK,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0,
+ client->new->resolv_conf, strlen(client->new->resolv_conf));
+
return;
die:
@@ -783,14 +793,6 @@ bind_lease(void)
if (nameservers == NULL)
error("no memory for nameservers");
- client->new->resolv_conf = resolv_conf_contents(
- &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]);
- if (client->new->resolv_conf)
- write_file("/etc/resolv.conf",
- O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_EXLOCK,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0,
- client->new->resolv_conf, strlen(client->new->resolv_conf));
-
/*
* Add address and default route last, so we know when the binding
* is done by the RTM_NEWADDR message being received.
@@ -804,6 +806,14 @@ bind_lease(void)
add_default_route(ifi->rdomain, client->new->address, gateway);
}
+ client->new->resolv_conf = resolv_conf_contents(
+ &options[DHO_DOMAIN_NAME], &options[DHO_DOMAIN_NAME_SERVERS]);
+ if (client->new->resolv_conf)
+ write_file("/etc/resolv.conf",
+ O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_EXLOCK,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0,
+ client->new->resolv_conf, strlen(client->new->resolv_conf));
+
free(domainname);
free(nameservers);
diff --git a/sbin/dhclient/kroute.c b/sbin/dhclient/kroute.c
index 47b52503167..20bc4444633 100644
--- a/sbin/dhclient/kroute.c
+++ b/sbin/dhclient/kroute.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kroute.c,v 1.28 2013/01/22 06:02:52 krw Exp $ */
+/* $OpenBSD: kroute.c,v 1.29 2013/02/01 01:33:44 krw Exp $ */
/*
* Copyright 2012 Kenneth R Westerback <krw@openbsd.org>
@@ -701,3 +701,87 @@ priv_cleanup(struct imsg_cleanup *imsg)
dimsg.addr = imsg->addr;
priv_delete_address(&dimsg);
}
+
+int
+resolv_conf_priority(int domain)
+{
+ struct sockaddr *rti_info[RTAX_MAX];
+ int mib[7];
+ size_t needed;
+ char *lim, *buf, *next, *routelabel;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa;
+ struct sockaddr_in *sa_in;
+ struct sockaddr_rtlabel *sa_rl;
+ int i, priority, mypriority;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_FLAGS;
+ mib[5] = RTF_GATEWAY;
+ mib[6] = domain;
+
+ if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) {
+ if (domain != 0 && errno == EINVAL)
+ return (1);
+ error("sysctl size of routes: %s", strerror(errno));
+ }
+
+ if (needed == 0)
+ return (1);
+
+ if ((buf = malloc(needed)) == NULL)
+ error("no memory for sysctl routes");
+
+ if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1)
+ error("sysctl retrieval of routes: %s", strerror(errno));
+
+ if (asprintf(&routelabel, "DHCLIENT %d", (int)getpid()) == -1)
+ error("recreating route label: %s", strerror(errno));
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+ priority = mypriority = INT_MAX;
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (rtm->rtm_version != RTM_VERSION)
+ continue;
+ if ((rtm->rtm_flags & RTF_UP) == 0)
+ continue;
+ if (rtm->rtm_flags & RTF_REJECT)
+ continue;
+
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+ memset(rti_info, 0, sizeof(rti_info));
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len));
+ }
+ }
+
+ sa_in = (struct sockaddr_in *)rti_info[RTAX_DST];
+ if (sa_in == NULL || sa_in->sin_addr.s_addr != INADDR_ANY)
+ continue;
+ sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
+ if (sa_in == NULL || sa_in->sin_addr.s_addr != INADDR_ANY)
+ continue;
+
+ sa_rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
+ if (sa_rl && strcmp(routelabel, sa_rl->sr_label) == 0) {
+ if (rtm->rtm_priority < mypriority)
+ mypriority = rtm->rtm_priority;
+ } else if (rtm->rtm_priority < priority)
+ priority = rtm->rtm_priority;
+ }
+
+ free(buf);
+ free(routelabel);
+
+ return (mypriority < priority);
+}