Import dhcpcd-5.0.0

Major changes from dhcpcd-4 include
 * Single daemon can now run DHCP on multiple interfaces at the same time
 * Configuration profiles per interface, ssid, arping and fallback
 * Listens to 3rd party programs changing routing information
 * Supports DHCP INFORM over PPP
 * Can configure static options for destination address
 * Control socket so 3rd party program can control or listen directly
   to dhcpcd events
 * Is also a BOOTP client
This commit is contained in:
roy 2009-04-28 22:14:20 +00:00
parent a82ec647f5
commit 2158200f72
43 changed files with 6126 additions and 1934 deletions

View File

@ -1,12 +1,13 @@
dhcpcd-4 - DHCP client daemon
Copyright 2006-2008 Roy Marples <roy@marples.name>
dhcpcd - DHCP client daemon
Copyright 2006-2009 Roy Marples <roy@marples.name>
Installation
------------
Edit config.h to match your building requirements.
Then just make; make install
man dhcpcd for command line options
man dhcpcd.conf for configuration options
man dhcpcd-run-hooks to learn how to hook scripts into dhcpcd events
Notes
@ -32,6 +33,12 @@ linux-2.6.24 finally ships with a working 32-bit header.
If your linux headers are older, or your distro hasn't patched them you can
set CSTD=gnu99 to work around this.
Some BSD systems do not allow the manipulation of automatically added subnet
routes. You can find discussion here:
http://mail-index.netbsd.org/tech-net/2008/12/03/msg000896.html
BSD systems where this has been fixed are:
NetBSD-5.99.6
Hooks
-----
@ -43,28 +50,13 @@ make HOOKSCRIPTS=50-ntp install
Compatibility
-------------
If you require compatibility with dhcpcd-3 and older style variables,
you can install 50-dhcpcd-compat into the directory $LIBEXECDIR/dhcpcd-hooks
We don't install this by default.
You should also add -DCMDLINE_COMPAT to your CPPFLAGS if you need to be fully
commandline compatible with prior versions.
dhcpcd-5.0 is only fully command line compatible with dhcpcd-4.0
For compatibility with older versions, use dhcpcd-4.0
dhcpcd-3 enabled DUID support by default - this has changed in dhcpcd-4.
You can enable it via the --duid, -D command line option or by using the
duid directive in dhcpcd.conf.
If CMDLINE_COMPAT is defined the we renable DUID support by default IF
the dhcpcd.duid file exits. This keeps the clients working as they were,
which is good.
dhcpcd no longer sends a default ClientID for ethernet interfaces.
This is so we can re-use the address the kernel DHCP client found.
To retain the old behaviour of sending a default ClientID based on the
dhcpcd no longer sends a default ClientID for ethernet interfaces.
This is so we can re-use the address the kernel DHCP client found.
To retain the old behaviour of sending a default ClientID based on the
hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
If CMDLINE_COMPAT is defined, we renable the sending of ClientID by default
AND adding clientid to dhcpcd.conf causes it NOT to be sent.
dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
changes the meaning of some options.
ChangeLog

300
external/bsd/dhcpcd/dist/arp.c vendored Normal file
View File

@ -0,0 +1,300 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "arp.h"
#include "bind.h"
#include "common.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if-options.h"
#include "ipv4ll.h"
#include "net.h"
#define ARP_LEN \
(sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
static int
send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
{
uint8_t arp_buffer[ARP_LEN];
struct arphdr ar;
size_t len;
uint8_t *p;
int retval;
ar.ar_hrd = htons(iface->family);
ar.ar_pro = htons(ETHERTYPE_IP);
ar.ar_hln = iface->hwlen;
ar.ar_pln = sizeof(sip);
ar.ar_op = htons(op);
memcpy(arp_buffer, &ar, sizeof(ar));
p = arp_buffer + sizeof(ar);
memcpy(p, iface->hwaddr, iface->hwlen);
p += iface->hwlen;
memcpy(p, &sip, sizeof(sip));
p += sizeof(sip);
/* ARP requests should ignore this */
retval = iface->hwlen;
while (retval--)
*p++ = '\0';
memcpy(p, &tip, sizeof(tip));
p += sizeof(tip);
len = p - arp_buffer;
retval = send_raw_packet(iface, ETHERTYPE_ARP, arp_buffer, len);
return retval;
}
static void
handle_arp_failure(struct interface *iface)
{
if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr))) {
handle_ipv4ll_failure(iface);
return;
}
if (iface->state->lease.frominfo)
unlink(iface->leasefile);
else
send_decline(iface);
close_sockets(iface);
delete_timeout(NULL, iface);
if (iface->state->lease.frominfo)
start_interface(iface);
else
add_timeout_sec(DHCP_ARP_FAIL, start_interface, iface);
}
static void
handle_arp_packet(void *arg)
{
struct interface *iface = arg;
uint8_t arp_buffer[ARP_LEN];
struct arphdr ar;
uint32_t reply_s;
uint32_t reply_t;
uint8_t *hw_s, *hw_t;
ssize_t bytes;
struct if_state *state = iface->state;
struct if_options *opts = state->options;
const char *hwaddr;
struct in_addr ina;
state->fail.s_addr = 0;
for(;;) {
bytes = get_raw_packet(iface, ETHERTYPE_ARP,
arp_buffer, sizeof(arp_buffer));
if (bytes == 0 || bytes == -1)
return;
/* We must have a full ARP header */
if ((size_t)bytes < sizeof(ar))
continue;
memcpy(&ar, arp_buffer, sizeof(ar));
/* Protocol must be IP. */
if (ar.ar_pro != htons(ETHERTYPE_IP))
continue;
if (ar.ar_pln != sizeof(reply_s))
continue;
/* Only these types are recognised */
if (ar.ar_op != htons(ARPOP_REPLY) &&
ar.ar_op != htons(ARPOP_REQUEST))
continue;
/* Get pointers to the hardware addreses */
hw_s = arp_buffer + sizeof(ar);
hw_t = hw_s + ar.ar_hln + ar.ar_pln;
/* Ensure we got all the data */
if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes)
continue;
/* Ignore messages from ourself */
if (ar.ar_hln == iface->hwlen &&
memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
continue;
/* Copy out the IP addresses */
memcpy(&reply_s, hw_s + ar.ar_hln, ar.ar_pln);
memcpy(&reply_t, hw_t + ar.ar_hln, ar.ar_pln);
/* Check for arping */
if (state->arping_index &&
state->arping_index <= opts->arping_len &&
(reply_s == opts->arping[state->arping_index - 1] ||
(reply_s == 0 &&
reply_t == opts->arping[state->arping_index - 1])))
{
ina.s_addr = reply_s;
hwaddr = hwaddr_ntoa((unsigned char *)hw_s,
(size_t)ar.ar_hln);
syslog(LOG_INFO,
"%s: found %s on hardware address %s",
iface->name, inet_ntoa(ina), hwaddr);
if (select_profile(iface, hwaddr) == -1 &&
errno == ENOENT)
select_profile(iface, inet_ntoa(ina));
close_sockets(iface);
delete_timeout(NULL, iface);
start_interface(iface);
return;
}
/* Check for conflict */
if (state->offer &&
(reply_s == state->offer->yiaddr ||
(reply_s == 0 && reply_t == state->offer->yiaddr)))
state->fail.s_addr = state->offer->yiaddr;
/* Handle IPv4LL conflicts */
if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
(reply_s == iface->addr.s_addr ||
(reply_s == 0 && reply_t == iface->addr.s_addr)))
state->fail.s_addr = iface->addr.s_addr;
if (state->fail.s_addr) {
syslog(LOG_ERR, "%s: hardware address %s claims %s",
iface->name,
hwaddr_ntoa((unsigned char *)hw_s,
(size_t)ar.ar_hln),
inet_ntoa(state->fail));
errno = EEXIST;
handle_arp_failure(iface);
return;
}
}
}
void
send_arp_announce(void *arg)
{
struct interface *iface = arg;
struct if_state *state = iface->state;
struct timeval tv;
if (iface->arp_fd == -1) {
open_socket(iface, ETHERTYPE_ARP);
add_event(iface->arp_fd, handle_arp_packet, iface);
}
if (++state->claims < ANNOUNCE_NUM)
syslog(LOG_DEBUG,
"%s: sending ARP announce (%d of %d), "
"next in %d.00 seconds",
iface->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
else
syslog(LOG_DEBUG,
"%s: sending ARP announce (%d of %d)",
iface->name, state->claims, ANNOUNCE_NUM);
if (send_arp(iface, ARPOP_REQUEST,
state->new->yiaddr, state->new->yiaddr) == -1)
syslog(LOG_ERR, "send_arp: %m");
if (state->claims < ANNOUNCE_NUM) {
add_timeout_sec(ANNOUNCE_WAIT, send_arp_announce, iface);
return;
}
if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
/* We should pretend to be at the end
* of the DHCP negotation cycle unless we rebooted */
if (state->interval != 0)
state->interval = 64;
state->probes = 0;
state->claims = 0;
tv.tv_sec = state->interval - DHCP_RAND_MIN;
tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
timernorm(&tv);
add_timeout_tv(&tv, start_discover, iface);
} else {
delete_event(iface->arp_fd);
close(iface->arp_fd);
iface->arp_fd = -1;
}
}
void
send_arp_probe(void *arg)
{
struct interface *iface = arg;
struct if_state *state = iface->state;
struct in_addr addr;
struct timeval tv;
int arping = 0;
if (state->arping_index < state->options->arping_len) {
addr.s_addr = state->options->arping[state->arping_index];
arping = 1;
} else if (state->offer) {
if (state->offer->yiaddr)
addr.s_addr = state->offer->yiaddr;
else
addr.s_addr = state->offer->ciaddr;
} else
addr.s_addr = iface->addr.s_addr;
if (iface->arp_fd == -1) {
open_socket(iface, ETHERTYPE_ARP);
add_event(iface->arp_fd, handle_arp_packet, iface);
}
if (state->probes == 0) {
if (arping)
syslog(LOG_INFO, "%s: searching for %s",
iface->name, inet_ntoa(addr));
else
syslog(LOG_INFO, "%s: checking for %s",
iface->name, inet_ntoa(addr));
}
if (++state->probes < PROBE_NUM) {
tv.tv_sec = PROBE_MIN;
tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
timernorm(&tv);
add_timeout_tv(&tv, send_arp_probe, iface);
} else {
tv.tv_sec = ANNOUNCE_WAIT;
tv.tv_usec = 0;
if (arping) {
state->probes = 0;
if (++state->arping_index < state->options->arping_len)
add_timeout_tv(&tv, send_arp_probe, iface);
else
add_timeout_tv(&tv, start_interface, iface);
} else
add_timeout_tv(&tv, bind_interface, iface);
}
syslog(LOG_DEBUG,
"%s: sending ARP probe (%d of %d), next in %0.2f seconds",
iface->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
timeval_to_double(&tv));
if (send_arp(iface, ARPOP_REQUEST, 0, addr.s_addr) == -1)
syslog(LOG_ERR, "send_arp: %m");
}
void
start_arping(struct interface *iface)
{
iface->state->probes = 0;
iface->state->arping_index = 0;
send_arp_probe(iface);
}

49
external/bsd/dhcpcd/dist/arp.h vendored Normal file
View File

@ -0,0 +1,49 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef ARP_H
#define ARP_H
/* These are for IPV4LL, RFC 3927.
* We put them here as we use the timings for all ARP foo. */
#define PROBE_WAIT 1
#define PROBE_NUM 3
#define PROBE_MIN 1
#define PROBE_MAX 2
#define ANNOUNCE_WAIT 2
#define ANNOUNCE_NUM 2
#define ANNOUNCE_INTERVAL 2
#define MAX_CONFLICTS 10
#define RATE_LIMIT_INTERVAL 60
#define DEFEND_INTERVAL 10
#include "dhcpcd.h"
void send_arp_announce(void *);
void send_arp_probe(void *);
void start_arping(struct interface *);
#endif

221
external/bsd/dhcpcd/dist/bind.c vendored Normal file
View File

@ -0,0 +1,221 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <fcntl.h>
#ifdef BSD
# include <paths.h>
#endif
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "arp.h"
#include "bind.h"
#include "common.h"
#include "configure.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if-options.h"
#include "net.h"
#include "signals.h"
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
#ifndef THERE_IS_NO_FORK
pid_t
daemonise(void)
{
pid_t pid;
sigset_t full;
sigset_t old;
char buf = '\0';
int sidpipe[2], fd;
if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
return 0;
sigfillset(&full);
sigprocmask(SIG_SETMASK, &full, &old);
/* Setup a signal pipe so parent knows when to exit. */
if (pipe(sidpipe) == -1) {
syslog(LOG_ERR, "pipe: %m");
return -1;
}
syslog(LOG_INFO, "forking to background");
switch (pid = fork()) {
case -1:
syslog(LOG_ERR, "fork: %m");
exit(EXIT_FAILURE);
/* NOTREACHED */
case 0:
setsid();
/* Notify parent it's safe to exit as we've detached. */
close(sidpipe[0]);
if (write(sidpipe[1], &buf, 1) == -1)
syslog(LOG_ERR, "failed to notify parent: %m");
close(sidpipe[1]);
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
close(fd);
}
break;
default:
signal_reset();
/* Wait for child to detach */
close(sidpipe[1]);
if (read(sidpipe[0], &buf, 1) == -1)
syslog(LOG_ERR, "failed to read child: %m");
close(sidpipe[0]);
break;
}
/* Done with the fd now */
if (pid != 0) {
writepid(pidfd, pid);
close(pidfd);
pidfd = -1;
exit(EXIT_SUCCESS);
}
options |= DHCPCD_DAEMONISED;
sigprocmask(SIG_SETMASK, &old, NULL);
return pid;
}
#endif
void
bind_interface(void *arg)
{
struct interface *iface = arg;
struct if_state *state = iface->state;
struct if_options *ifo = state->options;
struct dhcp_lease *lease = &state->lease;
struct timeval tv;
/* We're binding an address now - ensure that sockets are closed */
close_sockets(iface);
state->reason = NULL;
delete_timeout(handle_exit_timeout, NULL);
if (clock_monotonic)
get_monotonic(&lease->boundtime);
state->xid = 0;
free(state->old);
state->old = state->new;
state->new = state->offer;
state->offer = NULL;
get_lease(lease, state->new);
if (ifo->options & DHCPCD_STATIC) {
syslog(LOG_INFO, "%s: using static address %s",
iface->name, inet_ntoa(lease->addr));
lease->leasetime = ~0U;
lease->net.s_addr = ifo->req_mask.s_addr;
state->reason = "STATIC";
} else if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
syslog(LOG_INFO, "%s: using IPv4LL address %s",
iface->name, inet_ntoa(lease->addr));
lease->leasetime = ~0U;
state->reason = "IPV4LL";
} else if (ifo->options & DHCPCD_INFORM) {
if (ifo->req_addr.s_addr != 0)
lease->addr.s_addr = ifo->req_addr.s_addr;
else
lease->addr.s_addr = iface->addr.s_addr;
syslog(LOG_INFO, "%s: received approval for %s", iface->name,
inet_ntoa(lease->addr));
lease->leasetime = ~0U;
state->reason = "INFORM";
} else {
if (gettimeofday(&tv, NULL) == 0)
lease->leasedfrom = tv.tv_sec;
else if (lease->frominfo)
state->reason = "TIMEOUT";
if (lease->leasetime == ~0U) {
lease->renewaltime =
lease->rebindtime =
lease->leasetime;
syslog(LOG_INFO, "%s: leased %s for infinity",
iface->name, inet_ntoa(lease->addr));
} else {
if (lease->rebindtime == 0)
lease->rebindtime = lease->leasetime * T2;
else if (lease->rebindtime >= lease->leasetime) {
lease->rebindtime = lease->leasetime * T2;
syslog(LOG_ERR,
"%s: rebind time greater than lease "
"time, forcing to %u seconds",
iface->name, lease->rebindtime);
}
if (lease->renewaltime == 0)
lease->renewaltime = lease->leasetime * T1;
else if (lease->renewaltime > lease->rebindtime) {
lease->renewaltime = lease->leasetime * T1;
syslog(LOG_ERR,
"%s: renewal time greater than rebind "
"time, forcing to %u seconds",
iface->name, lease->renewaltime);
}
syslog(LOG_INFO,
"%s: leased %s for %u seconds", iface->name,
inet_ntoa(lease->addr), lease->leasetime);
}
}
if (options & DHCPCD_TEST) {
state->reason = "TEST";
run_script(iface);
exit(EXIT_SUCCESS);
}
if (state->reason == NULL) {
if (state->old) {
if (state->old->yiaddr == state->new->yiaddr &&
lease->server.s_addr)
state->reason = "RENEW";
else
state->reason = "REBIND";
} else if (state->state == DHS_REBOOT)
state->reason = "REBOOT";
else
state->reason = "BOUND";
}
if (lease->leasetime == ~0U)
lease->renewaltime = lease->rebindtime = lease->leasetime;
else {
add_timeout_sec(lease->renewaltime, start_renew, iface);
add_timeout_sec(lease->rebindtime, start_rebind, iface);
add_timeout_sec(lease->leasetime, start_expire, iface);
}
configure(iface);
daemonise();
state->state = DHS_BOUND;
if (ifo->options & DHCPCD_ARP) {
state->claims = 0;
send_arp_announce(iface);
}
}

39
external/bsd/dhcpcd/dist/bind.h vendored Normal file
View File

@ -0,0 +1,39 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef BIND_H
#define BIND_H
#include "config.h"
#ifdef THERE_IS_NO_FORK
# define daemonise() {}
#else
pid_t daemonise(void);
#endif
void bind_interface(void *);
#endif

View File

@ -39,12 +39,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dhcp.h"
#include "logger.h"
#include "net.h"
#include "bpf-filter.h"
@ -81,7 +81,7 @@ open_socket(struct interface *iface, int protocol)
goto eexit;
if (pv.bv_major != BPF_MAJOR_VERSION ||
pv.bv_minor < BPF_MINOR_VERSION) {
logger(LOG_ERR, "BPF version mismatch - recompile " PACKAGE);
syslog(LOG_ERR, "BPF version mismatch - recompile");
goto eexit;
}
@ -136,7 +136,7 @@ eexit:
ssize_t
send_raw_packet(const struct interface *iface, int protocol,
const void *data, ssize_t len)
const void *data, ssize_t len)
{
struct iovec iov[2];
struct ether_header hw;
@ -160,7 +160,7 @@ send_raw_packet(const struct interface *iface, int protocol,
* So we pass the buffer in the API so we can loop on >1 packet. */
ssize_t
get_raw_packet(struct interface *iface, int protocol,
void *data, ssize_t len)
void *data, ssize_t len)
{
int fd = -1;
struct bpf_hdr packet;
@ -184,7 +184,7 @@ get_raw_packet(struct interface *iface, int protocol,
}
bytes = -1;
memcpy(&packet, iface->buffer + iface->buffer_pos,
sizeof(packet));
sizeof(packet));
if (packet.bh_caplen != packet.bh_datalen)
goto next; /* Incomplete packet, drop. */
if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
@ -197,7 +197,7 @@ get_raw_packet(struct interface *iface, int protocol,
memcpy(data, payload, bytes);
next:
iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
packet.bh_caplen);
packet.bh_caplen);
if (iface->buffer_pos >= iface->buffer_len)
iface->buffer_len = iface->buffer_pos = 0;
if (bytes != -1)

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -25,6 +25,8 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifdef __APPLE__
# include <mach/mach_time.h>
# include <mach/kern_return.h>
@ -33,7 +35,6 @@
#include <sys/param.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#ifdef BSD
# include <paths.h>
@ -42,42 +43,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
#include "logger.h"
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
int clock_monotonic = 0;
int clock_monotonic;
static char *lbuf;
static size_t lbuf_len;
#ifdef DEBUG_MEMORY
static void
free_lbuf(void)
{
free(lbuf);
}
#endif
/* Handy routine to read very long lines in text files.
* This means we read the whole line and avoid any nasty buffer overflows. */
ssize_t
get_line(char **line, size_t *len, FILE *fp)
* This means we read the whole line and avoid any nasty buffer overflows.
* We strip leading space and avoid comment lines, making the code that calls
* us smaller.
* As we don't use threads, this API is clean too. */
char *
get_line(FILE * __restrict fp)
{
char *p;
size_t last = 0;
char *p, *e;
size_t last;
while(!feof(fp)) {
if (*line == NULL || last != 0) {
*len += BUFSIZ;
*line = xrealloc(*line, *len);
again:
if (feof(fp))
return NULL;
#ifdef DEBUG_MEMORY
if (lbuf == NULL)
atexit(free_lbuf);
#endif
last = 0;
do {
if (lbuf == NULL || last != 0) {
lbuf_len += BUFSIZ;
lbuf = xrealloc(lbuf, lbuf_len);
}
p = *line + last;
p = lbuf + last;
memset(p, 0, BUFSIZ);
if (fgets(p, BUFSIZ, fp) == NULL)
break;
last += strlen(p);
if (last && (*line)[last - 1] == '\n') {
(*line)[last - 1] = '\0';
if (last != 0 && lbuf[last - 1] == '\n') {
lbuf[last - 1] = '\0';
break;
}
} while(!feof(fp));
if (last == 0)
return NULL;
e = p + last - 1;
for (p = lbuf; p < e; p++) {
if (*p != ' ' && *p != '\t')
break;
}
return last;
if (p == e || *p == '#' || *p == ';')
goto again;
return p;
}
/* Simple hack to return a random number without arc4random */
@ -85,9 +120,9 @@ get_line(char **line, size_t *len, FILE *fp)
uint32_t arc4random(void)
{
int fd;
static unsigned long seed = 0;
static unsigned long seed;
if (!seed) {
if (seed == 0) {
fd = open("/dev/urandom", 0);
if (fd == -1 || read(fd, &seed, sizeof(seed)) == -1)
seed = time(0);
@ -140,32 +175,15 @@ closefrom(int fd)
}
#endif
/* Close our fd's */
int
close_fds(void)
{
int fd;
if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1)
return -1;
dup2(fd, fileno(stdin));
dup2(fd, fileno(stdout));
dup2(fd, fileno(stderr));
if (fd > 2)
close(fd);
return 0;
}
int
set_cloexec(int fd)
{
int flags;
if ((flags = fcntl(fd, F_GETFD, 0)) == -1
|| fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
{
logger(LOG_ERR, "fcntl: %s", strerror(errno));
syslog(LOG_ERR, "fcntl: %m");
return -1;
}
return 0;
@ -176,10 +194,10 @@ set_nonblock(int fd)
{
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1
|| fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
{
logger(LOG_ERR, "fcntl: %s", strerror(errno));
syslog(LOG_ERR, "fcntl: %m");
return -1;
}
return 0;
@ -199,7 +217,7 @@ get_monotonic(struct timeval *tp)
struct timespec ts;
static clockid_t posix_clock;
if (posix_clock_set == 0) {
if (!posix_clock_set) {
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
posix_clock = CLOCK_MONOTONIC;
clock_monotonic = posix_clock_set = 1;
@ -222,7 +240,7 @@ get_monotonic(struct timeval *tp)
uint64_t nano;
long rem;
if (posix_clock_set == 0) {
if (!posix_clock_set) {
if (mach_timebase_info(&info) == KERN_SUCCESS) {
factor = (double)info.numer / (double)info.denom;
clock_monotonic = posix_clock_set = 1;
@ -245,7 +263,7 @@ get_monotonic(struct timeval *tp)
/* Something above failed, so fall back to gettimeofday */
if (!posix_clock_set) {
logger(LOG_WARNING, NO_MONOTONIC);
syslog(LOG_WARNING, NO_MONOTONIC);
posix_clock_set = 1;
}
return gettimeofday(tp, NULL);
@ -281,9 +299,9 @@ xmalloc(size_t s)
{
void *value = malloc(s);
if (value)
if (value != NULL)
return value;
logger(LOG_ERR, "memory exhausted");
syslog(LOG_ERR, "memory exhausted (xalloc %zu bytes)", s);
exit (EXIT_FAILURE);
/* NOTREACHED */
}
@ -302,9 +320,9 @@ xrealloc(void *ptr, size_t s)
{
void *value = realloc(ptr, s);
if (value)
return (value);
logger(LOG_ERR, "memory exhausted");
if (value != NULL)
return value;
syslog(LOG_ERR, "memory exhausted (xrealloc %zu bytes)", s);
exit(EXIT_FAILURE);
/* NOTREACHED */
}
@ -314,13 +332,13 @@ xstrdup(const char *str)
{
char *value;
if (!str)
if (str == NULL)
return NULL;
if ((value = strdup(str)))
if ((value = strdup(str)) != NULL)
return value;
logger(LOG_ERR, "memory exhausted");
syslog(LOG_ERR, "memory exhausted (xstrdup)");
exit(EXIT_FAILURE);
/* NOTREACHED */
}

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -37,12 +37,37 @@
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
#define timernorm(tvp) \
do { \
while ((tvp)->tv_usec >= 1000000) { \
(tvp)->tv_sec++; \
(tvp)->tv_usec -= 1000000; \
} \
} while (0 /* CONSTCOND */);
#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
# define _unused __attribute__((__unused__))
# define _noreturn __attribute__((__noreturn__))
# define _packed __attribute__((__packed__))
# define _unused __attribute__((__unused__))
#else
# define _noreturn
# define _packed
# define _unused
#endif
/* We don't really need this as our supported systems define __restrict
* automatically for us, but it is here for completeness. */
#ifndef __restrict
# if defined(__lint__)
# define __restrict
# elif __STDC_VERSION__ >= 199901L
# define __restrict restrict
# elif !(2 < __GNUC__ || (2 == __GNU_C && 95 <= __GNUC_VERSION__))
# define __restrict
# endif
#endif
#ifndef HAVE_ARC4RANDOM
# ifdef __GLIBC__
uint32_t arc4random(void);
@ -71,10 +96,9 @@ size_t strlcpy(char *, const char *, size_t);
int closefrom(int);
#endif
int close_fds(void);
int set_cloexec(int);
int set_nonblock(int);
ssize_t get_line(char **, size_t *, FILE *);
char *get_line(FILE * __restrict);
extern int clock_monotonic;
int get_monotonic(struct timeval *);
time_t uptime(void);

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -28,16 +28,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "4.0.13"
/*
* By default we don't add a local link route if we got a routeable address.
* This is because dhcpcd can't really decide which interface should allow
* link local routing when we have more than one interface.
* Ideally the host network scripts should add the link local route for us.
* If not, you can define this to get dhcpcd to always add the link local route.
*/
// #define IPV4LL_ALWAYSROUTE
#define VERSION "5.0.0"
/* Some systems do not have a working fork. */
/* #define THERE_IS_NO_FORK */
@ -69,7 +60,10 @@
# define LEASEFILE DBDIR "/" PACKAGE "-%s.lease"
#endif
#ifndef PIDFILE
# define PIDFILE RUNDIR "/" PACKAGE "-%s.pid"
# define PIDFILE RUNDIR "/" PACKAGE "%s%s.pid"
#endif
#ifndef CONTROLSOCKET
# define CONTROLSOCKET RUNDIR "/" PACKAGE ".sock"
#endif
#endif

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -26,6 +26,7 @@
*/
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <netinet/in.h>
@ -35,19 +36,32 @@
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "configure.h"
#include "dhcp.h"
#include "dhcpcd.h"
#include "logger.h"
#include "if-options.h"
#include "if-pref.h"
#include "net.h"
#include "signals.h"
#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
/* Some systems have route metrics */
#ifndef HAVE_ROUTE_METRIC
# ifdef __linux__
# define HAVE_ROUTE_METRIC 1
# endif
# ifndef HAVE_ROUTE_METRIC
# define HAVE_ROUTE_METRIC 0
# endif
#endif
static struct rt *routes;
static int
exec_script(char *const *argv, char *const *env)
@ -63,12 +77,12 @@ exec_script(char *const *argv, char *const *env)
switch (pid = vfork()) {
case -1:
logger(LOG_ERR, "vfork: %s", strerror(errno));
syslog(LOG_ERR, "vfork: %m");
break;
case 0:
sigprocmask(SIG_SETMASK, &old, NULL);
execve(argv[0], argv, env);
logger(LOG_ERR, "%s: %s", argv[0], strerror(errno));
syslog(LOG_ERR, "%s: %m", argv[0]);
_exit(127);
/* NOTREACHED */
}
@ -79,70 +93,229 @@ exec_script(char *const *argv, char *const *env)
return pid;
}
int
run_script(const struct options *options, const char *iface,
const char *reason,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
static char *
make_var(const char *prefix, const char *var)
{
char *const argv[2] = { UNCONST(options->script), NULL };
char **env = NULL, **ep;
char *path;
ssize_t e, elen;
pid_t pid;
int status = 0;
size_t len;
char *v;
logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
len = strlen(prefix) + strlen(var) + 2;
v = xmalloc(len);
snprintf(v, len, "%s_%s", prefix, var);
return v;
}
static void
append_config(char ***env, ssize_t *len,
const char *prefix, const char *const *config)
{
ssize_t i, j, e1;
char **ne, *eq;
if (config == NULL)
return;
ne = *env;
for (i = 0; config[i] != NULL; i++) {
eq = strchr(config[i], '=');
e1 = eq - config[i] + 1;
for (j = 0; j < *len; j++) {
if (strncmp(ne[j] + strlen(prefix) + 1,
config[i], e1) == 0)
{
free(ne[j]);
ne[j] = make_var(prefix, config[i]);
break;
}
}
if (j == *len) {
j++;
ne = xrealloc(ne, sizeof(char *) * (j + 1));
ne[j - 1] = make_var(prefix, config[i]);
*len = j;
}
}
*env = ne;
}
static size_t
arraytostr(const char *const *argv, char **s)
{
const char *const *ap;
char *p;
size_t len, l;
len = 0;
ap = argv;
while (*ap)
len += strlen(*ap++) + 1;
*s = p = xmalloc(len);
ap = argv;
while (*ap) {
l = strlen(*ap) + 1;
memcpy(p, *ap, l);
p += l;
ap++;
}
return len;
}
static ssize_t
make_env(const struct interface *iface, char ***argv)
{
char **env, *p;
ssize_t e, elen, l;
const struct if_options *ifo = iface->state->options;
const struct interface *ifp;
/* Make our env */
elen = 5;
elen = 7;
env = xmalloc(sizeof(char *) * (elen + 1));
path = getenv("PATH");
if (path) {
e = strlen("PATH") + strlen(path) + 2;
env[0] = xmalloc(e);
snprintf(env[0], e, "PATH=%s", path);
} else
env[0] = xstrdup(DEFAULT_PATH);
e = strlen("interface") + strlen(iface) + 2;
e = strlen("interface") + strlen(iface->name) + 2;
env[0] = xmalloc(e);
snprintf(env[0], e, "interface=%s", iface->name);
e = strlen("reason") + strlen(iface->state->reason) + 2;
env[1] = xmalloc(e);
snprintf(env[1], e, "interface=%s", iface);
e = strlen("reason") + strlen(reason) + 2;
env[2] = xmalloc(e);
snprintf(env[2], e, "reason=%s", reason);
snprintf(env[1], e, "reason=%s", iface->state->reason);
e = 20;
env[2] = xmalloc(e);
snprintf(env[2], e, "pid=%d", getpid());
env[3] = xmalloc(e);
snprintf(env[3], e, "pid=%d", getpid());
snprintf(env[3], e, "ifmetric=%d", iface->metric);
env[4] = xmalloc(e);
snprintf(env[4], e, "metric=%d", options->metric);
if (dhcpo) {
e = configure_env(NULL, NULL, dhcpo, options);
if (e > 0) {
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
elen += configure_env(env + elen, "old", dhcpo, options);
snprintf(env[4], e, "ifwireless=%d", iface->wireless);
env[5] = xmalloc(e);
snprintf(env[5], e, "ifflags=%u", iface->flags);
l = e = strlen("interface_order=");
for (ifp = ifaces; ifp; ifp = ifp->next)
e += strlen(ifp->name) + 1;
p = env[6] = xmalloc(e);
strlcpy(p, "interface_order=", e);
e -= l;
p += l;
for (ifp = ifaces; ifp; ifp = ifp->next) {
l = strlcpy(p, ifp->name, e);
p += l;
e -= l;
*p++ = ' ';
e--;
}
*--p = '\0';
if (*iface->state->profile) {
e = strlen("profile=") + strlen(iface->state->profile) + 2;
env[elen] = xmalloc(e);
snprintf(env[elen++], e, "profile=%s", iface->state->profile);
}
if (iface->wireless) {
e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
if (iface->state->new != NULL ||
strcmp(iface->state->reason, "CARRIER") == 0)
{
env = xrealloc(env, sizeof(char *) * (elen + 2));
env[elen] = xmalloc(e);
snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
}
if (iface->state->old != NULL ||
strcmp(iface->state->reason, "NOCARRIER") == 0)
{
env = xrealloc(env, sizeof(char *) * (elen + 2));
env[elen] = xmalloc(e);
snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
}
}
if (dhcpn) {
e = configure_env(NULL, NULL, dhcpn, options);
if (iface->state->old) {
e = configure_env(NULL, NULL, iface->state->old, ifo);
if (e > 0) {
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
elen += configure_env(env + elen, "new", dhcpn, options);
elen += configure_env(env + elen, "old",
iface->state->old, ifo);
}
append_config(&env, &elen, "old",
(const char *const *)ifo->config);
}
if (iface->state->new) {
e = configure_env(NULL, NULL, iface->state->new, ifo);
if (e > 0) {
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
elen += configure_env(env + elen, "new",
iface->state->new, ifo);
}
append_config(&env, &elen, "new",
(const char *const *)ifo->config);
}
/* Add our base environment */
if (options->environ) {
if (ifo->environ) {
e = 0;
while (options->environ[e++])
while (ifo->environ[e++])
;
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
e = 0;
while (options->environ[e]) {
env[elen + e] = xstrdup(options->environ[e]);
while (ifo->environ[e]) {
env[elen + e] = xstrdup(ifo->environ[e]);
e++;
}
elen += e;
}
env[elen] = '\0';
*argv = env;
return elen;
}
int
send_interface(int fd, const struct interface *iface)
{
char **env, **ep, *s;
ssize_t elen;
struct iovec iov[2];
int retval;
retval = 0;
make_env(iface, &env);
elen = arraytostr((const char *const *)env, &s);
iov[0].iov_base = &elen;
iov[0].iov_len = sizeof(ssize_t);
iov[1].iov_base = s;
iov[1].iov_len = elen;
retval = writev(fd, iov, 2);
ep = env;
while (*ep)
free(*ep++);
free(env);
free(s);
return retval;
}
int
run_script(const struct interface *iface)
{
char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
char **env = NULL, **ep;
char *path, *bigenv;
ssize_t e, elen = 0;
pid_t pid;
int status = 0;
const struct fd_list *fd;
struct iovec iov[2];
syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
iface->name, argv[0], iface->state->reason);
/* Make our env */
elen = make_env(iface, &env);
env = xrealloc(env, sizeof(char *) * (elen + 2));
/* Add path to it */
path = getenv("PATH");
if (path) {
e = strlen("PATH") + strlen(path) + 2;
env[elen] = xmalloc(e);
snprintf(env[elen], e, "PATH=%s", path);
} else
env[elen] = xstrdup(DEFAULT_PATH);
env[++elen] = '\0';
pid = exec_script(argv, env);
if (pid == -1)
status = -1;
@ -150,13 +323,30 @@ run_script(const struct options *options, const char *iface,
/* Wait for the script to finish */
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
logger(LOG_ERR, "waitpid: %s", strerror(errno));
syslog(LOG_ERR, "waitpid: %m");
status = -1;
break;
}
}
}
/* Send to our listeners */
bigenv = NULL;
for (fd = fds; fd != NULL; fd = fd->next) {
if (fd->listener) {
if (bigenv == NULL) {
elen = arraytostr((const char *const *)env,
&bigenv);
iov[0].iov_base = &elen;
iov[0].iov_len = sizeof(ssize_t);
iov[1].iov_base = bigenv;
iov[1].iov_len = elen;
}
writev(fd->fd, iov, 2);
}
}
free(bigenv);
/* Cleanup */
ep = env;
while (*ep)
@ -166,251 +356,346 @@ run_script(const struct options *options, const char *iface,
}
static struct rt *
reverse_routes(struct rt *routes)
find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
const struct rt *srt)
{
struct rt *rt;
struct rt *rtn = NULL;
while (routes) {
rt = routes->next;
routes->next = rtn;
rtn = routes;
routes = rt;
if (lrt)
*lrt = NULL;
for (rt = rts; rt; rt = rt->next) {
if (rt->dest.s_addr == r->dest.s_addr &&
#if HAVE_ROUTE_METRIC
(srt || (!rt->iface ||
rt->iface->metric == r->iface->metric)) &&
#endif
(!srt || srt != rt) &&
rt->net.s_addr == r->net.s_addr)
return rt;
if (lrt)
*lrt = rt;
}
return rtn;
return NULL;
}
static void
desc_route(const char *cmd, const struct rt *rt, const char *ifname)
{
char addr[sizeof("000.000.000.000") + 1];
strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
if (rt->gate.s_addr == INADDR_ANY)
syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd,
addr, inet_ntocidr(rt->net));
else if (rt->gate.s_addr == rt->dest.s_addr &&
rt->net.s_addr == INADDR_BROADCAST)
syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd,
addr);
else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY)
syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd,
inet_ntoa(rt->gate));
else
syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd,
addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
}
/* If something other than dhcpcd removes a route,
* we need to remove it from our internal table. */
int
route_deleted(const struct rt *rt)
{
struct rt *f, *l;
f = find_route(routes, rt, &l, NULL);
if (f == NULL)
return 0;
desc_route("removing", f, f->iface->name);
if (l)
l->next = f->next;
else
routes = f->next;
free(f);
return 1;
}
static int
delete_route(const struct interface *iface, struct rt *rt, int metric)
n_route(struct rt *rt, const struct interface *iface)
{
char *addr;
int retval;
/* Don't set default routes if not asked to */
if (rt->dest.s_addr == 0 &&
rt->net.s_addr == 0 &&
!(iface->state->options->options & DHCPCD_GATEWAY))
return -1;
addr = xstrdup(inet_ntoa(rt->dest));
logger(LOG_DEBUG, "deleting route %s/%d via %s",
addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
free(addr);
retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
if (retval != 0 && errno != ENOENT && errno != ESRCH)
logger(LOG_ERR," del_route: %s", strerror(errno));
return retval;
}
static int
delete_routes(struct interface *iface, int metric)
{
struct rt *rt;
struct rt *rtn;
int retval = 0;
rt = reverse_routes(iface->routes);
while (rt) {
rtn = rt->next;
retval += delete_route(iface, rt, metric);
free(rt);
rt = rtn;
}
iface->routes = NULL;
return retval;
}
static int
in_routes(const struct rt *routes, const struct rt *rt)
{
while (routes) {
if (routes->dest.s_addr == rt->dest.s_addr &&
routes->net.s_addr == rt->net.s_addr &&
routes->gate.s_addr == rt->gate.s_addr)
desc_route("adding", rt, iface->name);
if (!add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric))
return 0;
if (errno == EEXIST) {
/* Pretend we added the subnet route */
if (rt->dest.s_addr == (iface->addr.s_addr & iface->net.s_addr) &&
rt->net.s_addr == iface->net.s_addr &&
rt->gate.s_addr == 0)
return 0;
routes = routes->next;
else
return -1;
}
syslog(LOG_ERR, "%s: add_route: %m", iface->name);
return -1;
}
static int
configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
const struct options *options)
c_route(struct rt *ort, struct rt *nrt, const struct interface *iface)
{
struct rt *rt, *ort;
struct rt *rtn = NULL, *nr = NULL;
int remember;
int retval = 0;
char *addr;
/* Don't set default routes if not asked to */
if (nrt->dest.s_addr == 0 &&
nrt->net.s_addr == 0 &&
!(iface->state->options->options & DHCPCD_GATEWAY))
return -1;
ort = get_option_routes(dhcp);
desc_route("changing", nrt, iface->name);
/* We delete and add the route so that we can change metric.
* This also has the nice side effect of flushing ARP entries so
* we don't have to do that manually. */
del_route(ort->iface, &ort->dest, &ort->net, &ort->gate,
ort->iface->metric);
if (!add_route(iface, &nrt->dest, &nrt->net, &nrt->gate,
iface->metric))
return 0;
syslog(LOG_ERR, "%s: add_route: %m", iface->name);
return -1;
}
#ifdef IPV4LL_ALWAYSROUTE
if (options->options & DHCPCD_IPV4LL &&
IN_PRIVATE(ntohl(dhcp->yiaddr)))
{
for (rt = ort; rt; rt = rt->next) {
/* Check if we have already got a link locale route
* dished out by the DHCP server */
if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
rt->net.s_addr == htonl(LINKLOCAL_MASK))
break;
rtn = rt;
}
static int
d_route(struct rt *rt, const struct interface *iface, int metric)
{
int retval;
if (!rt) {
rt = xmalloc(sizeof(*rt));
rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
rt->net.s_addr = htonl(LINKLOCAL_MASK);
rt->gate.s_addr = 0;
rt->next = NULL;
if (rtn)
rtn->next = rt;
else
ort = rt;
}
}
#endif
/* Now remove old routes we no longer use.
* We should do this in reverse order. */
iface->routes = reverse_routes(iface->routes);
for (rt = iface->routes; rt; rt = rt->next)
if (in_routes(ort, rt) != 0)
delete_route(iface, rt, options->metric);
for (rt = ort; rt; rt = rt->next) {
/* Don't set default routes if not asked to */
if (rt->dest.s_addr == 0 &&
rt->net.s_addr == 0 &&
!(options->options & DHCPCD_GATEWAY))
continue;
addr = xstrdup(inet_ntoa(rt->dest));
logger(LOG_DEBUG, "adding route to %s/%d via %s",
addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
free(addr);
remember = add_route(iface, &rt->dest,
&rt->net, &rt->gate,
options->metric);
retval += remember;
/* If we failed to add the route, we may have already added it
ourselves. If so, remember it again. */
if (remember < 0) {
if (errno != EEXIST)
logger(LOG_ERR, "add_route: %s",
strerror(errno));
if (in_routes(iface->routes, rt) == 0)
remember = 1;
}
/* This login is split from above due to the #ifdef below */
if (remember >= 0) {
if (nr) {
rtn->next = xmalloc(sizeof(*rtn));
rtn = rtn->next;
} else {
nr = rtn = xmalloc(sizeof(*rtn));
}
rtn->dest.s_addr = rt->dest.s_addr;
rtn->net.s_addr = rt->net.s_addr;
rtn->gate.s_addr = rt->gate.s_addr;
rtn->next = NULL;
}
}
free_routes(ort);
free_routes(iface->routes);
iface->routes = nr;
desc_route("deleting", rt, iface->name);
retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
if (retval != 0 && errno != ENOENT && errno != ESRCH)
syslog(LOG_ERR,"%s: del_route: %m", iface->name);
return retval;
}
static struct rt *
get_subnet_route(struct dhcp_message *dhcp)
{
in_addr_t addr, net;
struct rt *rt;
addr = dhcp->yiaddr;
if (addr == 0)
addr = dhcp->ciaddr;
/* Ensure we have all the needed values */
if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
net = get_netmask(addr);
if (net == INADDR_BROADCAST || net == INADDR_ANY)
return NULL;
rt = malloc(sizeof(*rt));
rt->dest.s_addr = addr & net;
rt->net.s_addr = net;
rt->gate.s_addr = 0;
return rt;
}
static struct rt *
add_subnet_route(struct rt *rt, const struct interface *iface)
{
struct rt *r;
if (iface->net.s_addr == INADDR_BROADCAST ||
iface->net.s_addr == INADDR_ANY ||
(iface->state->options->options &
(DHCPCD_INFORM | DHCPCD_STATIC) &&
iface->state->options->req_addr.s_addr == INADDR_ANY))
return rt;
r = xmalloc(sizeof(*r));
r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr;
r->net.s_addr = iface->net.s_addr;
r->gate.s_addr = 0;
r->next = rt;
return r;
}
static struct rt *
get_routes(const struct interface *iface)
{
struct rt *rt, *nrt = NULL, *r = NULL;
if (iface->state->options->routes != NULL) {
for (rt = iface->state->options->routes;
rt != NULL;
rt = rt->next)
{
if (rt->gate.s_addr == 0)
break;
if (r == NULL)
r = nrt = xmalloc(sizeof(*r));
else {
r->next = xmalloc(sizeof(*r));
r = r->next;
}
memcpy(r, rt, sizeof(*r));
r->next = NULL;
}
return nrt;
}
return get_option_routes(iface->state->new);
}
static struct rt *
add_destination_route(struct rt *rt, const struct interface *iface)
{
struct rt *r;
if (!(iface->flags & IFF_POINTOPOINT) ||
!has_option_mask(iface->state->options->dstmask, DHO_ROUTER))
return rt;
r = xmalloc(sizeof(*r));
r->dest.s_addr = INADDR_ANY;
r->net.s_addr = INADDR_ANY;
r->gate.s_addr = iface->dst.s_addr;
r->next = rt;
return r;
}
void
build_routes(void)
{
struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
const struct interface *ifp;
for (ifp = ifaces; ifp; ifp = ifp->next) {
if (ifp->state->new == NULL)
continue;
dnr = get_routes(ifp);
dnr = add_subnet_route(dnr, ifp);
dnr = add_destination_route(dnr, ifp);
for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) {
rt->iface = ifp;
/* Is this route already in our table? */
if ((find_route(nrs, rt, NULL, NULL)) != NULL)
continue;
/* Do we already manage it? */
if ((or = find_route(routes, rt, &rtl, NULL))) {
if (or->iface != ifp ||
rt->gate.s_addr != or->gate.s_addr)
{
if (c_route(or, rt, ifp) != 0)
continue;
}
if (rtl != NULL)
rtl->next = or->next;
else
routes = or->next;
free(or);
} else {
if (n_route(rt, ifp) != 0)
continue;
}
if (dnr == rt)
dnr = rtn;
else if (lrt)
lrt->next = rtn;
rt->next = nrs;
nrs = rt;
}
free_routes(dnr);
}
/* Remove old routes we used to manage */
for (rt = routes; rt; rt = rt->next) {
if (find_route(nrs, rt, NULL, NULL) == NULL)
d_route(rt, rt->iface, rt->iface->metric);
}
free_routes(routes);
routes = nrs;
}
static int
delete_address(struct interface *iface)
{
int retval;
logger(LOG_DEBUG, "deleting IP address %s/%d",
inet_ntoa(iface->addr),
inet_ntocidr(iface->net));
retval = del_address(iface->name, &iface->addr, &iface->net);
struct if_options *ifo;
ifo = iface->state->options;
if (ifo->options & DHCPCD_INFORM ||
(ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
return 0;
syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
iface->name,
inet_ntoa(iface->addr),
inet_ntocidr(iface->net));
retval = del_address(iface, &iface->addr, &iface->net);
if (retval == -1 && errno != EADDRNOTAVAIL)
logger(LOG_ERR, "del_address: %s", strerror(errno));
syslog(LOG_ERR, "del_address: %m");
iface->addr.s_addr = 0;
iface->net.s_addr = 0;
return retval;
}
int
configure(struct interface *iface, const char *reason,
const struct dhcp_message *dhcp, const struct dhcp_message *old,
const struct dhcp_lease *lease, const struct options *options,
int up)
configure(struct interface *iface)
{
struct in_addr addr;
struct in_addr net;
struct in_addr brd;
#ifdef __linux__
struct in_addr dest;
struct in_addr gate;
#endif
struct dhcp_message *dhcp = iface->state->new;
struct dhcp_lease *lease = &iface->state->lease;
struct if_options *ifo = iface->state->options;
struct rt *rt;
/* Grab our IP config */
if (dhcp == NULL)
up = 0;
else {
addr.s_addr = dhcp->yiaddr;
if (addr.s_addr == 0)
addr.s_addr = lease->addr.s_addr;
/* Ensure we have all the needed values */
if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
net.s_addr = get_netmask(addr.s_addr);
if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
brd.s_addr = addr.s_addr | ~net.s_addr;
}
/* As we are now adjusting an interface, we need to ensure
* we have them in the right order for routing and configuration. */
sort_interfaces();
/* If we aren't up, then reset the interface as much as we can */
if (!up) {
/* Only reset things if we had set them before */
if (iface->addr.s_addr != 0) {
delete_routes(iface, options->metric);
if (dhcp == NULL) {
build_routes();
if (iface->addr.s_addr != 0)
delete_address(iface);
}
run_script(options, iface->name, reason, NULL, old);
run_script(iface);
return 0;
}
/* This also changes netmask */
if (!(options->options & DHCPCD_INFORM) ||
!has_address(iface->name, &addr, &net)) {
logger(LOG_DEBUG, "adding IP address %s/%d",
inet_ntoa(addr), inet_ntocidr(net));
if (add_address(iface->name, &addr, &net, &brd) == -1 &&
if (!(ifo->options & DHCPCD_INFORM) ||
!has_address(iface->name, &lease->addr, &lease->net))
{
syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
iface->name, inet_ntoa(lease->addr),
inet_ntocidr(lease->net));
if (add_address(iface,
&lease->addr, &lease->net, &lease->brd) == -1 &&
errno != EEXIST)
{
logger(LOG_ERR, "add_address: %s", strerror(errno));
syslog(LOG_ERR, "add_address: %m");
return -1;
}
}
/* Now delete the old address if different */
if (iface->addr.s_addr != addr.s_addr &&
if (iface->addr.s_addr != lease->addr.s_addr &&
iface->addr.s_addr != 0)
delete_address(iface);
#ifdef __linux__
/* On linux, we need to change the subnet route to have our metric. */
if (iface->addr.s_addr != lease->addr.s_addr &&
options->metric > 0 && net.s_addr != INADDR_BROADCAST)
{
dest.s_addr = addr.s_addr & net.s_addr;
gate.s_addr = 0;
add_route(iface, &dest, &net, &gate, options->metric);
del_route(iface, &dest, &net, &gate, 0);
}
#endif
iface->addr.s_addr = lease->addr.s_addr;
iface->net.s_addr = lease->net.s_addr;
iface->addr.s_addr = addr.s_addr;
iface->net.s_addr = net.s_addr;
configure_routes(iface, dhcp, options);
if (!lease->frominfo)
/* We need to delete the subnet route to have our metric or
* prefer the interface. */
rt = get_subnet_route(dhcp);
if (rt != NULL) {
rt->iface = iface;
if (!find_route(routes, rt, NULL, NULL))
del_route(iface, &rt->dest, &rt->net, &rt->gate, 0);
free(rt);
}
build_routes();
if (!iface->state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(iface, dhcp) == -1)
logger(LOG_ERR, "write_lease: %s", strerror(errno));
run_script(options, iface->name, reason, dhcp, old);
syslog(LOG_ERR, "write_lease: %m");
run_script(iface);
return 0;
}

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -28,14 +28,11 @@
#ifndef DHCPCONFIG_H
#define DHCPCONFIG_H
#include "dhcpcd.h"
#include "dhcp.h"
#include "net.h"
int run_script(const struct options *, const char *, const char *,
const struct dhcp_message *, const struct dhcp_message *);
int configure(struct interface *, const char *,
const struct dhcp_message *, const struct dhcp_message *,
const struct dhcp_lease *, const struct options *, int);
int send_interface(int, const struct interface *);
int run_script(const struct interface *);
void build_routes(void);
int configure(struct interface *);
int route_deleted(const struct rt *);
#endif

206
external/bsd/dhcpcd/dist/control.c vendored Normal file
View File

@ -0,0 +1,206 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/un.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dhcpcd.h"
#include "control.h"
#include "eloop.h"
static int fd = -1;
static char buffer[1024];
static char *argvp[255];
struct sockaddr_un sun;
struct fd_list *fds = NULL;
static void
remove_control_data(void *arg)
{
struct fd_list *l, *last = NULL;
for (l = fds; l != NULL; l = l->next) {
if (l == arg) {
close(l->fd);
delete_event(l->fd);
if (last == NULL)
fds = l->next;
else
last->next = l->next;
free(l);
break;
}
last = l;
}
}
static void
handle_control_data(void *arg)
{
struct fd_list *l = arg;
ssize_t bytes;
int argc;
char *e, *p;
char **ap;
bytes = read(l->fd, buffer, sizeof(buffer) - 1);
if (bytes == -1 || bytes == 0) {
remove_control_data(l);
return;
}
buffer[bytes] = '\0';
p = buffer;
e = buffer + bytes;
argc = 0;
ap = argvp;
while (p < e && (size_t)argc < sizeof(argvp)) {
argc++;
*ap++ = p;
p += strlen(p) + 1;
}
handle_args(l, argc, argvp);
}
/* ARGSUSED */
static void
handle_control(_unused void *arg)
{
struct sockaddr_un run;
socklen_t len;
struct fd_list *l;
int f;
len = sizeof(run);
if ((f = accept(fd, (struct sockaddr *)&run, &len)) == -1)
return;
l = xmalloc(sizeof(*l));
l->fd = f;
l->listener = 0;
l->next = fds;
fds = l;
add_event(l->fd, handle_control_data, l);
}
static int
make_sock(void)
{
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
return -1;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, CONTROLSOCKET, sizeof(sun.sun_path));
return sizeof(sun.sun_family) + strlen(sun.sun_path) + 1;
}
int
start_control(void)
{
int len;
if ((len = make_sock()) == -1)
return -1;
unlink(CONTROLSOCKET);
if (bind(fd, (struct sockaddr *)&sun, len) == -1 ||
chmod(CONTROLSOCKET,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1 ||
set_cloexec(fd) == -1 ||
set_nonblock(fd) == -1 ||
listen(fd, sizeof(fds)) == -1)
{
close(fd);
return -1;
}
add_event(fd, handle_control, NULL);
return fd;
}
int
stop_control(void)
{
int retval = 0;
struct fd_list *l, *ll;
delete_event(fd);
if (shutdown(fd, SHUT_RDWR) == -1)
retval = 1;
fd = -1;
if (unlink(CONTROLSOCKET) == -1)
retval = -1;
l = fds;
while (l != NULL) {
ll = l->next;
delete_event(l->fd);
shutdown(l->fd, SHUT_RDWR);
free(l);
l = ll;
}
return retval;
}
int
open_control(void)
{
int len;
if ((len = make_sock()) == -1)
return -1;
return connect(fd, (struct sockaddr *)&sun, len);
}
int
send_control(int argc, char * const *argv)
{
char *p = buffer;
int i;
size_t len;
if (argc > 255) {
errno = ENOBUFS;
return -1;
}
for (i = 0; i < argc; i++) {
len = strlen(argv[i]) + 1;
if ((p - buffer) + len > sizeof(buffer)) {
errno = ENOBUFS;
return -1;
}
memcpy(p, argv[i], len);
p += len;
}
return write(fd, buffer, p - buffer);
}

45
external/bsd/dhcpcd/dist/control.h vendored Normal file
View File

@ -0,0 +1,45 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef CONTROL_H
#define CONTROL_H
#include "dhcpcd.h"
struct fd_list {
int fd;
int listener;
struct fd_list *next;
};
extern struct fd_list *fds;
int start_control(void);
int stop_control(void);
int open_control(void);
int send_control(int, char * const *);
#endif

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -57,7 +57,7 @@
/* Our aggregate option buffer.
* We ONLY use this when options are split, which for most purposes is
* practically never. See RFC3396 for details. */
static uint8_t *dhcp_opt_buffer = NULL;
static uint8_t *opt_buffer;
struct dhcp_opt {
uint8_t option;
@ -67,8 +67,9 @@ struct dhcp_opt {
static const struct dhcp_opt const dhcp_opts[] = {
{ 1, IPV4 | REQUEST, "subnet_mask" },
/* RFC 3442 states that the CSR has to come before all other routes.
* For completeness, we also specify static routes, then routers. */
/* RFC 3442 states that the CSR has to come before all other
* routes. For completeness, we also specify static routes,
* then routers. */
{ 121, RFC3442 | REQUEST, "classless_static_routes" },
{ 249, RFC3442, "ms_classless_static_routes" },
{ 33, IPV4 | ARRAY | REQUEST, "static_routes" },
@ -160,22 +161,52 @@ static const struct dhcp_opt const dhcp_opts[] = {
{ 0, 0, NULL }
};
static const char *if_params[] = {
"interface",
"reason",
"pid",
"ifmetric",
"ifwireless",
"ifflags",
"profile",
"interface_order",
NULL
};
static const char *dhcp_params[] = {
"ip_address",
"subnet_cidr",
"network_number",
"ssid",
"filename",
"server_name",
NULL
};
void
print_options(void)
{
const struct dhcp_opt *opt;
const char **p;
for (p = if_params; *p; p++)
printf(" - %s\n", *p);
for (p = dhcp_params; *p; p++)
printf(" %s\n", *p);
for (opt = dhcp_opts; opt->option; opt++)
if (opt->var)
printf("%03d %s\n", opt->option, opt->var);
}
int make_option_mask(uint8_t *mask, char **opts, int add)
int make_option_mask(uint8_t *mask, const char *opts, int add)
{
char *token, *p = *opts, *t;
char *token, *o, *p, *t;
const struct dhcp_opt *opt;
int match, n;
o = p = xstrdup(opts);
while ((token = strsep(&p, ", "))) {
if (*token == '\0')
continue;
@ -192,22 +223,28 @@ int make_option_mask(uint8_t *mask, char **opts, int add)
if (opt->option == n)
match = 1;
}
if (match) {
if (add == 1)
if (match) {
if (add == 2 && !(opt->type & IPV4)) {
free(o);
errno = EINVAL;
return -1;
}
if (add == 1 || add == 2)
add_option_mask(mask,
opt->option);
opt->option);
else
del_option_mask(mask,
opt->option);
opt->option);
break;
}
}
if (!opt->option) {
*opts = token;
free(o);
errno = ENOENT;
return -1;
}
}
free(o);
return 0;
}
@ -227,7 +264,9 @@ valid_length(uint8_t option, int dl, int *type)
if (type)
*type = opt->type;
if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
if (opt->type == 0 ||
opt->type & STRING ||
opt->type & RFC3442)
return 0;
sz = 0;
@ -246,11 +285,13 @@ valid_length(uint8_t option, int dl, int *type)
return 0;
}
#ifdef DEBUG_MEMORY
static void
free_option_buffer(void)
{
free(dhcp_opt_buffer);
free(opt_buffer);
}
#endif
#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
static const uint8_t *
@ -269,12 +310,14 @@ get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
o = *p++;
if (o == opt) {
if (op) {
if (!dhcp_opt_buffer) {
dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message));
if (!opt_buffer) {
opt_buffer = xmalloc(sizeof(*dhcp));
#ifdef DEBUG_MEMORY
atexit(free_option_buffer);
#endif
}
if (!bp)
bp = dhcp_opt_buffer;
bp = opt_buffer;
memcpy(bp, op, ol);
bp += ol;
}
@ -318,7 +361,7 @@ exit:
*len = bl;
if (bp) {
memcpy(bp, op, ol);
return (const uint8_t *)&dhcp_opt_buffer;
return (const uint8_t *)&opt_buffer;
}
if (op)
return op;
@ -369,7 +412,8 @@ get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
if (!p)
return -1;
*i = *(p);
if (i)
*i = *(p);
return 0;
}
@ -440,10 +484,8 @@ static ssize_t
decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
{
const uint8_t *e;
ssize_t bytes = 0;
ssize_t b;
ssize_t b, bytes = 0, ocets;
uint8_t cidr;
uint8_t ocets;
struct in_addr addr;
char *o = out;
@ -477,7 +519,7 @@ decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
/* If we have ocets then we have a destination and netmask */
if (ocets > 0) {
addr.s_addr = 0;
memcpy(&addr.s_addr, p, (size_t)ocets);
memcpy(&addr.s_addr, p, ocets);
b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
p += ocets;
} else
@ -504,7 +546,7 @@ decode_rfc3442_rt(int dl, const uint8_t *data)
const uint8_t *p = data;
const uint8_t *e;
uint8_t cidr;
uint8_t ocets;
size_t ocets;
struct rt *routes = NULL;
struct rt *rt = NULL;
@ -532,15 +574,9 @@ decode_rfc3442_rt(int dl, const uint8_t *data)
ocets = (cidr + 7) / 8;
/* If we have ocets then we have a destination and netmask */
if (ocets > 0) {
memcpy(&rt->dest.s_addr, p, (size_t)ocets);
memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
memset((uint8_t *)&rt->net.s_addr +
(ocets - 1),
(256 - (1 << (32 - cidr) % 8)), 1);
memcpy(&rt->dest.s_addr, p, ocets);
p += ocets;
} else {
rt->dest.s_addr = 0;
rt->net.s_addr = 0;
rt->net.s_addr = htonl(~0U << (32 - cidr));
}
/* Finally, snag the router */
@ -745,17 +781,42 @@ encode_rfc1035(const char *src, uint8_t *dst)
return p - dst;
}
#define PUTADDR(_type, _val) \
{ \
*p++ = _type; \
*p++ = 4; \
memcpy(p, &_val.s_addr, 4); \
p += 4; \
#define PUTADDR(_type, _val) \
{ \
*p++ = _type; \
*p++ = 4; \
memcpy(p, &_val.s_addr, 4); \
p += 4; \
}
int
dhcp_message_add_addr(struct dhcp_message *dhcp,
uint8_t type, struct in_addr addr)
{
uint8_t *p;
size_t len;
p = dhcp->options;
while (*p != DHO_END) {
p++;
p += *p + 1;
}
len = p - (uint8_t *)dhcp;
if (len + 6 > sizeof(*dhcp)) {
errno = ENOMEM;
return -1;
}
PUTADDR(type, addr);
*p = DHO_END;
return 0;
}
ssize_t
make_message(struct dhcp_message **message,
const struct interface *iface, const struct dhcp_lease *lease,
uint32_t xid, uint8_t type, const struct options *options)
const struct interface *iface,
uint8_t type)
{
struct dhcp_message *dhcp;
uint8_t *m, *lp, *p;
@ -763,21 +824,23 @@ make_message(struct dhcp_message **message,
time_t up = uptime() - iface->start_uptime;
uint32_t ul;
uint16_t sz;
const struct dhcp_opt *opt;
size_t len;
const char *hp;
const struct dhcp_opt *opt;
const struct if_options *ifo = iface->state->options;
const struct dhcp_lease *lease = &iface->state->lease;
dhcp = xzalloc(sizeof (*dhcp));
m = (uint8_t *)dhcp;
p = dhcp->options;
if ((type == DHCP_INFORM ||
type == DHCP_RELEASE ||
type == DHCP_REQUEST) &&
type == DHCP_RELEASE ||
type == DHCP_REQUEST) &&
!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
{
dhcp->ciaddr = iface->addr.s_addr;
/* Just incase we haven't actually configured the address yet */
/* In-case we haven't actually configured the address yet */
if (type == DHCP_INFORM && iface->addr.s_addr == 0)
dhcp->ciaddr = lease->addr.s_addr;
/* Zero the address if we're currently on a different subnet */
@ -809,7 +872,7 @@ make_message(struct dhcp_message **message,
else
dhcp->secs = htons(up);
}
dhcp->xid = xid;
dhcp->xid = iface->state->xid;
dhcp->cookie = htonl(MAGIC_COOKIE);
*p++ = DHO_MESSAGETYPE;
@ -826,12 +889,17 @@ make_message(struct dhcp_message **message,
if (type == DHCP_DECLINE ||
type == DHCP_DISCOVER ||
(type == DHCP_REQUEST &&
lease->addr.s_addr != iface->addr.s_addr))
lease->addr.s_addr != iface->addr.s_addr))
{
PUTADDR(DHO_IPADDRESS, lease->addr);
if (lease->server.s_addr)
PUTADDR(DHO_SERVERID, lease->server);
}
if (type == DHCP_RELEASE) {
if (lease->server.s_addr)
PUTADDR(DHO_SERVERID, lease->server);
}
}
if (type == DHCP_DECLINE) {
@ -842,11 +910,6 @@ make_message(struct dhcp_message **message,
p += len;
}
if (type == DHCP_RELEASE) {
if (lease->server.s_addr)
PUTADDR(DHO_SERVERID, lease->server);
}
if (type == DHCP_DISCOVER ||
type == DHCP_INFORM ||
type == DHCP_REQUEST)
@ -862,24 +925,25 @@ make_message(struct dhcp_message **message,
memcpy(p, &sz, 2);
p += 2;
if (options->userclass[0]) {
if (ifo->userclass[0]) {
*p++ = DHO_USERCLASS;
memcpy(p, options->userclass, options->userclass[0] + 1);
p += options->userclass[0] + 1;
memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
p += ifo->userclass[0] + 1;
}
if (options->vendorclassid[0]) {
if (ifo->vendorclassid[0]) {
*p++ = DHO_VENDORCLASSID;
memcpy(p, options->vendorclassid,
options->vendorclassid[0] + 1);
p += options->vendorclassid[0] + 1;
memcpy(p, ifo->vendorclassid,
ifo->vendorclassid[0] + 1);
p += ifo->vendorclassid[0] + 1;
}
if (type != DHCP_INFORM) {
if (options->leasetime != 0) {
if (ifo->leasetime != 0) {
*p++ = DHO_LEASETIME;
*p++ = 4;
ul = htonl(options->leasetime);
ul = htonl(ifo->leasetime);
memcpy(p, &ul, 4);
p += 4;
}
@ -889,18 +953,18 @@ make_message(struct dhcp_message **message,
* upto the first dot (the short hostname) as otherwise
* confuses some DHCP servers when updating DNS.
* The FQDN option should be used if a FQDN is required. */
if (options->hostname[0]) {
if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
*p++ = DHO_HOSTNAME;
hp = strchr(options->hostname, '.');
hp = strchr(ifo->hostname, '.');
if (hp)
len = hp - options->hostname;
len = hp - ifo->hostname;
else
len = strlen(options->hostname);
len = strlen(ifo->hostname);
*p++ = len;
memcpy(p, options->hostname, len);
memcpy(p, ifo->hostname, len);
p += len;
}
if (options->fqdn != FQDN_DISABLE) {
if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
/* IETF DHC-FQDN option (81), RFC4702 */
*p++ = DHO_FQDN;
lp = p;
@ -915,19 +979,19 @@ make_message(struct dhcp_message **message,
* N: 1 => Client requests Server to not
* update DNS
*/
*p++ = (options->fqdn & 0x09) | 0x04;
*p++ = (ifo->fqdn & 0x09) | 0x04;
*p++ = 0; /* from server for PTR RR */
*p++ = 0; /* from server for A RR if S=1 */
ul = encode_rfc1035(options->hostname, p);
ul = encode_rfc1035(ifo->hostname, p);
*lp += ul;
p += ul;
}
/* vendor is already encoded correctly, so just add it */
if (options->vendor[0]) {
if (ifo->vendor[0]) {
*p++ = DHO_VENDOR;
memcpy(p, options->vendor, options->vendor[0] + 1);
p += options->vendor[0] + 1;
memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
p += ifo->vendor[0] + 1;
}
*p++ = DHO_PARAMETERREQUESTLIST;
@ -935,15 +999,12 @@ make_message(struct dhcp_message **message,
*p++ = 0;
for (opt = dhcp_opts; opt->option; opt++) {
if (!(opt->type & REQUEST ||
has_option_mask(options->requestmask, opt->option)))
has_option_mask(ifo->requestmask, opt->option)))
continue;
if (type == DHCP_INFORM &&
(opt->option == DHO_RENEWALTIME ||
opt->option == DHO_REBINDTIME))
continue;
switch (opt->option) {
case DHO_RENEWALTIME: /* FALLTHROUGH */
case DHO_REBINDTIME:
if (type == DHCP_INFORM)
continue;
break;
}
*p++ = opt->option;
}
*n_params = p - n_params - 1;
@ -972,6 +1033,12 @@ write_lease(const struct interface *iface, const struct dhcp_message *dhcp)
uint8_t l;
uint8_t o = 0;
/* We don't write BOOTP leases */
if (is_bootp(dhcp)) {
unlink(iface->leasefile);
return 0;
}
fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (fd == -1)
return -1;
@ -1049,21 +1116,21 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
continue;
}
switch (c) {
case '"': /* FALLTHROUGH */
case '\'': /* FALLTHROUGH */
case '$': /* FALLTHROUGH */
case '`': /* FALLTHROUGH */
case '\\': /* FALLTHROUGH */
if (s) {
if (len < 3) {
errno = ENOBUFS;
return -1;
}
*s++ = '\\';
len--;
case '"': /* FALLTHROUGH */
case '\'': /* FALLTHROUGH */
case '$': /* FALLTHROUGH */
case '`': /* FALLTHROUGH */
case '\\': /* FALLTHROUGH */
if (s) {
if (len < 3) {
errno = ENOBUFS;
return -1;
}
bytes++;
break;
*s++ = '\\';
len--;
}
bytes++;
break;
}
if (s) {
*s++ = c;
@ -1116,17 +1183,22 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
if (!s) {
if (type & UINT8)
l = 3;
else if (type & UINT16)
else if (type & UINT16) {
l = 5;
else if (type & SINT16)
dl /= 2;
} else if (type & SINT16) {
l = 6;
else if (type & UINT32)
dl /= 2;
} else if (type & UINT32) {
l = 10;
else if (type & SINT32)
dl /= 4;
} else if (type & SINT32) {
l = 11;
else if (type & IPV4)
dl /= 4;
} else if (type & IPV4) {
l = 16;
else {
dl /= 4;
} else {
errno = EINVAL;
return -1;
}
@ -1190,7 +1262,7 @@ setvar(char ***e, const char *prefix, const char *var, const char *value)
ssize_t
configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
const struct options *options)
const struct if_options *ifo)
{
unsigned int i;
const uint8_t *p;
@ -1211,12 +1283,12 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
for (opt = dhcp_opts; opt->option; opt++) {
if (!opt->var)
continue;
if (has_option_mask(options->nomask, opt->option))
if (has_option_mask(ifo->nomask, opt->option))
continue;
if (get_option_raw(dhcp, opt->option))
e++;
}
if (dhcp->yiaddr)
if (dhcp->yiaddr || dhcp->ciaddr)
e += 5;
if (*dhcp->bootfile && !(overl & 1))
e++;
@ -1226,10 +1298,10 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
}
ep = env;
if (dhcp->yiaddr) {
if (dhcp->yiaddr || dhcp->ciaddr) {
/* Set some useful variables that we derive from the DHCP
* message but are not necessarily in the options */
addr.s_addr = dhcp->yiaddr;
addr.s_addr = dhcp->yiaddr ? dhcp->yiaddr : dhcp->ciaddr;
setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1) {
net.s_addr = get_netmask(addr.s_addr);
@ -1254,7 +1326,7 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
for (opt = dhcp_opts; opt->option; opt++) {
if (!opt->var)
continue;
if (has_option_mask(options->nomask, opt->option))
if (has_option_mask(ifo->nomask, opt->option))
continue;
val = NULL;
p = get_option(dhcp, opt->option, &pl, NULL);
@ -1277,3 +1349,30 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
return ep - env;
}
void
get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
{
struct timeval now;
/* BOOTP does not set yiaddr for replies when ciaddr is set. */
if (dhcp->yiaddr)
lease->addr.s_addr = dhcp->yiaddr;
else
lease->addr.s_addr = dhcp->ciaddr;
if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
lease->net.s_addr = get_netmask(lease->addr.s_addr);
if (get_option_addr(&lease->brd.s_addr, dhcp, DHO_BROADCAST) == -1)
lease->brd.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
/* Ensure that we can use the lease */
get_monotonic(&now);
if (now.tv_sec + (time_t)lease->leasetime < now.tv_sec)
lease->leasetime = ~0U; /* Infinite lease */
} else
lease->leasetime = ~0U; /* Default to infinite lease */
if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
lease->renewaltime = 0;
if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
lease->rebindtime = 0;
}

View File

@ -29,13 +29,10 @@
#define DHCP_H
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdint.h>
#include "config.h"
#include "dhcpcd.h"
#include "net.h"
/* Max MTU - defines dhcp option length */
#define MTU_MAX 1500
#define MTU_MIN 576
@ -61,9 +58,26 @@
#define DHCP_RELEASE 7
#define DHCP_INFORM 8
/* Constants taken from RFC 2131. */
#define T1 0.5
#define T2 0.875
#define DHCP_BASE 4
#define DHCP_MAX 64
#define DHCP_RAND_MIN -1
#define DHCP_RAND_MAX 1
#define DHCP_ARP_FAIL 10
/* number of usecs in a second. */
#define USECS_SECOND 1000000
/* As we use timevals, we should use the usec part for
* greater randomisation. */
#define DHCP_RAND_MIN_U DHCP_RAND_MIN * USECS_SECOND
#define DHCP_RAND_MAX_U DHCP_RAND_MAX * USECS_SECOND
#define PROBE_MIN_U PROBE_MIN * USECS_SECOND
#define PROBE_MAX_U PROBE_MAX * USECS_SECOND
/* DHCP options */
enum DHO
{
enum DHO {
DHO_PAD = 0,
DHO_SUBNETMASK = 1,
DHO_ROUTER = 3,
@ -112,13 +126,9 @@ enum FQDN {
#define DHCP_CHADDR_LEN 16
#define SERVERNAME_LEN 64
#define BOOTFILE_LEN 128
#define DHCP_UDP_LEN (20 + 8)
#define DHCP_BASE_LEN (4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4)
#define DHCP_RESERVE_LEN (4 + 4 + 4 + 4 + 2)
#define DHCP_FIXED_LEN (DHCP_BASE_LEN + DHCP_CHADDR_LEN + \
+ SERVERNAME_LEN + BOOTFILE_LEN)
#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_LEN \
- DHCP_RESERVE_LEN)
#define DHCP_UDP_LEN (14 + 20 + 8)
#define DHCP_FIXED_LEN (DHCP_UDP_LEN + 226)
#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN)
/* Some crappy DHCP servers require the BOOTP minimum length */
#define BOOTP_MESSAGE_LENTH_MIN 300
@ -140,11 +150,12 @@ struct dhcp_message {
uint8_t bootfile[BOOTFILE_LEN]; /* boot file name */
uint32_t cookie;
uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
};
} _packed;
struct dhcp_lease {
struct in_addr addr;
struct in_addr net;
struct in_addr brd;
uint32_t leasetime;
uint32_t renewaltime;
uint32_t rebindtime;
@ -154,25 +165,34 @@ struct dhcp_lease {
uint8_t frominfo;
};
#include "dhcpcd.h"
#include "if-options.h"
#include "net.h"
#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
int make_option_mask(uint8_t *, char **, int);
int make_option_mask(uint8_t *, const char *, int);
void print_options(void);
char *get_option_string(const struct dhcp_message *, uint8_t);
int get_option_addr(uint32_t *, const struct dhcp_message *, uint8_t);
int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
#define is_bootp(m) (m && \
!IN_LINKLOCAL(htonl((m)->yiaddr)) && \
get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
struct rt *get_option_routes(const struct dhcp_message *);
ssize_t configure_env(char **, const char *, const struct dhcp_message *,
const struct options *);
const struct if_options *);
ssize_t make_message(struct dhcp_message **,
const struct interface *, const struct dhcp_lease *,
uint32_t, uint8_t, const struct options *);
int dhcp_message_add_addr(struct dhcp_message *, uint8_t, struct in_addr);
ssize_t make_message(struct dhcp_message **, const struct interface *,
uint8_t);
int valid_dhcp_packet(unsigned char *);
ssize_t write_lease(const struct interface *, const struct dhcp_message *);
struct dhcp_message *read_lease(const struct interface *iface);
struct dhcp_message *read_lease(const struct interface *);
void get_lease(struct dhcp_lease *, const struct dhcp_message *);
#endif

View File

@ -1,6 +1,6 @@
# Just echo our DHCP options we have
if [ "${reason}" = "TEST" ]; then
if [ "$reason" = "TEST" ]; then
set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
set | grep "^\(new_\|old_\)" | sort
fi

View File

@ -1,5 +1,5 @@
# Configure the MTU for the interface
if [ -n "${new_interface_mtu}" ]; then
ifconfig "${interface}" mtu "${new_interface_mtu}"
if [ -n "$new_interface_mtu" ]; then
ifconfig "$interface" mtu "$new_interface_mtu"
fi

View File

@ -6,102 +6,121 @@
# Also, resolvconf can configure local nameservers such as bind
# or dnsmasq. This is important as the libc resolver isn't that powerful.
resolv_conf_dir="${state_dir}/resolv.conf"
resolv_conf_dir="$state_dir/resolv.conf"
build_resolv_conf()
{
local cf="/etc/resolv.conf.${interface}"
local cf="$state_dir/resolv.conf.$interface"
local interfaces= header= search= srvs= servers= x=
# Build a list of interfaces
interfaces=$(list_interfaces "${resolv_conf_dir}")
interfaces=$(list_interfaces "$resolv_conf_dir")
# Build the resolv.conf
if [ -n "${interfaces}" ]; then
if [ -n "$interfaces" ]; then
# Build the header
for x in ${interfaces}; do
header="${header}${header:+, }${x}"
header="$header${header:+, }$x"
done
# Build the search list
search=$(cd "${resolv_conf_dir}"; \
domain=$(cd "$resolv_conf_dir"; \
key_get_value "domain " ${interfaces})
search=$(cd "$resolv_conf_dir"; \
key_get_value "search " ${interfaces})
[ -n "${search}" ] && search="search $(uniqify ${search})\n"
set -- ${domain}
unset domain
if [ -n "$2" ]; then
search="$search $@"
elif [ -n "$1" ]; then
domain="domain $1\n"
fi
[ -n "$search" ] && search="search $(uniqify $search)\n"
# Build the nameserver list
srvs=$(cd "${resolv_conf_dir}"; \
srvs=$(cd "$resolv_conf_dir"; \
key_get_value "nameserver " ${interfaces})
for x in $(uniqify ${srvs}); do
servers="${servers}nameserver ${x}\n"
servers="${servers}nameserver $x\n"
done
fi
header="${signature_base}${header:+ ${from} }${header}"
header="$signature_base${header:+ $from }$header"
# Assemble resolv.conf using our head and tail files
[ -f "${cf}" ] && rm -f "${cf}"
echo "${header}" > "${cf}"
[ -f "$cf" ] && rm -f "$cf"
echo "$header" > "$cf"
if [ -f /etc/resolv.conf.head ]; then
cat /etc/resolv.conf.head >> "${cf}"
cat /etc/resolv.conf.head >> "$cf"
else
echo "# /etc/resolv.conf.head can replace this line" >> "${cf}"
echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
fi
printf "${search}${servers}" >> "${cf}"
printf "$domain$search$servers" >> "$cf"
if [ -f /etc/resolv.conf.tail ]; then
cat /etc/resolv.conf.tail >> "${cf}"
cat /etc/resolv.conf.tail >> "$cf"
else
echo "# /etc/resolv.conf.tail can replace this line" >> "${cf}"
echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
fi
mv -f "${cf}" /etc/resolv.conf
cat "$cf" > /etc/resolv.conf
chmod 644 /etc/resolv.conf
rm -f "$cf"
}
add_resolv_conf()
{
local x= conf="${signature}\n"
local x= conf="$signature\n"
# If we don't have any configuration, remove it
if [ -z "${new_domain_name_servers}" -a \
-z "${new_domain_name}" -a \
-z "${new_domain_search}" ]; then
if [ -z "$new_domain_name_servers" -a \
-z "$new_domain_name" -a \
-z "$new_domain_search" ]; then
remove_resolv_conf
return $?
fi
if [ -n "${new_domain_search}" ]; then
conf="${conf}search ${new_domain_search}\n"
elif [ -n "${new_domain_name}" ]; then
conf="${conf}search ${new_domain_name}\n"
if [ -n "$new_domain_name" ]; then
set -- $new_domain_name
new_domain_name="$1"
conf="${conf}domain $new_domain_name\n"
# Support RFC violating search in domain
if [ -z "$new_domain_search" -a -n "$2" ]; then
new_domain_search="$@"
fi
fi
if [ -n "$new_domain_search" ]; then
conf="${conf}search $new_domain_search\n"
fi
for x in ${new_domain_name_servers}; do
conf="${conf}nameserver ${x}\n"
conf="${conf}nameserver $x\n"
done
if type resolvconf >/dev/null 2>&1; then
printf "${conf}" | resolvconf -a "${interface}"
[ -n "$metric" ] && export IF_METRIC="$metric"
printf "$conf" | resolvconf -a "$interface"
return $?
fi
if [ -e "${resolv_conf_dir}/${interface}" ]; then
rm -f "${resolv_conf_dir}/${interface}"
if [ -e "$resolv_conf_dir/$interface" ]; then
rm -f "$resolv_conf_dir/$interface"
fi
if [ ! -d "${resolv_conf_dir}" ]; then
mkdir -p "${resolv_conf_dir}"
if [ ! -d "$resolv_conf_dir" ]; then
mkdir -p "$resolv_conf_dir"
fi
printf "${conf}" > "${resolv_conf_dir}/${interface}"
printf "$conf" > "$resolv_conf_dir/$interface"
build_resolv_conf
}
remove_resolv_conf()
{
if type resolvconf >/dev/null 2>&1; then
resolvconf -d "${interface}" -f
resolvconf -d "$interface" -f
else
if [ -e "${resolv_conf_dir}/${interface}" ]; then
rm -f "${resolv_conf_dir}/${interface}"
if [ -e "$resolv_conf_dir/$interface" ]; then
rm -f "$resolv_conf_dir/$interface"
fi
build_resolv_conf
fi
}
case "${reason}" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_resolv_conf;;
PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_resolv_conf;;
case "$reason" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) add_resolv_conf;;
PREINIT|EXPIRE|FAIL|IPV4LL|NAK|NOCARRIER|RELEASE|STOP) remove_resolv_conf;;
esac

View File

@ -2,19 +2,19 @@
lookup_hostname()
{
[ -z "${new_ip_address}" ] && return 1
[ -z "$new_ip_address" ] && return 1
local h=
# Silly ISC programs love to send error text to stdout
if type dig >/dev/null 2>&1; then
h=`dig +short -x ${new_ip_address}`
h=$(dig +short -x $new_ip_address)
if [ $? = 0 ]; then
echo "${h}" | sed 's/\.$//'
echo "$h" | sed 's/\.$//'
return 0
fi
elif type host >/dev/null 2>&1; then
h=`host ${new_ip_address}`
h=$(host $new_ip_address)
if [ $? = 0 ]; then
echo "${h}" \
echo "$h" \
| sed 's/.* domain name pointer \(.*\)./\1/'
return 0
fi
@ -24,11 +24,11 @@ lookup_hostname()
set_hostname()
{
if [ -z "${new_host_name}" -a -z "${new_fqdn_name}" ]; then
if [ -z "$new_host_name" -a -z "$new_fqdn_name" ]; then
export new_host_name="$(lookup_hostname)"
fi
}
case "${reason}" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
case "$reason" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) set_hostname;;
esac

View File

@ -4,8 +4,8 @@ need_hostname()
{
case "$(hostname)" in
""|"(none)"|localhost|localhost.localdomain)
[ -n "${new_host_name}" -o -n "${new_fqdn_name}" ];;
"${old_host_name}"|"${old_fqdn_name}")
[ -n "$new_host_name" -o -n "$new_fqdn_name" ];;
"$old_host_name"|"$old_fqdn_name")
true;;
*)
false;;
@ -15,14 +15,14 @@ need_hostname()
set_hostname()
{
if need_hostname; then
if [ -n "${new_host_name}" ]; then
hostname "${new_host_name}"
if [ -n "$new_host_name" ]; then
hostname "$new_host_name"
else
hostname "${new_fqdn_name}"
hostname "$new_fqdn_name"
fi
fi
}
case "${reason}" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
case "$reason" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) set_hostname;;
esac

View File

@ -18,58 +18,58 @@ elif [ -x /usr/local/etc/rc.d/ntpd ]; then
ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd status && /usr/local/etc/rc.d/ntpd restart"
fi
ntp_conf_dir="${state_dir}/ntp.conf"
ntp_conf_dir="$state_dir/ntp.conf"
ntp_conf=${NTP_CONF:-/etc/ntp.conf}
build_ntp_conf()
{
local cf="${ntp_conf}.${interface}"
local cf="$state_dir/ntp.conf.$interface"
local interfaces= header= srvs= servers= x=
# Build a list of interfaces
interfaces=$(list_interfaces "${ntp_conf_dir}")
interfaces=$(list_interfaces "$ntp_conf_dir")
if [ -n "${interfaces}" ]; then
if [ -n "$interfaces" ]; then
# Build the header
for x in ${interfaces}; do
header="${header}${header:+, }${x}"
header="$header${header:+, }$x"
done
# Build a server list
srvs=$(cd "${ntp_conf_dir}";
key_get_value "server " ${interfaces})
if [ -n "${srvs}" ]; then
for x in $(uniqify ${srvs}); do
servers="${servers}server ${x}\n"
srvs=$(cd "$ntp_conf_dir";
key_get_value "server " $interfaces)
if [ -n "$srvs" ]; then
for x in $(uniqify $srvs); do
servers="${servers}server $x\n"
done
fi
fi
# Merge our config into ntp.conf
[ -e "${cf}" ] && rm -f "${cf}"
remove_markers "${signature_base}" "${signature_base_end}" \
/etc/ntp.conf > "${cf}"
if [ -n "${servers}" ]; then
echo "${signature_base}${header:+ ${from} }${header}" >> "${cf}"
printf "${search}${servers}" >> "${cf}"
echo "${signature_base_end}${header:+ ${from} }${header}" >> "${cf}"
[ -e "$cf" ] && rm -f "$cf"
remove_markers "$signature_base" "$signature_base_end" \
"$ntp_conf" > "$cf"
if [ -n "$servers" ]; then
echo "$signature_base${header:+ $from }$header" >> "$cf"
printf "$search$servers" >> "$cf"
echo "$signature_base_end${header:+ $from }$header" >> "$cf"
fi
# If we changed anything, restart ntpd
if change_file "${ntp_conf}" "${cf}"; then
[ -n "${ntpd_restart_cmd}" ] && eval ${ntpd_restart_cmd}
if change_file "$ntp_conf" "$cf"; then
[ -n "$ntpd_restart_cmd" ] && eval $ntpd_restart_cmd
fi
}
add_ntp_conf()
{
local cf="${ntp_conf_dir}/${interface}" x=
local cf="$ntp_conf_dir/$interface" x=
[ -e "${cf}" ] && rm "${cf}"
[ -d "${ntp_conf_dir}" ] || mkdir -p "${ntp_conf_dir}"
if [ -n "${new_ntp_servers}" ]; then
for x in ${new_ntp_servers}; do
echo "server ${x}" >> "${cf}"
[ -e "$cf" ] && rm "$cf"
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
if [ -n "$new_ntp_servers" ]; then
for x in $new_ntp_servers; do
echo "server $x" >> "$cf"
done
fi
build_ntp_conf
@ -77,13 +77,13 @@ add_ntp_conf()
remove_ntp_conf()
{
if [ -e "${ntp_conf_dir}/${interface}" ]; then
rm "${ntp_conf_dir}/${interface}"
if [ -e "$ntp_conf_dir/$interface" ]; then
rm "$ntp_conf_dir/$interface"
fi
build_ntp_conf
}
case "${reason}" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_ntp_conf add;;
PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_ntp_conf del;;
case "$reason" in
BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) add_ntp_conf add;;
PREINIT|EXPIRE|FAIL|IPV4LL|NAK|NOCARRIER|RELEASE|STOP) remove_ntp_conf del;;
esac

View File

@ -1,4 +1,4 @@
.\" Copyright 2006-2008 Roy Marples
.\" Copyright 2006-2009 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd August 14, 2008
.Dd March 23, 2009
.Dt DHCPCD.SH 8 SMM
.Os
.Sh NAME
@ -69,9 +69,12 @@ argument.
Here's a list of reasons why
.Nm
could be invoked:
.Bl -tag -width indent
.Bl -tag -width PREINIT
.It Dv PREINIT
dhcpcd is starting up and any pre-initialisation should be done.
.It Dv CARRIER
dhcpcd has detected the carrier is up.
This is generally just a notification and no action need be taken.
.It Dv INFORM
dhcpcd informed a DHCP server about it's address and obtained other
configuration details.
@ -83,14 +86,30 @@ dhcpcd renewed it's lease.
dhcpcd has rebound to a new DHCP server.
.It Dv REBOOT
dhcpcd successfully requested a lease from a DHCP server.
.It Dv EXPIRE
dhcpcd's lease expired and it failed to obtain a new one.
.It Dv IPV4LL
dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
.It Dv FAIL
dhcpcd failed to contact any DHCP servers or use an old lease.
.It Dv STATIC
dhcpcd has been configured with a static configuration which has not been
obtained from a DHCP server.
.It Dv 3RDPARTY
dhcpcd is monitoring the interface for a 3rd party to give it an IP address.
.It Dv TIMEOUT
dhcpcd failed to contact any DHCP servers but was able to use an old lease.
.It Dv EXPIRE
dhcpcd's lease or state expired and it failed to obtain a new one.
.It Dv NAK
dhcpcd received a NAK from the DHCP server.
This should be treated as EXPIRE.
.It Dv NOCARRIER
dhcpcd lost the carrier.
The cable may have been unplugged or association to the wireless point lost.
.It Dv FAIL
dhcpcd failed to operate on the interface.
This normally happens when dhcpcd does not support the raw interface, which
means it cannot work as a DHCP or ZeroConf client.
Static configuration and DHCP INFORM is still allowed.
.It Dv STOP
dhcpcd stopped running on the interface.
.It Dv TEST
dhcpcd received an OFFER from a DHCP server but will not configure the
interface.
@ -109,6 +128,6 @@ in a lexical order and then finally
.Sh SEE ALSO
.Xr dhcpcd 8
.Sh AUTHORS
.An Roy Marples <roy@marples.name>
.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd

View File

@ -2,38 +2,47 @@
# dhcpcd client configuration script
# Handy variables and functions for our hooks to use
from="from"
from=from
signature_base="# Generated by dhcpcd"
signature="${signature_base} ${from} ${interface}"
signature="$signature_base $from $interface"
signature_base_end="# End of dhcpcd"
signature_end="${signature_base_end} ${from} ${interface}"
state_dir="/var/run/dhcpcd"
signature_end="$signature_base_end $from $interface"
state_dir=/var/run/dhcpcd
# Ensure that all arguments are unique
uniqify()
{
local result=
while [ -n "$1" ]; do
case " ${result} " in
*" $1 "*);;
*) result="${result}${result:+ }$1";;
local result= i=
for i; do
case " $result " in
*" $i "*);;
*) result="$result $i";;
esac
shift
done
echo "${result}"
echo "${result# *}"
}
# List interface config files in a dir
# We may wish to control the order at some point rather than just lexical
# If dhcpcd is running as a single instance then it will have a list of
# interfaces in the preferred order.
# Otherwise we just use what we have.
list_interfaces()
{
local x= interfaces=
for x in "$1"/*; do
[ -e "${x}" ] || continue
interfaces="${interfaces}${interfaces:+ }${x##*/}"
local i= x= ifaces=
for i in $interface_order; do
[ -e "$1/$i" ] && ifaces="$ifaces${ifaces:+ }$i"
done
echo "${interfaces}"
for x in "$1"/*; do
[ -e "$x" ] || continue
for i in $interface_order; do
if [ $i = "${x##*/}" ]; then
unset x
break
fi
done
[ -n "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
done
echo "$ifaces"
}
# We normally use sed to extract values using a key from a list of files
@ -44,14 +53,14 @@ key_get_value()
shift
if type sed >/dev/null 2>&1; then
sed -n "s/^${key}//p" $@
sed -n "s/^$key//p" $@
else
for x; do
while read line; do
case "${line}" in
"${key}"*) echo "${line##${key}}";;
case "$line" in
"$key"*) echo "${line##$key}";;
esac
done < "${x}"
done < "$x"
done
fi
}
@ -64,16 +73,16 @@ remove_markers()
shift; shift
if type sed >/dev/null 2>&1; then
sed "/^${m1}/,/^${m2}/d" $@
sed "/^$m1/,/^$m2/d" $@
else
for x; do
while read line; do
case "${line}" in
"${m1}"*) in_marker=1;;
"${m2}"*) in_marker=0;;
*) [ ${in_marker} = 0 ] && echo "${line}";;
case "$line" in
"$m1"*) in_marker=1;;
"$m2"*) in_marker=0;;
*) [ $in_marker = 0 ] && echo "$line";;
esac
done < "${x}"
done < "$x"
done
fi
}
@ -94,7 +103,8 @@ change_file()
rm -f "$2"
return 1
fi
mv -f "$2" "$1"
cat "$2" > "$1"
rm -f "$2"
return 0
}
@ -102,17 +112,17 @@ change_file()
save_conf()
{
if [ -f "$1" ]; then
rm -f "$1"-pre."${interface}"
mv -f "$1" "$1"-pre."${interface}"
rm -f "$1-pre.$interface"
cat "$1" > "$1-pre.$interface"
fi
}
# Restore a config file
restore_conf()
{
[ -f "$1"-pre."${interface}" ] || return 1
rm -f "$1"
mv -f "$1"-pre."${interface}" "$1"
[ -f "$1-pre.$interface" ] || return 1
cat "$1-pre.$interface" > "$1"
rm -f "$1-pre.$interface"
}
@ -125,14 +135,14 @@ for hook in \
@HOOKDIR@/* \
@SYSCONFDIR@/dhcpcd.exit-hook
do
for skip in ${skip_hooks}; do
case "${hook}" in
*/"${skip}") continue 2;;
*/[0-9][0-9]"-${skip}") continue 2;;
*/[0-9][0-9]"-${skip}.sh") continue 2;;
for skip in $skip_hooks; do
case "$hook" in
*/"$skip") continue 2;;
*/[0-9][0-9]"-$skip") continue 2;;
*/[0-9][0-9]"-$skip.sh") continue 2;;
esac
done
if [ -f "${hook}" ]; then
. "${hook}"
if [ -f "$hook" ]; then
. "$hook"
fi
done

View File

@ -1,4 +1,4 @@
.\" Copyright 2006-2008 Roy Marples
.\" Copyright 2006-2009 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd November 18, 2008
.Dd April 24, 2009
.Dt DHCPCD 8 SMM
.Os
.Sh NAME
@ -30,7 +30,7 @@
.Nd an RFC 2131 compliant DHCP client
.Sh SYNOPSIS
.Nm
.Op Fl bdknpqABDEGKLTV
.Op Fl bdeknpqABDEGKLTV
.Op Fl c , -script Ar script
.Op Fl f , -config Ar file
.Op Fl h , -hostname Ar hostname
@ -43,19 +43,24 @@
.Op Fl t , -timeout Ar seconds
.Op Fl u , -userclass Ar class
.Op Fl v , -vendor Ar code , Ar value
.Op Fl y , -reboot Ar seconds
.Op Fl z , -allowinterfaces Ar pattern
.Op Fl C , -nohook Ar hook
.Op Fl F , -fqdn Ar FQDN
.Op Fl I , -clientid Ar clientid
.Op Fl O , -nooption Ar option
.Op Fl Q , -require Ar option
.Op Fl X , -blacklist Ar address
.Ar interface
.Op Fl S , -static Ar value
.Op Fl X , -blacklist Ar address Ns Op Ar /cidr
.Op Fl Z , -denyinterfaces Ar pattern
.Op interface
.Op ...
.Nm
.Fl k , -release
.Ar interface
.Op interface
.Nm
.Fl x , -exit
.Ar interface
.Op interface
.Sh DESCRIPTION
.Nm
is an implementation of the DHCP client specified in
@ -80,6 +85,10 @@ sets the hostname to the one supplied by the DHCP server.
.Nm
then daemonises and waits for the lease renewal time to lapse.
Then it attempts to renew its lease and reconfigure if the new lease changes.
.Pp
.Nm
is also an implementation of the BOOTP client specified in
.Li RFC 951 .
.Ss Local Link configuration
If
.Nm
@ -98,6 +107,28 @@ installed which always defeats IPv4LL probing.
To disable this behaviour, you can use the
.Fl L , -noipv4ll
option.
.Ss Multiple interfaces
.Nm
can be run per interface or as a single instance to manage all interfaces.
If a list of interfaces are given on the command line, then
.Nm
only works with those interfaces, otherwise
.Nm
discovers available interfaces.
If link management is enabled and no interfaces are given on the command line,
.Nm
forks to the background right away.
.Pp
Interfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.
For systems that support route metrics, each route will be tagged with the
metric, otherwise
.Nm
changes the routes to use the interface with the same route and the lowest
metric.
See options below for controlling what interfaces we allow and deny through
the use of patterns. Also, see the BUGS section if
.Nm
runs on a BSD system.
.Ss Hooking into DHCP events
.Nm
runs
@ -141,6 +172,16 @@ Echo debug and informational messages to the console.
Subsequent debug options stop
.Nm
from daemonising.
.It Fl e , -reconfigure
.Nm
will re-apply IP address, routing and run
.Xr dhcpcd-run-hooks 8
for each interface.
This is useful so that a 3rd party such as PPP or VPN can change the routing
table and / or DNS, etc and then instruct dhcpcd to put things back afterwards.
.Nm
does not read a new configuration when this happens - you should rebind if you
need that functionality.
.It Fl f , -config Ar file
Specify a config to load instead of
.Pa @SYSCONFDIR@/dhcpcd.conf .
@ -180,14 +221,11 @@ By default
does not request any lease time and leaves the it in the hands of the
DHCP server.
.It Fl m , -metric Ar metric
Added routes will use the
.Ar metric
on systems where this is supported
.Po
presently only Linux
.Pc .
Route metrics allow the addition of routes to the same destination across
different interfaces, the lower the metric the more it is preferred.
Metrics are used to prefer an interface over another one, lowest wins.
.Nm
will supply a default metic of 200 +
.Xr if_nametoindex 3 .
An extra 100 will be added for wireless interfaces.
.It Fl o , -option Ar option
Request the DHCP
.Ar option
@ -292,6 +330,14 @@ to deconfigure the
and exit.
.Nm
then waits until this process has exited.
.It Fl y , -reboot Ar seconds
Allow
.Ar reboot
seconds before moving to the discover phase if we have an old lease to use.
The default is 10 seconds.
A setting if 0 seconds causes
.Nm
to skip the reboot phase and go straight into discover.
.It Fl D , -duid
Generate an
.Li RFC 4361
@ -316,9 +362,6 @@ hostname.
Valid values for
.Ar fqdn
are disable, none, ptr and both.
The current hostname or the hostname specified using the
.Fl h , -hostname
option must be a FQDN.
.Nm
itself never does any DNS updates.
.Nm
@ -348,6 +391,14 @@ Quiet
.Nm
on the command line, only warnings and errors will be displayed.
The messages are still logged though.
.It Fl z , -allowinterfaces Ar pattern
When discovering interfaces, the interface name must match
.Ar pattern
which is a space or comma separated list of patterns passed to
.Xr fnmatch 3 .
If the same interface is matched in
.Fl Z , -denyinterfaces
then it is still denied.
.It Fl A , -noarp
Don't request or claim the address by ARP.
This also disables IPv4LL.
@ -360,7 +411,9 @@ Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
.Pp
So to stop dhcpcd from touching your DNS or MTU settings you would do:-
So to stop
.Nm
from touching your DNS or MTU settings you would do:-
.D1 dhcpcd -C resolv.conf -C mtu eth0
.It Fl G , -nogateway
Don't set any default routes.
@ -379,6 +432,26 @@ configure the interface and routing.
Requires the
.Ar option
to be present in all DHCP messages, otherwise the message is ignored.
To enforce that
.Nm
only responds to DHCP servers and not BOOTP servers, you can
.Fl Q
.Ar dhcp_message_type .
.It Fl S, -static Ar value
Configures a static
.Ar value .
If you set
.Ic ip_address
then
.Nm
will not attempt to obtain a lease and just use the value for the address with
an infinite lease time.
.Pp
Here is an example which configures a static address, routes and dns.
.D1 dhcpcd -S ip_address=192.168.0.10/24 \e
.D1 -S routers=192.168.0.1 \e
.D1 -S domain_name_servers=192.168.0.1 \e
.D1 eth0
.It Fl T, -test
On receipt of OFFER messages just call
.Pa @SCRIPT@
@ -389,14 +462,31 @@ files.
.It Fl V, -variables
Display a list of option codes and the associated variable for use in
.Xr dhcpcd-run-hooks 8 .
.It Fl X, -blacklist Ar address
Ignores all DHCP messages which have this
.Ar address
as the server ID.
This may be expanded in future releases to ignore all packets
matching either the IP or hardware
.Ar address .
Variables are prefixed with new_ and old_ unless the option number is -.
Variables without an option are part of the DHCP message and cannot be
directly requested.
.It Fl X, -blacklist Ar address Ns Op Ar /cidr
Ignore all packets from
.Ar address Ns Op Ar /cidr .
.It Fl Z , -denyinterfaces Ar pattern
When discovering interfaces, the interface name must not match
.Ar pattern
which is a space or comma separated list of patterns passed to
.Xr fnmatch 3 .
.El
.Sh 3RDPARTY LINK MANAGEMENT
Some interfaces require configuration by 3rd parties, such as PPP or VPN.
When an interface configuration in
.Nm
is marked as STATIC or INFORM without an address then
.Nm
will monitor the interface until an address is added or removed from it and
act accordingly.
For point to point interfaces (like PPP), a default route to it's
destination is automatically added to the configuration.
If the point to point interface if configured for INFORM, then
.Nm
unicasts INFORM to the destination, otherwise it defaults to STATIC.
.Sh NOTES
.Nm
requires a Berkley Packet Filter, or BPF device on BSD based systems and a
@ -429,10 +519,19 @@ running on the
.Xr dhcpcd-run-hooks 8 ,
.Xr resolv.conf 5 ,
.Xr resolvconf 8 ,
.Xr if_nametoindex 3 ,
.Xr fnmatch 3
.Sh STANDARDS
RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
.Sh AUTHORS
.An Roy Marples <roy@marples.name>
.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd
.Pp
.Nm
does not wait for commands to complete when sending them to the master
.Nm
process, nor does it echo its progress to stderr.
This is a behaviour change from dhcpcd-4 and may need to be addressed if
there is sufficient need.

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,11 @@
# A sample configuration for dhcpcd.
# See dhcpcd.conf(5) for details.
# dhcpcd-run-hooks uses these options.
option domain_name_servers, domain_name, domain_search, host_name
# We normally want to inform the DHCP server of our hostname for DDNS.
hostname
# A list of options we should request from the DHCP server.
option domain_name_servers, domain_name, domain_search, host_name
# Most distros have ntp support.
option ntp_servers

View File

@ -1,4 +1,4 @@
.\" Copyright 2006-2008 Roy Marples
.\" Copyright 2006-2009 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd November 18, 2008
.Dd April 18, 2009
.Dt DHCPCD.CONF 5 SMM
.Os
.Sh NAME
@ -44,17 +44,38 @@ Blank lines and lines starting with # are ignored.
.Pp
Here's a list of available options:
.Bl -tag -width indent
.It Ic allowinterfaces Ar pattern
When discovering interfaces, the interface name must match
.Ar pattern
which is a space or comma separated list of patterns passed to
.Xr fnmatch 3 .
If the same interface is matched in
.Ic denyinterfaces
then it is still denied.
.It Ic denyinterfaces Ar pattern
When discovering interfaces, the interface name must not match
.Ar pattern
which is a space or comma separated list of patterns passed to
.Xr fnmatch 3 .
.It Ic arping Ar address Op address
.Nm dhcpcd
will arping each address in order before attempting DHCP.
If an address is found, we will select the replying hardware address as the
profile, otherwise the ip address.
Example:
.Pp
.D1 interface bge0
.D1 arping 192.168.0.1
.Pp
.D1 profile 192.168.0.1
.D1 static ip_address=192.168.0.10/24
.It Ic background
Background immediately.
This is useful for startup scripts which don't disable link messages for
carrier status.
.It Ic blacklist Ar address
Ignores all DHCP messages which have this
.Ar address
as the server ID.
This may be expanded in future releases to ignore all packets
matching either the IP or hardware
.Ar address .
.It Ic blacklist Ar address Ns Op /cidr
Ignores all packets from
.Ar address Ns Op /cidr .
.It Ic clientid Ar string
Send the
.Ar clientid .
@ -77,18 +98,18 @@ not enabled by default.
The duid generated will be held in
.Pa @SYSCONFDIR@/dhcpcd.duid
and should not be copied to other hosts.
.It Ic fallback Ar profile
Fallback to using this profile if DHCP fails.
This allows you to configure a static profile instead of using ZeroConf.
.It Ic hostname Ar name
Sends specified
.Ar hostname
.Ar hostname
to the DHCP server so it can be registered in DNS. If
.Ar hostname
if a FQDN (ie, contains a .) then it will be encoded as such.
.It Ic fqdn Op none | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record.
The current hostname or the hostname specified using the
.Ic hostname
option must be a FQDN.
.Nm dhcpcd
itself never does any DNS updates.
.Nm dhcpcd
@ -100,6 +121,12 @@ Subsequent options are only parsed for this
.It Ic leasetime Ar seconds
Request a leasetime of
.Ar seconds .
.It Ic metric Ar metric
Metrics are used to prefer an interface over another one, lowest wins.
.Nm dhcpcd
will supply a default metic of 200 +
.Xr if_nametoindex 3 .
An extra 100 will be added for wireless interfaces.
.It Ic noarp
Don't send any ARP requests.
This also disables IPv4LL.
@ -109,6 +136,11 @@ Don't install any default routes.
Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
.Pp
So to stop
.Nm dhcpcd
from touching your DNS or MTU settings you would do:-
.D1 nohook resolv.conf, mtu
.It Ic noipv4ll
Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
See
@ -126,8 +158,28 @@ It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
You can specify more options seperated by commas, spaces or more option lines.
.Ic quiet
.It Ic destination Ar option
If
.Nm
detects an address added to a point to point interface (PPP, TUN, etc) then
it will set the listed DHCP options to the destination address of the
interface.
.It Ic profile Ar name
Subsequent options are only parsed for this profile
.Ar name .
.It Ic quiet
Supress any dhcpcd output to the console, except for errors.
.It Ic reboot Ar seconds
Allow
.Ar reboot
seconds before moving to the discover phase if we have an old lease to use.
The default is 10 seconds.
A setting if 0 seconds causes
.Nm dhcpcd
to skip the reboot phase and go straight into discover.
.It Ic release
.Nm dhcpcd
will release the lease prior to stopping the interface.
.It Ic require Ar option
Requires the
.Ar option
@ -136,11 +188,41 @@ It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
You can specify more options seperated by commas, spaces or more require lines.
To enforce that
.Nm dhcpcd
only responds to DHCP servers and not BOOTP servers, you can
.Ic require
.Ar dhcp_message_type .
.It Ic script Ar script
Use
.Ar script
instead of the default
.Pa @SCRIPT@ .
.It Ic ssid Ar ssid
Subsequent options are only parsed for this wireless
.Ar ssid .
.It Ic static Ar value
Configures a static
.Ar value .
If you set
.Ic ip_address
then
.Nm dhcpcd
will not attempt to obtain a lease and just use the value for the address with
an infinite lease time.
.Pp
Here is an example which configures a static address, routes and dns.
.D1 interface eth0
.D1 static ip_address=192.168.0.10/24
.D1 static routers=192.168.0.1
.D1 static domain_name_servers=192.168.0.1
.Pp
Here is an example for PPP which gives the destination a default route.
It uses the special destination keyword to insert the destination address
into the value.
.D1 interface ppp0
.D1 static ip_address=
.D1 destination routers
.It Ic timeout Ar seconds
The default timeout for waiting for a DHCP response is 30 seconds which may
be too long or too short and can be changed here.
@ -165,8 +247,10 @@ If not set then none is sent.
.El
.Sh SEE ALSO
.Xr dhcpcd-run-hooks 8 ,
.Xr dhcpcd 8
.Xr dhcpcd 8 ,
.Xr if_nametoindex 3 ,
.Xr fnmatch 3
.Sh AUTHORS
.An Roy Marples <roy@marples.name>
.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -28,68 +28,115 @@
#ifndef DHCPCD_H
#define DHCPCD_H
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <limits.h>
#include "common.h"
#include "control.h"
#include "dhcp.h"
#include "if-options.h"
#define DEFAULT_TIMEOUT 30
#define DEFAULT_LEASETIME 3600 /* 1 hour */
#define HWADDR_LEN 20
#define IF_SSIDSIZE 33
#define PROFILE_LEN 64
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#define VENDORCLASSID_MAX_LEN 48
#define CLIENTID_MAX_LEN 48
#define USERCLASS_MAX_LEN 255
#define VENDOR_MAX_LEN 255
#define DHCPCD_ARP (1 << 0)
#define DHCPCD_DOMAIN (1 << 2)
#define DHCPCD_GATEWAY (1 << 3)
#define DHCPCD_LASTLEASE (1 << 7)
#define DHCPCD_INFORM (1 << 8)
#define DHCPCD_REQUEST (1 << 9)
#define DHCPCD_IPV4LL (1 << 10)
#define DHCPCD_DUID (1 << 11)
#define DHCPCD_PERSISTENT (1 << 12)
#define DHCPCD_DAEMONISE (1 << 14)
#define DHCPCD_DAEMONISED (1 << 15)
#define DHCPCD_TEST (1 << 16)
#define DHCPCD_FORKED (1 << 17)
#define DHCPCD_HOSTNAME (1 << 18)
#define DHCPCD_CLIENTID (1 << 19)
#define DHCPCD_LINK (1 << 20)
#define DHCPCD_BACKGROUND (1 << 21)
struct options {
char interface[IF_NAMESIZE];
int metric;
uint8_t requestmask[256 / 8];
uint8_t requiremask[256 / 8];
uint8_t nomask[256 / 8];
uint32_t leasetime;
time_t timeout;
int options;
struct in_addr request_address;
struct in_addr request_netmask;
char **environ;
char script[PATH_MAX];
char pidfile[PATH_MAX];
char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the lenth */
int fqdn;
uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
char clientid[CLIENTID_MAX_LEN + 2];
uint8_t userclass[USERCLASS_MAX_LEN + 2];
uint8_t vendor[VENDOR_MAX_LEN + 2];
size_t blacklist_len;
in_addr_t *blacklist;
enum DHS {
DHS_INIT,
DHS_DISCOVER,
DHS_REQUEST,
DHS_BOUND,
DHS_RENEW,
DHS_REBIND,
DHS_REBOOT,
DHS_INFORM,
DHS_RENEW_REQUESTED,
DHS_INIT_IPV4LL,
DHS_PROBE
};
#define LINK_UP 1
#define LINK_UNKNOWN 0
#define LINK_DOWN -1
struct if_state {
enum DHS state;
char profile[PROFILE_LEN];
struct if_options *options;
struct dhcp_message *sent;
struct dhcp_message *offer;
struct dhcp_message *new;
struct dhcp_message *old;
struct dhcp_lease lease;
const char *reason;
time_t interval;
time_t nakoff;
uint32_t xid;
int socket;
int probes;
int claims;
int conflicts;
time_t defend;
struct in_addr fail;
size_t arping_index;
};
struct interface {
char name[IF_NAMESIZE];
struct if_state *state;
int flags;
sa_family_t family;
unsigned char hwaddr[HWADDR_LEN];
size_t hwlen;
int metric;
int carrier;
int arpable;
int wireless;
char ssid[IF_SSIDSIZE];
int raw_fd;
int udp_fd;
int arp_fd;
size_t buffer_size, buffer_len, buffer_pos;
unsigned char *buffer;
struct in_addr addr;
struct in_addr net;
struct in_addr dst;
char leasefile[PATH_MAX];
time_t start_uptime;
unsigned char *clientid;
struct interface *next;
};
extern int pidfd;
extern int options;
extern int ifac;
extern char **ifav;
extern int ifdc;
extern char **ifdv;
extern struct interface *ifaces;
struct interface *find_interface(const char *);
int handle_args(struct fd_list *, int, char **);
void handle_interface(int, const char *);
void handle_ifa(int, const char *,
struct in_addr *, struct in_addr *, struct in_addr *);
void handle_exit_timeout(void *);
void start_interface(void *);
void start_discover(void *);
void start_request(void *);
void start_renew(void *);
void start_rebind(void *);
void start_reboot(struct interface *);
void start_expire(void *);
void send_decline(struct interface *);
void close_sockets(struct interface *);
void drop_config(struct interface *, const char *);
int select_profile(struct interface *, const char *);
#endif

99
external/bsd/dhcpcd/dist/duid.c vendored Normal file
View File

@ -0,0 +1,99 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define THIRTY_YEARS_IN_SECONDS 946707779
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
#include "duid.h"
#include "net.h"
size_t
get_duid(unsigned char *duid, const struct interface *iface)
{
FILE *f;
uint16_t type = 0;
uint16_t hw = 0;
uint32_t ul;
time_t t;
int x = 0;
unsigned char *p = duid;
size_t len = 0;
char *line;
/* If we already have a DUID then use it as it's never supposed
* to change once we have one even if the interfaces do */
if ((f = fopen(DUID, "r"))) {
while ((line = get_line(f))) {
len = hwaddr_aton(NULL, line);
if (len && len <= DUID_LEN) {
hwaddr_aton(duid, line);
break;
}
len = 0;
}
fclose(f);
if (len)
return len;
} else {
if (errno != ENOENT)
return 0;
}
/* No file? OK, lets make one based on our interface */
if (!(f = fopen(DUID, "w")))
return 0;
type = htons(1); /* DUI-D-LLT */
memcpy(p, &type, 2);
p += 2;
hw = htons(iface->family);
memcpy(p, &hw, 2);
p += 2;
/* time returns seconds from jan 1 1970, but DUID-LLT is
* seconds from jan 1 2000 modulo 2^32 */
t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
ul = htonl(t & 0xffffffff);
memcpy(p, &ul, 4);
p += 4;
/* Finally, add the MAC address of the interface */
memcpy(p, iface->hwaddr, iface->hwlen);
p += iface->hwlen;
len = p - duid;
x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
fclose(f);
/* Failed to write the duid? scrub it, we cannot use it */
if (x < 1) {
len = 0;
unlink(DUID);
}
return len;
}

35
external/bsd/dhcpcd/dist/duid.h vendored Normal file
View File

@ -0,0 +1,35 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef DUID_H
#define DUID_H
#include "net.h"
size_t get_duid(unsigned char *duid, const struct interface *iface);
#endif

351
external/bsd/dhcpcd/dist/eloop.c vendored Normal file
View File

@ -0,0 +1,351 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/time.h>
#include <errno.h>
#include <limits.h>
#include <poll.h>
#include <stdarg.h>
#include <stdlib.h>
#include <syslog.h>
#include "common.h"
#include "eloop.h"
static struct timeval now;
static struct event {
int fd;
void (*callback)(void *);
void *arg;
struct event *next;
} *events;
static struct event *free_events;
static struct timeout {
struct timeval when;
void (*callback)(void *);
void *arg;
struct timeout *next;
} *timeouts;
static struct timeout *free_timeouts;
static struct pollfd *fds;
static size_t fds_len;
void
add_event(int fd, void (*callback)(void *), void *arg)
{
struct event *e, *last = NULL;
/* We should only have one callback monitoring the fd */
for (e = events; e; e = e->next) {
if (e->fd == fd) {
e->callback = callback;
e->arg = arg;
return;
}
last = e;
}
/* Allocate a new event if no free ones already allocated */
if (free_events) {
e = free_events;
free_events = e->next;
} else
e = xmalloc(sizeof(*e));
e->fd = fd;
e->callback = callback;
e->arg = arg;
e->next = NULL;
if (last)
last->next = e;
else
events = e;
}
void
delete_event(int fd)
{
struct event *e, *last = NULL;
for (e = events; e; e = e->next) {
if (e->fd == fd) {
if (last)
last->next = e->next;
else
events = e->next;
e->next = free_events;
free_events = e;
break;
}
last = e;
}
}
void
add_timeout_tv(const struct timeval *when, void (*callback)(void *), void *arg)
{
struct timeval w;
struct timeout *t, *tt = NULL;
get_monotonic(&now);
timeradd(&now, when, &w);
/* Check for time_t overflow. */
if (timercmp(&w, &now, <)) {
errno = ERANGE;
return;
}
/* Remove existing timeout if present */
for (t = timeouts; t; t = t->next) {
if (t->callback == callback && t->arg == arg) {
if (tt)
tt->next = t->next;
else
timeouts = t->next;
break;
}
tt = t;
}
if (!t) {
/* No existing, so allocate or grab one from the free pool */
if (free_timeouts) {
t = free_timeouts;
free_timeouts = t->next;
} else
t = xmalloc(sizeof(*t));
}
t->when.tv_sec = w.tv_sec;
t->when.tv_usec = w.tv_usec;
t->callback = callback;
t->arg = arg;
/* The timeout list should be in chronological order,
* soonest first.
* This is the easiest algorithm - check the head, then middle
* and finally the end. */
if (!timeouts || timercmp(&t->when, &timeouts->when, <)) {
t->next = timeouts;
timeouts = t;
return;
}
for (tt = timeouts; tt->next; tt = tt->next)
if (timercmp(&t->when, &tt->next->when, <)) {
t->next = tt->next;
tt->next = t;
return;
}
tt->next = t;
t->next = NULL;
}
void
add_timeout_sec(time_t when, void (*callback)(void *), void *arg)
{
struct timeval tv;
tv.tv_sec = when;
tv.tv_usec = 0;
add_timeout_tv(&tv, callback, arg);
}
/* This deletes all timeouts for the interface EXCEPT for ones with the
* callbacks given. Handy for deleting everything apart from the expire
* timeout. */
void
delete_timeouts(void *arg, void (*callback)(void *), ...)
{
struct timeout *t, *tt, *last = NULL;
va_list va;
void (*f)(void *);
for (t = timeouts; t && (tt = t->next, 1); t = tt) {
if (t->arg == arg && t->callback != callback) {
va_start(va, callback);
while ((f = va_arg(va, void (*)(void *))))
if (f == t->callback)
break;
va_end(va);
if (!f) {
if (last)
last->next = t->next;
else
timeouts = t->next;
t->next = free_timeouts;
free_timeouts = t;
continue;
}
}
last = t;
}
}
void
delete_timeout(void (*callback)(void *), void *arg)
{
struct timeout *t, *tt, *last = NULL;
for (t = timeouts; t && (tt = t->next, 1); t = tt) {
if (t->arg == arg &&
(!callback || t->callback == callback))
{
if (last)
last->next = t->next;
else
timeouts = t->next;
t->next = free_timeouts;
free_timeouts = t;
continue;
}
last = t;
}
}
#ifdef DEBUG_MEMORY
/* Define this to free all malloced memory.
* Normally we don't do this as the OS will do it for us at exit,
* but it's handy for debugging other leaks in valgrind. */
static void
cleanup(void)
{
struct event *e;
struct timeout *t;
while (events) {
e = events->next;
free(events);
events = e;
}
while (free_events) {
e = free_events->next;
free(free_events);
free_events = e;
}
while (timeouts) {
t = timeouts->next;
free(timeouts);
timeouts = t;
}
while (free_timeouts) {
t = free_timeouts->next;
free(free_timeouts);
free_timeouts = t;
}
free(fds);
}
#endif
_noreturn void
start_eloop(void)
{
int msecs, n;
nfds_t nfds, i;
struct event *e;
struct timeout *t;
struct timeval tv;
#ifdef DEBUG_MEMORY
atexit(cleanup);
#endif
for (;;) {
/* Run all timeouts first.
* When we have one that has not yet occured,
* calculate milliseconds until it does for use in poll. */
if (timeouts) {
if (timercmp(&now, &timeouts->when, >)) {
t = timeouts;
timeouts = timeouts->next;
t->callback(t->arg);
t->next = free_timeouts;
free_timeouts = t;
continue;
}
timersub(&timeouts->when, &now, &tv);
if (tv.tv_sec > INT_MAX / 1000 ||
(tv.tv_sec == INT_MAX / 1000 &&
(tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
msecs = INT_MAX;
else
msecs = tv.tv_sec * 1000 +
(tv.tv_usec + 999) / 1000;
} else
/* No timeouts, so wait forever. */
msecs = -1;
/* Allocate memory for our pollfds as and when needed.
* We don't bother shrinking it. */
nfds = 0;
for (e = events; e; e = e->next)
nfds++;
if (msecs == -1 && nfds == 0) {
syslog(LOG_ERR, "nothing to do");
exit(EXIT_FAILURE);
}
if (nfds > fds_len) {
free(fds);
/* Allocate 5 more than we need for future use */
fds_len = nfds + 5;
fds = xmalloc(sizeof(*fds) * fds_len);
}
nfds = 0;
for (e = events; e; e = e->next) {
fds[nfds].fd = e->fd;
fds[nfds].events = POLLIN;
fds[nfds].revents = 0;
nfds++;
}
n = poll(fds, nfds, msecs);
if (n == -1) {
if (errno == EAGAIN || errno == EINTR) {
get_monotonic(&now);
continue;
}
syslog(LOG_ERR, "poll: %m");
exit(EXIT_FAILURE);
}
/* Get the now time and process any triggered events. */
get_monotonic(&now);
if (n == 0)
continue;
for (i = 0; i < nfds; i++) {
if (!(fds[i].revents & (POLLIN | POLLHUP)))
continue;
for (e = events; e; e = e->next) {
if (e->fd == fds[i].fd) {
e->callback(e->arg);
break;
}
}
}
}
}

41
external/bsd/dhcpcd/dist/eloop.h vendored Normal file
View File

@ -0,0 +1,41 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef ELOOP_H
#define ELOOP_H
#include <time.h>
void add_event(int fd, void (*)(void *), void *);
void delete_event(int fd);
void add_timeout_sec(time_t, void (*)(void *), void *);
void add_timeout_tv(const struct timeval *, void (*)(void *), void *);
void delete_timeout(void (*)(void *), void *);
void delete_timeouts(void *, void (*)(void *), ...);
void start_eloop(void);
#endif

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -25,44 +25,108 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#ifdef __DragonFly__
# include <netproto/802_11/ieee80211_ioctl.h>
#else
# include <net80211/ieee80211_ioctl.h>
#endif
#include <errno.h>
#include <fnmatch.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "configure.h"
#include "dhcp.h"
#include "if-options.h"
#include "net.h"
/* Darwin doesn't define this for some very odd reason */
#ifndef SA_SIZE
# define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
/* FIXME: Why do we need to check for sa_family 255 */
#define COPYOUT(sin, sa) \
sin.s_addr = ((sa) != NULL && ((sa)->sa_family == AF_INET || \
(sa)->sa_family == 255)) \
? \
(((struct sockaddr_in *)sa)->sin_addr).s_addr : 0
static int a_fd = -1;
static int r_fd = -1;
int
if_address(const char *ifname, const struct in_addr *address,
const struct in_addr *netmask, const struct in_addr *broadcast,
int action)
init_socket(void)
{
if ((a_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return -1;
set_cloexec(a_fd);
if ((r_fd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
return -1;
set_cloexec(r_fd);
return 0;
}
int
getifssid(const char *ifname, char *ssid)
{
int retval = -1;
#if defined(SIOCG80211NWID)
struct ifreq ifr;
struct ieee80211_nwid nwid;
#elif defined(IEEE80211_IOC_SSID)
struct ieee80211req ireq;
char nwid[IEEE80211_NWID_LEN + 1];
#endif
#if defined(SIOCG80211NWID) /* NetBSD */
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
memset(&nwid, 0, sizeof(nwid));
ifr.ifr_data = (void *)&nwid;
if (ioctl(a_fd, SIOCG80211NWID, &ifr) == 0) {
retval = nwid.i_len;
memcpy(ssid, nwid.i_nwid, nwid.i_len);
ssid[nwid.i_len] = '\0';
}
#elif defined(IEEE80211_IOC_SSID) /* FreeBSD */
memset(&ireq, 0, sizeof(ireq));
strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));
ireq.i_type = IEEE80211_IOC_SSID;
ireq.i_val = -1;
ireq.i_data = &nwid;
if (ioctl(a_fd, SIOCG80211, &ireq) == 0) {
retval = ireq.i_len;
memcpy(ssid, nwid, ireq.i_len);
ssid[ireq.i_len] = '\0';
}
#endif
return retval;
}
int
if_address(const struct interface *iface, const struct in_addr *address,
const struct in_addr *netmask, const struct in_addr *broadcast,
int action)
{
int s;
int retval;
struct ifaliasreq ifa;
union {
@ -70,39 +134,36 @@ if_address(const char *ifname, const struct in_addr *address,
struct sockaddr_in *sin;
} _s;
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return -1;
memset(&ifa, 0, sizeof(ifa));
strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
strlcpy(ifa.ifra_name, iface->name, sizeof(ifa.ifra_name));
#define ADDADDR(_var, _addr) \
_s.sa = &_var; \
_s.sin->sin_family = AF_INET; \
_s.sin->sin_len = sizeof(*_s.sin); \
memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr));
#define ADDADDR(_var, _addr) { \
_s.sa = &_var; \
_s.sin->sin_family = AF_INET; \
_s.sin->sin_len = sizeof(*_s.sin); \
memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr)); \
}
ADDADDR(ifa.ifra_addr, address);
ADDADDR(ifa.ifra_mask, netmask);
if (action >= 0) {
if (action >= 0 && broadcast) {
ADDADDR(ifa.ifra_broadaddr, broadcast);
}
#undef ADDADDR
if (action < 0)
retval = ioctl(s, SIOCDIFADDR, &ifa);
retval = ioctl(a_fd, SIOCDIFADDR, &ifa);
else
retval = ioctl(s, SIOCAIFADDR, &ifa);
close(s);
retval = ioctl(a_fd, SIOCAIFADDR, &ifa);
return retval;
}
/* ARGSUSED4 */
int
if_route(const struct interface *iface, const struct in_addr *dest,
const struct in_addr *net, const struct in_addr *gate,
_unused int metric, int action)
const struct in_addr *net, const struct in_addr *gate,
_unused int metric, int action)
{
int s;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
@ -121,21 +182,18 @@ if_route(const struct interface *iface, const struct in_addr *dest,
size_t l;
int retval = 0;
#define ADDSU(_su) { \
l = SA_SIZE(&(_su.sa)); \
memcpy(bp, &(_su), l); \
bp += l; \
}
#define ADDADDR(_addr) { \
memset (&su, 0, sizeof(su)); \
su.sin.sin_family = AF_INET; \
su.sin.sin_len = sizeof(su.sin); \
memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
ADDSU(su); \
}
if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
return -1;
#define ADDSU(_su) { \
l = ROUNDUP(_su.sa.sa_len); \
memcpy(bp, &(_su), l); \
bp += l; \
}
#define ADDADDR(_a) { \
memset (&su, 0, sizeof(su)); \
su.sin.sin_family = AF_INET; \
su.sin.sin_len = sizeof(su.sin); \
memcpy (&su.sin.sin_addr, _a, sizeof(su.sin.sin_addr)); \
ADDSU(su); \
}
memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION;
@ -194,41 +252,59 @@ if_route(const struct interface *iface, const struct in_addr *dest,
ADDADDR(&iface->addr);
rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
if (write(s, &rtm, l) == -1)
if (write(r_fd, &rtm, l) == -1)
retval = -1;
close(s);
return retval;
}
int
open_link_socket(struct interface *iface)
open_link_socket(void)
{
int fd;
fd = socket(PF_ROUTE, SOCK_RAW, 0);
if (fd == -1)
return -1;
set_cloexec(fd);
if (iface->link_fd != -1)
close(iface->link_fd);
iface->link_fd = fd;
return 0;
if (fd != -1) {
set_cloexec(fd);
set_nonblock(fd);
}
return fd;
}
static void
get_addrs(int type, char *cp, struct sockaddr **sa)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (type & (1 << i)) {
sa[i] = (struct sockaddr *)cp;
#ifdef DEBUG
printf ("got %d %d %s\n", i, sa[i]->sa_family,
inet_ntoa(((struct sockaddr_in *)sa[i])->
sin_addr));
#endif
ADVANCE(cp, sa[i]);
} else
sa[i] = NULL;
}
}
#define BUFFER_LEN 2048
int
link_changed(struct interface *iface)
manage_link(int fd)
{
char buffer[2048], *p;
char buffer[2048], *p, *e, *cp;
char ifname[IF_NAMESIZE];
ssize_t bytes;
struct rt_msghdr *rtm;
struct if_announcemsghdr *ifan;
struct if_msghdr *ifm;
int i;
struct ifa_msghdr *ifam;
struct rt rt;
struct sockaddr *sa, *rti_info[RTAX_MAX];
if ((i = if_nametoindex(iface->name)) == -1)
return -1;
for (;;) {
bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
bytes = read(fd, buffer, BUFFER_LEN);
if (bytes == -1) {
if (errno == EAGAIN)
return 0;
@ -236,20 +312,59 @@ link_changed(struct interface *iface)
continue;
return -1;
}
for (p = buffer; bytes > 0;
bytes -= ((struct rt_msghdr *)p)->rtm_msglen,
p += ((struct rt_msghdr *)p)->rtm_msglen)
{
rtm = (struct rt_msghdr *)p;
if (rtm->rtm_type != RTM_IFINFO)
continue;
ifm = (struct if_msghdr *)p;
if (ifm->ifm_index != i)
continue;
/* Link changed */
return 1;
e = buffer + bytes;
for (p = buffer; p < e; p += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)p;
switch(rtm->rtm_type) {
case RTM_IFANNOUNCE:
ifan = (struct if_announcemsghdr *)(void *)p;
switch(ifan->ifan_what) {
case IFAN_ARRIVAL:
handle_interface(1, ifan->ifan_name);
break;
case IFAN_DEPARTURE:
handle_interface(-1, ifan->ifan_name);
break;
}
break;
case RTM_IFINFO:
ifm = (struct if_msghdr *)(void *)p;
memset(ifname, 0, sizeof(ifname));
if (if_indextoname(ifm->ifm_index, ifname))
handle_interface(0, ifname);
break;
case RTM_DELETE:
if (!(rtm->rtm_addrs & RTA_DST) ||
!(rtm->rtm_addrs & RTA_GATEWAY) ||
!(rtm->rtm_addrs & RTA_NETMASK))
break;
if (rtm->rtm_pid == getpid())
break;
cp = (char *)(void *)(rtm + 1);
sa = (struct sockaddr *)(void *)cp;
if (sa->sa_family != AF_INET)
break;
get_addrs(rtm->rtm_addrs, cp, rti_info);
rt.iface = NULL;
rt.next = NULL;
COPYOUT(rt.dest, rti_info[RTAX_DST]);
COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
route_deleted(&rt);
break;
case RTM_DELADDR:
case RTM_NEWADDR:
ifam = (struct ifa_msghdr *)(void *)p;
cp = (char *)(void *)(ifam + 1);
get_addrs(ifam->ifam_addrs, cp, rti_info);
COPYOUT(rt.dest, rti_info[RTAX_IFA]);
COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
COPYOUT(rt.gate, rti_info[RTAX_BRD]);
if (if_indextoname(ifam->ifam_index, ifname))
handle_ifa(rtm->rtm_type, ifname,
&rt.dest, &rt.net, &rt.gate);
break;
}
}
}
return 0;
}

845
external/bsd/dhcpcd/dist/if-options.c vendored Normal file
View File

@ -0,0 +1,845 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <time.h>
#include "config.h"
#include "common.h"
#include "if-options.h"
#include "net.h"
/* These options only make sense in the config file, so don't use any
valid short options for them */
#define O_BASE MAX('z', 'Z') + 1
#define O_ARPING O_BASE + 1
#define O_FALLBACK O_BASE + 2
const struct option cf_options[] = {
{"background", no_argument, NULL, 'b'},
{"script", required_argument, NULL, 'c'},
{"debug", no_argument, NULL, 'd'},
{"reconfigure", no_argument, NULL, 'e'},
{"config", required_argument, NULL, 'f'},
{"hostname", optional_argument, NULL, 'h'},
{"vendorclassid", optional_argument, NULL, 'i'},
{"release", no_argument, NULL, 'k'},
{"leasetime", required_argument, NULL, 'l'},
{"metric", required_argument, NULL, 'm'},
{"rebind", no_argument, NULL, 'n'},
{"option", required_argument, NULL, 'o'},
{"persistent", no_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'q'},
{"request", optional_argument, NULL, 'r'},
{"inform", optional_argument, NULL, 's'},
{"timeout", required_argument, NULL, 't'},
{"userclass", required_argument, NULL, 'u'},
{"vendor", required_argument, NULL, 'v'},
{"exit", no_argument, NULL, 'x'},
{"allowinterfaces", required_argument, NULL, 'z'},
{"reboot", required_argument, NULL, 'y'},
{"noarp", no_argument, NULL, 'A'},
{"nobackground", no_argument, NULL, 'B'},
{"nohook", required_argument, NULL, 'C'},
{"duid", no_argument, NULL, 'D'},
{"lastlease", no_argument, NULL, 'E'},
{"fqdn", optional_argument, NULL, 'F'},
{"nogateway", no_argument, NULL, 'G'},
{"clientid", optional_argument, NULL, 'I'},
{"nolink", no_argument, NULL, 'K'},
{"noipv4ll", no_argument, NULL, 'L'},
{"destination", required_argument, NULL, 'N'},
{"nooption", optional_argument, NULL, 'O'},
{"require", required_argument, NULL, 'Q'},
{"static", required_argument, NULL, 'S'},
{"test", no_argument, NULL, 'T'},
{"variables", no_argument, NULL, 'V'},
{"blacklist", required_argument, NULL, 'X'},
{"denyinterfaces", required_argument, NULL, 'Z'},
{"arping", required_argument, NULL, O_ARPING},
{"fallback", required_argument, NULL, O_FALLBACK},
{NULL, 0, NULL, '\0'}
};
static int
atoint(const char *s)
{
char *t;
long n;
errno = 0;
n = strtol(s, &t, 0);
if ((errno != 0 && n == 0) || s == t ||
(errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
{
syslog(LOG_ERR, "`%s' out of range", s);
return -1;
}
return (int)n;
}
static char *
add_environ(struct if_options *ifo, const char *value, int uniq)
{
char **newlist;
char **lst = ifo->environ;
size_t i = 0, l, lv;
char *match = NULL, *p;
match = xstrdup(value);
p = strchr(match, '=');
if (p)
*p++ = '\0';
l = strlen(match);
while (lst && lst[i]) {
if (match && strncmp(lst[i], match, l) == 0) {
if (uniq) {
free(lst[i]);
lst[i] = xstrdup(value);
} else {
/* Append a space and the value to it */
l = strlen(lst[i]);
lv = strlen(p);
lst[i] = xrealloc(lst[i], l + lv + 2);
lst[i][l] = ' ';
memcpy(lst[i] + l + 1, p, lv);
lst[i][l + lv + 1] = '\0';
}
free(match);
return lst[i];
}
i++;
}
newlist = xrealloc(lst, sizeof(char *) * (i + 2));
newlist[i] = xstrdup(value);
newlist[i + 1] = NULL;
ifo->environ = newlist;
free(match);
return newlist[i];
}
#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
static ssize_t
parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid)
{
ssize_t l;
const char *p;
int i, punt_last = 0;
char c[4];
/* If surrounded by quotes then it's a string */
if (*str == '"') {
str++;
l = strlen(str);
p = str + l - 1;
if (*p == '"')
punt_last = 1;
} else {
l = hwaddr_aton(NULL, str);
if (l > 1) {
if (l > slen) {
errno = ENOBUFS;
return -1;
}
hwaddr_aton((uint8_t *)sbuf, str);
return l;
}
}
/* Process escapes */
l = 0;
/* If processing a string on the clientid, first byte should be
* 0 to indicate a non hardware type */
if (clid && *str) {
*sbuf++ = 0;
l++;
}
c[3] = '\0';
while (*str) {
if (++l > slen) {
errno = ENOBUFS;
return -1;
}
if (*str == '\\') {
str++;
switch(*str++) {
case '\0':
break;
case 'b':
*sbuf++ = '\b';
break;
case 'n':
*sbuf++ = '\n';
break;
case 'r':
*sbuf++ = '\r';
break;
case 't':
*sbuf++ = '\t';
break;
case 'x':
/* Grab a hex code */
c[1] = '\0';
for (i = 0; i < 2; i++) {
if (isxdigit((unsigned char)*str) == 0)
break;
c[i] = *str++;
}
if (c[1] != '\0') {
c[2] = '\0';
*sbuf++ = strtol(c, NULL, 16);
} else
l--;
break;
case '0':
/* Grab an octal code */
c[2] = '\0';
for (i = 0; i < 3; i++) {
if (*str < '0' || *str > '7')
break;
c[i] = *str++;
}
if (c[2] != '\0') {
i = strtol(c, NULL, 8);
if (i > 255)
i = 255;
*sbuf ++= i;
} else
l--;
break;
default:
*sbuf++ = *str++;
}
} else
*sbuf++ = *str++;
}
if (punt_last)
*--sbuf = '\0';
return l;
}
static char **
splitv(int *argc, char **argv, const char *arg)
{
char **v = argv;
char *o = xstrdup(arg), *p, *t;
p = o;
while ((t = strsep(&p, ", "))) {
(*argc)++;
v = xrealloc(v, sizeof(char *) * ((*argc)));
v[(*argc) - 1] = xstrdup(t);
}
free(o);
return v;
}
static int
parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
{
char *p;
int i;
if (arg == NULL || *arg == '\0') {
if (addr != NULL)
addr->s_addr = 0;
if (net != NULL)
net->s_addr = 0;
return 0;
}
if ((p = strchr(arg, '/')) != NULL) {
*p++ = '\0';
if (net != NULL &&
(sscanf(p, "%d", &i) != 1 ||
inet_cidrtoaddr(i, net) != 0))
{
syslog(LOG_ERR, "`%s' is not a valid CIDR", p);
return -1;
}
}
if (addr != NULL && inet_aton(arg, addr) == 0) {
syslog(LOG_ERR, "`%s' is not a valid IP address", arg);
return -1;
}
if (p != NULL)
*--p = '/';
else if (net != NULL)
net->s_addr = get_netmask(addr->s_addr);
return 0;
}
static int
parse_option(struct if_options *ifo, int opt, const char *arg)
{
int i;
char *p = NULL, *np;
ssize_t s;
struct in_addr addr, addr2;
struct rt *rt;
switch(opt) {
case 'e': /* FALLTHROUGH */
case 'n': /* FALLTHROUGH */
case 'x': /* FALLTHROUGH */
case 'T': /* We need to handle non interface options */
break;
case 'b':
ifo->options |= DHCPCD_BACKGROUND;
break;
case 'c':
strlcpy(ifo->script, arg, sizeof(ifo->script));
break;
case 'd':
ifo->options |= DHCPCD_DEBUG;
break;
case 'h':
if (arg) {
s = parse_string(ifo->hostname,
HOSTNAME_MAX_LEN, arg);
if (s == -1) {
syslog(LOG_ERR, "hostname: %m");
return -1;
}
if (s != 0 && ifo->hostname[0] == '.') {
syslog(LOG_ERR,
"hostname cannot begin with .");
return -1;
}
ifo->hostname[s] = '\0';
}
if (ifo->hostname[0] == '\0')
ifo->options &= ~DHCPCD_HOSTNAME;
else
ifo->options |= DHCPCD_HOSTNAME;
break;
case 'i':
if (arg)
s = parse_string((char *)ifo->vendorclassid + 1,
VENDORCLASSID_MAX_LEN, arg);
else
s = 0;
if (s == -1) {
syslog(LOG_ERR, "vendorclassid: %m");
return -1;
}
*ifo->vendorclassid = (uint8_t)s;
break;
case 'k':
ifo->options |= DHCPCD_RELEASE;
break;
case 'l':
if (*arg == '-') {
syslog(LOG_ERR,
"leasetime must be a positive value");
return -1;
}
errno = 0;
ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
if (errno == EINVAL || errno == ERANGE) {
syslog(LOG_ERR, "`%s' out of range", arg);
return -1;
}
break;
case 'm':
ifo->metric = atoint(arg);
if (ifo->metric < 0) {
syslog(LOG_ERR, "metric must be a positive value");
return -1;
}
break;
case 'o':
if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
}
break;
case 'p':
ifo->options |= DHCPCD_PERSISTENT;
break;
case 'q':
ifo->options |= DHCPCD_QUIET;
break;
case 'r':
ifo->options |= DHCPCD_REQUEST;
if (parse_addr(&ifo->req_addr, NULL, arg) != 0)
return -1;
ifo->req_mask.s_addr = 0;
break;
case 's':
ifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;
ifo->options &= ~(DHCPCD_ARP | DHCPCD_STATIC);
if (arg && *arg != '\0') {
if (parse_addr(&ifo->req_addr, &ifo->req_mask,
arg) != 0)
return -1;
} else {
ifo->req_addr.s_addr = 0;
ifo->req_mask.s_addr = 0;
}
break;
case 't':
ifo->timeout = atoint(arg);
if (ifo->timeout < 0) {
syslog(LOG_ERR, "timeout must be a positive value");
return -1;
}
break;
case 'u':
s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1;
s = parse_string((char *)ifo->userclass +
ifo->userclass[0] + 2,
s, arg);
if (s == -1) {
syslog(LOG_ERR, "userclass: %m");
return -1;
}
if (s != 0) {
ifo->userclass[ifo->userclass[0] + 1] = s;
ifo->userclass[0] += s + 1;
}
break;
case 'v':
p = strchr(arg, ',');
if (!p || !p[1]) {
syslog(LOG_ERR, "invalid vendor format");
return -1;
}
*p = '\0';
i = atoint(arg);
arg = p + 1;
if (i < 1 || i > 254) {
syslog(LOG_ERR, "vendor option should be between"
" 1 and 254 inclusive");
return -1;
}
s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
if (inet_aton(arg, &addr) == 1) {
if (s < 6) {
s = -1;
errno = ENOBUFS;
} else
memcpy(ifo->vendor + ifo->vendor[0] + 3,
&addr.s_addr, sizeof(addr.s_addr));
} else {
s = parse_string((char *)ifo->vendor +
ifo->vendor[0] + 3, s, arg);
}
if (s == -1) {
syslog(LOG_ERR, "vendor: %m");
return -1;
}
if (s != 0) {
ifo->vendor[ifo->vendor[0] + 1] = i;
ifo->vendor[ifo->vendor[0] + 2] = s;
ifo->vendor[0] += s + 2;
}
break;
case 'y':
ifo->reboot = atoint(arg);
if (ifo->reboot < 0) {
syslog(LOG_ERR, "reboot must be a positive value");
return -1;
}
break;
case 'z':
/* We only set this if we haven't got any interfaces */
if (!ifaces)
ifav = splitv(&ifac, ifav, arg);
break;
case 'A':
ifo->options &= ~DHCPCD_ARP;
/* IPv4LL requires ARP */
ifo->options &= ~DHCPCD_IPV4LL;
break;
case 'B':
ifo->options &= ~DHCPCD_DAEMONISE;
break;
case 'C':
/* Commas to spaces for shell */
while ((p = strchr(arg, ',')))
*p = ' ';
s = strlen("skip_hooks=") + strlen(arg) + 1;
p = xmalloc(sizeof(char) * s);
snprintf(p, s, "skip_hooks=%s", arg);
add_environ(ifo, p, 0);
free(p);
break;
case 'D':
ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
break;
case 'E':
ifo->options |= DHCPCD_LASTLEASE;
break;
case 'F':
if (!arg) {
ifo->fqdn = FQDN_BOTH;
break;
}
if (strcmp(arg, "none") == 0)
ifo->fqdn = FQDN_NONE;
else if (strcmp(arg, "ptr") == 0)
ifo->fqdn = FQDN_PTR;
else if (strcmp(arg, "both") == 0)
ifo->fqdn = FQDN_BOTH;
else if (strcmp(arg, "disable") == 0)
ifo->fqdn = FQDN_DISABLE;
else {
syslog(LOG_ERR, "invalid value `%s' for FQDN", arg);
return -1;
}
break;
case 'G':
ifo->options &= ~DHCPCD_GATEWAY;
break;
case 'I':
/* Strings have a type of 0 */;
ifo->clientid[1] = 0;
if (arg)
s = parse_string_hwaddr((char *)ifo->clientid + 1,
CLIENTID_MAX_LEN, arg, 1);
else
s = 0;
if (s == -1) {
syslog(LOG_ERR, "clientid: %m");
return -1;
}
ifo->options |= DHCPCD_CLIENTID;
ifo->clientid[0] = (uint8_t)s;
break;
case 'K':
ifo->options &= ~DHCPCD_LINK;
break;
case 'L':
ifo->options &= ~DHCPCD_IPV4LL;
break;
case 'N':
if (make_option_mask(ifo->dstmask, arg, 2) != 0) {
if (errno == EINVAL)
syslog(LOG_ERR, "option `%s' does not take"
" an IPv4 address", arg);
else
syslog(LOG_ERR, "unknown otpion `%s'", arg);
return -1;
}
break;
case 'O':
if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
make_option_mask(ifo->requiremask, arg, -1) != 0 ||
make_option_mask(ifo->nomask, arg, 1) != 0)
{
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
}
break;
case 'Q':
if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
make_option_mask(ifo->requestmask, arg, 1) != 0)
{
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
}
break;
case 'S':
p = strchr(arg, '=');
if (p == NULL) {
syslog(LOG_ERR, "static assignment required");
return -1;
}
p++;
if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
if (parse_addr(&ifo->req_addr, &ifo->req_mask, p) != 0)
return -1;
ifo->options |= DHCPCD_STATIC;
ifo->options &= ~DHCPCD_INFORM;
} else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 ||
strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 ||
strncmp(arg, "ms_classless_static_routes=", strlen("ms_classless_static_routes=")) == 0)
{
np = strchr(p, ' ');
if (np == NULL) {
syslog(LOG_ERR, "all routes need a gateway");
return -1;
}
*np++ = '\0';
while (*np == ' ')
np++;
if (ifo->routes == NULL) {
rt = ifo->routes = xmalloc(sizeof(*rt));
} else {
rt = ifo->routes;
while (rt->next)
rt = rt->next;
rt->next = xmalloc(sizeof(*rt));
rt = rt->next;
}
rt->next = NULL;
if (parse_addr(&rt->dest, &rt->net, p) == -1 ||
parse_addr(&rt->gate, NULL, np) == -1)
return -1;
} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
if (ifo->routes == NULL) {
rt = ifo->routes = xzalloc(sizeof(*rt));
} else {
rt = ifo->routes;
while (rt->next)
rt = rt->next;
rt->next = xmalloc(sizeof(*rt));
rt = rt->next;
}
rt->dest.s_addr = INADDR_ANY;
rt->net.s_addr = INADDR_ANY;
rt->next = NULL;
if (parse_addr(&rt->gate, NULL, p) == -1)
return -1;
} else {
s = 0;
if (ifo->config != NULL) {
while (ifo->config[s] != NULL) {
if (strncmp(ifo->config[s], arg,
p - arg) == 0)
{
free(ifo->config[s]);
ifo->config[s] = xstrdup(arg);
return 1;
}
s++;
}
}
ifo->config = xrealloc(ifo->config,
sizeof(char *) * (s + 2));
ifo->config[s] = xstrdup(arg);
ifo->config[s + 1] = NULL;
}
break;
case 'X':
if (parse_addr(&addr, &addr2, arg) != 0)
return -1;
if (strchr(arg, '/') == NULL)
addr2.s_addr = INADDR_BROADCAST;
ifo->blacklist = xrealloc(ifo->blacklist,
sizeof(in_addr_t) * (ifo->blacklist_len + 2));
ifo->blacklist[ifo->blacklist_len++] = addr.s_addr;
ifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;
break;
case 'Z':
/* We only set this if we haven't got any interfaces */
if (!ifaces)
ifdv = splitv(&ifdc, ifdv, arg);
break;
case O_ARPING:
if (parse_addr(&addr, NULL, arg) != 0)
return -1;
ifo->arping = xrealloc(ifo->arping,
sizeof(in_addr_t) * (ifo->arping_len + 1));
ifo->arping[ifo->arping_len++] = addr.s_addr;
break;
case O_FALLBACK:
free(ifo->fallback);
ifo->fallback = xstrdup(arg);
break;
default:
return 0;
}
return 1;
}
static int
parse_config_line(struct if_options *ifo, const char *opt, char *line)
{
unsigned int i;
for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
if (!cf_options[i].name ||
strcmp(cf_options[i].name, opt) != 0)
continue;
if (cf_options[i].has_arg == required_argument && !line) {
fprintf(stderr,
PACKAGE ": option requires an argument -- %s\n",
opt);
return -1;
}
return parse_option(ifo, cf_options[i].val, line);
}
fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
return -1;
}
struct if_options *
read_config(const char *file,
const char *ifname, const char *ssid, const char *profile)
{
struct if_options *ifo;
FILE *f;
char *line, *option, *p;
int skip = 0, have_profile = 0;
/* Seed our default options */
ifo = xzalloc(sizeof(*ifo));
ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
ifo->timeout = DEFAULT_TIMEOUT;
ifo->reboot = DEFAULT_REBOOT;
ifo->metric = -1;
strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
gethostname(ifo->hostname, HOSTNAME_MAX_LEN);
/* Ensure that the hostname is NULL terminated */
ifo->hostname[HOSTNAME_MAX_LEN] = '\0';
if (strcmp(ifo->hostname, "(none)") == 0 ||
strcmp(ifo->hostname, "localhost") == 0)
ifo->hostname[0] = '\0';
ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1,
VENDORCLASSID_MAX_LEN,
"%s %s", PACKAGE, VERSION);
/* Parse our options file */
f = fopen(file ? file : CONFIG, "r");
if (!f)
return ifo;
while ((line = get_line(f))) {
option = strsep(&line, " \t");
/* Trim trailing whitespace */
if (line && *line) {
p = line + strlen(line) - 1;
while (p != line &&
(*p == ' ' || *p == '\t') &&
*(p - 1) != '\\')
*p-- = '\0';
}
/* Start of an interface block, skip if not ours */
if (strcmp(option, "interface") == 0) {
if (ifname && line && strcmp(line, ifname) == 0)
skip = 0;
else
skip = 1;
continue;
}
/* Start of an ssid block, skip if not ours */
if (strcmp(option, "ssid") == 0) {
if (ssid && line && strcmp(line, ssid) == 0)
skip = 0;
else
skip = 1;
continue;
}
/* Start of a profile block, skip if not ours */
if (strcmp(option, "profile") == 0) {
if (profile && line && strcmp(line, profile) == 0) {
skip = 0;
have_profile = 1;
} else
skip = 1;
continue;
}
if (skip)
continue;
if (parse_config_line(ifo, option, line) != 1)
break;
}
fclose(f);
if (profile && !have_profile) {
free_options(ifo);
errno = ENOENT;
ifo = NULL;
}
/* Terminate the encapsulated options */
if (ifo && ifo->vendor[0]) {
ifo->vendor[0]++;
ifo->vendor[ifo->vendor[0]] = DHO_END;
}
return ifo;
}
int
add_options(struct if_options *ifo, int argc, char **argv)
{
int oi, opt, r = 1;
optind = 0;
while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
{
r = parse_option(ifo, opt, optarg);
if (r != 1)
break;
}
/* Terminate the encapsulated options */
if (r == 1 && ifo->vendor[0]) {
ifo->vendor[0]++;
ifo->vendor[ifo->vendor[0]] = DHO_END;
}
return r;
}
void
free_options(struct if_options *ifo)
{
size_t i;
if (ifo) {
if (ifo->environ) {
i = 0;
while (ifo->environ[i])
free(ifo->environ[i++]);
free(ifo->environ);
}
if (ifo->config) {
i = 0;
while (ifo->config[i])
free(ifo->config[i++]);
free(ifo->config);
}
free_routes(ifo->routes);
free(ifo->arping);
free(ifo->blacklist);
free(ifo->fallback);
free(ifo);
}
}

113
external/bsd/dhcpcd/dist/if-options.h vendored Normal file
View File

@ -0,0 +1,113 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef IF_OPTIONS_H
#define IF_OPTIONS_H
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <getopt.h>
#include <limits.h>
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
#define IF_OPTS "bc:def:h:i:kl:m:no:pqr:s:t:u:v:xy:z:ABC:DEF:GI:KLN:O:Q:TVX:Z:"
#define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 10
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#define VENDORCLASSID_MAX_LEN 48
#define CLIENTID_MAX_LEN 48
#define USERCLASS_MAX_LEN 255
#define VENDOR_MAX_LEN 255
#define DHCPCD_ARP (1 << 0)
#define DHCPCD_RELEASE (1 << 1)
#define DHCPCD_DOMAIN (1 << 2)
#define DHCPCD_GATEWAY (1 << 3)
#define DHCPCD_STATIC (1 << 4)
#define DHCPCD_DEBUG (1 << 5)
#define DHCPCD_LASTLEASE (1 << 7)
#define DHCPCD_INFORM (1 << 8)
#define DHCPCD_REQUEST (1 << 9)
#define DHCPCD_IPV4LL (1 << 10)
#define DHCPCD_DUID (1 << 11)
#define DHCPCD_PERSISTENT (1 << 12)
#define DHCPCD_DAEMONISE (1 << 14)
#define DHCPCD_DAEMONISED (1 << 15)
#define DHCPCD_TEST (1 << 16)
#define DHCPCD_MASTER (1 << 17)
#define DHCPCD_HOSTNAME (1 << 18)
#define DHCPCD_CLIENTID (1 << 19)
#define DHCPCD_LINK (1 << 20)
#define DHCPCD_QUIET (1 << 21)
#define DHCPCD_BACKGROUND (1 << 22)
extern const struct option cf_options[];
struct if_options {
int metric;
uint8_t requestmask[256 / 8];
uint8_t requiremask[256 / 8];
uint8_t nomask[256 / 8];
uint8_t dstmask[256 / 8];
uint32_t leasetime;
time_t timeout;
time_t reboot;
int options;
struct in_addr req_addr;
struct in_addr req_mask;
struct rt *routes;
char **config;
char **environ;
char script[PATH_MAX];
char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the length */
int fqdn;
uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
char clientid[CLIENTID_MAX_LEN + 2];
uint8_t userclass[USERCLASS_MAX_LEN + 2];
uint8_t vendor[VENDOR_MAX_LEN + 2];
size_t blacklist_len;
in_addr_t *blacklist;
size_t arping_len;
in_addr_t *arping;
char *fallback;
};
struct if_options *read_config(const char *,
const char *, const char *, const char *);
int add_options(struct if_options *, int, char **);
void free_options(struct if_options *);
#endif

106
external/bsd/dhcpcd/dist/if-pref.c vendored Normal file
View File

@ -0,0 +1,106 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include "dhcpcd.h"
#include "if-pref.h"
#include "net.h"
/* Interface comparer for working out ordering. */
static int
ifcmp(struct interface *si, struct interface *ti)
{
int sill, till;
if (si->state && !ti->state)
return -1;
if (!si->state && ti->state)
return 1;
if (!si->state && !ti->state)
return 0;
/* If one has a lease and the other not, it takes precedence. */
if (si->state->new && !ti->state->new)
return -1;
if (!si->state->new && ti->state->new)
return 1;
/* If we are either, they neither have a lease, or they both have.
* We need to check for IPv4LL and make it non-preferred. */
if (si->state->new) {
sill = IN_LINKLOCAL(htonl(si->state->new->yiaddr));
till = IN_LINKLOCAL(htonl(ti->state->new->yiaddr));
if (!sill && till)
return -1;
if (sill && !till)
return 1;
}
/* Then carrier status. */
if (si->carrier > ti->carrier)
return -1;
if (si->carrier < ti->carrier)
return 1;
/* Finally, metric */
if (si->metric < ti->metric)
return -1;
if (si->metric > ti->metric)
return 1;
return 0;
}
/* Sort the interfaces into a preferred order - best first, worst last. */
void
sort_interfaces(void)
{
struct interface *sorted, *ifp, *ifn, *ift;
if (!ifaces || !ifaces->next)
return;
sorted = ifaces;
ifaces = ifaces->next;
sorted->next = NULL;
for (ifp = ifaces; ifp && (ifn = ifp->next, 1); ifp = ifn) {
/* Are we the new head? */
if (ifcmp(ifp, sorted) == -1) {
ifp->next = sorted;
sorted = ifp;
continue;
}
/* Do we fit in the middle? */
for (ift = sorted; ift->next; ift = ift->next) {
if (ifcmp(ifp, ift->next) == -1) {
ifp->next = ift->next;
ift->next = ifp;
break;
}
}
/* We must be at the end */
if (!ift->next) {
ift->next = ifp;
ifp->next = NULL;
}
}
ifaces = sorted;
}

34
external/bsd/dhcpcd/dist/if-pref.h vendored Normal file
View File

@ -0,0 +1,34 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef IF_PREF_H
#define IF_PREF_H
#include "dhcpcd.h"
void sort_interfaces(void);
#endif

132
external/bsd/dhcpcd/dist/ipv4ll.c vendored Normal file
View File

@ -0,0 +1,132 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "arp.h"
#include "common.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if-options.h"
#include "ipv4ll.h"
#include "net.h"
static struct dhcp_message*
make_ipv4ll_lease(uint32_t old_addr)
{
uint32_t u32;
struct dhcp_message *dhcp;
uint8_t *p;
dhcp = xzalloc(sizeof(*dhcp));
/* Put some LL options in */
p = dhcp->options;
*p++ = DHO_SUBNETMASK;
*p++ = sizeof(u32);
u32 = htonl(LINKLOCAL_MASK);
memcpy(p, &u32, sizeof(u32));
p += sizeof(u32);
*p++ = DHO_BROADCAST;
*p++ = sizeof(u32);
u32 = htonl(LINKLOCAL_BRDC);
memcpy(p, &u32, sizeof(u32));
p += sizeof(u32);
*p++ = DHO_END;
for (;;) {
dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
(((uint32_t)abs((int)arc4random())
% 0xFD00) + 0x0100));
if (dhcp->yiaddr != old_addr &&
IN_LINKLOCAL(ntohl(dhcp->yiaddr)))
break;
}
return dhcp;
}
void
start_ipv4ll(void *arg)
{
struct interface *iface = arg;
iface->state->probes = 0;
iface->state->claims = 0;
if (iface->addr.s_addr) {
iface->state->conflicts = 0;
if (IN_LINKLOCAL(htonl(iface->addr.s_addr))) {
send_arp_announce(iface);
return;
}
}
/* We maybe rebooting an IPv4LL address. */
if (!iface->state->offer ||
!IN_LINKLOCAL(htonl(iface->state->offer->yiaddr)))
{
syslog(LOG_INFO, "%s: probing for an IPv4LL address",
iface->name);
delete_timeout(NULL, iface);
free(iface->state->offer);
iface->state->offer = make_ipv4ll_lease(0);
iface->state->lease.frominfo = 0;
}
send_arp_probe(iface);
}
void
handle_ipv4ll_failure(void *arg)
{
struct interface *iface = arg;
time_t up;
if (iface->state->fail.s_addr == iface->state->lease.addr.s_addr) {
up = uptime();
if (iface->state->defend + DEFEND_INTERVAL > up) {
drop_config(iface, "EXPIRE");
iface->state->conflicts = -1;
} else {
iface->state->defend = up;
return;
}
}
close_sockets(iface);
free(iface->state->offer);
iface->state->offer = NULL;
if (++iface->state->conflicts > MAX_CONFLICTS) {
syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
iface->name);
iface->state->interval = RATE_LIMIT_INTERVAL / 2;
start_discover(iface);
} else {
add_timeout_sec(PROBE_WAIT, start_ipv4ll, iface);
}
}

33
external/bsd/dhcpcd/dist/ipv4ll.h vendored Normal file
View File

@ -0,0 +1,33 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef IPV4LL_H
#define IPV4LL_H
void start_ipv4ll(void *);
void handle_ipv4ll_failure(void *);
#endif

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -27,45 +27,50 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#ifdef __linux__
#include <netinet/ether.h>
#include <netpacket/packet.h>
#ifdef AF_LINK
# include <net/if_dl.h>
# include <net/if_types.h>
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
#include <netinet/udp.h>
#undef __FAVOR_BSD
#ifdef SIOCGIFMEDIA
#include <net/if_media.h>
#ifdef AF_PACKET
# include <netpacket/packet.h>
#endif
#include <arpa/inet.h>
#ifdef AF_LINK
# include <net/if_dl.h>
#ifdef SIOCGIFMEDIA
# include <net/if_media.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <ifaddrs.h>
#include <fnmatch.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dhcp.h"
#include "logger.h"
#include "if-options.h"
#include "net.h"
#include "signals.h"
static char hwaddr_buffer[(HWADDR_LEN * 3) + 1];
int
inet_ntocidr(struct in_addr address)
{
@ -76,7 +81,6 @@ inet_ntocidr(struct in_addr address)
cidr++;
mask <<= 1;
}
return cidr;
}
@ -85,7 +89,7 @@ inet_cidrtoaddr(int cidr, struct in_addr *addr)
{
int ocets;
if (cidr < 0 || cidr > 32) {
if (cidr < 1 || cidr > 32) {
errno = EINVAL;
return -1;
}
@ -94,8 +98,9 @@ inet_cidrtoaddr(int cidr, struct in_addr *addr)
addr->s_addr = 0;
if (ocets > 0) {
memset(&addr->s_addr, 255, (size_t)ocets - 1);
memset((unsigned char *)&addr->s_addr + (ocets - 1),
(256 - (1 << (32 - cidr) % 8)), 1);
memset((unsigned char *)&addr->s_addr + (ocets - 1),
(256 - (1 << (32 - cidr) % 8)), 1);
}
return 0;
@ -123,8 +128,7 @@ get_netmask(uint32_t addr)
char *
hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
{
static char buffer[(HWADDR_LEN * 3) + 1];
char *p = buffer;
char *p = hwaddr_buffer;
size_t i;
for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
@ -135,7 +139,7 @@ hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
*p ++= '\0';
return buffer;
return hwaddr_buffer;
}
size_t
@ -176,114 +180,227 @@ hwaddr_aton(unsigned char *buffer, const char *addr)
return len;
}
int
do_interface(const char *ifname,
_unused unsigned char *hwaddr, _unused size_t *hwlen,
struct in_addr *addr, struct in_addr *net, int get)
struct interface *
init_interface(const char *ifname)
{
int s;
struct ifconf ifc;
int retval = 0, found = 0;
int len = 10 * sizeof(struct ifreq);
int lastlen = 0;
char *p;
union {
char *buffer;
struct ifreq *ifr;
} ifreqs;
struct sockaddr_in address;
struct ifreq *ifr;
struct sockaddr_in netmask;
#ifdef AF_LINK
struct sockaddr_dl *sdl;
#endif
struct ifreq ifr;
struct interface *iface = NULL;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return -1;
return NULL;
/* Not all implementations return the needed buffer size for
* SIOGIFCONF so we loop like so for all until it works */
memset(&ifc, 0, sizeof(ifc));
for (;;) {
ifc.ifc_len = len;
ifc.ifc_buf = xmalloc((size_t)len);
if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
if (errno != EINVAL || lastlen != 0) {
close(s);
free(ifc.ifc_buf);
return -1;
}
} else {
if (ifc.ifc_len == lastlen)
break;
lastlen = ifc.ifc_len;
}
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
goto eexit;
free(ifc.ifc_buf);
ifc.ifc_buf = NULL;
len *= 2;
iface = xzalloc(sizeof(*iface));
strlcpy(iface->name, ifname, sizeof(iface->name));
iface->flags = ifr.ifr_flags;
/* We reserve the 100 range for virtual interfaces, if and when
* we can work them out. */
iface->metric = 200 + if_nametoindex(iface->name);
if (getifssid(ifname, iface->ssid) != -1) {
iface->wireless = 1;
iface->metric += 100;
}
for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) {
/* Cast the ifc buffer to an ifreq cleanly */
ifreqs.buffer = p;
ifr = ifreqs.ifr;
if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
goto eexit;
/* Ensure that the MTU is big enough for DHCP */
if (ifr.ifr_mtu < MTU_MIN) {
ifr.ifr_mtu = MTU_MIN;
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
goto eexit;
}
#ifndef __linux__
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
p += offsetof(struct ifreq, ifr_ifru) +
ifr->ifr_addr.sa_len;
else
if (up_interface(ifname) != 0)
goto eexit;
snprintf(iface->leasefile, sizeof(iface->leasefile),
LEASEFILE, ifname);
/* 0 is a valid fd, so init to -1 */
iface->raw_fd = -1;
iface->udp_fd = -1;
iface->arp_fd = -1;
close(s);
return iface;
eexit:
free(iface);
close(s);
return NULL;
}
void
free_interface(struct interface *iface)
{
if (!iface)
return;
if (iface->state) {
free_options(iface->state->options);
free(iface->state->old);
free(iface->state->new);
free(iface->state->offer);
free(iface->state);
}
free(iface->clientid);
free(iface);
}
struct interface *
discover_interfaces(int argc, char * const *argv)
{
struct ifaddrs *ifaddrs, *ifa;
char *p;
int i;
struct interface *ifp, *ifs, *ifl;
#ifdef __linux__
char ifn[IF_NAMESIZE];
#endif
p += sizeof(*ifr);
if (strcmp(ifname, ifr->ifr_name) != 0)
continue;
found = 1;
#ifdef AF_LINK
if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
sdl = xmalloc(ifr->ifr_addr.sa_len);
memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
*hwlen = sdl->sdl_alen;
memcpy(hwaddr, LLADDR(sdl), *hwlen);
free(sdl);
const struct sockaddr_dl *sdl;
#elif AF_PACKET
const struct sockaddr_ll *sll;
#endif
if (getifaddrs(&ifaddrs) == -1)
return NULL;
ifs = ifl = NULL;
for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
#ifdef AF_LINK
if (ifa->ifa_addr->sa_family != AF_LINK)
continue;
#elif AF_PACKET
if (ifa->ifa_addr->sa_family != AF_PACKET)
continue;
#endif
/* It's possible for an interface to have >1 AF_LINK.
* For our purposes, we use the first one. */
for (ifp = ifs; ifp; ifp = ifp->next)
if (strcmp(ifp->name, ifa->ifa_name) == 0)
break;
if (ifp)
continue;
if (argc > 0) {
for (i = 0; i < argc; i++) {
#ifdef __linux__
/* Check the real interface name */
strlcpy(ifn, argv[i], sizeof(ifn));
p = strchr(ifn, ':');
if (p)
*p = '\0';
if (strcmp(ifn, ifa->ifa_name) == 0)
break;
#else
if (strcmp(argv[i], ifa->ifa_name) == 0)
break;
#endif
}
if (i == argc)
continue;
p = argv[i];
} else {
for (i = 0; i < ifdc; i++)
if (!fnmatch(ifdv[i], ifa->ifa_name, 0))
break;
if (i < ifdc)
continue;
for (i = 0; i < ifac; i++)
if (!fnmatch(ifav[i], ifa->ifa_name, 0))
break;
if (ifac && i == ifac)
continue;
p = ifa->ifa_name;
}
if ((ifp = init_interface(p)) == NULL)
continue;
/* Don't allow loopback unless explicit */
if (ifp->flags & IFF_LOOPBACK) {
if (argc == 0 && ifac == 0) {
free_interface(ifp);
continue;
}
} else {
#ifdef AF_LINK
sdl = (const struct sockaddr_dl *)(void *)ifa->ifa_addr;
switch(sdl->sdl_type) {
case IFT_ETHER:
ifp->family = ARPHRD_ETHER;
break;
case IFT_IEEE1394:
ifp->family = ARPHRD_IEEE1394;
break;
}
ifp->hwlen = sdl->sdl_alen;
#ifndef CLLADDR
# define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
#endif
memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
#elif AF_PACKET
sll = (const struct sockaddr_ll *)(void *)ifa->ifa_addr;
ifp->family = sll->sll_hatype;
ifp->hwlen = sll->sll_halen;
if (ifp->hwlen != 0)
memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
#endif
}
if (ifl)
ifl->next = ifp;
else
ifs = ifp;
ifl = ifp;
}
freeifaddrs(ifaddrs);
return ifs;
}
int
do_address(const char *ifname,
struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act)
{
struct ifaddrs *ifaddrs, *ifa;
const struct sockaddr_in *a, *n, *d;
int retval;
if (getifaddrs(&ifaddrs) == -1)
return -1;
retval = 0;
for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET ||
strcmp(ifa->ifa_name, ifname) != 0)
continue;
a = (const struct sockaddr_in *)(void *)&ifa->ifa_addr;
n = (const struct sockaddr_in *)(void *)&ifa->ifa_netmask;
if (ifa->ifa_flags & IFF_POINTOPOINT)
d = (const struct sockaddr_in *)(void *)
&ifa->ifa_dstaddr;
else
d = NULL;
if (act == 1) {
addr->s_addr = a->sin_addr.s_addr;
net->s_addr = n->sin_addr.s_addr;
if (dst && ifa->ifa_flags & IFF_POINTOPOINT)
dst->s_addr = d->sin_addr.s_addr;
retval = 1;
break;
}
#endif
if (ifr->ifr_addr.sa_family == AF_INET) {
memcpy(&address, &ifr->ifr_addr, sizeof(address));
if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
continue;
memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
if (get) {
addr->s_addr = address.sin_addr.s_addr;
net->s_addr = netmask.sin_addr.s_addr;
retval = 1;
break;
} else {
if (address.sin_addr.s_addr == addr->s_addr &&
(!net ||
netmask.sin_addr.s_addr == net->s_addr))
{
retval = 1;
break;
}
}
if (addr->s_addr == a->sin_addr.s_addr &&
(net == NULL || net->s_addr == n->sin_addr.s_addr))
{
retval = 1;
break;
}
}
if (!found)
errno = ENXIO;
close(s);
free(ifc.ifc_buf);
freeifaddrs(ifaddrs);
return retval;
}
int
up_interface(const char *ifname)
{
@ -348,103 +465,17 @@ carrier_status(const char *ifname)
#ifdef SIOCGIFMEDIA
if (retval == 1) {
memset(&ifmr, 0, sizeof(ifmr));
strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
strlcpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
retval = -1;
if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
ifmr.ifm_status & IFM_AVALID)
{
if (!(ifmr.ifm_status & IFM_ACTIVE))
retval = 0;
}
retval = (ifmr.ifm_status & IFM_ACTIVE) ? 1 : 0;
}
#endif
close(s);
return retval;
}
struct interface *
read_interface(const char *ifname, _unused int metric)
{
int s;
struct ifreq ifr;
struct interface *iface = NULL;
unsigned char *hwaddr = NULL;
size_t hwlen = 0;
sa_family_t family = 0;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return NULL;
#ifdef __linux__
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
goto eexit;
switch (ifr.ifr_hwaddr.sa_family) {
case ARPHRD_ETHER:
case ARPHRD_IEEE802:
hwlen = ETHER_ADDR_LEN;
break;
case ARPHRD_IEEE1394:
hwlen = EUI64_ADDR_LEN;
case ARPHRD_INFINIBAND:
hwlen = INFINIBAND_ADDR_LEN;
break;
}
hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
family = ifr.ifr_hwaddr.sa_family;
#else
ifr.ifr_metric = metric;
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
goto eexit;
hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
goto eexit;
family = ARPHRD_ETHER;
#endif
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
goto eexit;
/* Ensure that the MTU is big enough for DHCP */
if (ifr.ifr_mtu < MTU_MIN) {
ifr.ifr_mtu = MTU_MIN;
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
goto eexit;
}
if (up_interface(ifname) != 0)
goto eexit;
iface = xzalloc(sizeof(*iface));
strlcpy(iface->name, ifname, IF_NAMESIZE);
snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
memcpy(&iface->hwaddr, hwaddr, hwlen);
iface->hwlen = hwlen;
iface->family = family;
iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
/* 0 is a valid fd, so init to -1 */
iface->raw_fd = -1;
iface->udp_fd = -1;
iface->arp_fd = -1;
iface->link_fd = -1;
eexit:
close(s);
free(hwaddr);
return iface;
}
int
do_mtu(const char *ifname, short int mtu)
@ -482,13 +513,11 @@ int
open_udp_socket(struct interface *iface)
{
int s;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
} su;
struct sockaddr_in sin;
int n;
#ifdef SO_BINDTODEVICE
struct ifreq ifr;
char *p;
#endif
if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
@ -500,7 +529,12 @@ open_udp_socket(struct interface *iface)
#ifdef SO_BINDTODEVICE
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
/* We can only bind to the real device */
p = strchr(ifr.ifr_name, ':');
if (p)
*p = '\0';
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
sizeof(ifr)) == -1)
goto eexit;
#endif
/* As we don't use this socket for receiving, set the
@ -508,11 +542,11 @@ open_udp_socket(struct interface *iface)
n = 1;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
goto eexit;
memset(&su, 0, sizeof(su));
su.sin.sin_family = AF_INET;
su.sin.sin_port = htons(DHCP_CLIENT_PORT);
su.sin.sin_addr.s_addr = iface->addr.s_addr;
if (bind(s, &su.sa, sizeof(su)) == -1)
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(DHCP_CLIENT_PORT);
sin.sin_addr.s_addr = iface->addr.s_addr;
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
goto eexit;
iface->udp_fd = s;
@ -526,19 +560,16 @@ eexit:
ssize_t
send_packet(const struct interface *iface, struct in_addr to,
const uint8_t *data, ssize_t len)
const uint8_t *data, ssize_t len)
{
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
} su;
struct sockaddr_in sin;
memset(&su, 0, sizeof(su));
su.sin.sin_family = AF_INET;
su.sin.sin_addr.s_addr = to.s_addr;
su.sin.sin_port = htons(DHCP_SERVER_PORT);
return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su));
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = to.s_addr;
sin.sin_port = htons(DHCP_SERVER_PORT);
return sendto(iface->udp_fd, data, len, 0,
(struct sockaddr *)&sin, sizeof(sin));
}
struct udp_dhcp_packet
@ -574,7 +605,7 @@ checksum(const void *data, uint16_t len)
ssize_t
make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
struct in_addr source, struct in_addr dest)
struct in_addr source, struct in_addr dest)
{
struct udp_dhcp_packet *udpp;
struct ip *ip;
@ -630,20 +661,30 @@ get_udp_data(const uint8_t **data, const uint8_t *udp)
memcpy(&packet, udp, sizeof(packet));
*data = udp + offsetof(struct udp_dhcp_packet, dhcp);
return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp);
return ntohs(packet.ip.ip_len) -
sizeof(packet.ip) -
sizeof(packet.udp);
}
int
valid_udp_packet(const uint8_t *data, size_t data_len)
valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from)
{
struct udp_dhcp_packet packet;
uint16_t bytes, udpsum;
if (data_len < sizeof(packet.ip)) {
if (from)
from->s_addr = INADDR_ANY;
errno = EINVAL;
return -1;
}
memcpy(&packet, data, MIN(data_len, sizeof(packet)));
if (from)
from->s_addr = packet.ip.ip_src.s_addr;
if (data_len > sizeof(packet)) {
errno = EINVAL;
return -1;
}
memcpy(&packet, data, data_len);
if (checksum(&packet.ip, sizeof(packet.ip)) != 0) {
errno = EINVAL;
return -1;
@ -671,35 +712,3 @@ valid_udp_packet(const uint8_t *data, size_t data_len)
return 0;
}
int
send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
{
struct arphdr *arp;
size_t arpsize;
uint8_t *p;
int retval;
arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 * sizeof(sip);
arp = xmalloc(arpsize);
arp->ar_hrd = htons(iface->family);
arp->ar_pro = htons(ETHERTYPE_IP);
arp->ar_hln = iface->hwlen;
arp->ar_pln = sizeof(sip);
arp->ar_op = htons(op);
p = (uint8_t *)arp;
p += sizeof(*arp);
memcpy(p, iface->hwaddr, iface->hwlen);
p += iface->hwlen;
memcpy(p, &sip, sizeof(sip));
p += sizeof(sip);
/* ARP requests should ignore this */
retval = iface->hwlen;
while (retval--)
*p++ = '\0';
memcpy(p, &tip, sizeof(tip));
p += sizeof(tip);
retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
free(arp);
return retval;
}

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -39,6 +39,8 @@
#include <limits.h>
#include "config.h"
#include "dhcp.h"
#include "dhcpcd.h"
#ifndef DUID_LEN
# define DUID_LEN 128 + 2
@ -57,7 +59,6 @@
# define ARPHRD_INFINIBAND 32
#endif
#define HWADDR_LEN 20
/* Work out if we have a private address or not
* 10/8
@ -65,9 +66,9 @@
* 192.168/16
*/
#ifndef IN_PRIVATE
# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
((addr & 0xfff00000) == 0xac100000) || \
((addr & IN_CLASSB_NET) == 0xc0a80000))
# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
((addr & 0xfff00000) == 0xac100000) || \
((addr & IN_CLASSB_NET) == 0xc0a80000))
#endif
#define LINKLOCAL_ADDR 0xa9fe0000
@ -78,51 +79,22 @@
# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
#endif
/* There is an argument that this should be converted to an STAIL using
* queue(3). However, that isn't readily available on all libc's that
* dhcpcd works on. The only benefit of STAILQ over this is the ability to
* quickly loop backwards through the list - currently we reverse the list
* and then move through it forwards. This isn't that much of a big deal
* though as the norm is to just have one default route, and an IPV4LL route.
* You can (and do) get more routes in the DHCP message, but not enough to
* really warrant a change to STAIL queue for performance reasons. */
struct rt {
struct in_addr dest;
struct in_addr net;
struct in_addr gate;
const struct interface *iface;
struct rt *next;
};
struct interface
{
char name[IF_NAMESIZE];
sa_family_t family;
unsigned char hwaddr[HWADDR_LEN];
size_t hwlen;
int arpable;
int raw_fd;
int udp_fd;
int arp_fd;
int link_fd;
size_t buffer_size, buffer_len, buffer_pos;
unsigned char *buffer;
struct in_addr addr;
struct in_addr net;
struct rt *routes;
char leasefile[PATH_MAX];
time_t start_uptime;
unsigned char *clientid;
};
uint32_t get_netmask(uint32_t);
char *hwaddr_ntoa(const unsigned char *, size_t);
size_t hwaddr_aton(unsigned char *, const char *);
struct interface *read_interface(const char *, int);
int getifssid(const char *, char *);
struct interface *init_interface(const char *);
struct interface *discover_interfaces(int, char * const *);
void free_interface(struct interface *);
int do_mtu(const char *, short int);
#define get_mtu(iface) do_mtu(iface, 0)
#define set_mtu(iface, mtu) do_mtu(iface, mtu)
@ -131,47 +103,48 @@ int inet_ntocidr(struct in_addr);
int inet_cidrtoaddr(int, struct in_addr *);
int up_interface(const char *);
int do_interface(const char *, unsigned char *, size_t *,
struct in_addr *, struct in_addr *, int);
int if_address(const char *, const struct in_addr *, const struct in_addr *,
const struct in_addr *, int);
#define add_address(ifname, addr, net, brd) \
if_address(ifname, addr, net, brd, 1)
#define del_address(ifname, addr, net) \
if_address(ifname, addr, net, NULL, -1)
#define has_address(ifname, addr, net) \
do_interface(ifname, NULL, NULL, addr, net, 0)
#define get_address(ifname, addr, net) \
do_interface(ifname, NULL, NULL, addr, net, 1)
int do_address(const char *,
struct in_addr *, struct in_addr *, struct in_addr *, int);
int if_address(const struct interface *,
const struct in_addr *, const struct in_addr *,
const struct in_addr *, int);
#define add_address(iface, addr, net, brd) \
if_address(iface, addr, net, brd, 1)
#define del_address(iface, addr, net) \
if_address(iface, addr, net, NULL, -1)
#define has_address(iface, addr, net) \
do_address(iface, addr, net, NULL, 0)
#define get_address(iface, addr, net, dst) \
do_address(iface, addr, net, dst, 1)
int if_route(const struct interface *,
const struct in_addr *,const struct in_addr *,
const struct in_addr *, int, int);
#define add_route(iface, dest, mask, gate, metric) \
int if_route(const struct interface *, const struct in_addr *,
const struct in_addr *, const struct in_addr *, int, int);
#define add_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, 1)
#define change_route(iface, dest, mask, gate, metric) \
#define change_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, 0)
#define del_route(iface, dest, mask, gate, metric) \
#define del_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, -1)
#define del_src_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, -2)
void free_routes(struct rt *);
int open_udp_socket(struct interface *);
const size_t udp_dhcp_len;
ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
struct in_addr, struct in_addr);
struct in_addr, struct in_addr);
ssize_t get_udp_data(const uint8_t **, const uint8_t *);
int valid_udp_packet(const uint8_t *, size_t);
int valid_udp_packet(const uint8_t *, size_t, struct in_addr *);
int open_socket(struct interface *, int);
ssize_t send_packet(const struct interface *, struct in_addr,
const uint8_t *, ssize_t);
const uint8_t *, ssize_t);
ssize_t send_raw_packet(const struct interface *, int,
const void *, ssize_t);
const void *, ssize_t);
ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
int open_link_socket(struct interface *);
int link_changed(struct interface *);
int init_socket(void);
int open_link_socket(void);
int manage_link(int);
int carrier_status(const char *);
#endif

View File

@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* Copyright 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@ -40,10 +40,12 @@
static int signal_pipe[2];
static const int handle_sigs[] = {
SIGHUP,
SIGALRM,
SIGHUP,
SIGINT,
SIGPIPE,
SIGTERM,
SIGINT
SIGUSR1,
};
static void
@ -52,17 +54,11 @@ signal_handler(int sig)
int serrno = errno;
if (write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig))
syslog(LOG_ERR, "write signal %d: %s", sig, strerror(errno));
syslog(LOG_ERR, "failed to write signal %d: %m", sig);
/* Restore errno */
errno = serrno;
}
int
signal_fd(void)
{
return (signal_pipe[0]);
}
/* 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 */
@ -95,7 +91,7 @@ signal_init(void)
return -1;
if (set_cloexec(signal_pipe[1]) == -1)
return -1;
return 0;
return signal_pipe[0];
}
static int
@ -125,3 +121,4 @@ signal_reset(void)
{
return signal_handle(SIG_DFL);
}

View File

@ -31,7 +31,6 @@
int signal_init(void);
int signal_setup(void);
int signal_reset(void);
int signal_fd(void);
int signal_read(void);
#endif