summaryrefslogtreecommitdiffstats
path: root/configure.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2006-11-27 20:23:22 +0000
committerRoy Marples <roy@marples.name>2006-11-27 20:23:22 +0000
commitcced195e703c548e7375727ea91bbdcd76aae517 (patch)
tree6c97679eedd1d687263f7d65742b02bedd93895f /configure.c
downloaddhcpcd-cced195e703c548e7375727ea91bbdcd76aae517.tar.xz
Add dhcpcd-3 re-write
Diffstat (limited to 'configure.c')
-rw-r--r--configure.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/configure.c b/configure.c
new file mode 100644
index 00000000..ff6cecf4
--- /dev/null
+++ b/configure.c
@@ -0,0 +1,527 @@
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 2005 - 2006 Roy Marples <uberlord@gentoo.org>
+ *
+ * dhcpcd is an RFC2131 compliant DHCP client daemon.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <arpa/inet.h>
+
+#ifdef __linux__
+#include <netinet/ether.h>
+#endif
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "dhcp.h"
+#include "interface.h"
+#include "dhcpcd.h"
+#include "pathnames.h"
+#include "logger.h"
+#include "socket.h"
+
+static char *cleanmetas (char *cstr)
+{
+ if (!cstr)
+ return "";
+
+ register char *c = cstr;
+
+ do
+ if (*c == 39)
+ *c = ' ';
+ while (*c++);
+
+ return cstr;
+}
+
+void exec_script (char *script, char *infofile, char *arg)
+{
+ if (!script || !infofile || !arg)
+ return;
+
+ struct stat buf;
+ if (stat (script, &buf))
+ {
+ if (strcmp (script, DEFAULT_SCRIPT))
+ logger (LOG_ERR, "`%s': %s", script, strerror (ENOENT));
+ return;
+ }
+
+ char *argc[4];
+
+ argc[0] = script;
+ argc[1] = infofile;
+ argc[2] = arg;
+ argc[3] = NULL;
+ logger (LOG_DEBUG, "exec \"%s %s %s\"", script, infofile, arg);
+
+ /* We don't wait for the user script to finish - do we trust it? */
+ /* Don't use vfork as we lose our memory when dhcpcd exits
+ causing the script to fail */
+ pid_t pid;
+ if ((pid = fork ()) == 0)
+ {
+ if (execv (script, argc))
+ logger (LOG_ERR, "error executing \"%s %s %s\": %m",
+ argc[0], argc[1], argc[2]);
+ exit (0);
+ }
+ else if (pid == -1)
+ logger (LOG_ERR, "fork: %s", strerror (errno));
+}
+
+static int make_resolv (char *ifname, dhcp_t *dhcp, int wait)
+{
+ FILE *f;
+ struct stat buf;
+ char resolvconf[PATH_MAX];
+ address_t *address;
+
+ if (!stat ("/sbin/resolvconf", &buf))
+ {
+ logger (LOG_DEBUG, "sending DNS information to resolvconf");
+ snprintf (resolvconf, PATH_MAX, "/sbin/resolvconf -a %s", ifname);
+ f = popen (resolvconf, "w");
+
+ if (!f)
+ logger (LOG_ERR, "popen: %m");
+ }
+ else
+ {
+ if (! (f = fopen(RESOLVFILE, "w")))
+ logger (LOG_ERR, "fopen `%s': %m", RESOLVFILE);
+ }
+
+ if (f)
+ {
+ fprintf (f, "# Generated by dhcpcd for interface %s\n", ifname);
+ if (dhcp->dnssearch)
+ fprintf (f, "search %s\n", dhcp->dnssearch);
+ else if (dhcp->dnsdomain) {
+ fprintf (f, "search %s\n", dhcp->dnsdomain);
+ }
+
+ for (address = dhcp->dnsservers; address; address = address->next)
+ fprintf (f, "nameserver %s\n", inet_ntoa (address->address));
+
+ if (resolvconf)
+ {
+ pclose (f);
+ logger (LOG_DEBUG, "resolvconf completed");
+ }
+ else
+ fclose (f);
+ }
+ else
+ return -1;
+
+ /* Refresh the local resolver */
+ res_init ();
+ return 0;
+}
+
+static void restore_resolv(char *ifname)
+{
+ struct stat buf;
+
+ if (stat ("/sbin/resolvconf", &buf))
+ return;
+
+ logger (LOG_DEBUG, "removing information from resolvconf");
+
+ char *argc[4];
+
+ argc[0] = "/sbin/resolvconf";
+ argc[1] = "-d";
+ argc[2] = ifname;
+ argc[3] = NULL;
+
+ /* Don't wait around here as we should only be called when
+ dhcpcd is closing down and something may do a kill -9
+ if we take too long */
+ /* Don't use vfork as we lose our memory when dhcpcd exits
+ causing the script to fail */
+ pid_t pid;
+ if ((pid = fork ()) == 0)
+ {
+ if (execve (argc[0], argc, NULL))
+ logger (LOG_ERR, "error executing \"%s %s %s\": %m",
+ argc[0], argc[1], argc[2]);
+ exit (0);
+ }
+ else if (pid == -1)
+ logger (LOG_ERR, "fork: %s", strerror (errno));
+}
+
+static int make_ntp (char *ifname, dhcp_t *dhcp)
+{
+ FILE *f;
+ address_t *address;
+ char *a;
+
+ if (! (f = fopen(NTPFILE, "w")))
+ {
+ logger (LOG_ERR, "fopen `%s': %m", NTPFILE);
+ return -1;
+ }
+
+ fprintf (f, "# Generated by dhcpcd for interface %s\n", ifname);
+ fprintf (f, "restrict default noquery notrust nomodify\n");
+ fprintf (f, "restrict 127.0.0.1\n");
+
+ for (address = dhcp->ntpservers; address; address = address->next)
+ {
+ a = inet_ntoa (address->address);
+ fprintf (f, "restrict %s nomodify notrap noquery\nserver %s\n", a, a);
+ }
+
+ fprintf (f, "driftfile " NTPDRIFTFILE "\n");
+ fprintf (f, "logfile " NTPLOGFILE "\n");
+ fclose (f);
+ return 0;
+}
+
+static int make_nis (char *ifname, dhcp_t *dhcp)
+{
+ FILE *f;
+ address_t *address;
+ char prefix[256] = {0};
+
+ if (! (f = fopen(NISFILE, "w")))
+ {
+ logger (LOG_ERR, "fopen `%s': %m", NISFILE);
+ return -1;
+ }
+
+ fprintf (f, "# Generated by dhcpcd for interface %s\n", ifname);
+ if (dhcp->nisdomain)
+ {
+ setdomainname (dhcp->nisdomain, strlen (dhcp->nisdomain));
+
+ if (dhcp->nisservers)
+ snprintf (prefix, sizeof (prefix), "domain %s server", dhcp->nisdomain);
+ else
+ fprintf (f, "domain %s broadcast\n", dhcp->nisdomain);
+ }
+ else
+ sprintf(prefix, "ypserver %c", '\0');
+
+ for (address = dhcp->nisservers; address; address = address->next)
+ fprintf (f, "%s%s\n", prefix, inet_ntoa (address->address));
+
+ fclose (f);
+
+ return 0;
+}
+
+static int write_info(interface_t *iface, dhcp_t *dhcp)
+{
+ FILE *f;
+ route_t *route;
+ address_t *address;
+
+ if ((f = fopen (iface->infofile, "w")) == NULL)
+ {
+ logger (LOG_ERR, "fopen `%s': %m", iface->infofile);
+ return -1;
+ }
+
+ fprintf (f, "IPADDR=%s\n", inet_ntoa (dhcp->address));
+ fprintf (f, "NETMASK=%s\n", inet_ntoa (dhcp->netmask));
+ fprintf (f, "BROADCAST=%s\n", inet_ntoa (dhcp->broadcast));
+ if (dhcp->mtu > 0)
+ fprintf (f, "MTU=%d\n", dhcp->mtu);
+
+ if (dhcp->routes)
+ {
+ fprintf (f, "ROUTES='");
+ for (route = dhcp->routes; route; route = route->next)
+ {
+ fprintf (f, "%s", inet_ntoa (route->destination));
+ fprintf (f, ",%s", inet_ntoa (route->netmask));
+ fprintf (f, ",%s", inet_ntoa (route->gateway));
+ if (route->next)
+ fprintf (f, " ");
+ }
+ fprintf (f, "'\n");
+ }
+
+ if (dhcp->hostname)
+ fprintf (f, "HOSTNAME='%s'\n",cleanmetas (dhcp->hostname));
+
+ if (dhcp->dnsdomain)
+ fprintf (f, "DNSDOMAIN='%s'\n", cleanmetas (dhcp->dnsdomain));
+
+ if (dhcp->dnssearch)
+ fprintf (f, "DNSSEARCH='%s'\n", cleanmetas (dhcp->dnssearch));
+
+ if (dhcp->dnsservers)
+ {
+ fprintf (f, "DNSSERVERS='");
+ for (address = dhcp->dnsservers; address; address = address->next)
+ {
+ fprintf (f, "%s", inet_ntoa (address->address));
+ if (address->next)
+ fprintf (f, " ");
+ }
+ fprintf (f, "'\n");
+ }
+
+ if (dhcp->fqdn)
+ {
+ fprintf (f, "FQDNFLAGS=%u\n", dhcp->fqdn->flags);
+ fprintf (f, "FQDNRCODE1=%u\n", dhcp->fqdn->r1);
+ fprintf (f, "FQDNRCODE2=%u\n", dhcp->fqdn->r2);
+ fprintf (f, "FQDNHOSTNAME='%s'\n", dhcp->fqdn->name);
+ }
+
+ if (dhcp->ntpservers)
+ {
+ fprintf (f, "NTPSERVERS='");
+ for (address = dhcp->ntpservers; address; address = address->next)
+ {
+ fprintf (f, "%s", inet_ntoa (address->address));
+ if (address->next)
+ fprintf (f, " ");
+ }
+ fprintf (f, "'\n");
+ }
+
+ if (dhcp->nisdomain)
+ fprintf (f, "NISDOMAIN='%s'\n", cleanmetas (dhcp->nisdomain));
+
+ if (dhcp->nisservers)
+ {
+ fprintf (f, "NISSERVERS='");
+ for (address = dhcp->nisservers; address; address = address->next)
+ {
+ fprintf (f, "%s", inet_ntoa (address->address));
+ if (address->next)
+ fprintf (f, " ");
+ }
+ fprintf (f, "'\n");
+ }
+
+ if (dhcp->rootpath)
+ fprintf (f, "ROOTPATH='%s'\n", cleanmetas (dhcp->rootpath));
+
+ fprintf (f, "DHCPSID=%s\n", inet_ntoa (dhcp->serveraddress));
+ fprintf (f, "DHCPCHADDR=%s\n", ether_ntoa (&iface->ethernet_address));
+ fprintf (f, "DHCPSNAME='%s'\n", cleanmetas (dhcp->servername));
+ fprintf (f, "LEASETIME=%u\n", dhcp->leasetime);
+ fprintf (f, "RENEWALTIME=%u\n", dhcp->renewaltime);
+ fprintf (f, "REBINDTIME=%u\n", dhcp->rebindtime);
+ fprintf (f, "INTERFACE='%s'\n", iface->name);
+ fprintf (f, "CLASSID='%s'\n", cleanmetas (dhcp->classid));
+ fprintf (f, "CLIENTID='%s'\n", cleanmetas (dhcp->clientid));
+
+ fclose (f);
+ return 0;
+}
+
+int configure (options_t *options, interface_t *iface, dhcp_t *dhcp)
+{
+ route_t *route = NULL;
+ route_t *new_route = NULL;
+ route_t *old_route = NULL;
+ struct hostent *he = NULL;
+ char *newhostname[HOSTNAME_MAX_LEN] = {0};
+ char curhostname[HOSTNAME_MAX_LEN] = {0};
+ char *dname = NULL;
+ int dnamel = 0;
+
+ if (!options || !iface || !dhcp)
+ return -1;
+
+ /* Remove old routes
+ Always do this as the interface may have >1 address not added by us
+ so the routes we added may still exist */
+ if (iface->previous_routes)
+ {
+ for (route = iface->previous_routes; route; route = route->next)
+ if (route->destination.s_addr || options->dogateway)
+ {
+ int have = 0;
+ if (dhcp->address.s_addr != 0)
+ for (new_route = dhcp->routes; new_route; new_route = new_route->next)
+ if (new_route->destination.s_addr == route->destination.s_addr
+ && new_route->netmask.s_addr == route->netmask.s_addr
+ && new_route->gateway.s_addr == route->gateway.s_addr)
+ {
+ have = 1;
+ break;
+ }
+ if (! have)
+ del_route (iface->name, route->destination, route->netmask,
+ route->gateway, options->metric);
+ }
+ }
+
+ /* If we don't have an address, then return */
+ if (dhcp->address.s_addr == 0)
+ {
+ if (iface->previous_routes)
+ {
+ free_route (iface->previous_routes);
+ iface->previous_routes = NULL;
+ }
+
+ if (iface->previous_address.s_addr != 0)
+ del_address (iface->name, iface->previous_address);
+ memset (&iface->previous_address, 0, sizeof (struct in_addr));
+
+ restore_resolv (iface->name);
+
+ /* We currently don't have a resolvconf style programs for ntp/nis */
+ exec_script (options->script, iface->infofile, "down");
+ return 0;
+ }
+
+ if (add_address (iface->name, dhcp->address, dhcp->netmask,
+ dhcp->broadcast) < 0 && errno != EEXIST)
+ return -1;
+
+ /* Now delete the old address if different */
+ if (iface->previous_address.s_addr != dhcp->address.s_addr
+ && iface->previous_address.s_addr != 0)
+ del_address (iface->name, iface->previous_address);
+
+#ifdef __linux__
+ /* On linux, we need to change the subnet route to have our metric. */
+ if (iface->previous_address.s_addr != dhcp->address.s_addr
+ && options->metric > 0)
+ {
+ struct in_addr td;
+ struct in_addr tg;
+ memset (&td, 0, sizeof (td));
+ memset (&tg, 0, sizeof (tg));
+ td.s_addr = dhcp->address.s_addr & dhcp->netmask.s_addr;
+ add_route (iface->name, td, dhcp->netmask, tg, options->metric);
+ del_route (iface->name, td, dhcp->netmask, tg, 0);
+ }
+#endif
+
+ /* Remember added routes */
+ if (dhcp->routes)
+ {
+ route_t *new_routes = NULL;
+
+ for (route = dhcp->routes; route; route = route->next)
+ {
+ int remember = add_route (iface->name, route->destination,
+ route->netmask, route->gateway,
+ options->metric);
+ /* If we failed to add the route, we may have already added it
+ ourselves. If so, remember it again. */
+ if (remember < 0)
+ for (old_route = iface->previous_routes; old_route;
+ old_route = old_route->next)
+ if (old_route->destination.s_addr == route->destination.s_addr
+ && old_route->netmask.s_addr == route->netmask.s_addr
+ && old_route->gateway.s_addr == route->gateway.s_addr)
+ {
+ remember = 1;
+ break;
+ }
+
+ if (remember >= 0)
+ {
+ if (! new_routes)
+ {
+ new_routes = xmalloc (sizeof (route_t));
+ memset (new_routes, 0, sizeof (route_t));
+ new_route = new_routes;
+ }
+ else
+ {
+ new_route->next = xmalloc (sizeof (route_t));
+ new_route = new_route->next;
+ }
+ memcpy (new_route, route, sizeof (route_t));
+ new_route -> next = NULL;
+ }
+ }
+
+ if (iface->previous_routes)
+ free_route (iface->previous_routes);
+
+ iface->previous_routes = new_routes;
+ }
+
+ if (options->dodns && dhcp->dnsservers)
+ make_resolv(iface->name, dhcp, (options->dohostname && !dhcp->hostname));
+
+ if (options->dontp && dhcp->ntpservers)
+ make_ntp(iface->name, dhcp);
+
+ if (options->donis && (dhcp->nisservers || dhcp->nisdomain))
+ make_nis(iface->name, dhcp);
+
+ /* Now we have made a resolv.conf we can obtain a hostname if we need one */
+ if (options->dohostname && !dhcp->hostname)
+ {
+ he = gethostbyaddr (inet_ntoa (dhcp->address),
+ sizeof (struct in_addr), AF_INET);
+ if (he)
+ {
+ dname = he->h_name;
+ while (*dname > 32)
+ dname++;
+ dnamel = dname - he->h_name;
+ memcpy (newhostname, he->h_name, dnamel);
+ newhostname[dnamel] = 0;
+ }
+ }
+
+ gethostname (curhostname, sizeof (curhostname));
+
+ if (options->dohostname || !strlen (curhostname)
+ || !strcmp (curhostname, "(none)") || !strcmp (curhostname, "localhost"))
+ {
+ if (dhcp->hostname)
+ strcpy ((char *) newhostname, dhcp->hostname);
+
+ sethostname ((char *) newhostname, strlen ((char *) newhostname));
+ logger (LOG_INFO, "setting hostname to %s", newhostname);
+ }
+
+ write_info (iface, dhcp);
+
+ if (iface->previous_address.s_addr != dhcp->address.s_addr)
+ {
+ memcpy (&iface->previous_address,
+ &dhcp->address, sizeof (struct in_addr));
+ exec_script (options->script, iface->infofile, "new");
+ }
+ else
+ exec_script (options->script, iface->infofile, "up");
+
+ return 0;
+}
+