2011-03-31 09:29:22 +05:30
|
|
|
/* ndhc.c - DHCP client
|
2010-11-12 14:32:18 +05:30
|
|
|
*
|
2015-02-13 12:24:57 +05:30
|
|
|
* Copyright (c) 2004-2015 Nicholas J. Kain <njkain at gmail dot com>
|
2011-07-25 12:00:57 +05:30
|
|
|
* All rights reserved.
|
2010-11-12 14:32:18 +05:30
|
|
|
*
|
2011-07-25 12:00:57 +05:30
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
2010-11-12 14:32:18 +05:30
|
|
|
*
|
2011-07-25 12:00:57 +05:30
|
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
2010-11-12 14:32:18 +05:30
|
|
|
*
|
2011-07-25 12:00:57 +05:30
|
|
|
* - 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
2010-11-12 14:32:18 +05:30
|
|
|
*/
|
2010-11-13 01:03:17 +05:30
|
|
|
|
2010-11-12 14:32:18 +05:30
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/time.h>
|
2014-03-10 10:22:56 +05:30
|
|
|
#include <sys/stat.h>
|
2010-11-12 14:32:18 +05:30
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <string.h>
|
2011-07-03 15:06:47 +05:30
|
|
|
#include <ctype.h>
|
2011-06-30 09:17:31 +05:30
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2010-12-01 23:41:09 +05:30
|
|
|
#include <sys/epoll.h>
|
2010-12-01 23:05:13 +05:30
|
|
|
#include <sys/signalfd.h>
|
2014-03-11 00:14:12 +05:30
|
|
|
#include <sys/prctl.h>
|
2010-11-12 14:32:18 +05:30
|
|
|
#include <net/if.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
2014-03-19 10:43:11 +05:30
|
|
|
#include <limits.h>
|
2014-03-31 02:32:48 +05:30
|
|
|
#include "nk/log.h"
|
|
|
|
#include "nk/privilege.h"
|
|
|
|
#include "nk/pidfile.h"
|
|
|
|
#include "nk/io.h"
|
|
|
|
#include "nk/copy_cmdarg.h"
|
2010-11-12 14:32:18 +05:30
|
|
|
|
2014-03-13 02:21:10 +05:30
|
|
|
#include "ndhc.h"
|
2010-11-13 05:14:49 +05:30
|
|
|
#include "ndhc-defines.h"
|
2014-04-15 00:36:31 +05:30
|
|
|
#include "cfg.h"
|
2014-03-31 02:32:48 +05:30
|
|
|
#include "seccomp.h"
|
2011-06-30 09:17:31 +05:30
|
|
|
#include "state.h"
|
2010-11-12 14:32:18 +05:30
|
|
|
#include "options.h"
|
2011-07-02 13:21:44 +05:30
|
|
|
#include "dhcp.h"
|
2010-12-24 20:02:58 +05:30
|
|
|
#include "sys.h"
|
2010-12-24 20:42:41 +05:30
|
|
|
#include "ifchange.h"
|
2010-12-24 20:28:47 +05:30
|
|
|
#include "arp.h"
|
2011-07-04 03:00:55 +05:30
|
|
|
#include "nl.h"
|
2011-03-30 00:07:45 +05:30
|
|
|
#include "netlink.h"
|
2011-04-20 02:07:43 +05:30
|
|
|
#include "leasefile.h"
|
2014-03-17 15:26:30 +05:30
|
|
|
#include "ifset.h"
|
2014-03-13 01:43:47 +05:30
|
|
|
#include "ifchd.h"
|
2014-03-19 10:12:32 +05:30
|
|
|
#include "duiaid.h"
|
2014-04-04 13:42:25 +05:30
|
|
|
#include "sockd.h"
|
2015-02-14 02:55:36 +05:30
|
|
|
#include "rfkill.h"
|
2010-11-12 14:32:18 +05:30
|
|
|
|
2010-12-24 17:30:42 +05:30
|
|
|
struct client_state_t cs = {
|
2011-06-27 03:51:40 +05:30
|
|
|
.init = 1,
|
2010-12-24 17:30:42 +05:30
|
|
|
.epollFd = -1,
|
|
|
|
.signalFd = -1,
|
|
|
|
.listenFd = -1,
|
|
|
|
.arpFd = -1,
|
2011-03-30 01:04:00 +05:30
|
|
|
.nlFd = -1,
|
2012-04-13 05:36:05 +05:30
|
|
|
.nlPortId = -1,
|
2015-02-14 02:55:36 +05:30
|
|
|
.rfkillFd = -1,
|
2015-02-18 16:01:13 +05:30
|
|
|
.dhcp_wake_ts = -1,
|
2011-03-31 08:47:27 +05:30
|
|
|
.routerArp = "\0\0\0\0\0\0",
|
2011-07-11 22:54:59 +05:30
|
|
|
.serverArp = "\0\0\0\0\0\0",
|
2010-12-24 17:30:42 +05:30
|
|
|
};
|
2010-11-12 14:32:18 +05:30
|
|
|
|
|
|
|
struct client_config_t client_config = {
|
2010-11-13 01:03:17 +05:30
|
|
|
.interface = "eth0",
|
2011-03-31 08:47:27 +05:30
|
|
|
.arp = "\0\0\0\0\0\0",
|
2014-03-18 12:43:51 +05:30
|
|
|
.clientid_len = 0,
|
2014-03-19 10:43:11 +05:30
|
|
|
.metric = 0,
|
2014-03-28 09:42:33 +05:30
|
|
|
.foreground = 1,
|
2010-11-12 14:32:18 +05:30
|
|
|
};
|
|
|
|
|
2015-02-14 09:44:08 +05:30
|
|
|
void set_client_addr(const char v[static 1]) { cs.clientAddr = inet_addr(v); }
|
2014-04-15 00:36:31 +05:30
|
|
|
|
|
|
|
void print_version(void)
|
|
|
|
{
|
|
|
|
printf("ndhc %s, dhcp client.\n", NDHC_VERSION);
|
2015-02-13 12:24:57 +05:30
|
|
|
printf("Copyright (c) 2004-2015 Nicholas J. Kain\n"
|
2014-04-15 00:36:31 +05:30
|
|
|
"All rights reserved.\n\n"
|
|
|
|
"Redistribution and use in source and binary forms, with or without\n"
|
|
|
|
"modification, are permitted provided that the following conditions are met:\n\n"
|
|
|
|
"- Redistributions of source code must retain the above copyright notice,\n"
|
|
|
|
" this list of conditions and the following disclaimer.\n"
|
|
|
|
"- Redistributions in binary form must reproduce the above copyright notice,\n"
|
|
|
|
" this list of conditions and the following disclaimer in the documentation\n"
|
|
|
|
" and/or other materials provided with the distribution.\n\n"
|
|
|
|
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
|
|
|
|
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
|
|
|
|
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
|
|
|
|
"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
|
|
|
|
"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
|
|
|
|
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
|
|
|
|
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
|
|
|
|
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
|
|
|
|
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
|
|
|
|
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
|
|
|
|
"POSSIBILITY OF SUCH DAMAGE.\n");
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void show_usage(void)
|
2010-11-12 14:32:18 +05:30
|
|
|
{
|
2010-11-13 01:03:17 +05:30
|
|
|
printf(
|
2012-07-23 22:54:15 +05:30
|
|
|
"ndhc " NDHC_VERSION ", dhcp client. Licensed under 2-clause BSD.\n"
|
2015-02-13 12:24:57 +05:30
|
|
|
"Copyright (C) 2004-2015 Nicholas J. Kain\n"
|
2010-11-12 14:32:18 +05:30
|
|
|
"Usage: ndhc [OPTIONS]\n\n"
|
2014-04-15 00:36:31 +05:30
|
|
|
" -c, --config=FILE Path to ndhc configuration file\n"
|
|
|
|
" -I, --clientid=CLIENTID Client identifier\n"
|
2011-07-05 20:44:35 +05:30
|
|
|
" -h, --hostname=HOSTNAME Client hostname\n"
|
2011-07-05 20:48:28 +05:30
|
|
|
" -V, --vendorid=VENDORID Client vendor identification string\n"
|
2010-11-12 14:32:18 +05:30
|
|
|
" -b, --background Fork to background if lease cannot be\n"
|
|
|
|
" immediately negotiated.\n"
|
2014-03-10 10:22:56 +05:30
|
|
|
" -p, --pidfile=FILE File where the ndhc pid will be written\n"
|
2010-11-12 14:32:18 +05:30
|
|
|
" -i, --interface=INTERFACE Interface to use (default: eth0)\n"
|
|
|
|
" -n, --now Exit with failure if lease cannot be\n"
|
|
|
|
" immediately negotiated.\n"
|
|
|
|
" -q, --quit Quit after obtaining lease\n"
|
|
|
|
" -r, --request=IP IP address to request (default: none)\n"
|
2014-03-10 10:22:56 +05:30
|
|
|
" -u, --user=USER Change ndhc privileges to this user\n"
|
|
|
|
" -U, --ifch-user=USER Change ndhc-ifch privileges to this user\n"
|
2014-04-05 14:53:37 +05:30
|
|
|
" -D, --sockd-user=USER Change ndhc-sockd privileges to this user\n"
|
2011-07-05 20:48:28 +05:30
|
|
|
" -C, --chroot=DIR Chroot to this directory\n"
|
2014-03-19 10:16:54 +05:30
|
|
|
" -s, --state-dir=DIR State storage dir (default: /etc/ndhc)\n"
|
2014-03-15 08:54:22 +05:30
|
|
|
#ifdef ENABLE_SECCOMP_FILTER
|
2014-03-10 10:22:56 +05:30
|
|
|
" -S, --seccomp-enforce Enforce seccomp syscall restrictions\n"
|
2014-03-15 08:54:22 +05:30
|
|
|
#endif
|
2011-07-05 22:33:55 +05:30
|
|
|
" -d, --relentless-defense Never back off in defending IP against\n"
|
|
|
|
" conflicting hosts (servers only)\n"
|
2013-02-09 11:00:19 +05:30
|
|
|
" -w, --arp-probe-wait Time to delay before first ARP probe\n"
|
|
|
|
" -W, --arp-probe-num Number of ARP probes before lease is ok\n"
|
|
|
|
" -m, --arp-probe-min Min ms to wait for ARP response\n"
|
|
|
|
" -M, --arp-probe-max Max ms to wait for ARP response\n"
|
2014-03-19 10:43:11 +05:30
|
|
|
" -t, --gw-metric Route metric for default gw (default: 0)\n"
|
2014-03-10 10:22:56 +05:30
|
|
|
" -R, --resolve-conf=FILE Path to resolv.conf or equivalent\n"
|
2014-04-15 00:36:31 +05:30
|
|
|
" -H, --dhcp-set-hostname Allow DHCP to set machine hostname\n"
|
2010-11-12 14:32:18 +05:30
|
|
|
" -v, --version Display version\n"
|
2010-11-13 01:03:17 +05:30
|
|
|
);
|
|
|
|
exit(EXIT_SUCCESS);
|
2010-11-12 14:32:18 +05:30
|
|
|
}
|
|
|
|
|
2014-03-13 02:21:10 +05:30
|
|
|
static void setup_signals_ndhc(void)
|
|
|
|
{
|
|
|
|
sigset_t mask;
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGUSR1);
|
|
|
|
sigaddset(&mask, SIGUSR2);
|
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
sigaddset(&mask, SIGTERM);
|
|
|
|
if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
|
|
|
|
suicide("sigprocmask failed");
|
|
|
|
if (cs.signalFd >= 0) {
|
|
|
|
epoll_del(cs.epollFd, cs.signalFd);
|
|
|
|
close(cs.signalFd);
|
|
|
|
}
|
|
|
|
cs.signalFd = signalfd(-1, &mask, SFD_NONBLOCK);
|
|
|
|
if (cs.signalFd < 0)
|
|
|
|
suicide("signalfd failed");
|
|
|
|
epoll_add(cs.epollFd, cs.signalFd);
|
|
|
|
}
|
|
|
|
|
2015-02-15 17:08:03 +05:30
|
|
|
static int signal_dispatch(void)
|
2010-12-01 23:05:13 +05:30
|
|
|
{
|
2015-02-14 09:55:42 +05:30
|
|
|
struct signalfd_siginfo si;
|
|
|
|
memset(&si, 0, sizeof si);
|
2014-04-16 06:25:13 +05:30
|
|
|
ssize_t r = safe_read(cs.signalFd, (char *)&si, sizeof si);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("%s: ndhc: error reading from signalfd: %s",
|
|
|
|
client_config.interface, strerror(errno));
|
2015-02-15 17:08:03 +05:30
|
|
|
return SIGNAL_NONE;
|
2014-04-16 06:25:13 +05:30
|
|
|
}
|
|
|
|
if ((size_t)r < sizeof si) {
|
|
|
|
log_error("%s: ndhc: short read from signalfd: %zd < %zu",
|
|
|
|
client_config.interface, r, sizeof si);
|
2015-02-15 17:08:03 +05:30
|
|
|
return SIGNAL_NONE;
|
2010-12-01 23:05:13 +05:30
|
|
|
}
|
|
|
|
switch (si.ssi_signo) {
|
2015-02-15 17:08:03 +05:30
|
|
|
case SIGUSR1: return SIGNAL_RENEW;
|
|
|
|
case SIGUSR2: return SIGNAL_RELEASE;
|
2014-03-12 22:17:39 +05:30
|
|
|
case SIGCHLD:
|
2014-03-31 02:51:27 +05:30
|
|
|
suicide("ndhc-master: Subprocess terminated unexpectedly. Exiting.");
|
2010-12-01 23:05:13 +05:30
|
|
|
case SIGTERM:
|
2010-11-13 01:03:17 +05:30
|
|
|
log_line("Received SIGTERM. Exiting gracefully.");
|
|
|
|
exit(EXIT_SUCCESS);
|
2015-02-15 17:08:03 +05:30
|
|
|
default: return SIGNAL_NONE;
|
2010-12-01 23:05:13 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-14 09:44:08 +05:30
|
|
|
static int is_string_hwaddr(const char str[static 1], size_t slen)
|
2011-07-03 15:06:47 +05:30
|
|
|
{
|
2011-07-03 15:15:05 +05:30
|
|
|
if (slen == 17 && str[2] == ':' && str[5] == ':' && str[8] == ':' &&
|
|
|
|
str[11] == ':' && str[14] == ':' &&
|
2011-07-03 15:06:47 +05:30
|
|
|
isxdigit(str[0]) && isxdigit(str[1]) && isxdigit(str[3]) &&
|
|
|
|
isxdigit(str[4]) && isxdigit(str[6]) && isxdigit(str[7]) &&
|
|
|
|
isxdigit(str[9]) && isxdigit(str[10]) && isxdigit(str[12]) &&
|
2011-07-03 15:15:05 +05:30
|
|
|
isxdigit(str[13]) && isxdigit(str[15]) && isxdigit(str[16])
|
|
|
|
)
|
2011-07-03 15:06:47 +05:30
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-14 09:44:08 +05:30
|
|
|
int get_clientid_string(const char str[static 1], size_t slen)
|
2011-07-03 15:15:05 +05:30
|
|
|
{
|
2014-03-18 12:43:51 +05:30
|
|
|
if (!slen)
|
|
|
|
return -1;
|
|
|
|
if (!is_string_hwaddr(str, slen)) {
|
|
|
|
client_config.clientid[0] = 0;
|
2014-04-16 00:25:50 +05:30
|
|
|
memcpy(client_config.clientid + 1, str,
|
2014-03-18 12:43:51 +05:30
|
|
|
min_size_t(slen, sizeof client_config.clientid - 1));
|
|
|
|
client_config.clientid_len = slen + 1;
|
2011-07-03 15:15:05 +05:30
|
|
|
return 0;
|
2014-03-18 12:43:51 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t mac[6];
|
|
|
|
for (size_t i = 0; i < sizeof mac; ++i)
|
|
|
|
mac[i] = strtol(str+i*3, NULL, 16);
|
|
|
|
client_config.clientid[0] = 1; // Ethernet MAC type
|
2014-04-16 00:25:50 +05:30
|
|
|
memcpy(client_config.clientid + 1, mac,
|
|
|
|
min_size_t(sizeof mac, sizeof client_config.clientid - 1));
|
2014-03-18 12:43:51 +05:30
|
|
|
client_config.clientid_len = 7;
|
2011-07-03 15:15:05 +05:30
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-03-19 13:42:24 +05:30
|
|
|
static void fail_if_state_dir_dne(void)
|
|
|
|
{
|
2014-03-31 02:51:27 +05:30
|
|
|
if (strlen(state_dir) == 0)
|
|
|
|
suicide("state_dir path is empty; it must be specified");
|
2014-03-19 13:42:24 +05:30
|
|
|
struct stat st;
|
2014-03-31 02:51:27 +05:30
|
|
|
if (stat(state_dir, &st) < 0)
|
|
|
|
suicide("failed to stat state_dir path '%s': %s",
|
|
|
|
state_dir, strerror(errno));
|
|
|
|
if (!S_ISDIR(st.st_mode))
|
|
|
|
suicide("state_dir path '%s' does not specify a directory", state_dir);
|
2014-03-19 13:42:24 +05:30
|
|
|
}
|
|
|
|
|
2014-03-10 10:22:56 +05:30
|
|
|
static void do_ndhc_work(void)
|
2010-12-01 23:05:13 +05:30
|
|
|
{
|
2015-02-18 16:01:13 +05:30
|
|
|
static bool rfkill_set; // Is the rfkill switch set?
|
|
|
|
static bool rfkill_nl_state_changed; // iface state changed during rfkill
|
|
|
|
static int rfkill_nl_state; // current iface state during rfkill
|
2015-02-15 17:08:03 +05:30
|
|
|
struct dhcpmsg dhcp_packet;
|
2015-02-18 16:06:13 +05:30
|
|
|
struct epoll_event events[1];
|
2011-07-11 21:01:27 +05:30
|
|
|
long long nowts;
|
2015-05-27 21:53:16 +05:30
|
|
|
int timeout = 0;
|
|
|
|
bool had_event;
|
2010-11-13 01:03:17 +05:30
|
|
|
|
2010-12-24 17:30:42 +05:30
|
|
|
cs.epollFd = epoll_create1(0);
|
2014-04-06 16:03:14 +05:30
|
|
|
if (cs.epollFd < 0)
|
2010-12-01 23:41:09 +05:30
|
|
|
suicide("epoll_create1 failed");
|
2012-07-20 18:53:18 +05:30
|
|
|
|
2014-03-10 10:22:56 +05:30
|
|
|
if (enforce_seccomp_ndhc())
|
|
|
|
log_line("ndhc seccomp filter cannot be installed");
|
2012-07-20 18:53:18 +05:30
|
|
|
|
2014-03-13 02:21:10 +05:30
|
|
|
setup_signals_ndhc();
|
2012-07-21 04:18:26 +05:30
|
|
|
|
2014-03-13 02:21:10 +05:30
|
|
|
epoll_add(cs.epollFd, cs.nlFd);
|
2014-04-16 08:49:24 +05:30
|
|
|
epoll_add(cs.epollFd, ifchStream[0]);
|
|
|
|
epoll_add(cs.epollFd, sockdStream[0]);
|
2015-02-14 02:55:36 +05:30
|
|
|
if (client_config.enable_rfkill && cs.rfkillFd != -1)
|
2015-02-14 03:26:56 +05:30
|
|
|
epoll_add(cs.epollFd, cs.rfkillFd);
|
2015-02-14 05:38:50 +05:30
|
|
|
start_dhcp_listen(&cs);
|
2010-11-13 01:03:17 +05:30
|
|
|
|
2010-12-01 23:41:09 +05:30
|
|
|
for (;;) {
|
2015-05-27 21:53:16 +05:30
|
|
|
had_event = false;
|
2015-02-18 16:01:13 +05:30
|
|
|
int maxi = epoll_wait(cs.epollFd, events, 1, timeout);
|
2015-02-15 17:08:03 +05:30
|
|
|
if (maxi < 0) {
|
2010-12-01 23:41:09 +05:30
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
suicide("epoll_wait failed");
|
|
|
|
}
|
2015-02-18 21:32:13 +05:30
|
|
|
bool sev_dhcp = false;
|
2015-02-15 17:18:49 +05:30
|
|
|
uint32_t dhcp_srcaddr;
|
|
|
|
uint8_t dhcp_msgtype;
|
2015-02-18 21:32:13 +05:30
|
|
|
bool sev_arp = false;
|
2015-02-15 17:18:49 +05:30
|
|
|
int sev_nl = IFS_NONE;
|
|
|
|
int sev_rfk = RFK_NONE;
|
|
|
|
int sev_signal = SIGNAL_NONE;
|
2015-02-18 16:01:13 +05:30
|
|
|
bool force_fingerprint = false;
|
2015-02-15 17:08:03 +05:30
|
|
|
for (int i = 0; i < maxi; ++i) {
|
2015-05-27 21:53:16 +05:30
|
|
|
had_event = true;
|
2010-12-01 23:41:09 +05:30
|
|
|
int fd = events[i].data.fd;
|
2014-04-16 08:49:24 +05:30
|
|
|
if (fd == cs.signalFd) {
|
2015-02-15 17:08:03 +05:30
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
|
|
return;
|
2015-02-15 17:18:49 +05:30
|
|
|
sev_signal = signal_dispatch();
|
2014-04-16 08:49:24 +05:30
|
|
|
} else if (fd == cs.listenFd) {
|
2015-02-15 17:08:03 +05:30
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
|
|
return;
|
2015-02-15 17:18:49 +05:30
|
|
|
sev_dhcp = dhcp_packet_get(&cs, &dhcp_packet, &dhcp_msgtype,
|
|
|
|
&dhcp_srcaddr);
|
2014-04-16 08:49:24 +05:30
|
|
|
} else if (fd == cs.arpFd) {
|
2015-02-15 17:08:03 +05:30
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
|
|
return;
|
2015-02-15 17:18:49 +05:30
|
|
|
sev_arp = arp_packet_get(&cs);
|
2014-04-16 08:49:24 +05:30
|
|
|
} else if (fd == cs.nlFd) {
|
2015-02-15 17:08:03 +05:30
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
|
|
return;
|
2015-02-15 17:18:49 +05:30
|
|
|
sev_nl = nl_event_get(&cs);
|
2014-04-16 08:49:24 +05:30
|
|
|
} else if (fd == ifchStream[0]) {
|
|
|
|
if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP))
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
} else if (fd == sockdStream[0]) {
|
|
|
|
if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP))
|
|
|
|
exit(EXIT_FAILURE);
|
2015-02-14 02:55:36 +05:30
|
|
|
} else if (fd == cs.rfkillFd && client_config.enable_rfkill) {
|
2015-02-15 17:08:03 +05:30
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
|
|
return;
|
2015-02-15 17:18:49 +05:30
|
|
|
sev_rfk = rfkill_get(&cs, 1, client_config.rfkillIdx);
|
2014-04-16 08:49:24 +05:30
|
|
|
} else
|
2010-12-01 23:41:09 +05:30
|
|
|
suicide("epoll_wait: unknown fd");
|
2015-02-18 16:01:13 +05:30
|
|
|
|
|
|
|
if (sev_rfk == RFK_ENABLED) {
|
|
|
|
rfkill_set = 1;
|
|
|
|
rfkill_nl_state = cs.ifsPrevState;
|
|
|
|
rfkill_nl_state_changed = false;
|
|
|
|
log_line("rfkill: radio now blocked");
|
|
|
|
} else if (sev_rfk == RFK_DISABLED) {
|
|
|
|
rfkill_set = 0;
|
|
|
|
log_line("rfkill: radio now unblocked");
|
|
|
|
// We now simulate the state changes that may have happened
|
|
|
|
// during rfkill.
|
|
|
|
if (rfkill_nl_state != cs.ifsPrevState)
|
|
|
|
nl_event_react(&cs, rfkill_nl_state);
|
|
|
|
else if (rfkill_nl_state_changed && rfkill_nl_state == IFS_UP) {
|
|
|
|
// We might have changed networks even if we ended up
|
|
|
|
// back in IFS_UP state. We need to fingerprint the
|
|
|
|
// network and confirm that we're on the same network.
|
|
|
|
force_fingerprint = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sev_nl != IFS_NONE) {
|
|
|
|
if (!rfkill_set) {
|
|
|
|
if (nl_event_react(&cs, sev_nl))
|
|
|
|
force_fingerprint = true;
|
|
|
|
} else {
|
|
|
|
// Store the state so it can be replayed later.
|
|
|
|
rfkill_nl_state_changed = true;
|
|
|
|
rfkill_nl_state = sev_nl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rfkill_set || cs.ifsPrevState != IFS_UP) {
|
|
|
|
// We can't do anything while the iface is disabled, anyway.
|
|
|
|
// XXX: It may be smart to set a non-infinite timeout
|
|
|
|
// and periodically poll to see if the rfkill or iface
|
|
|
|
// state changed; it might happen during suspend.
|
|
|
|
timeout = -1;
|
|
|
|
continue;
|
2010-11-13 01:03:17 +05:30
|
|
|
}
|
2010-12-02 10:45:03 +05:30
|
|
|
|
2015-02-18 16:01:13 +05:30
|
|
|
nowts = curms();
|
|
|
|
long long arp_wake_ts = arp_get_wake_ts();
|
|
|
|
int dhcp_ok = dhcp_handle(&cs, nowts, sev_dhcp, &dhcp_packet,
|
|
|
|
dhcp_msgtype, dhcp_srcaddr,
|
|
|
|
sev_arp, force_fingerprint,
|
|
|
|
cs.dhcp_wake_ts <= nowts,
|
2015-02-18 18:01:19 +05:30
|
|
|
arp_wake_ts <= nowts, sev_signal);
|
2015-02-18 16:01:13 +05:30
|
|
|
if (sev_arp)
|
|
|
|
arp_reply_clear();
|
|
|
|
|
|
|
|
// XXX: Would be best if we detected RFKILL being set via an
|
|
|
|
// error message and propagated it back to here as a
|
|
|
|
// distinct return value.
|
|
|
|
if (dhcp_ok == COR_ERROR) {
|
|
|
|
timeout = -1;
|
|
|
|
continue;
|
2015-02-15 17:18:49 +05:30
|
|
|
}
|
|
|
|
|
2015-05-27 21:53:16 +05:30
|
|
|
int prev_timeout = timeout;
|
|
|
|
|
2015-02-18 16:01:13 +05:30
|
|
|
arp_wake_ts = arp_get_wake_ts();
|
2015-05-27 21:53:16 +05:30
|
|
|
if (arp_wake_ts < 0 && cs.dhcp_wake_ts < 0) {
|
|
|
|
timeout = -1;
|
|
|
|
continue;
|
|
|
|
} else if (arp_wake_ts < 0) {
|
|
|
|
timeout = cs.dhcp_wake_ts - nowts;
|
|
|
|
} else if (cs.dhcp_wake_ts < 0) {
|
|
|
|
timeout = arp_wake_ts - nowts;
|
2015-02-18 16:01:13 +05:30
|
|
|
} else {
|
|
|
|
timeout = (arp_wake_ts < cs.dhcp_wake_ts ?
|
|
|
|
arp_wake_ts : cs.dhcp_wake_ts) - nowts;
|
2010-12-23 22:04:57 +05:30
|
|
|
}
|
2015-05-27 21:53:16 +05:30
|
|
|
if (timeout < 0)
|
|
|
|
timeout = 0;
|
|
|
|
|
|
|
|
// Failsafe to prevent busy-spin.
|
2015-05-27 22:05:16 +05:30
|
|
|
if (timeout == 0 && prev_timeout == 0 && !had_event)
|
2015-05-27 21:59:46 +05:30
|
|
|
timeout = 10000;
|
2010-11-13 01:03:17 +05:30
|
|
|
}
|
2010-11-12 14:32:18 +05:30
|
|
|
}
|
|
|
|
|
2014-03-31 02:32:48 +05:30
|
|
|
char state_dir[PATH_MAX] = "/etc/ndhc";
|
|
|
|
char chroot_dir[PATH_MAX] = "";
|
|
|
|
char resolv_conf_d[PATH_MAX] = "";
|
2014-04-15 00:36:31 +05:30
|
|
|
char pidfile[PATH_MAX] = PID_FILE_DEFAULT;
|
|
|
|
uid_t ndhc_uid = 0;
|
|
|
|
gid_t ndhc_gid = 0;
|
2014-04-07 13:14:02 +05:30
|
|
|
int ifchSock[2];
|
2014-04-05 14:55:56 +05:30
|
|
|
int sockdSock[2];
|
2014-04-16 08:49:24 +05:30
|
|
|
int ifchStream[2];
|
|
|
|
int sockdStream[2];
|
2014-04-04 13:42:25 +05:30
|
|
|
|
2014-04-16 03:31:01 +05:30
|
|
|
static void create_ifch_ipc_sockets(void) {
|
2014-04-07 13:14:02 +05:30
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, ifchSock) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/ifch socket: %s", strerror(errno));
|
2014-04-16 08:49:24 +05:30
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ifchStream) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/ifch socket: %s", strerror(errno));
|
2014-03-10 10:22:56 +05:30
|
|
|
}
|
|
|
|
|
2014-04-16 03:31:01 +05:30
|
|
|
static void create_sockd_ipc_sockets(void) {
|
2014-04-05 14:55:56 +05:30
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockdSock) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/sockd socket: %s", strerror(errno));
|
2014-04-16 08:49:24 +05:30
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockdStream) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/ifch socket: %s", strerror(errno));
|
2014-04-04 13:42:25 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
static void spawn_ifch(void)
|
|
|
|
{
|
2014-04-16 03:31:01 +05:30
|
|
|
create_ifch_ipc_sockets();
|
2014-04-04 13:42:25 +05:30
|
|
|
pid_t ifch_pid = fork();
|
|
|
|
if (ifch_pid == 0) {
|
2014-04-07 13:14:02 +05:30
|
|
|
close(ifchSock[0]);
|
2014-04-16 08:49:24 +05:30
|
|
|
close(ifchStream[0]);
|
2014-04-04 13:42:25 +05:30
|
|
|
// Don't share the RNG state with the master process.
|
|
|
|
nk_random_u32_init(&cs.rnd32_state);
|
|
|
|
ifch_main();
|
|
|
|
} else if (ifch_pid > 0) {
|
2014-04-07 13:14:02 +05:30
|
|
|
close(ifchSock[1]);
|
2014-04-16 08:49:24 +05:30
|
|
|
close(ifchStream[1]);
|
2014-04-04 13:42:25 +05:30
|
|
|
} else
|
|
|
|
suicide("failed to fork ndhc-ifch: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spawn_sockd(void)
|
|
|
|
{
|
2014-04-16 03:31:01 +05:30
|
|
|
create_sockd_ipc_sockets();
|
2014-04-04 13:42:25 +05:30
|
|
|
pid_t sockd_pid = fork();
|
|
|
|
if (sockd_pid == 0) {
|
2014-04-05 14:55:56 +05:30
|
|
|
close(sockdSock[0]);
|
2014-04-16 08:49:24 +05:30
|
|
|
close(sockdStream[0]);
|
2014-04-04 13:42:25 +05:30
|
|
|
// Don't share the RNG state with the master process.
|
|
|
|
nk_random_u32_init(&cs.rnd32_state);
|
|
|
|
sockd_main();
|
|
|
|
} else if (sockd_pid > 0) {
|
2014-04-05 14:55:56 +05:30
|
|
|
close(sockdSock[1]);
|
2014-04-16 08:49:24 +05:30
|
|
|
close(sockdStream[1]);
|
2014-04-04 13:42:25 +05:30
|
|
|
} else
|
|
|
|
suicide("failed to fork ndhc-sockd: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
2014-03-11 08:30:57 +05:30
|
|
|
static void ndhc_main(void) {
|
2014-03-11 00:14:12 +05:30
|
|
|
prctl(PR_SET_NAME, "ndhc: master");
|
2014-03-10 10:22:56 +05:30
|
|
|
log_line("ndhc client " NDHC_VERSION " started on interface [%s].",
|
|
|
|
client_config.interface);
|
|
|
|
|
2014-03-31 02:51:27 +05:30
|
|
|
if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &cs.nlPortId)) < 0)
|
|
|
|
suicide("%s: failed to open netlink socket", __func__);
|
2014-03-17 15:26:30 +05:30
|
|
|
|
2015-02-14 02:55:36 +05:30
|
|
|
cs.rfkillFd = rfkill_open(&client_config.enable_rfkill);
|
|
|
|
|
2014-03-10 10:22:56 +05:30
|
|
|
if (client_config.foreground && !client_config.background_if_no_lease) {
|
2014-04-06 16:03:14 +05:30
|
|
|
if (file_exists(pidfile, "w") < 0)
|
2014-03-31 02:51:27 +05:30
|
|
|
suicide("%s: can't open pidfile '%s' for write!",
|
|
|
|
__func__, pidfile);
|
2014-03-10 10:22:56 +05:30
|
|
|
write_pid(pidfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
open_leasefile();
|
|
|
|
|
2014-03-31 02:32:48 +05:30
|
|
|
nk_set_chroot(chroot_dir);
|
2014-03-10 10:22:56 +05:30
|
|
|
memset(chroot_dir, '\0', sizeof chroot_dir);
|
2014-04-08 00:35:34 +05:30
|
|
|
nk_set_uidgid(ndhc_uid, ndhc_gid, NULL, 0);
|
2014-03-10 10:22:56 +05:30
|
|
|
|
2015-02-15 07:17:14 +05:30
|
|
|
if (cs.ifsPrevState != IFS_UP) {
|
|
|
|
if (ifchange_deconfig(&cs) < 0)
|
|
|
|
suicide("%s: can't deconfigure interface settings", __func__);
|
|
|
|
}
|
2014-03-10 10:22:56 +05:30
|
|
|
|
|
|
|
do_ndhc_work();
|
|
|
|
}
|
|
|
|
|
2014-03-13 02:21:10 +05:30
|
|
|
void background(void)
|
|
|
|
{
|
|
|
|
static char called;
|
|
|
|
if (!called) {
|
|
|
|
called = 1; // Do not fork again.
|
2014-04-06 16:03:14 +05:30
|
|
|
if (daemon(0, 0) < 0) {
|
2014-03-13 02:21:10 +05:30
|
|
|
perror("fork");
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
}
|
2014-04-06 16:03:14 +05:30
|
|
|
if (file_exists(pidfile, "w") < 0) {
|
2014-03-31 02:51:27 +05:30
|
|
|
log_warning("Cannot open pidfile for write!");
|
2014-03-13 02:21:10 +05:30
|
|
|
} else
|
|
|
|
write_pid(pidfile);
|
|
|
|
}
|
|
|
|
|
2015-02-14 05:38:50 +05:30
|
|
|
static void wait_for_rfkill()
|
|
|
|
{
|
|
|
|
struct epoll_event events[2];
|
2015-02-15 02:03:02 +05:30
|
|
|
cs.rfkillFd = rfkill_open(&client_config.enable_rfkill);
|
|
|
|
if (cs.rfkillFd < 0)
|
2015-02-14 05:38:50 +05:30
|
|
|
suicide("can't wait for rfkill to end if /dev/rfkill can't be opened");
|
|
|
|
int epfd = epoll_create1(0);
|
|
|
|
if (epfd < 0)
|
|
|
|
suicide("epoll_create1 failed");
|
2015-02-15 02:03:02 +05:30
|
|
|
epoll_add(epfd, cs.rfkillFd);
|
2015-02-14 05:38:50 +05:30
|
|
|
for (;;) {
|
|
|
|
int r = epoll_wait(epfd, events, 2, -1);
|
|
|
|
if (r < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
suicide("epoll_wait failed");
|
|
|
|
}
|
|
|
|
for (int i = 0; i < r; ++i) {
|
|
|
|
int fd = events[i].data.fd;
|
2015-02-15 17:08:03 +05:30
|
|
|
if (fd != cs.rfkillFd)
|
2015-02-14 05:38:50 +05:30
|
|
|
suicide("epoll_wait: unknown fd");
|
2015-02-15 17:08:03 +05:30
|
|
|
if (events[i].events & EPOLLIN) {
|
|
|
|
int rfk = rfkill_get(&cs, 0, 0);
|
|
|
|
if (rfk == RFK_DISABLED) {
|
|
|
|
switch (perform_ifup()) {
|
|
|
|
case 1: case 0: goto rfkill_gone;
|
|
|
|
case -3:
|
|
|
|
log_line("rfkill: radio immediately blocked again; spurious?");
|
|
|
|
break;
|
|
|
|
default: suicide("failed to set the interface to up state");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-14 05:38:50 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
rfkill_gone:
|
|
|
|
close(epfd);
|
2015-02-15 02:03:02 +05:30
|
|
|
// We always close because ifchd and sockd shouldn't keep
|
|
|
|
// an rfkill fd open.
|
|
|
|
close(cs.rfkillFd);
|
|
|
|
cs.rfkillFd = -1;
|
2015-02-14 05:38:50 +05:30
|
|
|
}
|
|
|
|
|
2014-04-04 13:42:25 +05:30
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2014-04-15 00:36:31 +05:30
|
|
|
parse_cmdline(argc, argv);
|
2010-11-13 01:03:17 +05:30
|
|
|
|
2014-03-19 10:12:32 +05:30
|
|
|
nk_random_u32_init(&cs.rnd32_state);
|
|
|
|
|
2014-03-10 10:22:56 +05:30
|
|
|
if (getuid())
|
2014-03-31 02:51:27 +05:30
|
|
|
suicide("I need to be started as root.");
|
2014-03-10 10:22:56 +05:30
|
|
|
if (!strncmp(chroot_dir, "", sizeof chroot_dir))
|
2014-03-31 02:51:27 +05:30
|
|
|
suicide("No chroot path is specified. Refusing to run.");
|
2014-03-19 13:42:24 +05:30
|
|
|
fail_if_state_dir_dne();
|
2014-03-10 10:22:56 +05:30
|
|
|
|
2014-03-31 02:51:27 +05:30
|
|
|
if (nl_getifdata() < 0)
|
|
|
|
suicide("failed to get interface MAC or index");
|
2014-03-17 15:26:30 +05:30
|
|
|
|
2014-03-19 10:12:32 +05:30
|
|
|
get_clientid(&cs, &client_config);
|
|
|
|
|
2014-03-17 15:26:30 +05:30
|
|
|
switch (perform_ifup()) {
|
2014-04-04 13:42:25 +05:30
|
|
|
case 1: cs.ifsPrevState = IFS_UP;
|
|
|
|
case 0: break;
|
2015-02-14 05:38:50 +05:30
|
|
|
case -3: wait_for_rfkill(); break;
|
2014-04-04 13:42:25 +05:30
|
|
|
default: suicide("failed to set the interface to up state");
|
2014-03-13 00:37:37 +05:30
|
|
|
}
|
|
|
|
|
2014-04-16 00:32:20 +05:30
|
|
|
if (setpgid(0, 0) < 0) {
|
|
|
|
// EPERM is returned if we are already a process group leader.
|
|
|
|
if (errno != EPERM)
|
|
|
|
suicide("setpgid failed: %s", strerror(errno));
|
|
|
|
}
|
2014-04-07 07:37:12 +05:30
|
|
|
|
2014-04-04 13:42:25 +05:30
|
|
|
spawn_ifch();
|
|
|
|
spawn_sockd();
|
|
|
|
ndhc_main();
|
2014-03-10 10:22:56 +05:30
|
|
|
exit(EXIT_SUCCESS);
|
2010-11-12 14:32:18 +05:30
|
|
|
}
|
2014-04-04 13:42:25 +05:30
|
|
|
|