summaryrefslogtreecommitdiff
path: root/trans
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>2000-03-04 20:58:29 +0000
committerThomas Bushnell <thomas@gnu.org>2000-03-04 20:58:29 +0000
commit588b9dcfd643d74398c83d5317b32bc82936b4d5 (patch)
tree67a479fafe47ce87aa9e5d9edcb3e3ae25eafc97 /trans
parent50c67b66a0a7a7c54169032ff4388dfb10ad7cb5 (diff)
new file
Diffstat (limited to 'trans')
-rw-r--r--trans/pump.c1874
1 files changed, 1874 insertions, 0 deletions
diff --git a/trans/pump.c b/trans/pump.c
new file mode 100644
index 00000000..a4014eca
--- /dev/null
+++ b/trans/pump.c
@@ -0,0 +1,1874 @@
+/* Initialize an Ethernet interface
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/*
+ * Copyright 1999 Red Hat Software, Inc.
+ *
+ * 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.
+ *
+ */
+
+/* This probably doesn't work on anything other then Ethernet! It may work
+ on PLIP as well, but ARCnet and Token Ring are unlikely at best. */
+
+#define DHCP_FLAG_NODAEMON (1 << 0)
+#define DHCP_FLAG_NOCONFIG (1 << 1)
+#define DHCP_FLAG_FORCEHNLOOKUP (1 << 2)
+
+#define RESULT_OKAY 0
+#define RESULT_FAILED 1
+#define RESULT_UNKNOWNIFACE 2
+
+#define N_(foo) (foo)
+
+#define PROGNAME "pump"
+#define CONTROLSOCKET "/var/run/pump.sock"
+
+#define _(foo) ((foo))
+#include <stdarg.h>
+
+void logMessage(char * mess, ...) {
+ va_list args;
+
+ va_start(args, mess);
+ vprintf(mess, args);
+ va_end(args);
+ puts("");
+}
+
+typedef int bp_int32;
+typedef short bp_int16;
+
+#define BOOTP_OPTION_NETMASK 1
+#define BOOTP_OPTION_GATEWAY 3
+#define BOOTP_OPTION_DNS 6
+#define BOOTP_OPTION_HOSTNAME 12
+#define BOOTP_OPTION_BOOTFILE 13
+#define BOOTP_OPTION_DOMAIN 15
+#define BOOTP_OPTION_BROADCAST 28
+
+#define DHCP_OPTION_REQADDR 50
+#define DHCP_OPTION_LEASE 51
+#define DHCP_OPTION_OVERLOAD 52
+#define DHCP_OPTION_TYPE 53
+#define DHCP_OPTION_SERVER 54
+#define DHCP_OPTION_OPTIONREQ 55
+#define DHCP_OPTION_MAXSIZE 57
+#define DHCP_OPTION_T1 58
+
+#define BOOTP_CLIENT_PORT 68
+#define BOOTP_SERVER_PORT 67
+
+#define BOOTP_OPCODE_REQUEST 1
+#define BOOTP_OPCODE_REPLY 2
+
+#define NORESPONSE -10
+#define DHCP_TYPE_DISCOVER 1
+#define DHCP_TYPE_OFFER 2
+#define DHCP_TYPE_REQUEST 3
+#define DHCP_TYPE_DECLINE 4
+#define DHCP_TYPE_ACK 5
+#define DHCP_TYPE_NAK 6
+#define DHCP_TYPE_RELEASE 7
+#define DHCP_TYPE_INFORM 8
+
+#define BOOTP_VENDOR_LENGTH 64
+#define DHCP_VENDOR_LENGTH 312
+
+struct bootpRequest {
+ char opcode;
+ char hw;
+ char hwlength;
+ char hopcount;
+ bp_int32 id;
+ bp_int16 secs;
+ bp_int16 flags;
+ bp_int32 ciaddr, yiaddr, server_ip, bootp_gw_ip;
+ char hwaddr[16];
+ char servername[64];
+ char bootfile[128];
+ char vendor[DHCP_VENDOR_LENGTH];
+} ;
+
+struct psuedohUdpHeader {
+ bp_int32 source, dest;
+ char zero;
+ char protocol;
+ bp_int16 len;
+};
+
+struct command {
+ enum { CMD_STARTIFACE, CMD_RESULT, CMD_DIE, CMD_STOPIFACE,
+ CMD_FORCERENEW, CMD_REQSTATUS, CMD_STATUS } type;
+ union {
+ struct {
+ char device[20];
+ int flags;
+ int reqLease; /* in hours */
+ char reqHostname[200];
+ } start;
+ int result; /* 0 for success */
+ struct {
+ char device[20];
+ } stop;
+ struct {
+ char device[20];
+ } renew;
+ struct {
+ char device[20];
+ } reqstatus;
+ struct {
+ struct intfInfo intf;
+ char hostname[1024];
+ char domain[1024];
+ char bootFile[1024];
+ } status;
+ } u;
+};
+
+static char * doDhcp(char * device, int flags, int lease,
+ char * reqHostname, struct intfInfo * intf,
+ struct overrideInfo * override);
+
+static const char vendCookie[] = { 99, 130, 83, 99, 255 };
+
+static char * perrorstr(char * msg);
+static char * setupInterface(struct intfInfo * intf, int s);
+static char * prepareInterface(struct intfInfo * intf, int s);
+static char * getInterfaceInfo(struct intfInfo * intf, int s);
+static char * disableInterface(char * device);
+static void parseReply(struct bootpRequest * breq, struct intfInfo * intf);
+static char * prepareRequest(struct bootpRequest * breq,
+ int sock, char * device, time_t startTime);
+static void parseLease(struct bootpRequest * bresp, struct intfInfo * intf);
+static void initVendorCodes(struct bootpRequest * breq);
+static char * handleTransaction(int s, struct overrideInfo * override,
+ struct bootpRequest * breq,
+ struct bootpRequest * bresp,
+ struct sockaddr_in * serverAddr,
+ struct sockaddr_in * respondant,
+ int useBootPacket, int dhcpResponseType);
+static int dhcpMessageType(struct bootpRequest * response);
+static void setMissingIpInfo(struct intfInfo * intf);
+static int openControlSocket(char * configFile);
+static int dhcpRenew(struct intfInfo * intf);
+static int dhcpRelease(struct intfInfo * intf);
+
+/* Various hurd-specific decls here */
+#define _HURD_PFINET _HURD "pfinet"
+io_t underlying_pfinet;
+device_t ethernet_device;
+char ethernet_address[ETH_ALEN];
+
+
+/* Open the ethernet device */
+char *
+prepareInterface (struct intfInfo *intf, int s);
+{
+ u_int count;
+ int addr[2];
+
+ err = device_open (master_device, D_WRITE|D_READ,
+ intf->device, &ethernet_device);
+ if (err)
+ return err;
+
+ err = device_get_status (ethernet_device, NET_ADDRESS, addr, &count);
+ if (err)
+ return err;
+
+ addr[0] = ntohl (addr[0]);
+ addr[1] = ntohl (addr[1]);
+ bcopy (addr, ethernet_address, ETH_ALEN);
+ return 0;
+}
+
+/* Open the node underlying the pfinet translator, and set
+ underlying_pfinet to a port to that file. */
+error_t
+open_underlying_pfinet ()
+{
+ char *path;
+
+ if (underlying_pfinet != MACH_PORT_NULL)
+ return 0;
+
+ asprintf (&path, "%s/%d", _SERVERS_SOCKET, PF_INET);
+
+ underlying_pfinet = file_name_lookup (path, O_NOTRANS, 0);
+ free (path);
+ return underlying_pfinet == MACH_PORT_NULL ? errno : 0;
+}
+
+/* Start pfinet with the specified arguments */
+error_t
+start_pfinet (char *argz, int argz_len)
+{
+ error_t open_function (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type)
+ {
+ int err;
+
+ err = open_underlying_pfinet ();
+ if (err)
+ return err;
+
+ *underlying = underlying_pfinet;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+
+ err = fshelp_start_translator (open_function,
+ _HURD_PFINET, argz, argz_len,
+ 60 * 1000, &control);
+ if (err)
+ return err;
+
+ err = file_set_translator (underlying_pfinet,
+ 0, FS_TRANS_SET,
+ FSYS_GOAWAY_FORCE,
+ argz, argz_len,
+ control, MACH_MSG_TYPE_MOVE_SEND);
+
+ /* Force the C library to forget about any old cached server
+ access port. */
+ _hurd_socket_server (PFINET, 1);
+}
+
+
+
+int newKernel(void) {
+#ifdef NOTHURD
+ struct utsname ubuf;
+ int major1, major2;
+
+ uname(&ubuf);
+ if (!strcasecmp(ubuf.sysname, "linux")) {
+ if (sscanf(ubuf.release, "%d.%d", &major1, &major2) != 2 ||
+ (major1 < 2) || (major1 == 2 && major2 == 0)) {
+ return 0;
+ }
+ }
+#endif
+
+ return 1;
+}
+
+static char * disableInterface(char * device) {
+#ifdef NOTHURD
+ struct ifreq req;
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ strcpy(req.ifr_name, device);
+ if (ioctl(s, SIOCGIFFLAGS, &req)) {
+ close(s);
+ return perrorstr("SIOCGIFFLAGS");
+ }
+
+ req.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
+ if (ioctl(s, SIOCSIFFLAGS, &req)) {
+ close(s);
+ return perrorstr("SIOCSIFFLAGS");
+ }
+
+ close(s);
+
+ return NULL;
+#else
+ error_t err;
+
+ err = open_underlying_pfinet ();
+ if (err)
+ return strerror (err);
+
+ err = file_set_translator (underlying_pfinet,
+ 0, FS_TRANS_SET | FS_TRANS_FORCE,
+ FSYS_GOAWAY_FORCE,
+ 0, 0, MACH_PORT_NULL);
+ return err ? strerror (err) : 0;
+#endif
+}
+
+static char * setupInterface(struct intfInfo * intf, int s) {
+ char * rc;
+ char * s;
+ char * argz = 0;
+ int argz_len = 0;
+
+ /* Construct the args to pfinet. */
+ /* /hurd/pfinet */
+ argz_add (&argz, &argz_len, _HURD_PFINET);
+
+ /* Interface name */
+ asprintf (&s, "--interface=%s", intf->decive);
+ argz_add (&argz, &argz_len, s);
+ free (s);
+
+ /* Address */
+ asprintf (&s, "--address=%s", inet_ntoa (intf->ip));
+ argz_add (&argz, &argz_len, s);
+ free (s);
+
+ /* Netmask */
+ asprintf (&s, "--netmask=%s", inet_ntoa (intf->netmask));
+ argz_add (&argz, &argz_len, s);
+ free (s);
+
+ /* Gateway */
+ asprintf (&s, "--gateway=%s", inet_ntoa (intf->gateway));
+ argz_add (&argz, &argz_len, s);
+ free (s);
+
+ /* Now set it up */
+ err = start_pfinet (argz, argz_len);
+ free (argz);
+
+ return err ? strerror (err) : 0;
+
+
+#ifdef NOTHURD
+ struct sockaddr_in * addrp;
+ struct ifreq req;
+ struct rtentry route;
+
+ if ((rc = disableInterface(intf->device))) return rc;
+
+ /* we have to have basic information to get this far */
+ addrp = (struct sockaddr_in *) &req.ifr_addr;
+ addrp->sin_family = AF_INET;
+ strcpy(req.ifr_name, intf->device);
+
+ addrp->sin_addr = intf->ip;
+ if (ioctl(s, SIOCSIFADDR, &req))
+ return perrorstr("SIOCSIFADDR");
+
+ addrp->sin_addr = intf->netmask;
+ if (ioctl(s, SIOCSIFNETMASK, &req))
+ return perrorstr("SIOCSIFNETMASK");
+
+ addrp->sin_addr = intf->broadcast;
+ if (ioctl(s, SIOCSIFBRDADDR, &req))
+ return perrorstr("SIOCSIFBRDADDR");
+
+ /* bring up the device, and specifically allow broadcasts through it */
+ req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST;
+ if (ioctl(s, SIOCSIFFLAGS, &req))
+ return perrorstr("SIOCSIFFLAGS");
+
+ /* add a route for this network */
+ route.rt_dev = intf->device;
+ route.rt_flags = RTF_UP;
+ route.rt_metric = 0;
+
+ if (!newKernel()) {
+ addrp->sin_family = AF_INET;
+ addrp->sin_port = 0;
+ addrp->sin_addr = intf->network;
+ memcpy(&route.rt_dst, addrp, sizeof(*addrp));
+ addrp->sin_addr = intf->netmask;
+ memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
+
+ if (ioctl(s, SIOCADDRT, &route)) {
+ /* the route cannot already exist, as we've taken the device down */
+ return perrorstr("SIOCADDRT 1");
+ }
+ }
+ return NULL;
+}
+
+#ifdef NOTHURD
+static char * setupDefaultGateway(struct in_addr * gw, int s) {
+ struct sockaddr_in addr;
+ struct rtentry route;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ memcpy(&route.rt_dst, &addr, sizeof(addr));
+ memcpy(&route.rt_genmask, &addr, sizeof(addr));
+ addr.sin_addr = *gw;
+ memcpy(&route.rt_gateway, &addr, sizeof(addr));
+
+ route.rt_flags = RTF_UP | RTF_GATEWAY;
+ route.rt_metric = 0;
+ route.rt_dev = NULL;
+
+ if (ioctl(s, SIOCADDRT, &route)) {
+ /* the route cannot already exist, as we've taken the device
+ down */
+ return perrorstr("SIOCADDRT 2");
+ }
+
+ return NULL;
+}
+
+static char * getInterfaceInfo(struct intfInfo * intf, int s) {
+ struct ifreq req;
+ struct sockaddr_in * addrp;
+
+ strcpy(req.ifr_name, intf->device);
+ if (ioctl(s, SIOCGIFBRDADDR, &req))
+ return perrorstr("SIOCGIFBRDADDR");
+
+ addrp = (struct sockaddr_in *) &req.ifr_addr;
+ intf->broadcast = addrp->sin_addr;
+ intf->set = INTFINFO_HAS_BROADCAST;
+
+ return NULL;
+}
+
+static char * prepareInterface(struct intfInfo * intf, int s) {
+ struct sockaddr_in * addrp;
+ struct ifreq req;
+ struct rtentry route;
+
+ addrp = (struct sockaddr_in *) &req.ifr_addr;
+
+ strcpy(req.ifr_name, intf->device);
+ addrp->sin_family = AF_INET;
+ addrp->sin_port = 0;
+ memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr));
+
+ addrp->sin_family = AF_INET;
+ addrp->sin_addr.s_addr = htonl(0);
+
+ if (ioctl(s, SIOCSIFADDR, &req))
+ return perrorstr("SIOCSIFADDR");
+
+ if (!newKernel()) {
+ if (ioctl(s, SIOCSIFNETMASK, &req))
+ return perrorstr("SIOCSIFNETMASK");
+
+ /* the broadcast address is 255.255.255.255 */
+ memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
+ if (ioctl(s, SIOCSIFBRDADDR, &req))
+ return perrorstr("SIOCSIFBRDADDR");
+ }
+
+ req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+ if (ioctl(s, SIOCSIFFLAGS, &req))
+ return perrorstr("SIOCSIFFLAGS");
+
+ memset(&route, 0, sizeof(route));
+ memcpy(&route.rt_gateway, addrp, sizeof(*addrp));
+
+ addrp->sin_family = AF_INET;
+ addrp->sin_port = 0;
+ addrp->sin_addr.s_addr = INADDR_ANY;
+ memcpy(&route.rt_dst, addrp, sizeof(*addrp));
+ memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
+
+ route.rt_dev = intf->device;
+ route.rt_flags = RTF_UP;
+ route.rt_metric = 0;
+
+ if (ioctl(s, SIOCADDRT, &route)) {
+ if (errno != EEXIST) {
+ close(s);
+ return perrorstr("SIOCADDRT 3");
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+static int dhcpMessageType(struct bootpRequest * response) {
+ unsigned char * chptr;
+ unsigned char option, length;
+
+ chptr = response->vendor;
+
+ chptr += 4;
+ while (*chptr != 0xFF) {
+ option = *chptr++;
+ if (!option) continue;
+ length = *chptr++;
+ if (option == DHCP_OPTION_TYPE)
+ return *chptr;
+
+ chptr += length;
+ }
+
+ return -1;
+}
+
+static void setMissingIpInfo(struct intfInfo * intf) {
+ bp_int32 ipNum = *((bp_int32 *) &intf->ip);
+ bp_int32 nmNum = *((bp_int32 *) &intf->netmask);
+ bp_int32 ipRealNum = ntohl(ipNum);
+
+ if (!(intf->set & INTFINFO_HAS_NETMASK)) {
+ if (((ipRealNum & 0xFF000000) >> 24) <= 127)
+ nmNum = 0xFF000000;
+ else if (((ipRealNum & 0xFF000000) >> 24) <= 191)
+ nmNum = 0xFFFF0000;
+ else
+ nmNum = 0xFFFFFF00;
+ *((bp_int32 *) &intf->netmask) = nmNum = htonl(nmNum);
+ syslog (LOG_DEBUG, "intf: netmask: %s", inet_ntoa (intf->netmask));
+ }
+
+ if (!(intf->set & INTFINFO_HAS_BROADCAST)) {
+ *((bp_int32 *) &intf->broadcast) = (ipNum & nmNum) | ~(nmNum);
+ syslog (LOG_DEBUG, "intf: broadcast: %s", inet_ntoa (intf->broadcast));
+ }
+
+ if (!(intf->set & INTFINFO_HAS_NETWORK)) {
+ *((bp_int32 *) &intf->network) = ipNum & nmNum;
+ syslog (LOG_DEBUG, "intf: network: %s", inet_ntoa (intf->network));
+ }
+
+ intf->set |= INTFINFO_HAS_BROADCAST | INTFINFO_HAS_NETWORK |
+ INTFINFO_HAS_NETMASK;
+}
+
+static void parseReply(struct bootpRequest * breq, struct intfInfo * intf) {
+ unsigned int i;
+ unsigned char * chptr;
+ unsigned char option, length;
+
+ syslog (LOG_DEBUG, "intf: device: %s", intf->device);
+ syslog (LOG_DEBUG, "intf: set: %i", intf->set);
+ syslog (LOG_DEBUG, "intf: bootServer: %s", inet_ntoa (intf->bootServer));
+ syslog (LOG_DEBUG, "intf: reqLease: %i", intf->reqLease);
+
+ i = ~(INTFINFO_HAS_IP | INTFINFO_HAS_NETMASK | INTFINFO_HAS_NETWORK |
+ INTFINFO_HAS_BROADCAST);
+ intf->set &= i;
+
+ if (strlen(breq->bootfile)) {
+ intf->bootFile = strdup(breq->bootfile);
+ intf->set |= INTFINFO_HAS_BOOTFILE;
+ } else {
+ intf->set &= ~INTFINFO_HAS_BOOTFILE;
+ }
+ syslog (LOG_DEBUG, "intf: bootFile: %s", intf->bootFile);
+
+ memcpy(&intf->ip, &breq->yiaddr, 4);
+ intf->set |= INTFINFO_HAS_IP;
+ syslog (LOG_DEBUG, "intf: ip: %s", inet_ntoa (intf->ip));
+
+ chptr = breq->vendor;
+ chptr += 4;
+ while (*chptr != 0xFF && (void *) chptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH) {
+ option = *chptr++;
+ if (!option) continue;
+ length = *chptr++;
+
+ switch (option) {
+ case BOOTP_OPTION_DNS:
+ intf->numDns = 0;
+ for (i = 0; i < length; i += 4) {
+ if (intf->numDns < MAX_DNS_SERVERS) {
+ memcpy(&intf->dnsServers[intf->numDns++], chptr + i, 4);
+ syslog(LOG_DEBUG, "intf: dnsServers[%i]: %s",
+ i/4, inet_ntoa (intf->dnsServers[i/4]));
+ }
+ }
+ intf->set |= NETINFO_HAS_DNS;
+ syslog (LOG_DEBUG, "intf: numDns: %i", intf->numDns);
+ break;
+
+ case BOOTP_OPTION_NETMASK:
+ memcpy(&intf->netmask, chptr, 4);
+ intf->set |= INTFINFO_HAS_NETMASK;
+ syslog (LOG_DEBUG, "intf: netmask: %s", inet_ntoa (intf->netmask));
+ break;
+
+ case BOOTP_OPTION_DOMAIN:
+ if ((intf->domain = malloc(length + 1))) {
+ memcpy(intf->domain, chptr, length);
+ intf->domain[length] = '\0';
+ intf->set |= NETINFO_HAS_DOMAIN;
+ syslog (LOG_DEBUG, "intf: domain: %s", intf->domain);
+ }
+ break;
+
+ case BOOTP_OPTION_BROADCAST:
+ memcpy(&intf->broadcast, chptr, 4);
+ intf->set |= INTFINFO_HAS_BROADCAST;
+ syslog (LOG_DEBUG, "intf: broadcast: %s", inet_ntoa (intf->broadcast));
+ break;
+
+ case BOOTP_OPTION_GATEWAY:
+ memcpy(&intf->gateway, chptr, 4);
+ intf->set |= NETINFO_HAS_GATEWAY;
+ syslog (LOG_DEBUG, "intf: gateway: %s", inet_ntoa (intf->gateway));
+ break;
+
+ case BOOTP_OPTION_HOSTNAME:
+ if ((intf->hostname = malloc(length + 1))) {
+ memcpy(intf->hostname, chptr, length);
+ intf->hostname[length] = '\0';
+ intf->set |= NETINFO_HAS_HOSTNAME;
+ syslog (LOG_DEBUG, "intf: hostname: %s", intf->hostname);
+ }
+ break;
+
+ case BOOTP_OPTION_BOOTFILE:
+ /* we ignore this right now */
+ break;
+
+ case DHCP_OPTION_OVERLOAD:
+ /* FIXME: we should pay attention to this */
+ logMessage("dhcp overload option is currently ignored!");
+ break;
+ }
+
+ chptr += length;
+ }
+
+ setMissingIpInfo(intf);
+}
+
+static char * perrorstr(char * msg) {
+ static char * err = NULL;
+ static int errsize = 0;
+ static int newsize;
+
+ newsize = strlen(msg) + strlen(strerror(errno)) + 3;
+ if (!errsize) {
+ errsize = newsize;
+ err = malloc(errsize);
+ } else if (errsize < newsize) {
+ free(err);
+ errsize = newsize;
+ err = malloc(errsize);
+ }
+
+ if (err)
+ sprintf(err, "%s: %s", msg, strerror(errno));
+ else
+ err = "out of memory!";
+
+ return err;
+}
+
+static void initVendorCodes(struct bootpRequest * breq) {
+ memcpy(breq->vendor, vendCookie, sizeof(vendCookie));
+}
+
+static char * prepareRequest(struct bootpRequest * breq,
+ int sock, char * device, time_t startTime) {
+#ifdef NOTHURD
+ struct ifreq req;
+#endif
+ int i;
+
+ memset(breq, 0, sizeof(*breq));
+
+ breq->opcode = BOOTP_OPCODE_REQUEST;
+
+#ifdef NOTHURD
+ strcpy(req.ifr_name, device);
+ if (ioctl(sock, SIOCGIFHWADDR, &req))
+ return perrorstr("SIOCSIFHWADDR");
+
+ breq->hw = 1; /* ethernet */
+ breq->hwlength = IFHWADDRLEN;
+ memcpy(breq->hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN);
+#else
+ breq->hw = 1; /* ethernet */
+ breq->hwlength = IFHWADDRLEN;
+ memcpy(breq->hwaddr, ethernet_address, IFHWADDRLEN);
+#endif
+
+ /* we should use something random here, but I don't want to start using
+ stuff from the math library */
+ breq->id = time(NULL);
+ for (i = 0; i < IFHWADDRLEN; i++)
+ breq->id ^= breq->hwaddr[i] << (8 * (i % 4));
+
+ breq->hopcount = 0;
+ breq->secs = time(NULL) - startTime;
+
+ initVendorCodes(breq);
+
+ return NULL;
+}
+
+static unsigned int verifyChecksum(void * buf, int length, void * buf2,
+ int length2) {
+ unsigned int csum;
+ unsigned short * sp;
+
+ csum = 0;
+ for (sp = (unsigned short *) buf; length > 0; (length -= 2), sp++)
+ csum += *sp;
+
+ /* this matches rfc 1071, but not Steven's */
+ if (length)
+ csum += *((unsigned char *) sp);
+
+ for (sp = (unsigned short *) buf2; length2 > 0; (length2 -= 2), sp++)
+ csum += *sp;
+
+ /* this matches rfc 1071, but not Steven's */
+ if (length)
+ csum += *((unsigned char *) sp);
+
+ while (csum >> 16)
+ csum = (csum & 0xffff) + (csum >> 16);
+
+ if (csum!=0x0000 && csum != 0xffff) return 0; else return 1;
+}
+
+void debugbootpRequest(char *name, struct bootpRequest *breq) {
+ char vendor[28], vendor2[28];
+ int i;
+ struct in_addr address;
+ unsigned char *vndptr;
+ unsigned char option, length;
+
+ syslog (LOG_DEBUG, "%s: opcode: %i", name, breq->opcode);
+ syslog (LOG_DEBUG, "%s: hw: %i", name, breq->hw);
+ syslog (LOG_DEBUG, "%s: hwlength: %i", name, breq->hwlength);
+ syslog (LOG_DEBUG, "%s: hopcount: %i", name, breq->hopcount);
+ syslog (LOG_DEBUG, "%s: id: 0x%8x", name, breq->id);
+ syslog (LOG_DEBUG, "%s: secs: %i", name, breq->secs);
+ syslog (LOG_DEBUG, "%s: flags: 0x%4x", name, breq->flags);
+
+ address.s_addr = breq->ciaddr;
+ syslog (LOG_DEBUG, "%s: ciaddr: %s", name, inet_ntoa (address));
+
+ address.s_addr = breq->yiaddr;
+ syslog (LOG_DEBUG, "%s: yiaddr: %s", name, inet_ntoa (address));
+
+ address.s_addr = breq->server_ip;
+ syslog (LOG_DEBUG, "%s: server_ip: %s", name, inet_ntoa (address));
+
+ address.s_addr = breq->bootp_gw_ip;
+ syslog (LOG_DEBUG, "%s: bootp_gw_ip: %s", name, inet_ntoa (address));
+
+ syslog (LOG_DEBUG, "%s: hwaddr: %s", name, breq->hwaddr);
+ syslog (LOG_DEBUG, "%s: servername: %s", name, breq->servername);
+ syslog (LOG_DEBUG, "%s: bootfile: %s", name, breq->bootfile);
+
+ vndptr = breq->vendor;
+ sprintf (vendor, "0x%2x 0x%2x 0x%2x 0x%2x", *vndptr++, *vndptr++, *vndptr++, *vndptr++);
+ syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor);
+
+
+ for (; (void *) vndptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH;)
+ {
+ option = *vndptr++;
+ if (option == 0xFF)
+ {
+ sprintf (vendor, "0x%2x", option);
+ vndptr = breq->vendor + DHCP_VENDOR_LENGTH;
+ }
+ else if (option == 0x00)
+ {
+ for (i = 1; *vndptr == 0x00; i++, vndptr++);
+ sprintf (vendor, "0x%2x x %i", option, i);
+ }
+ else
+ {
+ length = *vndptr++;
+ sprintf (vendor, "%3u %3u", option, length);
+ for (i = 0; i < length; i++)
+ {
+ if (strlen (vendor) > 22)
+ {
+ syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor);
+ strcpy (vendor, "++++++");
+ }
+ snprintf (vendor2, 27, "%s 0x%2x", vendor, *vndptr++);
+ strcpy (vendor, vendor2);
+
+ }
+ }
+
+ syslog (LOG_DEBUG, "%s: vendor: %s", name, vendor);
+ }
+
+ return;
+
+}
+
+static char * handleTransaction(int s, struct overrideInfo * override,
+ struct bootpRequest * breq,
+ struct bootpRequest * bresp,
+ struct sockaddr_in * serverAddr,
+ struct sockaddr_in * respondant,
+ int useBootpPacket, int dhcpResponseType) {
+ struct timeval tv;
+ fd_set readfs;
+ int i, j;
+ struct sockaddr_pkt tmpAddress;
+ int gotit = 0;
+ int tries;
+ int nextTimeout = 2;
+ time_t timeoutTime;
+ int sin;
+ int resend = 1;
+ struct ethhdr;
+ char ethPacket[ETH_FRAME_LEN];
+ struct iphdr * ipHdr;
+ struct udphdr * udpHdr;
+ struct psuedohUdpHeader pHdr;
+ time_t start = time(NULL);
+
+ debugbootpRequest("breq", breq);
+
+ if (!override) {
+ override = alloca(sizeof(*override));
+ initOverride(override);
+ }
+
+ tries = override->numRetries + 1;
+
+ sin = socket(AF_PACKET, SOCK_DGRAM, ntohs(ETH_P_IP));
+ if (sin < 0) {
+ return strerror(errno);
+ }
+
+ while (!gotit && tries) {
+ i = sizeof(*breq);
+ if (useBootpPacket)
+ i -= (DHCP_VENDOR_LENGTH - BOOTP_VENDOR_LENGTH);
+
+ if (resend) {
+ if (sendto(s, breq, i, 0, (struct sockaddr *) serverAddr,
+ sizeof(*serverAddr)) != i) {
+ close(sin);
+ return perrorstr("sendto");
+ }
+
+ tries--;
+ nextTimeout *= 2;
+
+ switch (time(NULL) & 4) {
+ case 0: if (nextTimeout >= 2) nextTimeout--; break;
+ case 1: nextTimeout++; break;
+ }
+
+ timeoutTime = time(NULL) + nextTimeout;
+ i = override->timeout + start;
+ if (timeoutTime > i) timeoutTime = i;
+
+ resend = 0;
+ }
+
+ if (dhcpResponseType == NORESPONSE) {
+ close(sin);
+ return NULL;
+ }
+
+ tv.tv_usec = 0;
+ tv.tv_sec = timeoutTime - time(NULL);
+ if (timeoutTime < time(NULL)) {
+ tries = 0;
+ continue;
+ }
+
+ FD_ZERO(&readfs);
+ FD_SET(sin, &readfs);
+ switch ((select(sin + 1, &readfs, NULL, NULL, &tv))) {
+ case 0:
+ resend = 1;
+ break;
+
+ case 1:
+ i = sizeof(tmpAddress);
+ if ((j = recvfrom(sin, ethPacket, sizeof(ethPacket), 0,
+ (struct sockaddr *) &tmpAddress, &i)) < 0)
+ return perrorstr("recvfrom");
+
+ /* We need to do some basic sanity checking of the header */
+ if (j < (sizeof(*ipHdr) + sizeof(*udpHdr))) continue;
+
+ ipHdr = (void *) ethPacket;
+ if (!verifyChecksum(NULL, 0, ipHdr, sizeof(*ipHdr)))
+ continue;
+
+ if (ntohs(ipHdr->tot_len) > j)
+ continue;
+ j = ntohs(ipHdr->tot_len);
+
+ if (ipHdr->protocol != IPPROTO_UDP) continue;
+
+ udpHdr = (void *) (ethPacket + sizeof(*ipHdr));
+ pHdr.source = ipHdr->saddr;
+ pHdr.dest = ipHdr->daddr;
+ pHdr.zero = 0;
+ pHdr.protocol = ipHdr->protocol;
+ pHdr.len = udpHdr->len;
+
+/*
+ egcs bugs make this problematic
+
+ if (udpHdr->check && !verifyChecksum(&pHdr, sizeof(pHdr),
+ udpHdr, j - sizeof(*ipHdr)))
+ continue;
+*/
+
+ if (ntohs(udpHdr->source) != BOOTP_SERVER_PORT)
+ continue;
+ if (ntohs(udpHdr->dest) != BOOTP_CLIENT_PORT)
+ continue;
+ /* Go on with this packet; it looks sane */
+
+ /* Originally copied sizeof (*bresp) - this is a security
+ problem due to a potential underflow of the source
+ buffer. Also, it trusted that the packet was properly
+ 0xFF terminated, which is not true in the case of the
+ DHCP server on Cisco 800 series ISDN router. */
+
+ memset (bresp, 0xFF, sizeof (*bresp));
+ memcpy (bresp, (char *) udpHdr + sizeof (*udpHdr), j - sizeof (*ipHdr) - sizeof (*udpHdr));
+
+ /* sanity checks */
+ if (bresp->id != breq->id) continue;
+ if (bresp->opcode != BOOTP_OPCODE_REPLY) continue;
+ if (bresp->hwlength != breq->hwlength) continue;
+ if (memcmp(bresp->hwaddr, breq->hwaddr, bresp->hwlength)) continue;
+ i = dhcpMessageType(bresp);
+ if (!(i == -1 && useBootpPacket) && (i != dhcpResponseType))
+ continue;
+ if (memcmp(bresp->vendor, vendCookie, 4)) continue;
+ /* if (respondant) *respondant = tmpAddress; */
+ gotit = 1;
+
+ break;
+
+ default:
+ close(sin);
+ return perrorstr("select");
+ }
+ }
+
+ if (!gotit) {
+ close(sin);
+ return _("No DHCP reply received");
+ }
+
+ close(sin);
+
+ debugbootpRequest("bresp", bresp);
+
+ return NULL;
+}
+
+static void addVendorCode(struct bootpRequest * breq, unsigned char option,
+ unsigned char length, void * data) {
+ unsigned char * chptr;
+ int theOption, theLength;
+
+ chptr = breq->vendor;
+ chptr += 4;
+ while (*chptr != 0xFF && *chptr != option) {
+ theOption = *chptr++;
+ if (!theOption) continue;
+ theLength = *chptr++;
+ chptr += theLength;
+ }
+
+ *chptr++ = option;
+ *chptr++ = length;
+ memcpy(chptr, data, length);
+ chptr[length] = 0xff;
+}
+
+static int getVendorCode(struct bootpRequest * bresp, unsigned char option,
+ void * data) {
+ unsigned char * chptr;
+ unsigned int length, theOption;
+
+ chptr = bresp->vendor;
+ chptr += 4;
+ while (*chptr != 0xFF && *chptr != option) {
+ theOption = *chptr++;
+ if (!theOption) continue;
+ length = *chptr++;
+ chptr += length;
+ }
+
+ if (*chptr++ == 0xff) return 1;
+
+ length = *chptr++;
+ memcpy(data, chptr, length);
+
+ return 0;
+}
+
+static int createSocket(void) {
+ struct sockaddr_in clientAddr;
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return -1;
+
+ memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr));
+ clientAddr.sin_family = AF_INET;
+ clientAddr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */
+
+ if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+static int dhcpRelease(struct intfInfo * intf) {
+ struct bootpRequest breq, bresp;
+ unsigned char messageType;
+ struct sockaddr_in serverAddr;
+ char * chptr;
+ int s;
+
+ if (!(intf->set & INTFINFO_HAS_LEASE)) {
+ disableInterface(intf->device);
+ syslog(LOG_INFO, "disabling interface %s", intf->device);
+
+ return 0;
+ }
+
+ if ((s = createSocket()) < 0) return 1;
+
+ if ((chptr = prepareRequest(&breq, s, intf->device, time(NULL)))) {
+ close(s);
+ while (1) {
+ disableInterface(intf->device);
+ return 0;
+ }
+ }
+
+ messageType = DHCP_TYPE_RELEASE;
+ addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
+ memcpy(&breq.ciaddr, &intf->ip, sizeof(breq.ciaddr));
+
+ serverAddr.sin_family = AF_INET;
+ serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */
+ serverAddr.sin_addr = intf->bootServer;
+
+ if (!handleTransaction(s, NULL, &breq, &bresp, &serverAddr, NULL, 0,
+ NORESPONSE)) {
+ disableInterface(intf->device);
+ close(s);
+ return 0;
+ }
+
+ disableInterface(intf->device);
+ close(s);
+
+ syslog(LOG_INFO, "disabling interface %s", intf->device);
+
+ return 1;
+}
+
+/* This is somewhat broken. We try only to renew the lease. If we fail,
+ we don't try to completely rebind. This doesn't follow the DHCP spec,
+ but for the install it should be a reasonable compromise. */
+static int dhcpRenew(struct intfInfo * intf) {
+ struct bootpRequest breq, bresp;
+ unsigned char messageType;
+ struct sockaddr_in serverAddr;
+ char * chptr;
+ int s;
+ int i;
+
+ s = createSocket();
+
+ if ((chptr = prepareRequest(&breq, s, intf->device, time(NULL)))) {
+ close(s);
+ while (1); /* problem */
+ }
+
+ messageType = DHCP_TYPE_REQUEST;
+ addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
+ memcpy(&breq.ciaddr, &intf->ip, sizeof(breq.ciaddr));
+
+ i = htonl(intf->reqLease);
+ addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i);
+
+ serverAddr.sin_family = AF_INET;
+ serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */
+ serverAddr.sin_addr = intf->bootServer;
+
+ if (handleTransaction(s, NULL, &breq, &bresp, &serverAddr, NULL, 0,
+ DHCP_TYPE_ACK)) {
+ close(s);
+ return 1;
+ }
+
+ parseLease(&bresp, intf);
+
+ syslog(LOG_INFO, "renewed lease for interface %s", intf->device);
+
+ close(s);
+ return 0;
+}
+
+static void parseLease(struct bootpRequest * bresp, struct intfInfo * intf) {
+ int lease;
+ time_t now;
+
+ intf->set &= INTFINFO_HAS_LEASE;
+ if (getVendorCode(bresp, DHCP_OPTION_LEASE, &lease))
+ return;
+
+ lease = ntohl(lease);
+
+ if (lease && lease != 0xffffffff) {
+ now = time(NULL);
+ intf->set |= INTFINFO_HAS_LEASE;
+ intf->leaseExpiration = now + lease;
+ intf->renewAt = now + (7 * lease / 8);
+ }
+}
+
+char * readSearchPath(void) {
+ int fd;
+ struct stat sb;
+ char * buf;
+ char * start;
+
+ fd = open("/etc/resolv.conf", O_RDONLY);
+ if (fd < 0) return NULL;
+
+ fstat(fd, &sb);
+ buf = alloca(sb.st_size + 2);
+ if (read(fd, buf, sb.st_size) != sb.st_size) return NULL;
+ buf[sb.st_size] = '\n';
+ buf[sb.st_size + 1] = '\0';
+ close(fd);
+
+ start = buf;
+ while (start && *start) {
+ while (isspace(*start) && (*start != '\n')) start++;
+ if (*start == '\n') {
+ start++;
+ continue;
+ }
+
+ if (!strncmp("search", start, 6) && isspace(start[6])) {
+ start += 6;
+ while (isspace(*start) && *start != '\n') start++;
+ if (*start == '\n') return NULL;
+
+ buf = strchr(start, '\n');
+ *buf = '\0';
+ return strdup(start);
+ }
+ }
+
+ return NULL;
+}
+
+static void createResolvConf(struct intfInfo * intf, char * domain,
+ int isSearchPath) {
+ FILE * f;
+ int i;
+ char * chptr;
+
+ /* force a reread of /etc/resolv.conf if we need it again */
+ res_close();
+
+ if (!domain) {
+ domain = readSearchPath();
+ if (domain) {
+ chptr = alloca(strlen(domain) + 1);
+ strcpy(chptr, domain);
+ free(domain);
+ domain = chptr;
+ isSearchPath = 1;
+ }
+ }
+
+ f = fopen("/etc/resolv.conf", "w");
+ if (!f) {
+ syslog(LOG_ERR, "cannot create /etc/resolv.conf: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ if (domain && isSearchPath) {
+ fprintf(f, "search %s\n", domain);
+ } else if (domain) {
+ fprintf(f, "search");
+ chptr = domain;
+ do {
+ fprintf(f, " %s", chptr);
+ chptr = strchr(chptr, '.');
+ if (chptr) {
+ chptr++;
+ if (!strchr(chptr, '.'))
+ chptr = NULL;
+ }
+ } while (chptr);
+
+ fprintf(f, "\n");
+ }
+
+ for (i = 0; i < intf->numDns; i++)
+ fprintf(f, "nameserver %s\n", inet_ntoa(intf->dnsServers[i]));
+
+ fclose(f);
+
+ /* force a reread of /etc/resolv.conf */
+ endhostent();
+}
+
+void setupDns(struct intfInfo * intf, struct overrideInfo * override) {
+ char * hn, * dn = NULL;
+ struct hostent * he;
+
+ if (override->flags & OVERRIDE_FLAG_NODNS) {
+ return;
+ }
+
+ if (override->searchPath) {
+ createResolvConf(intf, override->searchPath, 1);
+ return;
+ }
+
+ if (intf->set & NETINFO_HAS_DNS) {
+ if (!(intf->set & NETINFO_HAS_DOMAIN)) {
+ if (intf->set & NETINFO_HAS_HOSTNAME) {
+ hn = intf->hostname;
+ } else {
+ createResolvConf(intf, NULL, 0);
+
+ he = gethostbyaddr((char *) &intf->ip, sizeof(intf->ip),
+ AF_INET);
+ if (he) {
+ hn = he->h_name;
+ } else {
+ hn = NULL;
+ }
+ }
+
+ if (hn) {
+ dn = strchr(hn, '.');
+ if (dn)
+ dn++;
+ }
+ } else {
+ dn = intf->domain;
+ }
+
+ createResolvConf(intf, dn, 0);
+ }
+}
+
+static char * doDhcp(char * device, int flags, int reqLease,
+ char * reqHostname, struct intfInfo * intf,
+ struct overrideInfo * override) {
+ int s, i;
+ struct sockaddr_in serverAddr;
+ struct sockaddr_in clientAddr;
+ struct sockaddr_in broadcastAddr;
+ struct bootpRequest breq, bresp;
+ struct bootpRequest protoReq;
+ unsigned char * chptr;
+ unsigned char messageType;
+ time_t startTime = time(NULL);
+ int true = 1;
+ char optionsRequested[50];
+ int numOptions;
+ short aShort;
+
+ memset(intf, 0, sizeof(*intf));
+ strcpy(intf->device, device);
+ intf->reqLease = reqLease;
+ intf->set |= INTFINFO_HAS_REQLEASE;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ return perrorstr("socket");
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true))) {
+ close(s);
+ return perrorstr("setsockopt");
+ }
+
+ if (flags & DHCP_FLAG_NOCONFIG) {
+ if ((chptr = getInterfaceInfo(intf, s))) {
+ close(s);
+ return chptr;
+ }
+ } else if ((chptr = prepareInterface(intf, s))) {
+ close(s);
+ return chptr;
+ }
+
+ if ((chptr = prepareRequest(&breq, s, intf->device, startTime))) {
+ close(s);
+ disableInterface(intf->device);
+ return chptr;
+ }
+
+ messageType = DHCP_TYPE_DISCOVER;
+ addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
+
+ memset(&clientAddr.sin_addr, 0, sizeof(&clientAddr.sin_addr));
+ clientAddr.sin_family = AF_INET;
+ clientAddr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */
+
+ if (bind(s, (struct sockaddr *) &clientAddr, sizeof(clientAddr))) {
+ disableInterface(intf->device);
+ close(s);
+ return perrorstr("bind");
+ }
+
+ serverAddr.sin_family = AF_INET;
+ serverAddr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */
+
+#if 0
+ /* seems like a good idea?? */
+ if (intf->set & INTFINFO_HAS_BOOTSERVER)
+ serverAddr.sin_addr = intf->bootServer;
+#endif
+
+ broadcastAddr.sin_family = AF_INET;
+ broadcastAddr.sin_port = htons(BOOTP_SERVER_PORT);
+
+#if 0
+ /* this too! */
+ if (intf->set & INTFINFO_HAS_BROADCAST)
+ broadcastAddr.sin_addr = intf->broadcast;
+#endif
+
+ memset(&broadcastAddr.sin_addr, 0xff,
+ sizeof(broadcastAddr.sin_addr)); /* all 1's broadcast */
+
+ syslog (LOG_DEBUG, "PUMP: sending discover\n");
+
+ if ((chptr = handleTransaction(s, override, &breq, &bresp, &broadcastAddr,
+ NULL, 1, DHCP_TYPE_OFFER))) {
+ close(s);
+ disableInterface(intf->device);
+ return chptr;
+ }
+
+ /* Otherwise we're in the land of bootp */
+ if (dhcpMessageType(&bresp) == DHCP_TYPE_OFFER) {
+ /* Admittedly, this seems a bit odd. If we find a dhcp server, we
+ rerun the dhcp discover broadcast, but with the proper option
+ field this time. This makes me rfc compliant. */
+ syslog (LOG_DEBUG, "got dhcp offer\n");
+
+ initVendorCodes(&breq);
+
+ aShort = ntohs(sizeof(struct bootpRequest));
+ addVendorCode(&breq, DHCP_OPTION_MAXSIZE, 2, &aShort);
+
+ numOptions = 0;
+ optionsRequested[numOptions++] = BOOTP_OPTION_NETMASK;
+ optionsRequested[numOptions++] = BOOTP_OPTION_GATEWAY;
+ optionsRequested[numOptions++] = BOOTP_OPTION_DNS;
+ optionsRequested[numOptions++] = BOOTP_OPTION_DOMAIN;
+ optionsRequested[numOptions++] = BOOTP_OPTION_BROADCAST;
+ optionsRequested[numOptions++] = BOOTP_OPTION_HOSTNAME;
+ addVendorCode(&breq, DHCP_OPTION_OPTIONREQ, numOptions,
+ optionsRequested);
+
+ if (reqHostname) {
+ syslog(LOG_DEBUG, "HOSTNAME: requesting %s\n", reqHostname);
+ addVendorCode(&breq, BOOTP_OPTION_HOSTNAME, strlen(reqHostname),
+ reqHostname);
+ }
+
+ i = htonl(intf->reqLease);
+ addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i);
+
+ protoReq = breq;
+
+ syslog (LOG_DEBUG, "PUMP: sending second discover");
+
+ messageType = DHCP_TYPE_DISCOVER;
+ addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
+
+ /* Send another DHCP_REQUEST with the proper option list */
+ if ((chptr = handleTransaction(s, override, &breq, &bresp,
+ &broadcastAddr, NULL, 1,
+ DHCP_TYPE_OFFER))) {
+ close(s);
+ disableInterface(intf->device);
+ return chptr;
+ }
+
+
+ if (dhcpMessageType(&bresp) != DHCP_TYPE_OFFER) {
+ close(s);
+ disableInterface(intf->device);
+ return "dhcp offer expected";
+ }
+
+ syslog (LOG_DEBUG, "PUMP: got an offer");
+
+ if (getVendorCode(&bresp, DHCP_OPTION_SERVER, &serverAddr.sin_addr)) {
+ syslog (LOG_DEBUG, "DHCPOFFER didn't include server address");
+ intf->bootServer = broadcastAddr.sin_addr;
+ }
+
+ breq = protoReq;
+ messageType = DHCP_TYPE_REQUEST;
+ addVendorCode(&breq, DHCP_OPTION_TYPE, 1, &messageType);
+
+ addVendorCode(&breq, DHCP_OPTION_SERVER, 4, &serverAddr.sin_addr);
+ addVendorCode(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr);
+
+ /* why do we need to use the broadcast address here? better reread the
+ spec! */
+ if ((chptr = handleTransaction(s, override, &breq, &bresp,
+ &broadcastAddr, NULL, 0,
+ DHCP_TYPE_ACK))) {
+ close(s);
+ disableInterface(intf->device);
+ return chptr;
+ }
+
+ syslog (LOG_DEBUG, "PUMP: got lease");
+
+ parseLease(&bresp, intf);
+
+ if (getVendorCode(&bresp, DHCP_OPTION_SERVER, &intf->bootServer)) {
+ syslog (LOG_DEBUG, "DHCPACK didn't include server address");
+ intf->bootServer = broadcastAddr.sin_addr;
+ }
+
+ intf->set |= INTFINFO_HAS_BOOTSERVER;
+ }
+
+ parseReply(&bresp, intf);
+ if (flags & DHCP_FLAG_FORCEHNLOOKUP)
+ intf->set &= ~(NETINFO_HAS_DOMAIN | NETINFO_HAS_HOSTNAME);
+
+ chptr = setupInterface(intf, s);
+ if (chptr) {
+ close(s);
+ disableInterface(intf->device);
+ return chptr;
+ }
+
+ syslog(LOG_INFO, "configured interface %s", intf->device);
+
+ if (intf->set & NETINFO_HAS_GATEWAY) {
+ chptr = setupDefaultGateway(&intf->gateway, s);
+ }
+
+ setupDns(intf, override);
+
+ close(s);
+
+ return NULL;
+}
+
+static void runDaemon(int sock, char * configFile) {
+ int conn;
+ struct sockaddr_un addr;
+ int addrLength;
+ struct command cmd;
+ struct intfInfo intf[20];
+ int numInterfaces = 0;
+ int i;
+ int closest;
+ struct timeval tv;
+ fd_set fds;
+ struct overrideInfo * overrides = NULL;
+ struct overrideInfo emptyOverride, * o;
+
+ readPumpConfig(configFile, &overrides);
+ if (!overrides) {
+ overrides = &emptyOverride;
+ overrides->intf.device[0] = '\0';
+ }
+
+ while (1) {
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+
+ tv.tv_sec = tv.tv_usec = 0;
+ closest = -1;
+ if (numInterfaces) {
+ for (i = 0; i < numInterfaces; i++)
+ if ((intf[i].set & INTFINFO_HAS_LEASE) &&
+ (closest == -1 ||
+ (intf[closest].renewAt > intf[i].renewAt)))
+ closest = i;
+ if (closest != -1) {
+ tv.tv_sec = intf[closest].renewAt - time(NULL);
+ if (tv.tv_sec <= 0) {
+ dhcpRenew(intf + closest);
+ continue;
+ }
+ }
+ }
+
+ if (select(sock + 1, &fds, NULL, NULL,
+ closest != -1 ? &tv : NULL) > 0) {
+ conn = accept(sock, &addr, &addrLength);
+
+ if (read(conn, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+ close(conn);
+ continue;
+ }
+
+ switch (cmd.type) {
+ case CMD_DIE:
+ for (i = 0; i < numInterfaces; i++)
+ dhcpRelease(intf + i);
+
+ syslog(LOG_INFO, "terminating at root's request");
+
+ cmd.type = CMD_RESULT;
+ cmd.u.result = 0;
+ write(conn, &cmd, sizeof(cmd));
+ exit(0);
+
+ case CMD_STARTIFACE:
+ o = overrides;
+ while (*o->intf.device &&
+ strcmp(o->intf.device, cmd.u.start.device)) {
+ o++;
+ }
+ if (!*o->intf.device) o = overrides;
+
+ if (doDhcp(cmd.u.start.device,
+ cmd.u.start.flags, cmd.u.start.reqLease,
+ cmd.u.start.reqHostname[0] ?
+ cmd.u.start.reqHostname : NULL,
+ intf + numInterfaces, o)) {
+ cmd.u.result = 1;
+ } else {
+ cmd.u.result = 0;
+ numInterfaces++;
+ }
+ break;
+
+ case CMD_FORCERENEW:
+ for (i = 0; i < numInterfaces; i++)
+ if (!strcmp(intf[i].device, cmd.u.renew.device)) break;
+ if (i == numInterfaces)
+ cmd.u.result = RESULT_UNKNOWNIFACE;
+ else
+ cmd.u.result = dhcpRenew(intf + i);
+ break;
+
+ case CMD_STOPIFACE:
+ for (i = 0; i < numInterfaces; i++)
+ if (!strcmp(intf[i].device, cmd.u.stop.device)) break;
+ if (i == numInterfaces)
+ cmd.u.result = RESULT_UNKNOWNIFACE;
+ else {
+ cmd.u.result = dhcpRelease(intf + i);
+ if (numInterfaces == 1) {
+ cmd.type = CMD_RESULT;
+ write(conn, &cmd, sizeof(cmd));
+
+ syslog(LOG_INFO, "terminating as there are no "
+ "more devices under management");
+
+ exit(0);
+ }
+
+ intf[i] = intf[numInterfaces - 1];
+ numInterfaces--;
+ }
+ break;
+
+ case CMD_REQSTATUS:
+ for (i = 0; i < numInterfaces; i++)
+ if (!strcmp(intf[i].device, cmd.u.stop.device)) break;
+ if (i == numInterfaces) {
+ cmd.u.result = RESULT_UNKNOWNIFACE;
+ } else {
+ cmd.type = CMD_STATUS;
+ cmd.u.status.intf = intf[i];
+ if (intf[i].set & NETINFO_HAS_HOSTNAME)
+ strncpy(cmd.u.status.hostname,
+ intf->hostname, sizeof(cmd.u.status.hostname));
+ cmd.u.status.hostname[sizeof(cmd.u.status.hostname)] = '\0';
+
+ if (intf[i].set & NETINFO_HAS_DOMAIN)
+ strncpy(cmd.u.status.domain,
+ intf->domain, sizeof(cmd.u.status.domain));
+ cmd.u.status.domain[sizeof(cmd.u.status.domain) - 1] = '\0';
+
+ if (intf[i].set & INTFINFO_HAS_BOOTFILE)
+ strncpy(cmd.u.status.bootFile,
+ intf->bootFile, sizeof(cmd.u.status.bootFile));
+ cmd.u.status.bootFile[sizeof(cmd.u.status.bootFile) - 1] =
+ '\0';
+ }
+
+ case CMD_STATUS:
+ case CMD_RESULT:
+ /* can't happen */
+ break;
+ }
+
+ if (cmd.type != CMD_STATUS) cmd.type = CMD_RESULT;
+ write(conn, &cmd, sizeof(cmd));
+
+ close(conn);
+ }
+ }
+
+ exit(0);
+}
+
+static int openControlSocket(char * configFile) {
+ struct sockaddr_un addr;
+ int sock;
+ size_t addrLength;
+ pid_t child;
+ int status;
+
+ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, CONTROLSOCKET);
+ addrLength = sizeof(addr.sun_family) + strlen(addr.sun_path);
+
+ if (!connect(sock, (struct sockaddr *) &addr, addrLength))
+ return sock;
+
+ if (errno != ENOENT && errno != ECONNREFUSED) {
+ fprintf(stderr, "failed to connect to %s: %s\n", CONTROLSOCKET,
+ strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ if (!(child = fork())) {
+ close(sock);
+
+ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "failed to create socket: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ unlink(CONTROLSOCKET);
+ umask(077);
+ if (bind(sock, (struct sockaddr *) &addr, addrLength)) {
+ fprintf(stderr, "bind to %s failed: %s\n", CONTROLSOCKET,
+ strerror(errno));
+ exit(1);
+ }
+ umask(033);
+
+ listen(sock, 5);
+
+ if (fork()) _exit(0);
+
+ close(0);
+ close(1);
+ close(2);
+
+ openlog("pumpd", LOG_PID, LOG_DAEMON);
+ {
+ time_t t;
+
+ t = time(NULL);
+ syslog(LOG_INFO, "starting at %s\n", ctime(&t));
+ }
+
+ runDaemon(sock, configFile);
+ }
+
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return -1;
+
+ if (!connect(sock, (struct sockaddr *) &addr, addrLength))
+ return sock;
+
+ fprintf(stderr, "failed to connect to %s: %s\n", CONTROLSOCKET,
+ strerror(errno));
+
+ return 0;
+}
+
+void printStatus(struct intfInfo i, char * hostname, char * domain,
+ char * bootFile) {
+ int j;
+
+ printf("Device %s\n", i.device);
+ printf("\tIP: %s\n", inet_ntoa(i.ip));
+ printf("\tNetmask: %s\n", inet_ntoa(i.netmask));
+ printf("\tBroadcast: %s\n", inet_ntoa(i.broadcast));
+ printf("\tNetwork: %s\n", inet_ntoa(i.network));
+ printf("\tBoot server %s\n", inet_ntoa(i.bootServer));
+
+ if (i.set & NETINFO_HAS_GATEWAY)
+ printf("\tGateway: %s\n", inet_ntoa(i.gateway));
+
+ if (i.set & INTFINFO_HAS_BOOTFILE)
+ printf("\tBoot file: %s\n", bootFile);
+
+ if (i.set & NETINFO_HAS_HOSTNAME)
+ printf("\tHostname: %s\n", hostname);
+
+ if (i.set & NETINFO_HAS_DOMAIN)
+ printf("\tDomain: %s\n", domain);
+
+ if (i.numDns) {
+ printf("\tNameservers:");
+ for (j = 0; j < i.numDns; j++)
+ printf(" %s", inet_ntoa(i.dnsServers[j]));
+ printf("\n");
+ }
+
+ if (i.set & INTFINFO_HAS_LEASE) {
+ printf("\tRenewal time: %s", ctime(&i.renewAt));
+ printf("\tExpiration time: %s", ctime(&i.leaseExpiration));
+ }
+}
+
+int main (int argc, char ** argv) {
+ char * device = "eth0";
+ char * hostname = "";
+ poptContext optCon;
+ int rc;
+ int test = 0;
+ int flags = 0;
+ int lease = 6;
+ int killDaemon = 0;
+ int release = 0, renew = 0, status = 0, lookupHostname = 0;
+ struct command cmd, response;
+ char * configFile = "/etc/pump.conf";
+ struct overrideInfo * overrides;
+ int cont;
+ struct poptOption options[] = {
+ { "config-file", 'c', POPT_ARG_STRING, &configFile, 0,
+ N_("Configuration file to use instead of "
+ "/etc/pump.conf") },
+ { "hostname", 'h', POPT_ARG_STRING, &hostname, 0,
+ N_("Hostname to request"), N_("hostname") },
+ { "interface", 'i', POPT_ARG_STRING, &device, 0,
+ N_("Interface to configure (normally eth0)"),
+ N_("iface") },
+ { "kill", 'k', POPT_ARG_NONE, &killDaemon, 0,
+ N_("Kill daemon (and disable all interfaces)"), NULL },
+ { "lease", 'l', POPT_ARG_INT, &lease, 0,
+ N_("Lease time to request (in hours)"), N_("hours") },
+ { "lookup-hostname", '\0', POPT_ARG_NONE, &lookupHostname, 0,
+ N_("Force lookup of hostname") },
+ { "release", 'r', POPT_ARG_NONE, &release, 0,
+ N_("Release interface"), NULL },
+ { "renew", 'R', POPT_ARG_NONE, &renew, 0,
+ N_("Force immediate lease renewal"), NULL },
+ { "status", 's', POPT_ARG_NONE, &status, 0,
+ N_("Display interface status"), NULL },
+ /*{ "test", 't', POPT_ARG_NONE, &test, 0,
+ N_("Don't change the interface configuration or "
+ "run as a deamon.") },*/
+ POPT_AUTOHELP
+ { NULL, '\0', 0, NULL, 0 }
+ };
+
+ optCon = poptGetContext(PROGNAME, argc, argv, options,0);
+ poptReadDefaultConfig(optCon, 1);
+
+ if ((rc = poptGetNextOpt(optCon)) < -1) {
+ fprintf(stderr, _("%s: bad argument %s: %s\n"), PROGNAME,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc));
+ return 1;
+ }
+
+ if (poptGetArg(optCon)) {
+ fprintf(stderr, _("%s: no extra parameters are expected\n"), PROGNAME);
+ return 1;
+ }
+
+ /* make sure the config file is parseable before going on any further */
+ if (readPumpConfig(configFile, &overrides)) return 1;
+
+ if (geteuid()) {
+ fprintf(stderr, _("%s: must be run as root\n"), PROGNAME);
+ exit(1);
+ }
+
+ if (test)
+ flags = DHCP_FLAG_NODAEMON | DHCP_FLAG_NOCONFIG;
+ if (lookupHostname)
+ flags |= DHCP_FLAG_FORCEHNLOOKUP;
+
+ cont = openControlSocket(configFile);
+ if (cont < 0)
+ exit(1);
+
+ if (killDaemon) {
+ cmd.type = CMD_DIE;
+ } else if (status) {
+ cmd.type = CMD_REQSTATUS;
+ strcpy(cmd.u.reqstatus.device, device);
+ } else if (renew) {
+ cmd.type = CMD_FORCERENEW;
+ strcpy(cmd.u.renew.device, device);
+ } else if (release) {
+ cmd.type = CMD_STOPIFACE;
+ strcpy(cmd.u.stop.device, device);
+ } else {
+ cmd.type = CMD_STARTIFACE;
+ strcpy(cmd.u.start.device, device);
+ cmd.u.start.flags = flags;
+ cmd.u.start.reqLease = lease * 60 * 60;
+ strcpy(cmd.u.start.reqHostname, hostname);
+ }
+
+ write(cont, &cmd, sizeof(cmd));
+ read(cont, &response, sizeof(response));
+
+ if (response.type == CMD_RESULT && response.u.result &&
+ cmd.type == CMD_STARTIFACE) {
+ cont = openControlSocket(configFile);
+ if (cont < 0)
+ exit(1);
+ write(cont, &cmd, sizeof(cmd));
+ read(cont, &response, sizeof(response));
+ }
+
+ if (response.type == CMD_RESULT) {
+ if (response.u.result) {
+ fprintf(stderr, "Operation failed.\n");
+ return 1;
+ }
+ } else if (response.type == CMD_STATUS) {
+ printStatus(response.u.status.intf, response.u.status.hostname,
+ response.u.status.domain, response.u.status.bootFile);
+ }
+
+ return 0;
+}