--- /dev/null
+dhcpcd-3.0.0
+A complete rewrite by Roy Marples <uberlord@gentoo.org>
+
+Functional changes since version 2:
+We now support FreeBSD as well as Linux.
+RFC 3004 User Class support has been added.
+RFC 3442 Classless Static Routes support has been added
+(thanks to apexman for helping).
+Options -r, -e, -v, -w, -B, -C, -D, -L, -S have been dropped.
+-a now means "do arp" instead of "don't arp".
+-o has been dropped, but we never bring down the interface anymore.
+IP address is now required for the -s option.
+-G no longer takes an IP address as an replacement gateway.
+The .cache file is no longer created or used.
+Default script is now /etc/dhcpcd.sh instead of /etc/dhcpc/dhcpcd.exe.
+The .info file has changed slightly to better support mulitple entries with
+more than one entity, such as route now having network,genmask and gateway.
+We no longer create and restore .sv files as it's up to the interface
+manager to restore them correctly as >1 dhcp client may be running.
+
+For ChangeLog for prior versions can be found in their tarballs.
--- /dev/null
+# Should work for both GNU make and BSD mke
+
+VERSION = 3.0.0_alpha3
+
+CFLAGS ?= -Wall -O2 -pedantic -std=gnu99
+
+DESTDIR =
+SBINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESDIR)/usr/share/man
+
+SBIN_TARGETS = dhcpcd
+MAN8_TARGETS = dhcpcd.8
+TARGET = $(SBIN_TARGETS)
+
+dhcpcd_H = version.h
+dhcpcd_OBJS = arp.o client.o common.o configure.o dhcp.o dhcpcd.o \
+ interface.o logger.o signals.o socket.o
+
+dhcpcd: $(dhcpcd_H) $(dhcpcd_OBJS)
+ $(CC) $(LDFLAGS) $(dhcpcd_OBJS) -o dhcpcd
+
+version.h:
+ echo '#define VERSION "$(VERSION)"' > version.h
+
+$(dhcpcd_OBJS):
+ $(CC) $(CFLAGS) -c $*.c
+
+all: $(TARGET)
+
+install: $(TARGET)
+ $(INSTALL) -m 0755 -d $(SBINDIR)
+ $(INSTALL) -m 0755 $(SBIN_TARGETS) $(SBINDIR)
+ $(INSTALL) -m 0755 -d $(MANDIR)/man8
+ $(INSTALL) -m 0755 $(MAN8_TARGETS) $(MANDIR)/man8
+
+clean:
+ rm -f $(TARGET) $(dhcpcd_H) *.o *~
+
+dist:
+ $(INSTALL) -m 0755 -d /tmp/dhcpcd-$(VERSION)
+ cp -RPp . /tmp/dhcpcd-$(VERSION)
+ $(MAKE) -C /tmp/dhcpcd-$(VERSION) clean
+ rm -f /tmp/dhcpcd-$(VERSION)/*.bz2
+ tar cvjpf dhcpcd-$(VERSION).tar.bz2 -C /tmp dhcpcd-$(VERSION)
+ rm -rf /tmp/dhcpcd-$(VERSION)
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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.
+ */
+
+/* OK, a lot of this was lifting from iputils as the existing code
+ for dhcpcd was kinda klunky and had some issues */
+
+#define _BSD_SOURCE
+
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#ifdef __linux
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+#endif
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "interface.h"
+#include "logger.h"
+#include "socket.h"
+
+/* Longer is safer and slower - 2 seconds seems a happy medium */
+#define TIMEOUT 2
+
+/* Linux does not seem to define these handy macros */
+#ifndef ar_sha
+#define ar_sha(ap) (((unsigned char *) ((ap) + 1)) + 0)
+#define ar_spa(ap) (((unsigned char *) ((ap) + 1)) + (ap)->ar_hln)
+#define ar_tha(ap) (((unsigned char *) ((ap) + 1)) + (ap)->ar_hln + (ap)->ar_pln)
+#define ar_tpa(ap) (((unsigned char *) ((ap) + 1)) + 2 * (ap)->ar_hln + (ap)->ar_pln)
+
+#define arphdr_len2(ar_hln, ar_pln) (sizeof (struct arphdr) + 2 * (ar_hln) + 2 * (ar_pln))
+#define arphdr_len(ap) (arphdr_len2 ((ap)->ar_hln, (ap)->ar_pln))
+#endif
+
+int arp_check (interface_t *iface, struct in_addr address)
+{
+ if (! iface->arpable)
+ {
+ logger (LOG_DEBUG, "arp_check: interface `%s' is not ARPable",
+ iface->name);
+ return 0;
+ }
+
+ unsigned char buf[256];
+ struct arphdr *ah = (struct arphdr *) buf;
+
+ memset (&buf, 0, sizeof (buf));
+
+ ah->ar_hrd = htons (ARPHRD_ETHER);
+ ah->ar_pro = htons (ETHERTYPE_IP);
+ ah->ar_hln = ETHER_ADDR_LEN;
+ ah->ar_pln = sizeof (struct in_addr);
+ ah->ar_op = htons (ARPOP_REQUEST);
+ memcpy (ar_sha (ah), &iface->ethernet_address, ah->ar_hln);
+ memcpy (ar_tpa (ah), &address, ah->ar_pln);
+
+ logger (LOG_INFO, "checking %s is available on attached networks", inet_ntoa
+ (address));
+
+ open_socket (iface, true);
+ send_packet (iface, ETHERTYPE_ARP, (unsigned char *) &buf, arphdr_len(ah));
+
+ unsigned char reply[4096];
+ int bytes;
+ unsigned char buffer[iface->buffer_length];
+
+ struct timeval tv;
+ long timeout = 0;
+ fd_set rset;
+
+ timeout = uptime() + TIMEOUT;
+ while (1)
+ {
+ tv.tv_sec = timeout - uptime ();
+ tv.tv_usec = 0;
+
+ if (tv.tv_sec < 1)
+ break; /* Time out */
+
+ FD_ZERO (&rset);
+ FD_SET (iface->fd, &rset);
+
+ if (select (iface->fd + 1, &rset, NULL, NULL, &tv) == 0)
+ break;
+
+ if (! FD_ISSET (iface->fd, &rset))
+ continue;
+
+ memset (buffer, 0, sizeof (buffer));
+ int buflen = sizeof (buffer);
+ int bufpos = -1;
+
+ while (bufpos != 0)
+ {
+ memset (&reply, 0, sizeof (reply));
+ if ((bytes = get_packet (iface, (unsigned char *) &reply, buffer,
+ &buflen, &bufpos)) < 0)
+ break;
+
+ ah = (struct arphdr *) reply;
+
+ /* Only these types are recognised */
+ if (ah->ar_op != htons(ARPOP_REPLY)
+ || ah->ar_hrd != htons (ARPHRD_ETHER))
+ continue;
+
+ /* Protocol must be IP. */
+ if (ah->ar_pro != htons (ETHERTYPE_IP))
+ continue;
+ if (ah->ar_pln != sizeof (struct in_addr))
+ continue;
+
+ if (ah->ar_hln != ETHER_ADDR_LEN)
+ continue;
+ if (bytes < sizeof (*ah) + 2 * (4 + ah->ar_hln))
+ continue;
+
+ logger (LOG_ERR, "ARPOP_REPLY received from %s (%s)",
+ inet_ntoa (* (struct in_addr *) ar_spa (ah)),
+ ether_ntoa ((struct ether_addr *) ar_sha (ah)));
+ close (iface->fd);
+ iface->fd = -1;
+ return 1;
+ }
+ }
+
+ close (iface->fd);
+ iface->fd = -1;
+ return 0;
+}
+
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 2005 - 2006 Roy Marples <uberlord@gentoo.org>
+ *
+ * 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.
+ */
+
+#ifndef ARP_H
+#define ARP_H
+
+#include <netinet/in.h>
+
+#include "interface.h"
+
+int arp_check (interface_t *iface, struct in_addr address);
+
+#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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/select.h>
+#include <arpa/inet.h>
+#ifdef __linux__
+#include <netinet/ether.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "interface.h"
+#include "logger.h"
+#include "signals.h"
+#include "socket.h"
+
+/* This is out mini timeout.
+ Basically we resend the last request every TIMEOUT_MINI seconds. */
+#define TIMEOUT_MINI 3
+
+#define STATE_INIT 0
+#define STATE_REQUESTING 1
+#define STATE_BOUND 2
+#define STATE_RENEWING 3
+#define STATE_REBINDING 4
+#define STATE_REBOOT 5
+#define STATE_RENEW_REQUESTED 6
+#define STATE_RELEASED 7
+
+#define SOCKET_CLOSED 0
+#define SOCKET_OPEN 1
+
+#define SOCKET_MODE(_mode) \
+ if (iface->fd >= 0) close (iface->fd); \
+iface->fd = -1; \
+if (_mode == SOCKET_OPEN) \
+if (open_socket (iface, false) < 0) { retval = -1; goto eexit; } \
+mode = _mode;
+
+#define SEND_MESSAGE(_type) \
+ memcpy (&last_dhcp, &dhcp, sizeof (struct dhcp_t)); \
+last_type = _type; \
+send_message (iface, &dhcp, xid, _type, options);
+
+static int daemonise (char *pidfile)
+{
+ FILE *fp;
+
+ if (daemon (0, 0) < 0)
+ {
+ logger (LOG_ERR, "unable to daemonise: %s", strerror (errno));
+ return -1;
+ }
+
+ if ((fp = fopen (pidfile, "w")) == NULL)
+ {
+ logger (LOG_ERR, "fopen `%s': %m", pidfile);
+ return -1;
+ }
+
+ fprintf (fp, "%u\n", getpid ());
+ fclose (fp);
+
+ return 0;
+}
+
+unsigned long random_xid (void)
+{
+ static int initialized;
+
+ if (! initialized)
+ {
+ int fd;
+ unsigned long seed;
+
+ fd = open ("/dev/urandom", 0);
+ if (fd < 0 || read (fd, &seed, sizeof(seed)) < 0)
+ {
+ logger (LOG_WARNING, "Could not load seed from /dev/urandom: %m");
+ seed = time (0);
+ }
+ if (fd >= 0)
+ close(fd);
+
+ srand(seed);
+ initialized++;
+ }
+
+ return rand();
+}
+
+/* This state machine is based on the one from udhcpc
+ written by Russ Dill */
+int dhcp_run (options_t *options)
+{
+ interface_t *iface;
+ int mode = SOCKET_CLOSED;
+ int state = STATE_INIT;
+ struct timeval tv;
+ int xid = 0;
+ unsigned long timeout = 0;
+ fd_set rset;
+ int maxfd;
+ int retval;
+ dhcpmessage_t message;
+ dhcp_t dhcp;
+ dhcp_t last_dhcp;
+ int type;
+ int last_type = DHCP_REQUEST;
+ bool daemonised = false;
+ unsigned long start = 0;
+ int sig;
+ unsigned char *buffer = NULL;
+ int buffer_len = 0;
+ int buffer_pos = 0;
+
+ if (! options || (iface = (read_interface (options->interface,
+ options->metric))) == NULL)
+ return -1;
+
+ /* Remove all existing addresses.
+ After all, we ARE a DHCP client whose job it is to configure the
+ interface. We only do this on start, so persistent addresses can be added
+ afterwards by the user if needed.
+ */
+ flush_addresses (iface->name);
+
+ memset (&dhcp, 0, sizeof (dhcp_t));
+ memset (&last_dhcp, 0, sizeof (dhcp_t));
+
+ dhcp.leasetime = options->leasetime;
+ strcpy (dhcp.classid, options->classid);
+ if (options->clientid[0])
+ strcpy (dhcp.clientid, options->clientid);
+ else
+ sprintf (dhcp.clientid, "%s", ether_ntoa (&iface->ethernet_address));
+
+ if (options->requestaddress.s_addr != 0)
+ memcpy (&dhcp.address, &options->requestaddress, sizeof (struct in_addr));
+
+ signal_setup ();
+
+ while (1)
+ {
+ int timeout_secs = timeout - uptime();
+ tv.tv_sec = timeout - uptime ();
+ tv.tv_usec = 0;
+
+ maxfd = signal_fd_set (&rset, iface->fd);
+
+ if (timeout_secs > 0 || (options->timeout == 0 &&
+ (state != STATE_INIT || xid)))
+ {
+ if (options->timeout == 0)
+ {
+ logger (LOG_DEBUG, "waiting on select for infinity");
+ retval = select (maxfd + 1, &rset, NULL, NULL, NULL);
+ }
+ else
+ {
+ logger (LOG_DEBUG, "waiting on select for %d seconds",
+ timeout_secs);
+ /* If we're waiting for a reply, then we re-send the last
+ DHCP request periodically in-case of a bad line */
+ if (iface->fd == -1)
+ {
+ tv.tv_sec = timeout_secs;
+ tv.tv_usec = 0;
+ retval = select (maxfd + 1, &rset, NULL, NULL, &tv);
+ }
+ else
+ {
+ while (timeout_secs > 0)
+ {
+ tv.tv_sec = TIMEOUT_MINI;
+ tv.tv_usec = 0;
+ retval = select (maxfd + 1, &rset, NULL, NULL, &tv);
+ if (retval != 0)
+ break;
+ send_message (iface, &last_dhcp, xid, last_type, options);
+ timeout_secs -= TIMEOUT_MINI;
+ }
+ }
+ }
+ }
+ else
+ retval = 0;
+
+ /* We should always handle our signals first */
+ if (retval > 0 && (sig = signal_read (&rset)))
+ {
+ switch (sig)
+ {
+ case SIGINT:
+ logger (LOG_INFO, "receieved SIGINT, stopping");
+ retval = 0;
+ goto eexit;
+
+ case SIGTERM:
+ logger (LOG_INFO, "receieved SIGTERM, stopping");
+ retval = 0;
+ goto eexit;
+
+ case SIGALRM:
+
+ logger (LOG_INFO, "receieved SIGALRM, renewing lease");
+ switch (state)
+ {
+ case STATE_BOUND:
+ SOCKET_MODE (SOCKET_OPEN);
+ case STATE_RENEWING:
+ case STATE_REBINDING:
+ state = STATE_RENEW_REQUESTED;
+ break;
+ case STATE_RENEW_REQUESTED:
+ case STATE_REQUESTING:
+ case STATE_RELEASED:
+ state = STATE_INIT;
+ break;
+ }
+
+ timeout = 0;
+ xid = 0;
+ break;
+
+ case SIGHUP:
+ if (state == STATE_BOUND || state == STATE_RENEWING
+ || state == STATE_REBINDING)
+ {
+ logger (LOG_INFO, "received SIGHUP, releasing lease");
+ SOCKET_MODE (SOCKET_OPEN);
+ xid = random_xid ();
+ if ((open_socket (iface, false)) >= 0)
+ SEND_MESSAGE (DHCP_RELEASE);
+ SOCKET_MODE (SOCKET_CLOSED);
+ unlink (iface->infofile);
+ }
+ else
+ logger (LOG_ERR,
+ "receieved SIGUP, but no we have lease to release");
+ retval = 0;
+ goto eexit;
+
+ default:
+ logger (LOG_ERR,
+ "received signal %d, but don't know what to do with it",
+ sig);
+ }
+ }
+ else if (retval == 0) /* timed out */
+ {
+ switch (state)
+ {
+ case STATE_INIT:
+ if (iface->previous_address.s_addr != 0)
+ {
+ logger (LOG_ERR, "lost lease");
+ xid = 0;
+ SOCKET_MODE (SOCKET_CLOSED);
+ if (! options->persistent)
+ {
+ dhcp.address.s_addr = 0;
+ dhcp.netmask.s_addr = 0;
+ dhcp.broadcast.s_addr = 0;
+ configure (options, iface, &dhcp);
+ }
+ if (! daemonised)
+ {
+ retval = -1;
+ goto eexit;
+ }
+ break;
+ }
+
+ if (xid == 0)
+ xid = random_xid ();
+ else
+ {
+ logger (LOG_ERR, "timed out");
+ if (! daemonised)
+ {
+ retval = -1;
+ goto eexit;
+ }
+ }
+
+ timeout = uptime () + options->timeout;
+ if (dhcp.address.s_addr == 0)
+ logger (LOG_INFO, "broadcasting for lease");
+ else
+ logger (LOG_INFO, "broadcasting for lease of %s",
+ inet_ntoa (dhcp.address));
+
+ SOCKET_MODE (SOCKET_OPEN);
+ SEND_MESSAGE (DHCP_DISCOVER);
+ break;
+ case STATE_BOUND:
+ state = STATE_RENEWING;
+ case STATE_RENEWING:
+ logger (LOG_INFO, "renewing lease of %s", inet_ntoa
+ (dhcp.address));
+ SOCKET_MODE (SOCKET_OPEN);
+ xid = random_xid ();
+ SEND_MESSAGE (DHCP_REQUEST);
+ timeout = uptime() + (dhcp.rebindtime - dhcp.renewaltime);
+ state = STATE_REBINDING;
+ break;
+ case STATE_REBINDING:
+ logger (LOG_ERR, "lost lease, attemping to rebind");
+ xid = random_xid ();
+ SEND_MESSAGE (DHCP_DISCOVER);
+ timeout = uptime() + (dhcp.leasetime - dhcp.rebindtime);
+ state = STATE_INIT;
+ break;
+ case STATE_REQUESTING:
+ case STATE_RENEW_REQUESTED:
+ logger (LOG_ERR, "timed out");
+ if (! daemonised)
+ {
+ retval = -1;
+ goto eexit;
+ }
+
+ state = STATE_INIT;
+ timeout = uptime();
+ xid = 0;
+ SOCKET_MODE (SOCKET_OPEN);
+ break;
+
+ case STATE_RELEASED:
+ timeout = 0x7fffffff;
+ break;
+ }
+ }
+ else if (retval > 0 && mode != SOCKET_CLOSED && FD_ISSET(iface->fd, &rset))
+ {
+
+ /* Allocate our buffer space for BPF.
+ We cannot do this until we have opened our socket as we don't
+ know how much of a buffer we need until then. */
+ if (! buffer)
+ buffer = xmalloc (iface->buffer_length);
+ buffer_len = iface->buffer_length;
+ buffer_pos = -1;
+
+ /* We loop through until our buffer is empty.
+ The benefit is that if we get >1 DHCP packet in our buffer and
+ the first one fails for any reason, we can use the next. */
+
+ memset (&message, 0, sizeof (struct dhcpmessage_t));
+ int valid = 0;
+ while (buffer_pos != 0)
+ {
+ if (get_packet (iface, (unsigned char *) &message, buffer,
+ &buffer_len, &buffer_pos) < 0)
+ break;
+
+ if (xid != message.xid)
+ {
+ logger (LOG_ERR, "ignoring transaction %d as it's not ours (%d)",
+ message.xid, xid);
+ continue;
+ }
+
+ logger (LOG_DEBUG, "got packet with transaction %d", message.xid);
+ if ((type = parse_dhcpmessage (&dhcp, &message)) < 0)
+ {
+ logger (LOG_ERR, "failed to parse message");
+ continue;
+ }
+
+ /* If we got here then the DHCP packet is valid and appears to
+ be for us, so let's clear the buffer as we don't care about
+ any more DHCP packets at this point. */
+ valid = 1;
+ break;
+ }
+
+ /* No packets for us, so wait until we get one */
+ if (! valid)
+ continue;
+
+ switch (state)
+ {
+ case STATE_INIT:
+ if (type == DHCP_OFFER)
+ {
+ logger (LOG_INFO, "offered lease of %s",
+ inet_ntoa (dhcp.address));
+
+ SEND_MESSAGE (DHCP_REQUEST);
+ state = STATE_REQUESTING;
+ }
+ break;
+
+ case STATE_RENEW_REQUESTED:
+ case STATE_REQUESTING:
+ case STATE_RENEWING:
+ case STATE_REBINDING:
+ if (type == DHCP_ACK)
+ {
+ SOCKET_MODE (SOCKET_CLOSED);
+ if (options->doarp && iface->previous_address.s_addr !=
+ dhcp.address.s_addr)
+ {
+ if (arp_check (iface, dhcp.address))
+ {
+ SOCKET_MODE (SOCKET_OPEN);
+ SEND_MESSAGE (DHCP_DECLINE);
+ SOCKET_MODE (SOCKET_CLOSED);
+ dhcp.address.s_addr = 0;
+ if (daemonised)
+ configure (options, iface, &dhcp);
+
+ xid = 0;
+ state = STATE_INIT;
+ /* RFC 2131 says that we should wait for 10 seconds
+ before doing anything else */
+ sleep (10);
+ continue;
+ }
+ }
+
+ if (! dhcp.leasetime)
+ {
+ dhcp.leasetime = DEFAULT_TIMEOUT;
+ logger(LOG_INFO,
+ "no lease time supplied, assuming %d seconds",
+ dhcp.leasetime);
+ }
+
+ if (! dhcp.renewaltime)
+ {
+ dhcp.renewaltime = dhcp.leasetime / 2;
+ logger (LOG_INFO,
+ "no renewal time supplied, assuming %d seconds",
+ dhcp.renewaltime);
+ }
+
+ if (! dhcp.rebindtime)
+ {
+ dhcp.rebindtime = (dhcp.leasetime * 0x7) >> 3;
+ logger (LOG_INFO,
+ "no rebind time supplied, assuming %d seconds",
+ dhcp.rebindtime);
+ }
+
+ logger (LOG_INFO, "leased %s for %d seconds",
+ inet_ntoa (dhcp.address), dhcp.leasetime);
+ state = STATE_BOUND;
+ start = uptime ();
+ timeout = start + dhcp.renewaltime;
+ xid = 0;
+
+ if (configure (options, iface, &dhcp) < 0 && ! daemonised)
+ {
+ retval = -1;
+ goto eexit;
+ }
+
+ if (! daemonised)
+ {
+ if ((daemonise (options->pidfile)) < 0 )
+ {
+ retval = -1;
+ goto eexit;
+ }
+ daemonised = true;
+ }
+ }
+ else if (type == DHCP_NAK)
+ logger (LOG_INFO, "received NAK: %s", dhcp.message);
+ else if (type == DHCP_OFFER)
+ logger (LOG_INFO, "got subsequent offer of %s, ignoring ",
+ inet_ntoa (dhcp.address));
+ else
+ logger (LOG_ERR,
+ "no idea what to do with DHCP type %d at this point",
+ type);
+ break;
+ }
+ }
+ else if (retval == -1 && errno == EINTR)
+ {
+ /* Signal interupt will be handled above */
+ }
+ else
+ {
+ /* An error occured */
+ logger (LOG_ERR, "error on select: %s", strerror (errno));
+ }
+ }
+
+eexit:
+ SOCKET_MODE (SOCKET_CLOSED);
+
+ /* Remove our config if we need to */
+ if (dhcp.address.s_addr != 0 && ! options->persistent && daemonised)
+ {
+ dhcp.address.s_addr = 0;
+ dhcp.netmask.s_addr = 0;
+ dhcp.broadcast.s_addr = 0;
+ configure (options, iface, &dhcp);
+ }
+
+ free_dhcp (&dhcp);
+
+ if (iface)
+ {
+ if (iface->previous_routes)
+ free_route (iface->previous_routes);
+ free (iface);
+ }
+
+ if (buffer)
+ free (buffer);
+
+ logger (LOG_INFO, "exiting");
+
+ /* Unlink our pidfile */
+ unlink (options->pidfile);
+
+ return retval;
+}
+
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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.
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "dhcpcd.h"
+
+int dhcp_run (options_t *options);
+
+#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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/time.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "logger.h"
+
+/* This requires us to link to rt on glibc, so we use sysinfo instead */
+#ifdef __linux__
+#include <sys/sysinfo.h>
+long uptime (void)
+{
+ struct sysinfo info;
+
+ sysinfo (&info);
+ return info.uptime;
+}
+#else
+#include <time.h>
+long uptime (void)
+{
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1)
+ {
+ logger (LOG_ERR, "Unable to get uptime: %m");
+ return -1;
+ }
+
+ return tp.tv_sec;
+}
+#endif /* __linux__ */
+
+void *xmalloc (size_t size)
+{
+ register void *value = malloc (size);
+
+ if (value)
+ return value;
+
+ logger (LOG_ERR, "memory exhausted");
+ exit (1);
+}
+
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+long uptime (void);
+void *xmalloc (size_t size);
+
+#endif
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef DHCPCONFIG_H
+#define DHCPCONFIG_H
+
+#include "dhcpcd.h"
+#include "interface.h"
+#include "dhcp.h"
+
+int configure (options_t *options, interface_t *iface, dhcp_t *dhcp);
+
+#endif
--- /dev/null
+/*
+ * 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/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <net/if_arp.h>
+
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "dhcp.h"
+#include "interface.h"
+#include "logger.h"
+#include "socket.h"
+
+static char *dhcp_message[] = {
+ [DHCP_DISCOVER] = "DHCP_DISCOVER",
+ [DHCP_OFFER] = "DHCP_OFFER",
+ [DHCP_REQUEST] = "DHCP_REQUEST",
+ [DHCP_DECLINE] = "DHCP_DECLINE",
+ [DHCP_ACK] = "DHCP_ACK",
+ [DHCP_NAK] = "DHCP_NAK",
+ [DHCP_RELEASE] = "DHCP_RELEASE",
+ [DHCP_INFORM] = "DHCP_INFORM",
+ [DHCP_INFORM + 1] = NULL
+};
+
+size_t send_message (interface_t *iface, dhcp_t *dhcp,
+ unsigned long xid, char type, options_t *options)
+{
+ dhcpmessage_t message;
+ unsigned char *p = (unsigned char *) &message.options;
+ unsigned char *n_params = NULL;
+ unsigned long l;
+
+ if (!iface || !options || !dhcp)
+ return -1;
+
+ memset (&message, 0, sizeof (dhcpmessage_t));
+
+ message.op = DHCP_BOOTREQUEST;
+ message.hwtype = ARPHRD_ETHER;
+ message.hwlen = ETHER_ADDR_LEN;
+ message.secs = htons (10);
+ message.xid = xid;
+ memcpy (&message.hwaddr, &iface->ethernet_address, ETHER_ADDR_LEN);
+ message.cookie = htonl(MAGIC_COOKIE);
+
+ if (iface->previous_address.s_addr != 0 &&
+ iface->previous_address.s_addr == dhcp->address.s_addr)
+ message.ciaddr = iface->previous_address.s_addr;
+
+ *p++ = DHCP_MESSAGETYPE;
+ *p++ = 1;
+ *p++ = type;
+
+ if (type == DHCP_REQUEST)
+ {
+ *p++ = DHCP_MAXMESSAGESIZE;
+ *p++ = 2;
+ uint16_t sz = htons (sizeof (struct udp_dhcp_packet));
+ memcpy (p, &sz, 2);
+ p += 2;
+ }
+
+ if (dhcp->address.s_addr != 0 && iface->previous_address.s_addr == 0)
+ {
+ *p++ = DHCP_ADDRESS;
+ *p++ = 4;
+ memcpy (p, &dhcp->address.s_addr, 4);
+ p += 4;
+ }
+
+ if (dhcp->serveraddress.s_addr != 0 && dhcp->address.s_addr !=0 &&
+ iface->previous_address.s_addr == 0)
+ {
+ *p++ = DHCP_SERVERIDENTIFIER;
+ *p++ = 4;
+ memcpy (p, &dhcp->serveraddress.s_addr, 4);
+ p += 4;
+
+ /* Blank out the server address so we broadcast */
+ if (type == DHCP_REQUEST) dhcp->serveraddress.s_addr = 0;
+ }
+
+ if (type == DHCP_REQUEST || type == DHCP_DISCOVER)
+ {
+ if (dhcp->leasetime > 0)
+ {
+ *p++ = DHCP_LEASETIME;
+ *p++ = 4;
+ uint32_t ul = htonl (dhcp->leasetime);
+ memcpy (p, &ul, 4);
+ p += 4;
+ }
+ }
+
+ *p++ = DHCP_PARAMETERREQUESTLIST;
+ n_params = p;
+ *p++ = 0;
+
+ if (type == DHCP_REQUEST)
+ {
+ *p++ = DHCP_RENEWALTIME;
+ *p++ = DHCP_REBINDTIME;
+ *p++ = DHCP_NETMASK;
+ *p++ = DHCP_BROADCAST;
+ *p++ = DHCP_CSR;
+ /* RFC 3442 states classless static routes should be before routers
+ * and static routes as classless static routes override them both */
+ *p++ = DHCP_ROUTERS;
+ *p++ = DHCP_STATICROUTE;
+ *p++ = DHCP_HOSTNAME;
+ *p++ = DHCP_DNSSEARCH;
+ *p++ = DHCP_DNSDOMAIN;
+ *p++ = DHCP_DNSSERVER;
+ *p++ = DHCP_NISDOMAIN;
+ *p++ = DHCP_NISSERVER;
+ *p++ = DHCP_NTPSERVER;
+ /* These parameters were requested by dhcpcd-2.0 and earlier
+ but we never did anything with them */
+ /* *p++ = DHCP_DEFAULTIPTTL;
+ *p++ = DHCP_MASKDISCOVERY;
+ *p++ = DHCP_ROUTERDISCOVERY; */
+ }
+ else
+ /* Always request one parameter so we don't get the server default
+ when we don't actally need any at this time */
+ *p++ = DHCP_DNSSERVER;
+
+ *n_params = p - n_params - 1;
+
+ if (type == DHCP_REQUEST)
+ {
+ if (options->hostname)
+ {
+ if (options->fqdn == FQDN_DISABLE)
+ {
+ *p++ = DHCP_HOSTNAME;
+ *p++ = l = strlen (options->hostname);
+ memcpy (p, options->hostname, l);
+ p += l;
+ }
+ else
+ {
+ /* Draft IETF DHC-FQDN option (81) */
+ *p++ = DHCP_FQDN;
+ *p++ = (l = strlen (options->hostname)) + 3;
+ /* Flags: 0000NEOS
+ * S: 1 => Client requests Server to update A RR in DNS as well as PTR
+ * O: 1 => Server indicates to client that DNS has been updated
+ * E: 1 => Name data is DNS format
+ * N: 1 => Client requests Server to not update DNS
+ */
+ *p++ = options->fqdn & 0x9;
+ *p++ = 0; /* rcode1, response from DNS server for PTR RR */
+ *p++ = 0; /* rcode2, response from DNS server for A RR if S=1 */
+ memcpy (p, options->hostname, l);
+ p += l;
+ }
+ }
+ }
+
+ if (options->userclass)
+ {
+ *p++ = DHCP_USERCLASS;
+ *p++ = l = strlen (options->userclass);
+ memcpy (p, options->userclass, l);
+ p += l;
+ }
+
+ *p++ = DHCP_CLASSID;
+ *p++ = l = strlen (options->classid);
+ memcpy (p, options->classid, l);
+ p += l;
+
+ *p++ = DHCP_CLIENTID;
+ if (options->clientid[0])
+ {
+ l = strlen (options->clientid);
+ *p++ = l + 1;
+ *p++ = 0; /* string */
+ memcpy (p, options, l);
+ p += l;
+ }
+ else
+ {
+ *p++ = ETHER_ADDR_LEN + 1;
+ *p++ = ARPHRD_ETHER;
+ memcpy (p, &iface->ethernet_address, ETHER_ADDR_LEN);
+ p += ETHER_ADDR_LEN;
+ }
+
+ *p = DHCP_END;
+
+ struct udp_dhcp_packet packet;
+ memset (&packet, 0, sizeof (struct udp_dhcp_packet));
+ make_dhcp_packet (&packet, (unsigned char *) &message,
+ dhcp->address, dhcp->serveraddress);
+
+ logger (LOG_DEBUG, "Sending %s with xid %d", dhcp_message[(int) type], xid);
+ return send_packet (iface, ETHERTYPE_IP, (unsigned char *) &packet,
+ sizeof (struct udp_dhcp_packet));
+}
+
+static unsigned long getnetmask (unsigned long ip_in)
+{
+ unsigned long t, p = ntohl (ip_in);
+
+ if (IN_CLASSA (p))
+ t = ~IN_CLASSA_NET;
+ else
+ {
+ if (IN_CLASSB (p))
+ t = ~IN_CLASSB_NET;
+ else
+ {
+ if (IN_CLASSC (p))
+ t = ~IN_CLASSC_NET;
+ else
+ t = 0;
+ }
+ }
+ while (t & p) t >>= 1;
+ return htonl (~t);
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ seperated string. Returns length of string (including
+ terminating zero) or zero on error. out may be NULL
+ to just determine output length. */
+static unsigned int decode_search (u_char *p, int len, char *out)
+{
+ u_char *r, *q = p;
+ unsigned int count = 0, l, hops;
+
+ while (q - p < len)
+ {
+ r = NULL;
+ hops = 0;
+ while ((l = *q++))
+ {
+ unsigned int label_type = l & 0xc0;
+ if (label_type == 0x80 || label_type == 0x40)
+ return 0;
+ else if (label_type == 0xc0) /* pointer */
+ {
+ l = (l & 0x3f) << 8;
+ l |= *q++;
+
+ /* save source of first jump. */
+ if (!r)
+ r = q;
+
+ hops++;
+ if (hops > 255)
+ return 0;
+
+ q = p + l;
+ if (q - p >= len)
+ return 0;
+ }
+ else
+ {
+ /* straightforward name segment, add with '.' */
+ count += l + 1;
+ if (out)
+ {
+ memcpy (out, q, l);
+ out += l;
+ *out++ = '.';
+ }
+ q += l;
+ }
+ }
+
+ /* change last dot to space */
+ if (out)
+ *(out - 1) = ' ';
+
+ if (r)
+ q = r;
+ }
+
+ /* change last space to zero terminator */
+ if (out)
+ *(out - 1) = 0;
+
+ return count;
+}
+
+/* Add our classless static routes to the routes variable
+ * and return the last route set */
+static route_t *decodeCSR(unsigned char *p, int len)
+{
+ /* Minimum is 5 -first is CIDR and a router length of 4 */
+ if (len < 5)
+ return NULL;
+
+ unsigned char *q = p;
+ int cidr;
+ int ocets;
+ route_t *first = xmalloc (sizeof (route_t));
+ route_t *route = first;
+
+ while (q - p < len)
+ {
+ memset (route, 0, sizeof (route_t));
+
+ cidr = (int) *q++;
+ if (cidr == 0)
+ ocets = 0;
+ else if (cidr < 9)
+ ocets = 1;
+ else if (cidr < 17)
+ ocets = 2;
+ else if (cidr < 25)
+ ocets = 3;
+ else
+ ocets = 4;
+
+ if (ocets > 0)
+ {
+ memcpy (&route->destination.s_addr, q, ocets);
+ q += ocets;
+ }
+
+ /* Now enter the netmask */
+ if (ocets > 0)
+ {
+ memset (&route->netmask.s_addr, 255, ocets - 1);
+ memset ((unsigned char *) &route->netmask.s_addr + (ocets - 1),
+ (256 - (1 << (32 - cidr) % 8)), 1);
+ }
+
+ /* Finally, snag the router */
+ memcpy (&route->gateway.s_addr, q, 4);
+ q += 4;
+
+ /* We have another route */
+ if (q - p < len)
+ {
+ route->next = xmalloc (sizeof (route_t));
+ route = route->next;
+ }
+ }
+
+ return first;
+}
+
+void free_dhcp (dhcp_t *dhcp)
+{
+ if (!dhcp)
+ return;
+
+ if (dhcp->routes)
+ free_route (dhcp->routes);
+
+ if (dhcp->hostname)
+ free (dhcp->hostname);
+
+ if (dhcp->dnsservers)
+ free_address (dhcp->dnsservers);
+ if (dhcp->dnsdomain)
+ free (dhcp->dnsdomain);
+ if (dhcp->dnssearch)
+ free (dhcp->dnssearch);
+
+ if (dhcp->ntpservers)
+ free_address (dhcp->ntpservers);
+
+ if (dhcp->nisdomain)
+ free (dhcp->nisdomain);
+ if (dhcp->nisservers)
+ free_address (dhcp->nisservers);
+
+ if (dhcp->rootpath)
+ free (dhcp->rootpath);
+
+ if (dhcp->fqdn)
+ {
+ if (dhcp->fqdn->name)
+ free (dhcp->fqdn->name);
+ free (dhcp->fqdn);
+ }
+}
+
+static void dhcp_add_address(address_t *address, unsigned char *data, int length)
+{
+ int i;
+ address_t *p = address;
+
+ for (i = 0; i < length; i += 4)
+ {
+ memset (p, 0, sizeof (address_t));
+ memcpy (&p->address.s_addr, data + i, 4);
+ if (length - i > 4)
+ {
+ p->next = xmalloc (sizeof (address_t));
+ p = p->next;
+ }
+ }
+}
+
+int parse_dhcpmessage (dhcp_t *dhcp, dhcpmessage_t *message)
+{
+ unsigned char *p = message->options;
+ unsigned char option;
+ unsigned char length;
+ unsigned char *end = message->options + sizeof (message->options);
+ unsigned int len = 0;
+ int i;
+ int retval = -1;
+ route_t *first_route = xmalloc (sizeof (route_t));
+ route_t *route = first_route;
+ route_t *last_route = NULL;
+ route_t *csr = NULL;
+ char classid[CLASS_ID_MAX_LEN];
+ char clientid[CLIENT_ID_MAX_LEN];
+
+ memset (first_route, 0, sizeof (route_t));
+
+ /* The message back never has the class or client id's so we save them */
+ strcpy (classid, dhcp->classid);
+ strcpy (clientid, dhcp->clientid);
+
+ free_dhcp (dhcp);
+ memset (dhcp, 0, sizeof (dhcp_t));
+
+ dhcp->address.s_addr = message->yiaddr;
+ strcpy (dhcp->servername, message->servername);
+
+ while (p < end)
+ {
+ option = *p++;
+ if (!option)
+ continue;
+
+ length = *p++;
+
+ if (p + length >= end)
+ {
+ retval = -1;
+ goto eexit;
+ }
+
+ switch (option)
+ {
+ case DHCP_END:
+ goto eexit;
+
+ case DHCP_MESSAGETYPE:
+ retval = (int) *p;
+ break;
+
+ case DHCP_ADDRESS:
+ memcpy (&dhcp->address.s_addr, p, 4);
+ break;
+ case DHCP_NETMASK:
+ memcpy (&dhcp->netmask.s_addr, p, 4);
+ break;
+ case DHCP_BROADCAST:
+ memcpy (&dhcp->broadcast.s_addr, p, 4);
+ break;
+ case DHCP_SERVERIDENTIFIER:
+ memcpy (&dhcp->serveraddress.s_addr, p, 4);
+ break;
+
+ case DHCP_LEASETIME:
+ dhcp->leasetime = ntohl (* (uint32_t *) p);
+ break;
+ case DHCP_RENEWALTIME:
+ dhcp->renewaltime = ntohl (* (uint32_t *) p);
+ break;
+ case DHCP_REBINDTIME:
+ dhcp->rebindtime = ntohl (* (uint32_t *) p);
+ break;
+ case DHCP_MTU:
+ dhcp->mtu = ntohs (* (uint16_t *) p);
+ /* Minimum legal mtu is 68 */
+ if (dhcp->mtu > 0 && dhcp->mtu < 68)
+ dhcp->mtu = 68;
+ break;
+
+ case DHCP_HOSTNAME:
+ if (dhcp->hostname)
+ free (dhcp->hostname);
+ dhcp->hostname = xmalloc (length + 1);
+ memcpy (dhcp->hostname, p, length);
+ dhcp->hostname[length] = '\0';
+ break;
+
+ case DHCP_DNSDOMAIN:
+ if (dhcp->dnsdomain)
+ free (dhcp->dnsdomain);
+ dhcp->dnsdomain = xmalloc (length + 1);
+ memcpy (dhcp->dnsdomain, p, length);
+ dhcp->dnsdomain[length] = '\0';
+ break;
+
+ case DHCP_MESSAGE:
+ if (dhcp->message)
+ free (dhcp->message);
+ dhcp->message = xmalloc (length + 1);
+ memcpy (dhcp->message, p, length);
+ dhcp->message[length] = '\0';
+ break;
+
+ case DHCP_ROOTPATH:
+ if (dhcp->rootpath)
+ free (dhcp->rootpath);
+ dhcp->rootpath = xmalloc (length + 1);
+ memcpy (dhcp->rootpath, p, length);
+ dhcp->rootpath[length] = '\0';
+ break;
+
+ case DHCP_NISDOMAIN:
+ if (dhcp->nisdomain)
+ free (dhcp->nisdomain);
+ dhcp->nisdomain = xmalloc (length + 1);
+ memcpy (dhcp->nisdomain, p, length);
+ dhcp->nisdomain[length] = '\0';
+ break;
+
+ case DHCP_DNSSERVER:
+ if (dhcp->dnsservers)
+ free_address (dhcp->dnsservers);
+ dhcp->dnsservers = xmalloc (sizeof (address_t));
+ dhcp_add_address (dhcp->dnsservers, p, length);
+ break;
+ case DHCP_NTPSERVER:
+ if (dhcp->ntpservers)
+ free_address (dhcp->ntpservers);
+ dhcp->ntpservers = xmalloc (sizeof (address_t));
+ dhcp_add_address (dhcp->ntpservers, p, length);
+ break;
+ case DHCP_NISSERVER:
+ if (dhcp->nisservers)
+ free_address (dhcp->nisservers);
+ dhcp->nisservers = xmalloc (sizeof (address_t));
+ dhcp_add_address (dhcp->nisservers, p, length);
+ break;
+
+ case DHCP_DNSSEARCH:
+ if (dhcp->dnssearch)
+ free (dhcp->dnssearch);
+ if ((len = decode_search (p, length, NULL)))
+ {
+ dhcp->dnssearch = xmalloc (len);
+ decode_search (p, length, dhcp->dnssearch);
+ }
+ break;
+
+ case DHCP_CSR:
+ csr = decodeCSR (p, length);
+ break;
+
+ case DHCP_STATICROUTE:
+ for (i = 0; i < length; i += 8)
+ {
+ memcpy (&route->destination.s_addr, p + i, 4);
+ memcpy (&route->gateway.s_addr, p + i + 4, 4);
+ route->netmask.s_addr = getnetmask (route->destination.s_addr);
+ last_route = route;
+ route->next = xmalloc (sizeof (route_t));
+ route = route->next;
+ memset (route, 0, sizeof (route_t));
+ }
+ break;
+
+ case DHCP_ROUTERS:
+ for (i = 0; i < length; i += 4)
+ {
+ memcpy (&route->gateway.s_addr, p + i, 4);
+ last_route = route;
+ route->next = xmalloc (sizeof (route_t));
+ route = route->next;
+ memset (route, 0, sizeof (route_t));
+ }
+ break;
+
+ default:
+ logger (LOG_DEBUG, "no facility to parse DHCP code %u", option);
+ break;
+ }
+
+ p += length;
+ }
+
+eexit:
+ /* Fill in any missing fields */
+ if (!dhcp->netmask.s_addr)
+ dhcp->netmask.s_addr = getnetmask (dhcp->address.s_addr);
+ if (!dhcp->broadcast.s_addr)
+ dhcp->broadcast.s_addr = dhcp->address.s_addr | ~dhcp->netmask.s_addr;
+
+ /* If we have classess static routes then we discard
+ static routes and routers according to RFC 3442 */
+ if (csr)
+ {
+ dhcp->routes = csr;
+ free_route (first_route);
+ }
+ else
+ {
+ dhcp->routes = first_route;
+ if (last_route)
+ {
+ free (last_route->next);
+ last_route->next = NULL;
+ }
+ else
+ {
+ free_route (dhcp->routes);
+ dhcp->routes = NULL;
+ }
+ }
+
+ /* The message back never has the class or client id's so we restore them */
+ strcpy (dhcp->classid, classid);
+ strcpy (dhcp->clientid, clientid);
+
+ return retval;
+}
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "dhcpcd.h"
+#include "interface.h"
+
+/* UDP port numbers for DHCP */
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+#define MAGIC_COOKIE 0x63825363
+
+/* DHCP message OP code */
+#define DHCP_BOOTREQUEST 1
+#define DHCP_BOOTREPLY 2
+
+/* DHCP message type */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+#define DHCP_INFORM 8
+
+/* DHCP options */
+enum DHCP_OPTIONS
+{
+ DHCP_PAD = 0,
+ DHCP_NETMASK = 1,
+ DHCP_TIMEROFFSET = 2,
+ DHCP_ROUTERS = 3,
+ DHCP_TIMESERVER = 4,
+ DHCP_NAMESERVER = 5,
+ DHCP_DNSSERVER = 6,
+ DHCP_LOGSERVER = 7,
+ DHCP_COOKIESERVER = 8,
+ DHCP_HOSTNAME = 12,
+ DHCP_DNSDOMAIN = 15,
+ DHCP_ROOTPATH = 17,
+ DHCP_DEFAULTIPTTL = 23,
+ DHCP_MTU = 26,
+ DHCP_BROADCAST = 28,
+ DHCP_MASKDISCOVERY = 29,
+ DHCP_ROUTERDISCOVERY = 31,
+ DHCP_STATICROUTE = 33,
+ DHCP_NISDOMAIN = 40,
+ DHCP_NISSERVER = 41,
+ DHCP_NTPSERVER = 42,
+ DHCP_ADDRESS = 50,
+ DHCP_LEASETIME = 51,
+ DHCP_MESSAGETYPE = 53,
+ DHCP_SERVERIDENTIFIER = 54,
+ DHCP_PARAMETERREQUESTLIST = 55,
+ DHCP_MESSAGE = 56,
+ DHCP_MAXMESSAGESIZE = 57,
+ DHCP_RENEWALTIME = 58,
+ DHCP_REBINDTIME = 59,
+ DHCP_CLASSID = 60,
+ DHCP_CLIENTID = 61,
+ DHCP_USERCLASS = 77, /* RFC 3004 */
+ DHCP_FQDN = 81,
+ DHCP_DNSSEARCH = 119, /* RFC 3397 */
+ DHCP_CSR = 121, /* RFC 3442 */
+ DHCP_END = 255
+};
+
+/* SetFQDNHostName values - lsnybble used in flags
+ * byte (see buildmsg.c), hsnybble to create order
+ * and to allow 0x00 to mean disable
+ */
+enum FQQN {
+ FQDN_DISABLE = 0x00,
+ FQDN_NONE = 0x18,
+ FQDN_PTR = 0x20,
+ FQDN_BOTH = 0x31
+};
+
+typedef struct fqdn_t
+{
+ uint8_t flags;
+ uint8_t r1;
+ uint8_t r2;
+ char *name;
+} fqdn_t;
+
+typedef struct dhcp_t
+{
+ char version[11];
+
+ struct in_addr serveraddress;
+ char serverhw[IF_NAMESIZE];
+ char servername[64];
+
+ char classid[CLASS_ID_MAX_LEN];
+ char clientid[CLIENT_ID_MAX_LEN];
+
+ struct in_addr address;
+ struct in_addr netmask;
+ struct in_addr broadcast;
+ unsigned short mtu;
+
+ unsigned leasetime;
+ unsigned renewaltime;
+ unsigned rebindtime;
+
+ route_t *routes;
+
+ char *hostname;
+ fqdn_t *fqdn;
+
+ address_t *dnsservers;
+ char *dnsdomain;
+ char *dnssearch;
+
+ address_t *ntpservers;
+
+ address_t *nisservers;
+ char *nisdomain;
+
+ char *message;
+ char *rootpath;
+} dhcp_t;
+
+typedef struct dhcpmessage_t
+{
+ char op; /* message type */
+ char hwtype; /* hardware address type */
+ char hwlen; /* hardware address length */
+ char hwopcount; /* should be zero in client's message */
+ int32_t xid; /* transaction id */
+ int16_t secs; /* elapsed time in sec. from trying to boot */
+ int16_t flags;
+ int32_t ciaddr; /* (previously allocated) client IP address */
+ int32_t yiaddr; /* 'your' client IP address */
+ int32_t siaddr; /* should be zero in client's messages */
+ int32_t giaddr; /* should be zero in client's messages */
+ unsigned char hwaddr[16]; /* client's hardware address */
+ char servername[64]; /* server host name, null terminated string */
+ char bootfile[128]; /* boot file name, null terminated string */
+ uint32_t cookie;
+ unsigned char options[308]; /* message options - cookie */
+} dhcpmessage_t;
+
+struct udp_dhcp_packet
+{
+ struct ip ip;
+ struct udphdr udp;
+ dhcpmessage_t dhcp;
+};
+
+size_t send_message (interface_t *iface, dhcp_t *dhcp,
+ unsigned long xid, char type, options_t *options);
+void free_dhcp (dhcp_t *dhcp);
+int parse_dhcpmessage (dhcp_t *dhcp, dhcpmessage_t *message);
+
+#endif
--- /dev/null
+.\" $Id$
+.\"
+.TH dhcpcd 8 "15 August 2006" "dhcpcd 3.0"
+
+.SH NAME
+dhcpcd \- DHCP client daemon
+
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+dhcpcd
+\%[\-adknpGHNRY]
+\%[\-c\ script]
+\%[\-h\ hostname]
+\%[\-i\ vendorClassID]
+\%[\-l\ leasetime]
+\%[\-m\ metric]
+\%[\-s\ ipaddr]
+\%[\-t\ timeout]
+\%[\-u\ userClass]
+\%[\-F\ none | ptr | both]
+\%[\-I\ clientID]
+\%[interface]
+.in -.5i
+.SH DESCRIPTION
+.B dhcpcd
+is an implementation of the DHCP client specified in
+.B RFC2131.
+
+It gets the host information (IP address, netmask, broadcast address,
+etc.) from a DHCP server and configures the network interface of the
+machine on which it is running. It also tries to renew the lease time
+according to
+.B RFC2131.
+
+.SH OPTIONS
+.TP
+.BI interface
+Specifies the network interface name (eth0, eth1, etc.).
+.TP
+.BI \-a
+Do an
+.B ARP
+check on the IP address give to us by the DHCP server. We may need to do this
+if a client on the same network segment has the same IP address, however we do
+not do this by default as most DHCP servers test the IP briefly with an ICMP
+Echo request before assigning the IP address.
+.TP
+.BI \-c \ script
+.B dhcpcd
+will try to execute
+.I script
+instead of the default script
+.I /etc/dhcpcd.sh
+every time it configures or brings down the interface. See the
+description of
+.I dhcpcd.sh
+script in
+.B FILES
+section below.
+.TP
+.BI \-d
+Echos debugging and information messages to the console.
+.TP
+.BI \-h \ hostname
+specifies a string used for the hostname option field when
+.B dhcpcd
+sends DHCP messages. Some DHCP servers, notably those used by
+@Home Networks, require the hostname option
+field containing a specific string in the DHCP messages from clients.
+When combined with the -F switch, specifies the string used for the
+FQDN option field instead of the hostname option field.
+.TP
+.BI \-i \ vendorClassID
+Specifies the vendor class identifier string.
+.B dhcpcd
+uses the default vendor class identifier string (system name, system release,
+and machine type) if it is not specified.
+.TP
+.BI \-k
+Sends
+.B SIGHUP
+signal to the
+.B dhcpcd
+process associated with the specified interface if one is currently running. If
+.B dhcpcd
+receives
+.B SIGHUP
+it will send
+.B DCHP_RELEASE
+message to the server and destroy dhcpcd cache. In a case
+.B dhcpcd
+receives
+.B SIGTERM
+which is normally used by
+.B shutdown(8)
+when rebooting the system
+.B dhcpcd
+will not send
+.B DHCP_RELEASE
+and will not destroy cache. When system boots
+.B dhcpcd
+will use cache to request the same IP address
+from DHCP server which was assigned before the
+system went down. (see also
+.B -p
+)
+.TP
+.BI \-l \ leasetime
+Specifies (in seconds) the recommended lease time to the server. (Note
+that the server can override this value if it sees fit). This value is
+used in the
+.B DHCP_DISCOVER
+message. Use -1 for an infinite lease time. We don't request a specific
+lease time by default.
+.TP
+.BI \-m \ metric
+Routes will be added with the given metric. The default is 0.
+On some systems such as FreeBSD the interface is given the metric.
+.TP
+.BI \-n
+Sends
+.B SIGALRM
+signal to the
+.B dhcpcd
+process that is currently running which
+forces
+.B dhcpcd
+to try to renew the lease. If dhcpcd is not running, the flag
+is ignored and
+.B dhcpcd
+follows the normal startup procedure.
+.TP
+.BI \-p
+Stops
+.B dhcpcd
+from removing the interface configuration when it is terminated with the
+.B SIGTERM
+signal. This is useful when a host is running with an NFS-mounted root
+filesystem over an interface controlled by DHCP. It should not be used
+except in those circumstances, since if
+.B dhcp
+is stopped it can no longer down an interface at the end of its
+lease period when the lease is not renewed.
+.TP
+.BI \-s \ ipaddr
+Sends DHCP_DISCOVER message to DHCP server requesting to lease ip address
+ipaddr.
+The ipaddr parameter must be in the form xxx.xxx.xxx.xxx.
+.TP
+.BI \-t \ timeout
+Specifies (in seconds ) for how long
+.B dhcpcd
+will try to get an IP address. The default is 10 seconds.
+.B dhcpcd
+will not fork into background until it gets a valid IP address
+in which case dhcpcd will return 0 to the parent process.
+In a case
+.B dhcpcd
+times out before receiving a valid IP address from DHCP server
+.B dhcpcd
+will return exit code 1 to the parent process. Setting the timeout to
+zero disables it: dhcp will keep trying forever to get a lease, and if
+the lease is lost, it will try forever to get another.
+.TP
+.BI \-u \ userClass
+Tags the DHCP message with the specified user class. DHCP servers can use
+these fields to send back different information instead of grouping by
+fixed hardware addresses. You can specify more than one user class, but the
+total length must be less than 255 characters, -1 character for each user
+class.
+.TP
+.BI \-H
+Forces
+.B dhcpcd
+to set hostname of the host to the hostname option supplied by DHCP server.
+By default
+.B dhcpcd
+will NOT set hostname of the host to the hostname option
+received from DHCP server unless the current hostname is blank, (none) or
+localhost.
+.TP
+.BI \-F \ none | ptr | both
+Forces
+.B dhcpcd
+to request the DHCP server update the DNS using the FQDN option
+instead of the Hostname option. The name used by this option
+is specified with the \fB-h\fP switch, which must be present. If
+the \fB-h\fP switch is not present, the FQDN option is ignored.
+The name should be fully qualified, although servers usually
+accept a simple name.
+.I both
+requests that the DHCP server update both the A and PTR
+records in the DNS.
+.I ptr
+requests that the DHCP server updates only the PTR record in
+the DNS.
+.I none
+requests that the DHCP server perform no updates.
+.B dhcpcd
+does not perform any DNS update, even when the server is
+requested to perform no updates. This can be easily
+implemented outside the client; all the necessary
+information is recorded in the
+.I /var/lib/dhcpcd/dhcpcd-<interface>.info
+file.
+.TP
+.BI \-I \ clientID
+Specifies the client identifier string.
+.B dhcpcd
+uses the default client identifier (MAC address of the network
+interface) if it is not specified.
+.TP
+.BI \-N
+Prevents
+.B dhcpcd
+from replacing existing
+.I /etc/ntp.conf
+file.
+.TP
+.BI \-R
+Prevents
+.B dhcpcd
+from replacing existing using resolvconf or replacing
+.I /etc/resolv.conf
+file.
+.TP
+.BI \-Y
+Prevents
+.B dhcpcd
+from replacing existing
+.I /etc/yp.conf
+file.
+.TP
+.BI \-G
+Prevents
+.B dhcpcd
+from installing default routes provided by DHCP server.
+.SH NOTES
+.TP
+.B dhcpcd
+uses
+.I LOCAL0
+syslog facility for all logging. To catch
+.B dhcpcd
+debugging output add the following line to
+.I /etc/syslog.conf
+file:
+
+local0.* /var/log/dhcpcd.log
+
+and then refresh syslogd daemon:
+
+kill -1 `cat /var/run/syslogd.pid`
+
+.SH FILES
+.PD 0
+.TP
+.BI /var/lib/dhcpcd
+Directory used for storing files information files created by
+.B dhcpcd
+that can be used by shell scripts.
+.PD 1
+.TP
+.BI /etc/dhcpcd.sh
+script file, which
+.B dhcpcd
+will try to execute whenever it configures or brings down the interface. The
+path to this executable script can be changed with
+.I \-c \ script
+option.
+.B dhcpcd
+passes 3 parameters to
+.I dhcpcd.sh
+script:
+.TP
+.I dhcpcd.sh infofile [up | down | new]
+The first parameter infofile is the path to a file containing all DHCP
+information we have. The second parameter value
+.I up | down | new
+mean the interface has been brought up with the same IP address as before ("up"), or
+with the new IP address ("new"), or the interface has been brought down ("down").
+.TP
+.BI /etc/resolv.conf
+file created by
+.B dhcpcd
+when the client receives DNS and domain name options.
+If resolvconf is present on the system then we send the data to it instead
+of overwriting resolv.conf
+.TP
+.BI /etc/yp.conf
+file created by
+.B dhcpcd
+when the client receives NIS options.
+.TP
+.BI /etc/ntp.conf
+file created by
+.B dhcpcd
+when the client receives NTP options.
+.TP
+.BI /var/run/dhcpcd-<interface>.pid
+file containing the process id of
+.B dhcpcd.
+The word
+.I <interface>
+is actually replaced with the network interface name like
+.I eth0
+to which
+.B dhcpcd
+is attached.
+
+.SH SEE ALSO
+.BR dig (1),
+.BR nslookup (8),
+.BR nsupdate (8)
+.LP
+.I Dynamic Host Configuration Protocol,
+RFC2132
+.LP
+.I DHCP Options and BOOTP Vendor Extensions,
+RFC2132
+.LP
+.I Draft DHC FQDN Option specification,
+draft-ietf-dhc-fqdn-option
+
+.SH BUGS
+Probably many.
+Please report them to http://bugs.gentoo.org.
+.PD 0
+
+.SH AUTHORS
+Roy Marples <uberlord@gentoo.org>
--- /dev/null
+/*
+ * 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/stat.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "client.h"
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "interface.h"
+#include "logger.h"
+#include "pathnames.h"
+#include "version.h"
+
+#define PACKAGE "dhcpcd"
+
+#define STRINGINT(_string, _int) { \
+ char *_tmp; \
+ errno = 0; \
+ long _number = strtol (_string, &_tmp, 0); \
+ if ((errno != 0 && _number == 0) || _string == _tmp || \
+ (errno == ERANGE && (_number == LONG_MAX || _number == LONG_MIN))) \
+ { \
+ logger (LOG_ERR, "`%s' out of range", _string);; \
+ exit (EXIT_FAILURE); \
+ } \
+ else \
+ _int = (int) _number; \
+}
+
+static pid_t readpid(char *pidfile)
+{
+ FILE *fp;
+ pid_t pid;
+
+ if ((fp = fopen (pidfile, "r")) == NULL)
+ {
+ errno = ENOENT;
+ return 0;
+ }
+
+ fscanf (fp, "%d", &pid);
+ fclose (fp);
+
+ return pid;
+}
+
+static int kill_pid (char *pidfile, int sig)
+{
+ pid_t pid = readpid (pidfile);
+ int r = 0;
+
+ if (!pid || (r = kill (pid, sig)))
+ {
+ logger (LOG_ERR, ""PACKAGE" not running");
+ unlink (pidfile);
+ }
+
+ return r;
+}
+
+static void usage ()
+{
+ printf ("usage: "PACKAGE" [-adknpGHNRY] [-c script] [-h hostame] [-i classID]\n"
+ " [-l leasetime] [-m metric] [-s ipaddress] [-t timeout]\n"
+ " [-u userclass] [-F [none | ptr | both]] [-I clientID]\n");
+}
+
+int main(int argc, char **argv)
+{
+ options_t options;
+
+ /* Sanitize our fd's */
+ int zero;
+ if ((zero = open (_PATH_DEVNULL, O_RDWR, 0)) >= 0)
+ {
+ while (zero < 3)
+ zero = dup (zero);
+ close(zero);
+ }
+
+ openlog (PACKAGE, LOG_PID, LOG_LOCAL0);
+
+ memset (&options, 0, sizeof (options_t));
+ options.script = DEFAULT_SCRIPT;
+ snprintf (options.classid, CLASS_ID_MAX_LEN, "%s %s", PACKAGE, VERSION);
+
+ options.doarp = false;
+ options.dodns = true;
+ options.dontp = true;
+ options.dogateway = true;
+ options.timeout = DEFAULT_TIMEOUT;
+
+ int doversion = 0;
+ int dohelp = 0;
+ int userclasses = 0;
+
+ const struct option longopts[] =
+ {
+ {"arp", no_argument, NULL, 'a'},
+ {"script",required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"hostname", required_argument, NULL, 'h'},
+ {"classid", required_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"renew", no_argument, NULL, 'n'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"request", required_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"fqdn", optional_argument, NULL, 'F'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"sethostname", no_argument, NULL, 'H'},
+ {"clientid", required_argument, NULL, 'I'},
+ {"nontp", no_argument, NULL, 'N'},
+ {"nodns", no_argument, NULL, 'R'},
+ {"nonis", no_argument, NULL, 'Y'},
+ {"help", no_argument, &dohelp, 1},
+ {"version", no_argument, &doversion, 1},
+ {NULL, 0, NULL, 0}
+ };
+
+ int ch;
+ int option_index = 0;
+ while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps:t:u:F:GHI:NRY", longopts,
+ &option_index)) != -1)
+ switch (ch)
+ {
+ case 0:
+ if (longopts[option_index].flag)
+ break;
+ logger (LOG_ERR, "option `%s' should set a flag",
+ longopts[option_index].name);
+ exit (EXIT_FAILURE);
+ break;
+
+ case 'a':
+ options.doarp = true;
+ break;
+ case 'c':
+ options.script = optarg;
+ break;
+ case 'd':
+ setloglevel(LOG_DEBUG);
+ break;
+ case 'h':
+ if (strlen (optarg) > HOSTNAME_MAX_LEN)
+ {
+ logger(LOG_ERR, "`%s' too long for HostName string, max is %d",
+ optarg, HOSTNAME_MAX_LEN);
+ exit (EXIT_FAILURE);
+ }
+ else
+ options.hostname = optarg;
+ break;
+ case 'i':
+ if (strlen(optarg) > CLASS_ID_MAX_LEN)
+ {
+ logger (LOG_ERR, "`%s' too long for ClassID string, max is %d",
+ optarg, CLASS_ID_MAX_LEN);
+ exit (EXIT_FAILURE);
+ }
+ else
+ sprintf(options.classid, "%s", optarg);
+ break;
+ case 'k':
+ options.signal = SIGHUP;
+ break;
+ case 'l':
+ STRINGINT (optarg, options.leasetime);
+ if (options.leasetime <= 0)
+ {
+ logger (LOG_ERR, "leasetime must be a positive value");
+ exit (EXIT_FAILURE);
+ }
+ break;
+ case 'm':
+ STRINGINT(optarg, options.metric);
+ break;
+ case 'n':
+ options.signal = SIGALRM;
+ break;
+ case 'p':
+ options.persistent = true;
+ break;
+ case 's':
+ if (! inet_aton (optarg, &options.requestaddress))
+ {
+ logger (LOG_ERR, "`%s' is not a valid IP address", optarg);
+ exit (EXIT_FAILURE);
+ }
+ break;
+ case 't':
+ STRINGINT (optarg, options.timeout);
+ if (options.timeout < 0)
+ {
+ logger (LOG_ERR, "timeout must be a positive value");
+ exit (EXIT_FAILURE);
+ }
+ break;
+ case 'u':
+ {
+ int i;
+ int offset = 0;
+ for (i = 0; i < userclasses; i++)
+ offset += (int) options.userclass[offset] + 1;
+ if (offset + 1 + strlen (optarg) > USERCLASS_MAX_LEN)
+ {
+ logger (LOG_ERR, "userclass overrun, max is %d",
+ USERCLASS_MAX_LEN);
+ exit (EXIT_FAILURE);
+ }
+ userclasses++;
+ memcpy (options.userclass + offset + 1 , optarg, strlen (optarg));
+ options.userclass[offset] = strlen (optarg);
+ }
+ break;
+ case 'F':
+ if (strcmp (optarg, "none") == 0)
+ options.fqdn = FQDN_NONE;
+ else if (strcmp (optarg, "ptr") == 0)
+ options.fqdn = FQDN_PTR;
+ else if (strcmp (optarg, "both") == 0)
+ options.fqdn = FQDN_BOTH;
+ else
+ {
+ logger (LOG_ERR, "invalid value `%s' for FQDN", optarg);
+ exit (EXIT_FAILURE);
+ }
+ break;
+ case 'G':
+ options.dogateway = false;
+ break;
+ case 'H':
+ options.dohostname = true;
+ break;
+ case 'I':
+ if (strlen (optarg) > CLIENT_ID_MAX_LEN)
+ {
+ logger (LOG_ERR, "`%s' is too long for ClientID, max is %d",
+ optarg, CLIENT_ID_MAX_LEN);
+ exit (EXIT_FAILURE);
+ }
+ else
+ sprintf(options.clientid, "%s", optarg);
+ break;
+ case 'N':
+ options.dontp = false;
+ break;
+ case 'R':
+ options.dodns = false;
+ break;
+ case 'Y':
+ options.donis = false;
+ break;
+ case '?':
+ usage ();
+ exit (EXIT_FAILURE);
+ default:
+ usage ();
+ exit (EXIT_FAILURE);
+ }
+
+ if (doversion)
+ printf (""PACKAGE" "VERSION"\n");
+
+ if (dohelp)
+ usage ();
+
+ if (optind < argc)
+ {
+ if (strlen (argv[optind]) > IF_NAMESIZE)
+ {
+ logger (LOG_ERR, "`%s' is too long for an interface name (max=%d)",
+ argv[optind], IF_NAMESIZE);
+ exit (EXIT_FAILURE);
+ }
+ options.interface = argv[optind];
+ }
+ else
+ {
+ /* If only version was requested then exit now */
+ if (doversion || dohelp)
+ exit (EXIT_SUCCESS);
+
+ logger (LOG_ERR, "no interface specified", options.interface);
+ exit (EXIT_FAILURE);
+ }
+
+ if (geteuid ())
+ {
+ logger (LOG_ERR, "you need to be root to run "PACKAGE);
+ exit (EXIT_FAILURE);
+ }
+
+ char prefix[IF_NAMESIZE + 3];
+ snprintf (prefix, IF_NAMESIZE, "%s: ", options.interface);
+ setlogprefix (prefix);
+ snprintf (options.pidfile, sizeof (options.pidfile), PIDFILE,
+ options.interface);
+
+ if (options.signal != 0)
+ exit (kill_pid (options.pidfile, options.signal));
+
+ umask (022);
+
+ if (readpid (options.pidfile))
+ {
+ logger (LOG_ERR, ""PACKAGE" already running (%s)", options.pidfile);
+ exit (EXIT_FAILURE);
+ }
+
+ if (mkdir (CONFIGDIR, S_IRUSR |S_IWUSR |S_IXUSR | S_IRGRP | S_IXGRP
+ | S_IROTH | S_IXOTH) && errno != EEXIST )
+ {
+ logger( LOG_ERR, "mkdir(\"%s\",0): %m\n", CONFIGDIR);
+ exit (EXIT_FAILURE);
+ }
+
+ if (mkdir (ETCDIR, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
+ | S_IROTH | S_IXOTH) && errno != EEXIST )
+ {
+ logger (LOG_ERR, "mkdir(\"%s\",0): %m\n", ETCDIR);
+ exit (EXIT_FAILURE);
+ }
+
+ logger (LOG_INFO, PACKAGE " " VERSION " starting");
+ if (dhcp_run (&options))
+ exit (EXIT_FAILURE);
+
+ exit (EXIT_SUCCESS);
+}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 2005 - 2006 Roy Marples <uberlord@gentoo.org>
+ *
+ * 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.
+ */
+
+#ifndef DHCPCD_H
+#define DHCPCD_H
+
+#ifdef __linux__
+#include <linux/limits.h>
+#endif
+#include <netinet/in.h>
+#include <limits.h>
+#include <stdbool.h>
+
+#include "common.h"
+
+#define DEFAULT_TIMEOUT 10
+// #define DEFAULT_LEASETIME 0xffffffff /* infinite lease time */
+#define DEFAULT_LEASETIME 3600 /* 1 hour */
+
+#define CLASS_ID_MAX_LEN 48
+#define CLIENT_ID_MAX_LEN 48
+#define HOSTNAME_MAX_LEN 64
+#define USERCLASS_MAX_LEN 255
+
+typedef struct options_t {
+ char *interface;
+ char *hostname;
+ int fqdn;
+ char classid[CLASS_ID_MAX_LEN];
+ char clientid[CLIENT_ID_MAX_LEN];
+ char userclass[USERCLASS_MAX_LEN];
+ unsigned leasetime;
+ time_t timeout;
+ int metric;
+ struct in_addr requestaddress;
+
+ bool doarp;
+ bool dodns;
+ bool dontp;
+ bool donis;
+ bool dogateway;
+ bool dohostname;
+ bool dodomainname;
+ int signal;
+ bool persistent;
+
+ char *script;
+ char pidfile[PATH_MAX];
+} options_t;
+
+#endif
--- /dev/null
+#!/bin/sh
+#
+# This is a sample /etc/dhcpcd.sh script.
+# /etc/dhcpcd.sh script is executed by dhcpcd daemon
+# any time it configures or shuts down interface.
+# The following parameters are passed to dhcpcd.exe script:
+# $1 = HostInfoFilePath, e.g "/var/lib/dhcpcd/dhcpcd-eth0.info"
+# $2 = "up" if interface has been configured with the same
+# IP address as before reboot;
+# $2 = "down" if interface has been shut down;
+# $2 = "new" if interface has been configured with new IP address;
+#
+# Sanity checks
+
+if [ $# -lt 2 ]; then
+ logger -s -p local0.err -t dhcpcd.sh "wrong usage"
+ exit 1
+fi
+
+hostinfo="$1"
+state="$2"
+
+# Reading HostInfo file for configuration parameters
+. "${hostinfo}"
+
+case "${state}" in
+ up)
+ logger -s -p local0.info -t dhcpcd.sh \
+ "interface ${INTERFACE} has been configured with old IP=${IPADDR}"
+ # Put your code here for when the interface has been brought up with an
+ # old IP address here
+ ;;
+
+ new)
+ logger -s -p local0.info -t dhcpcd.sh \
+ "interface ${INTERFACE} has been configured with new IP=${IPADDR}"
+ # Put your code here for when the interface has been brought up with a
+ # new IP address
+ ;;
+
+ down) logger -s -p local0.info -t dhcpcd.sh \
+ "interface ${INTERFACE} has been brought down"
+ # Put your code here for the when the interface has been shut down
+ ;;
+esac
+exit 0
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <arpa/inet.h>
+
+/* Netlink suff */
+#ifdef __linux__
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+#else
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#endif /* __linux__ */
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "interface.h"
+#include "logger.h"
+#include "pathnames.h"
+
+#ifndef IFF_NOTRAILERS
+#define IFF_NOTRAILERS 0
+#endif
+
+void free_address (address_t *addresses)
+{
+ if (!addresses)
+ return;
+
+ address_t *p = addresses;
+ address_t *n = NULL;
+
+ while (p)
+ {
+ n = p->next;
+ free (p);
+ p = n;
+ }
+}
+
+void free_route (route_t *routes)
+{
+ if (!routes)
+ return;
+
+ route_t *p = routes;
+ route_t *n = NULL;
+
+ while (p)
+ {
+ n = p->next;
+ free (p);
+ p = n;
+ }
+}
+
+interface_t *read_interface (const char *ifname, int metric)
+{
+ if (! ifname)
+ return NULL;
+
+ int s;
+ struct ifreq ifr;
+ interface_t *iface;
+ unsigned char hwaddr[ETHER_ADDR_LEN];
+
+ struct ifaddrs *ifap;
+ struct ifaddrs *p;
+ unsigned int flags;
+
+ if (getifaddrs (&ifap) != 0)
+ return NULL;
+
+ for (p = ifap; p; p = p->ifa_next)
+ {
+ if (strcmp (p->ifa_name, ifname) != 0)
+ continue;
+#ifdef __linux__
+ struct sockaddr_ll *sll = (struct sockaddr_ll*) p->ifa_addr;
+ if (p->ifa_addr->sa_family != AF_PACKET
+ || sll->sll_hatype != ARPHRD_ETHER)
+#else
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *) p->ifa_addr;
+ if (p->ifa_addr->sa_family != AF_LINK || sdl->sdl_type != IFT_ETHER)
+#endif
+ {
+ logger (LOG_ERR, "not Ethernet");
+ freeifaddrs (ifap);
+ return NULL;
+ }
+
+ flags = p->ifa_flags;
+#ifdef __linux__
+ memcpy (hwaddr, sll->sll_addr, ETHER_ADDR_LEN);
+#else
+ memcpy (hwaddr, sdl->sdl_data + sdl->sdl_nlen, ETHER_ADDR_LEN);
+#endif
+ break;
+ }
+ freeifaddrs (ifap);
+
+ if (!p)
+ {
+ logger (LOG_ERR, "could not find interface %s", ifname);
+ return NULL;
+ }
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ strncpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ logger (LOG_ERR, "socket: %s", strerror (errno));
+ return NULL;
+ }
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ {
+ logger (LOG_ERR, "ioctl SIOCGIFFLAGS: %s", strerror (errno));
+ close (s);
+ return NULL;
+ }
+
+ ifr.ifr_flags |= IFF_UP | IFF_BROADCAST | IFF_NOTRAILERS | IFF_RUNNING;
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0)
+ {
+ logger (LOG_ERR, "ioctl SIOCSIFFLAGS: %s", strerror (errno));
+ close (s);
+ return NULL;
+ }
+
+#ifndef __linux__
+ ifr.ifr_metric = metric;
+ if (ioctl(s, SIOCSIFMETRIC, &ifr) < 0)
+ {
+ logger (LOG_ERR, "ioctl SIOCSIFMETRIC: %s", strerror (errno));
+ close (s);
+ return NULL;
+ }
+#endif
+
+ close (s);
+
+ iface = xmalloc (sizeof (interface_t));
+ memset (iface, 0, sizeof (interface_t));
+ strncpy (iface->name, ifname, IF_NAMESIZE);
+ snprintf (iface->infofile, PATH_MAX, INFOFILE, ifname);
+ memcpy (&iface->ethernet_address, &hwaddr, ETHER_ADDR_LEN);
+
+ iface->arpable = ! (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
+
+ logger (LOG_INFO, "ethernet address = %s",
+ ether_ntoa (&iface->ethernet_address));
+
+ /* 0 is a valid fd, so init to -1 */
+ iface->fd = -1;
+
+ return iface;
+}
+
+#ifdef __FreeBSD__
+static int do_address (const char *ifname, struct in_addr address,
+ struct in_addr netmask, struct in_addr broadcast, int del)
+{
+ if (! ifname)
+ return -1;
+
+ int s;
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ logger (LOG_ERR, "socket: %s", strerror (errno));
+ return -1;
+ }
+
+ struct ifaliasreq ifa;
+ memset (&ifa, 0, sizeof (ifa));
+ strcpy (ifa.ifra_name, ifname);
+
+#define ADDADDR(_var, _addr) \
+ { \
+ struct sockaddr_in *_sin = (struct sockaddr_in *) &_var; \
+ _sin->sin_family = AF_INET; \
+ _sin->sin_len = sizeof (struct sockaddr_in); \
+ memcpy (&_sin->sin_addr, &_addr, sizeof (struct in_addr)); \
+ }
+
+ ADDADDR (ifa.ifra_addr, address);
+ if (! del)
+ {
+ ADDADDR (ifa.ifra_mask, netmask);
+ ADDADDR (ifa.ifra_broadaddr, broadcast);
+ }
+
+#undef ADDADDR
+
+ if (ioctl (s, del ? SIOCDIFADDR : SIOCAIFADDR, &ifa) == -1)
+ {
+ logger (LOG_ERR, "ioctl %s: %s", del ? "SIOCDIFADDR" : "SIOCAIFADDR",
+ strerror (errno));
+ close (s);
+ return -1;
+ }
+
+ close (s);
+ return 0;
+}
+
+static int do_route (const char *ifname,
+ struct in_addr destination,
+ struct in_addr netmask,
+ struct in_addr gateway,
+ int metric,
+ int change, int del)
+{
+ if (! ifname)
+ return -1;
+
+ char *destd = strdup (inet_ntoa (destination));
+ char *gend = strdup (inet_ntoa (netmask));
+ logger (LOG_INFO, "%s route to %s (%s) via %s",
+ change ? "changing" : del ? "removing" : "adding",
+ destd, gend, inet_ntoa(gateway));
+ if (destd)
+ free (destd);
+ if (gend)
+ free (gend);
+
+ int s;
+ if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
+ {
+ logger (LOG_ERR, "socket: %s", strerror (errno));
+ return -1;
+ }
+
+ struct rtm
+ {
+ struct rt_msghdr hdr;
+ struct sockaddr_in destination;
+ struct sockaddr_in gateway;
+ struct sockaddr_in netmask;
+ } rtm;
+ memset (&rtm, 0, sizeof (struct rtm));
+
+ rtm.hdr.rtm_version = RTM_VERSION;
+ static int seq;
+ rtm.hdr.rtm_seq = ++seq;
+ rtm.hdr.rtm_type = change ? RTM_CHANGE : del ? RTM_DELETE : RTM_ADD;
+
+ rtm.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+ if (netmask.s_addr == 0xffffffff)
+ rtm.hdr.rtm_flags |= RTF_HOST;
+
+ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+#define ADDADDR(_var, _addr) \
+ _var.sin_family = AF_INET; \
+ _var.sin_len = sizeof (struct sockaddr_in); \
+ memcpy (&_var.sin_addr, &_addr, sizeof (struct in_addr));
+
+ ADDADDR (rtm.destination, destination);
+ ADDADDR (rtm.gateway, gateway);
+ ADDADDR (rtm.netmask, netmask);
+
+#undef ADDADDR
+
+ rtm.hdr.rtm_msglen = sizeof (rtm);
+
+ if (write(s, &rtm, sizeof (rtm)) < 0)
+ {
+ /* Don't report error about routes already existing */
+ if (errno != EEXIST)
+ logger (LOG_ERR, "write: %s", strerror (errno));
+ close (s);
+ return -1;
+ }
+
+ close (s);
+ return 0;
+}
+
+#elif __linux__
+/* This netlink stuff is overly compex IMO.
+ The BSD implementation is much cleaner and a lot less code.
+ send_netlink handles the actual transmission so we can work out
+ if there was an error or not.
+
+ As always throughout this code, credit is due :)
+ This blatently taken from libnetlink.c from the iproute2 package
+ which is the only good source of netlink code.
+ */
+static int send_netlink(struct nlmsghdr *hdr)
+{
+ int s;
+ if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
+ {
+ logger (LOG_ERR, "socket: %s", strerror (errno));
+ return -1;
+ }
+
+ int mypid = getpid ();
+ struct sockaddr_nl nl;
+ memset (&nl, 0, sizeof (struct sockaddr_nl));
+ nl.nl_family = AF_NETLINK;
+ if (bind (s, (struct sockaddr *) &nl, sizeof (nl)) < 0)
+ {
+ logger (LOG_ERR, "bind: %s", strerror (errno));
+ close (s);
+ return -1;
+ }
+
+ struct iovec iov;
+ memset (&iov, 0, sizeof (struct iovec));
+ iov.iov_base = hdr;
+ iov.iov_len = hdr->nlmsg_len;
+
+ struct msghdr msg;
+ memset (&msg, 0, sizeof (struct msghdr));
+ msg.msg_name = &nl;
+ msg.msg_namelen = sizeof (nl);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /* Request a reply */
+ hdr->nlmsg_flags |= NLM_F_ACK;
+ static int seq;
+ hdr->nlmsg_seq = ++seq;
+
+ if (sendmsg (s, &msg, 0) < 0)
+ {
+ logger (LOG_ERR, "write: %s", strerror (errno));
+ close (s);
+ return -1;
+ }
+
+ char buffer[16384];
+ memset (&buffer, 0, sizeof (buffer));
+ iov.iov_base = buffer;
+
+ struct nlmsghdr *h;
+ while (1)
+ {
+ iov.iov_len = sizeof (buffer);
+ int bytes = recvmsg(s, &msg, 0);
+
+ if (bytes < 0)
+ {
+ if (errno != EINTR)
+ logger (LOG_ERR, "overrun");
+ continue;
+ }
+
+ if (bytes == 0)
+ {
+ logger (LOG_ERR, "EOF on netlink");
+ goto eexit;
+ }
+
+ if (msg.msg_namelen != sizeof (nl))
+ {
+ logger (LOG_ERR, "sender address length == %d", msg.msg_namelen);
+ goto eexit;
+ }
+
+ for (h = (struct nlmsghdr *) buffer; bytes >= sizeof (*h); )
+ {
+ int len = h->nlmsg_len;
+ int l = len - sizeof (*h);
+
+ if (l < 0 || len > bytes)
+ {
+ if (msg.msg_flags & MSG_TRUNC)
+ logger (LOG_ERR, "truncated message");
+ else
+ logger (LOG_ERR, "malformed message");
+ goto eexit;
+ }
+
+ if (nl.nl_pid != 0 ||
+ h->nlmsg_pid != mypid ||
+ h->nlmsg_seq != seq)
+ /* Message isn't for us, so skip it */
+ goto next;
+
+ /* We get an NLMSG_ERROR back with a code of zero for success */
+ if (h->nlmsg_type == NLMSG_ERROR)
+ {
+ struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
+ if (l < sizeof (struct nlmsgerr))
+ logger (LOG_ERR, "truncated error message");
+ else
+ {
+ errno = -err->error;
+ if (errno == 0)
+ {
+ close (s);
+ return 0;
+ }
+
+ /* Don't report on something already existing */
+ if (errno != EEXIST)
+ logger (LOG_ERR, "RTNETLINK answers: %s", strerror (errno));
+ }
+ goto eexit;
+ }
+
+ logger (LOG_ERR, "unexpected reply");
+next:
+ bytes -= NLMSG_ALIGN (len);
+ h = (struct nlmsghdr *) ((char *) h + NLMSG_ALIGN (len));
+ }
+
+ if (msg.msg_flags & MSG_TRUNC)
+ {
+ logger (LOG_ERR, "message truncated");
+ continue;
+ }
+
+ if (bytes)
+ {
+ logger (LOG_ERR, "remnant of size %d", bytes);
+ goto eexit;
+ }
+ }
+
+eexit:
+ close (s);
+ return -1;
+}
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((unsigned char *) (nmsg)) \
+ + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+static int add_attr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen)
+ {
+ logger (LOG_ERR, "add_attr_l: message exceeded bound of %d\n", maxlen);
+ return -1;
+ }
+
+ rta = NLMSG_TAIL (n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy (RTA_DATA (rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len);
+
+ return 0;
+}
+
+static int add_attr_32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH (sizeof (uint32_t));
+ struct rtattr *rta;
+ if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
+ {
+ logger (LOG_ERR, "add_attr32: message exceeded bound of %d\n", maxlen);
+ return -1;
+ }
+
+ rta = NLMSG_TAIL (n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy (RTA_DATA (rta), &data, sizeof (uint32_t));
+ n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len;
+
+ return 0;
+}
+
+
+static int do_address(const char *ifname,
+ struct in_addr address, struct in_addr netmask,
+ struct in_addr broadcast, int del)
+{
+ if (!ifname)
+ return -1;
+
+ struct
+ {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg ifa;
+ char buffer[256];
+ }
+ nlm;
+
+ memset (&nlm, 0, sizeof (nlm));
+
+ nlm.hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
+ nlm.hdr.nlmsg_flags = NLM_F_REQUEST;
+ nlm.hdr.nlmsg_type = del ? RTM_DELADDR : RTM_NEWADDR;
+ nlm.ifa.ifa_index = if_nametoindex (ifname);
+ nlm.ifa.ifa_family = AF_INET;
+
+ /* Store the netmask in the prefix */
+ uint32_t mask = htonl (netmask.s_addr);
+ while (mask)
+ {
+ nlm.ifa.ifa_prefixlen++;
+ mask <<= 1;
+ }
+
+ add_attr_l (&nlm.hdr, sizeof (nlm), IFA_LOCAL, &address.s_addr,
+ sizeof (address.s_addr));
+ if (! del)
+ add_attr_l (&nlm.hdr, sizeof (nlm), IFA_BROADCAST, &broadcast.s_addr,
+ sizeof (broadcast.s_addr));
+
+ return send_netlink (&nlm.hdr);
+}
+
+static int do_route (const char *ifname,
+ struct in_addr destination,
+ struct in_addr netmask,
+ struct in_addr gateway,
+ int metric, int change, int del)
+{
+ if (! ifname)
+ return -1;
+
+ char *dstd = strdup (inet_ntoa (destination));
+ char *gend = strdup (inet_ntoa (netmask));
+ logger (LOG_INFO, "%s route to %s (%s) via %s, metric %d",
+ change ? "changing" : del ? "removing" : "adding",
+ dstd, gend, inet_ntoa (gateway), metric);
+ if (dstd)
+ free (dstd);
+ if (gend)
+ free (gend);
+
+ struct
+ {
+ struct nlmsghdr hdr;
+ struct rtmsg rt;
+ char buffer[256];
+ }
+ nlm;
+ memset (&nlm, 0, sizeof (nlm));
+
+ nlm.hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
+ if (change)
+ nlm.hdr.nlmsg_flags = NLM_F_REPLACE;
+ else if (! del)
+ nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
+ nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
+ nlm.hdr.nlmsg_type = del ? RTM_DELROUTE : RTM_NEWROUTE;
+ nlm.rt.rtm_family = AF_INET;
+ nlm.rt.rtm_table = RT_TABLE_MAIN;
+
+ if (del)
+ nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
+ else
+ {
+ nlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+ nlm.rt.rtm_protocol = RTPROT_BOOT;
+ if (gateway.s_addr == 0)
+ nlm.rt.rtm_scope = RT_SCOPE_LINK;
+ else
+ nlm.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ nlm.rt.rtm_type = RTN_UNICAST;
+ }
+
+ /* Store the netmask in the prefix */
+ uint32_t mask = htonl (netmask.s_addr);
+ while (mask)
+ {
+ nlm.rt.rtm_dst_len++;
+ mask <<= 1;
+ }
+
+ add_attr_l (&nlm.hdr, sizeof (nlm), RTA_DST, &destination.s_addr,
+ sizeof (destination.s_addr));
+ if (gateway.s_addr != 0)
+ add_attr_l (&nlm.hdr, sizeof (nlm), RTA_GATEWAY, &gateway.s_addr,
+ sizeof (gateway.s_addr));
+
+ add_attr_32 (&nlm.hdr, sizeof (nlm), RTA_OIF, if_nametoindex (ifname));
+ add_attr_32 (&nlm.hdr, sizeof (nlm), RTA_PRIORITY, metric);
+
+ return send_netlink (&nlm.hdr);
+}
+
+#else
+#error "Platform not supported!"
+#error "We currently support BPF and Linux sockets."
+#error "Other platforms may work using BPF. If yours does, please let me know"
+#error "so I can add it to our list."
+#endif
+
+
+int add_address (const char *ifname, struct in_addr address,
+ struct in_addr netmask, struct in_addr broadcast)
+{
+ char *daddress = strdup (inet_ntoa (address));
+ logger (LOG_INFO, "adding IP address %s netmask %s",
+ daddress, inet_ntoa (netmask));
+ free (daddress);
+
+ return (do_address (ifname, address, netmask, broadcast, 0));
+}
+
+int del_address (const char *ifname, struct in_addr address)
+{
+ logger (LOG_INFO, "deleting IP address %s", inet_ntoa (address));
+
+ struct in_addr t;
+ memset (&t, 0, sizeof (t));
+ return (do_address (ifname, address, t, t, 1));
+}
+
+/* This should work on all platforms */
+int flush_addresses (const char *ifname)
+{
+ if (! ifname)
+ return -1;
+
+ struct ifaddrs *ifap;
+ struct ifaddrs *p;
+
+ if (getifaddrs (&ifap) != 0)
+ return -1;
+
+ for (p = ifap; p; p = p->ifa_next)
+ {
+ if (strcmp (p->ifa_name, ifname) != 0)
+ continue;
+
+ struct sockaddr_in *sin = (struct sockaddr_in*) p->ifa_addr;
+ if (sin->sin_family == AF_INET)
+ del_address (ifname, sin->sin_addr);
+ }
+ freeifaddrs (ifap);
+
+ return 0;
+}
+
+int add_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric)
+{
+ return (do_route (ifname, destination, netmask, gateway, metric, 0, 0));
+}
+
+int change_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric)
+{
+ return (do_route (ifname, destination, netmask, gateway, metric, 1, 0));
+}
+
+int del_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric)
+{
+ return (do_route (ifname, destination, netmask, gateway, metric, 0, 1));
+}
+
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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.
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <stdbool.h>
+
+typedef struct route_t
+{
+ struct in_addr destination;
+ struct in_addr netmask;
+ struct in_addr gateway;
+ struct route_t *next;
+} route_t;
+
+typedef struct address_t
+{
+ struct in_addr address;
+ struct address_t *next;
+} address_t;
+
+typedef struct interface_t
+{
+ char name[IF_NAMESIZE];
+ struct ether_addr ethernet_address;
+ bool arpable;
+
+ int fd;
+ int buffer_length;
+
+#ifdef __linux__
+ int socket_protocol;
+#endif
+
+ char infofile[PATH_MAX];
+
+ struct in_addr previous_address;
+ route_t *previous_routes;
+} interface_t;
+
+void free_address (address_t *addresses);
+void free_route (route_t *routes);
+interface_t *read_interface (const char *ifname, int metric);
+
+int add_address (const char *ifname, struct in_addr address,
+ struct in_addr netmask, struct in_addr broadcast);
+int del_address (const char *ifname, struct in_addr address);
+int flush_addresses (const char *ifname);
+
+int add_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric);
+int change_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric);
+int del_route (const char *ifname, struct in_addr destination,
+ struct in_addr netmask, struct in_addr gateway, int metric);
+#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "logger.h"
+
+static int loglevel = LOG_WARNING;
+static char logprefix[12] = {0};
+
+static char *syslog_level_msg[] = {
+ [LOG_EMERG] = "EMERGENCY!",
+ [LOG_ALERT] = "ALERT!",
+ [LOG_CRIT] = "Critical!",
+ [LOG_WARNING] = "Warning",
+ [LOG_ERR] = "Error",
+ [LOG_INFO] = "Info",
+ [LOG_DEBUG] = "Debug",
+ [LOG_DEBUG + 1] = NULL
+};
+
+static char *syslog_level[] = {
+ [LOG_EMERG] = "LOG_EMERG",
+ [LOG_ALERT] = "LOG_ALERT",
+ [LOG_CRIT] = "LOG_CRIT",
+ [LOG_ERR] = "LOG_ERR",
+ [LOG_WARNING] = "LOG_WARNING",
+ [LOG_NOTICE] = "LOG_NOTICE",
+ [LOG_INFO] = "LOG_INFO",
+ [LOG_DEBUG] = "LOG_DEBUG",
+ [LOG_DEBUG + 1] = NULL
+};
+
+int logtolevel (const char *priority)
+{
+ int i = 0;
+
+ while (syslog_level[i])
+ {
+ if (!strcmp (priority, syslog_level[i]))
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+void setloglevel (int level)
+{
+ loglevel = level;
+}
+
+void setlogprefix (const char *prefix)
+{
+ snprintf (logprefix, sizeof (logprefix), "%s", prefix);
+}
+
+void logger(int level, const char *fmt, ...)
+{
+ va_list p;
+ va_list p2;
+ FILE *f = stderr;
+
+ va_start (p, fmt);
+ va_copy (p2, p);
+
+ if (level <= LOG_ERR || level <= loglevel)
+ {
+ if (level == LOG_DEBUG || level == LOG_INFO)
+ f = stdout;
+ fprintf (f, "%s, %s", syslog_level_msg[level], logprefix);
+ vfprintf (f, fmt, p);
+ fputc ('\n', f);
+ }
+
+ if (level < LOG_DEBUG || level <= loglevel)
+ {
+ int len = strlen (logprefix);
+ char *fmt2 = xmalloc (strlen (fmt) + len + 1);
+ char *p = fmt2;
+ memcpy (p, &logprefix, len);
+ p += len;
+ strcpy (p, fmt);
+ vsyslog (level, fmt2, p2);
+ free (fmt2);
+ }
+
+ va_end (p);
+}
+
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 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.
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <syslog.h>
+
+int logtolevel (const char *priority);
+void setloglevel (int level);
+void setlogprefix (const char *prefix);
+void logger (int level, const char *fmt, ...);
+
+#endif
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#define PACKAGE "dhcpcd"
+
+#define ETCDIR "/etc"
+#define RESOLVFILE ETCDIR "/resolv.conf"
+#define NISFILE ETCDIR "/yp.conf"
+#define NTPFILE ETCDIR "/ntp.conf"
+#define NTPDRIFTFILE ETCDIR "/ntp.drift"
+#define DEFAULT_SCRIPT ETCDIR "/" PACKAGE ".sh"
+
+#define STATEDIR "/var"
+#define PIDFILE STATEDIR "/run/" PACKAGE "-%s.pid"
+
+#define CONFIGDIR STATEDIR "/lib/" PACKAGE
+#define INFOFILE CONFIGDIR "/" PACKAGE "-%s.info"
+
+#define NTPLOGFILE "/var/log/ntp.log"
+
+#endif
--- /dev/null
+/*
+ * Shameless taken from udhcp as I think it's a good idea.
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program 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/socket.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "logger.h"
+
+static int signal_pipe[2];
+
+static void signal_handler (int sig)
+{
+ if (send (signal_pipe[1], &sig, sizeof (sig), MSG_DONTWAIT) < 0)
+ logger (LOG_ERR, "Could not send signal: %s", strerror (errno));
+}
+
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void signal_setup(void)
+{
+ int i;
+ int flags;
+
+ socketpair (AF_UNIX, SOCK_STREAM, 0, signal_pipe);
+
+ /* Stop any scripts from inheriting us */
+ for (i = 0; i < 2; i++)
+ if ((flags = fcntl (signal_pipe[i], F_GETFD, 0)) < 0 ||
+ fcntl (signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) < 0)
+ logger (LOG_ERR ,"fcntl: %s", strerror (errno));
+
+ signal (SIGHUP, signal_handler);
+ signal (SIGALRM, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGINT, signal_handler);
+}
+
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int signal_fd_set (fd_set *rfds, int extra_fd)
+{
+ FD_ZERO (rfds);
+ FD_SET (signal_pipe[0], rfds);
+ if (extra_fd >= 0)
+ FD_SET (extra_fd, rfds);
+ return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
+}
+
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int signal_read (fd_set *rfds)
+{
+ int sig;
+
+ if (!FD_ISSET (signal_pipe[0], rfds))
+ return 0;
+
+ if (read (signal_pipe[0], &sig, sizeof (sig)) < 0)
+ return -1;
+
+ return sig;
+}
+
--- /dev/null
+/*
+ * Shameless taken from udhcp as I think it's a good idea.
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program 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.
+ */
+
+#ifndef SIGNALS_H
+#define SIGNALS_H
+
+void signal_setup (void);
+int signal_fd_set (fd_set *rfds, int extra_fd);
+int signal_read (fd_set *rfds);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2006 Roy Marples <uberlord@gentoo.org>
+ * although a lot was lifted from udhcp
+ *
+ * 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.
+ */
+
+/* We use BSD structure so our code is more portable */
+#define _BSD_SOURCE
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dhcp.h"
+#include "interface.h"
+#include "logger.h"
+
+/* A suitably large buffer for all transactions.
+ BPF buffer size is set by the kernel, so no define. */
+#ifdef __linux__
+#define BUFFER_LENGTH 4096
+#endif
+
+static uint16_t checksum (unsigned char *addr, uint16_t len)
+{
+ register uint32_t sum = 0;
+ register uint16_t *w = (uint16_t *) addr;
+ register uint16_t nleft = len;
+
+ while (nleft > 1)
+ {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1)
+ {
+ uint8_t a = 0;
+ memcpy (&a, w, 1);
+ sum += ntohs (a) << 8;
+ // sum += a;
+ }
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+
+ return ~sum;
+}
+
+void make_dhcp_packet(struct udp_dhcp_packet *packet,
+ unsigned char *data,
+ struct in_addr source, struct in_addr dest)
+{
+ struct ip *ip = &packet->ip;
+ struct udphdr *udp = &packet->udp;
+
+ /* OK, this is important :)
+ We copy the data to our packet and then create a small part of the
+ ip structure and an invalid ip_len (basically udp length).
+ We then fill the udp structure and put the checksum
+ of the whole packet into the udp checksum.
+ Finally we complete the ip structure and ip checksum.
+ If we don't do the ordering like so then the udp checksum will be
+ broken, so find another way of doing it! */
+
+ memcpy (&packet->dhcp, data, sizeof (dhcpmessage_t));
+
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_src.s_addr = htonl (source.s_addr);
+ if (dest.s_addr == 0)
+ ip->ip_dst.s_addr = htonl (INADDR_BROADCAST);
+ else
+ ip->ip_dst.s_addr = htonl (dest.s_addr);
+
+ udp->uh_sport = htons (DHCP_CLIENT_PORT);
+ udp->uh_dport = htons (DHCP_SERVER_PORT);
+ udp->uh_ulen = htons (sizeof (struct udphdr) + sizeof (struct dhcpmessage_t));
+ ip->ip_len = udp->uh_ulen;
+ udp->uh_sum = checksum ((unsigned char *) packet,
+ sizeof (struct udp_dhcp_packet));
+
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = 5;
+ ip->ip_id = 0;
+ ip->ip_tos = IPTOS_LOWDELAY;
+ ip->ip_len = htons (sizeof (struct ip) + sizeof (struct udphdr) +
+ sizeof (struct dhcpmessage_t));
+ ip->ip_id = 0;
+ ip->ip_off = 0;
+ ip->ip_ttl = IPDEFTTL;
+
+ ip->ip_sum = checksum ((unsigned char *) ip, sizeof (struct ip));
+}
+
+static int valid_dhcp_packet (unsigned char * data)
+{
+ struct udp_dhcp_packet *packet = (struct udp_dhcp_packet *) data;
+ uint16_t bytes = ntohs (packet->ip.ip_len);
+ uint16_t ipsum = packet->ip.ip_sum;
+ uint16_t iplen = packet->ip.ip_len;
+ uint16_t udpsum = packet->udp.uh_sum;
+ struct in_addr source;
+ struct in_addr dest;
+ int retval = 0;
+
+ packet->ip.ip_sum = 0;
+ if (ipsum != checksum ((unsigned char *) &packet->ip, sizeof (struct ip)))
+ {
+ logger (LOG_DEBUG, "bad IP header checksum, ignoring");
+ retval = -1;
+ goto eexit;
+ }
+
+ memcpy (&source, &packet->ip.ip_src, sizeof (struct in_addr));
+ memcpy (&dest, &packet->ip.ip_dst, sizeof (struct in_addr));
+ memset (&packet->ip, 0, sizeof (struct ip));
+ packet->udp.uh_sum = 0;
+
+ packet->ip.ip_p = IPPROTO_UDP;
+ memcpy (&packet->ip.ip_src, &source, sizeof (struct in_addr));
+ memcpy (&packet->ip.ip_dst, &dest, sizeof (struct in_addr));
+ packet->ip.ip_len = packet->udp.uh_ulen;
+ if (udpsum && udpsum != checksum ((unsigned char *) packet, bytes))
+ {
+ logger (LOG_ERR, "bad UDP checksum, ignoring");
+ retval = -1;
+ }
+
+eexit:
+ packet->ip.ip_sum = ipsum;
+ packet->ip.ip_len = iplen;
+ packet->udp.uh_sum = udpsum;
+
+ return retval;
+}
+
+#ifdef __FreeBSD__
+
+/* Credit where credit is due :)
+ The below BPF filter is taken from ISC DHCP */
+
+# include <net/bpf.h>
+
+static struct bpf_insn dhcp_bpf_filter [] = {
+ /* Make sure this is an IP packet... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+ /* Get the IP header length... */
+ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1),
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT (BPF_RET+BPF_K, (u_int) - 1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT (BPF_RET+BPF_K, 0),
+};
+
+static struct bpf_insn arp_bpf_filter [] = {
+ /* Make sure this is an ARP packet... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3),
+
+ /* Make sure this is an ARP REPLY... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1),
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT (BPF_RET+BPF_K, (u_int) - 1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT (BPF_RET+BPF_K, 0),
+};
+
+int open_socket (interface_t *iface, bool arp)
+{
+ int n = 0;
+ int fd = 0;
+ char device[PATH_MAX];
+
+ do
+ {
+ snprintf (device, PATH_MAX, "/dev/bpf%d", n++);
+ fd = open (device, O_RDWR);
+ } while (fd < 0 && errno == EBUSY);
+
+ if (fd < 0)
+ {
+ logger (LOG_ERR, "unable to open a BPF device");
+ return -1;
+ }
+
+ int flags;
+ if ((flags = fcntl (fd, F_GETFD, 0)) < 0
+ || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+ {
+ logger (LOG_ERR, "fcntl: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ struct ifreq ifr;
+ strncpy (ifr.ifr_name, iface->name, sizeof (ifr.ifr_name));
+ if (ioctl (fd, BIOCSETIF, &ifr) < 0)
+ {
+ logger (LOG_ERR, "cannot attach interface `%s' to bpf device `%s': %s",
+ iface->name, strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Get the required BPF buffer length from the kernel. */
+ int buf = 0;
+ if (ioctl (fd, BIOCGBLEN, &buf) < 0)
+ {
+ logger (LOG_ERR, "ioctl BIOCGBLEN: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+ iface->buffer_length = buf;
+
+ int flag = 1;
+ if (ioctl (fd, BIOCIMMEDIATE, &flag) < 0)
+ {
+ logger (LOG_ERR, "ioctl BIOCIMMEDIATE: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Install the DHCP filter */
+ struct bpf_program p;
+ if (arp)
+ {
+ p.bf_insns = arp_bpf_filter;
+ p.bf_len = sizeof (arp_bpf_filter) / sizeof (struct bpf_insn);
+ }
+ else
+ {
+ p.bf_insns = dhcp_bpf_filter;
+ p.bf_len = sizeof (dhcp_bpf_filter) / sizeof (struct bpf_insn);
+ }
+ if (ioctl (fd, BIOCSETF, &p) < 0)
+ {
+ logger (LOG_ERR, "ioctl BIOCSETF: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (iface->fd > -1)
+ close (iface->fd);
+ iface->fd = fd;
+
+ return fd;
+}
+
+int send_packet (interface_t *iface, int type, unsigned char *data,
+ unsigned int len)
+{
+ /* We only support ethernet atm */
+ struct ether_header hw;
+ memset (&hw, 0, sizeof (struct ether_header));
+ memset (&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
+ hw.ether_type = htons (type);
+
+ int retval = -1;
+ struct iovec iov[2];
+
+ iov[0].iov_base = &hw;
+ iov[0].iov_len = sizeof (struct ether_header);
+ iov[1].iov_base = data;
+ iov[1].iov_len = len;
+
+ if ((retval = writev(iface->fd, iov, 2)) == -1)
+ logger (LOG_ERR, "writev: %s", strerror (errno));
+
+ return retval;
+}
+
+/* BPF requires that we read the entire buffer.
+ So we pass the buffer in the API so we can loop on >1 dhcp packet. */
+int get_packet (interface_t *iface, unsigned char *data,
+ unsigned char *buffer, int *buffer_len, int *buffer_pos)
+{
+ unsigned char *buf = buffer;
+ struct bpf_hdr *packet;
+ struct ether_header *hw;
+ unsigned char *hdr;
+
+ if (*buffer_pos < 1)
+ {
+ memset (buf, 0, iface->buffer_length);
+ *buffer_len = read (iface->fd, buf, iface->buffer_length);
+ *buffer_pos = 0;
+ if (*buffer_len < 1)
+ {
+ logger (LOG_ERR, "read: %s", strerror (errno));
+ return -1;
+ }
+ }
+ else
+ buf += *buffer_pos;
+
+ packet = (struct bpf_hdr *) buf;
+ while (packet)
+ {
+ /* Ensure that the entire packet is in our buffer */
+ if (*buffer_pos + packet->bh_hdrlen + packet->bh_caplen > *buffer_len)
+ break;
+
+ hw = (struct ether_header *) ((char *) packet + packet->bh_hdrlen);
+ hdr = (unsigned char *) ((char *) hw + sizeof (struct ether_header));
+
+ /* If it's an ARP reply, then just send it back */
+ int len = -1;
+ if (hw->ether_type == htons (ETHERTYPE_ARP))
+ {
+ len = packet->bh_caplen - sizeof (struct ether_header);
+ memcpy (data, hdr, len);
+ }
+ else
+ {
+ if (valid_dhcp_packet (hdr) >= 0)
+ {
+ struct udp_dhcp_packet *dhcp = (struct udp_dhcp_packet *) hdr;
+ len = ntohs (dhcp->ip.ip_len) - sizeof (struct ip) -
+ sizeof (struct udphdr);
+ memcpy (data, &dhcp->dhcp, len);
+ }
+ }
+
+ /* Update the buffer_pos pointer */
+ packet += BPF_WORDALIGN (packet->bh_hdrlen + packet->bh_caplen);
+ if (packet - (struct bpf_hdr *) buffer < *buffer_len)
+ *buffer_pos = (packet - (struct bpf_hdr *) buffer);
+ else
+ *buffer_pos = 0;
+
+ if (len != -1)
+ return len;
+
+ if (*buffer_pos == 0)
+ break;
+ }
+
+ /* No valid packets left, so return */
+ *buffer_pos = 0;
+ return -1;
+}
+
+#elif __linux__
+
+#include <netpacket/packet.h>
+
+int open_socket (interface_t *iface, bool arp)
+{
+ int fd;
+ int flags;
+ struct sockaddr_ll sll;
+
+ if ((fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_IP))) == -1)
+ {
+ logger (LOG_ERR, "socket: %s", strerror (errno));
+ return -1;
+ }
+
+ if ((flags = fcntl (fd, F_GETFD, 0)) < 0
+ || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+ {
+ logger (LOG_ERR, "fcntl: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ memset (&sll, 0, sizeof (struct sockaddr_ll));
+ sll.sll_family = AF_PACKET;
+ if (arp)
+ sll.sll_protocol = htons (ETH_P_ARP);
+ else
+ sll.sll_protocol = htons (ETH_P_IP);
+ sll.sll_ifindex = if_nametoindex (iface->name);
+ sll.sll_halen = ETHER_ADDR_LEN;
+ memset(sll.sll_addr, 0xff, sizeof (sll.sll_addr));
+
+ if (bind(fd, (struct sockaddr *) &sll, sizeof (struct sockaddr_ll)) == -1)
+ {
+ logger (LOG_ERR, "bind: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (iface->fd > -1)
+ close (iface->fd);
+ iface->fd = fd;
+ iface->socket_protocol = ntohs (sll.sll_protocol);
+
+ iface->buffer_length = BUFFER_LENGTH;
+
+ return fd;
+}
+
+int send_packet (interface_t *iface, int type, unsigned char *data, int len)
+{
+ struct sockaddr_ll sll;
+ int retval;
+
+ if (! iface)
+ return -1;
+
+ memset (&sll, 0, sizeof (struct sockaddr_ll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_protocol = htons (type);
+ sll.sll_ifindex = if_nametoindex (iface->name);
+ sll.sll_halen = ETHER_ADDR_LEN;
+ memset(sll.sll_addr, 0xff, sizeof (sll.sll_addr));
+
+ if ((retval = sendto (iface->fd, data, len, 0, (struct sockaddr *) &sll,
+ sizeof (struct sockaddr_ll))) < 0)
+
+ logger (LOG_ERR, "sendto: %s", strerror (errno));
+ return retval;
+}
+
+/* Linux has no need for the buffer as we can read as much as we want.
+ We only have the buffer listed to keep the same API. */
+size_t get_packet (interface_t *iface, unsigned char *data,
+ unsigned char *buffer, int *buffer_len, int *buffer_pos)
+{
+ long bytes;
+
+ /* We don't use the given buffer, but we need to rewind the position */
+ *buffer_pos = 0;
+
+ memset (buffer, 0, iface->buffer_length);
+ bytes = read (iface->fd, buffer, iface->buffer_length);
+ if (bytes < 0)
+ {
+ logger (LOG_ERR, "read: %s", strerror (errno));
+ return -1;
+ }
+
+ *buffer_len = bytes;
+ /* If it's an ARP reply, then just send it back */
+ if (iface->socket_protocol == ETH_P_ARP)
+ {
+ memcpy (data, buffer, bytes);
+ return bytes;
+ }
+
+ if (bytes < (sizeof (struct ip) + sizeof (struct udphdr)))
+ {
+ logger (LOG_DEBUG, "message too short, ignoring");
+ return -1;
+ }
+
+ struct udp_dhcp_packet *dhcp = (struct udp_dhcp_packet *) buffer;
+ if (bytes < ntohs (dhcp->ip.ip_len))
+ {
+ logger (LOG_DEBUG, "truncated packet, ignoring");
+ return -1;
+ }
+
+ bytes = ntohs (dhcp->ip.ip_len);
+
+ /* This is like our BPF filter above */
+ if (dhcp->ip.ip_p != IPPROTO_UDP || dhcp->ip.ip_v != IPVERSION ||
+ dhcp->ip.ip_hl != sizeof (dhcp->ip) >> 2 ||
+ dhcp->udp.uh_dport != htons (DHCP_CLIENT_PORT) ||
+ bytes > (int) sizeof (struct udp_dhcp_packet) ||
+ ntohs (dhcp->udp.uh_ulen) != (uint16_t) (bytes - sizeof (dhcp->ip)))
+ {
+ return -1;
+ }
+
+ if (valid_dhcp_packet (buffer) < 0)
+ return -1;
+
+ memcpy(data, &dhcp->dhcp, bytes - (sizeof (dhcp->ip) +
+ sizeof (dhcp->udp)));
+
+ return bytes - (sizeof (dhcp->ip) + sizeof (dhcp->udp));
+}
+
+#else
+#error "Platform not supported!"
+#error "We currently support BPF and Linux sockets."
+#error "Other platforms may work using BPF. If yours does, please let me know"
+#error "so I can add it to our list."
+#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright (C) 2006 Roy Marples <uberlord@gentoo.org>
+ * although a lot was lifted from udhcp
+ *
+ * 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.
+ */
+
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <stdbool.h>
+
+#include "dhcp.h"
+#include "interface.h"
+
+void make_dhcp_packet(struct udp_dhcp_packet *packet,
+ unsigned char *data,
+ struct in_addr source, struct in_addr dest);
+
+int open_socket (interface_t *iface, bool arp);
+int send_packet (interface_t *iface, int type, unsigned char *data, unsigned int len);
+int get_packet (interface_t *iface, unsigned char *data,
+ unsigned char *buffer, int *buffer_len, int *buffer_pos);
+#endif