2022-02-06 20:05:29 -05:00
|
|
|
// Copyright 2004-2022 Nicholas J. Kain <njkain at gmail dot com>
|
|
|
|
// SPDX-License-Identifier: MIT
|
2010-11-12 04:02:18 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/time.h>
|
2014-03-10 00:52:56 -04:00
|
|
|
#include <sys/stat.h>
|
2010-11-12 04:02:18 -05:00
|
|
|
#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 05:36:47 -04:00
|
|
|
#include <ctype.h>
|
2011-06-29 23:47:31 -04:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2020-10-20 05:54:00 -04:00
|
|
|
#include <poll.h>
|
2014-03-10 14:44:12 -04:00
|
|
|
#include <sys/prctl.h>
|
2010-11-12 04:02:18 -05:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
2014-03-19 01:13:11 -04:00
|
|
|
#include <limits.h>
|
2014-03-30 17:02:48 -04:00
|
|
|
#include "nk/log.h"
|
2020-10-20 06:44:31 -04:00
|
|
|
#include "nk/privs.h"
|
2014-03-30 17:02:48 -04:00
|
|
|
#include "nk/io.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
|
2014-03-12 16:51:10 -04:00
|
|
|
#include "ndhc.h"
|
2010-11-12 18:44:49 -05:00
|
|
|
#include "ndhc-defines.h"
|
2014-04-14 15:06:31 -04:00
|
|
|
#include "cfg.h"
|
2011-06-29 23:47:31 -04:00
|
|
|
#include "state.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
#include "options.h"
|
2011-07-02 03:51:44 -04:00
|
|
|
#include "dhcp.h"
|
2010-12-24 09:32:58 -05:00
|
|
|
#include "sys.h"
|
2010-12-24 10:12:41 -05:00
|
|
|
#include "ifchange.h"
|
2010-12-24 09:58:47 -05:00
|
|
|
#include "arp.h"
|
2011-07-03 17:30:55 -04:00
|
|
|
#include "nl.h"
|
2011-03-29 14:37:45 -04:00
|
|
|
#include "netlink.h"
|
2011-04-19 16:37:43 -04:00
|
|
|
#include "leasefile.h"
|
2014-03-17 05:56:30 -04:00
|
|
|
#include "ifset.h"
|
2014-03-12 16:13:47 -04:00
|
|
|
#include "ifchd.h"
|
2014-03-19 00:42:32 -04:00
|
|
|
#include "duiaid.h"
|
2014-04-04 04:12:25 -04:00
|
|
|
#include "sockd.h"
|
2015-02-13 16:25:36 -05:00
|
|
|
#include "rfkill.h"
|
2022-02-24 00:52:26 -05:00
|
|
|
#include "scriptd.h"
|
2010-11-12 04:02:18 -05:00
|
|
|
|
2010-12-24 07:00:42 -05:00
|
|
|
struct client_state_t cs = {
|
2017-01-19 05:05:35 -05:00
|
|
|
.program_init = true,
|
2010-12-24 07:00:42 -05:00
|
|
|
.listenFd = -1,
|
|
|
|
.arpFd = -1,
|
2011-03-29 15:34:00 -04:00
|
|
|
.nlFd = -1,
|
2018-02-09 02:39:46 -05:00
|
|
|
.nlPortId = 0,
|
2015-02-13 16:25:36 -05:00
|
|
|
.rfkillFd = -1,
|
2015-02-18 05:31:13 -05:00
|
|
|
.dhcp_wake_ts = -1,
|
2011-03-30 23:17:27 -04:00
|
|
|
.routerArp = "\0\0\0\0\0\0",
|
2011-07-11 13:24:59 -04:00
|
|
|
.serverArp = "\0\0\0\0\0\0",
|
2010-12-24 07:00:42 -05:00
|
|
|
};
|
2010-11-12 04:02:18 -05:00
|
|
|
|
|
|
|
struct client_config_t client_config = {
|
2010-11-12 14:33:17 -05:00
|
|
|
.interface = "eth0",
|
2011-03-30 23:17:27 -04:00
|
|
|
.arp = "\0\0\0\0\0\0",
|
2022-02-12 16:31:39 -05:00
|
|
|
.s6_notify_fd = 3,
|
2014-03-18 03:13:51 -04:00
|
|
|
.clientid_len = 0,
|
2014-03-19 01:13:11 -04:00
|
|
|
.metric = 0,
|
2010-11-12 04:02:18 -05:00
|
|
|
};
|
|
|
|
|
2020-10-20 04:23:55 -04:00
|
|
|
static volatile sig_atomic_t l_signal_exit;
|
|
|
|
static volatile sig_atomic_t l_signal_renew;
|
|
|
|
static volatile sig_atomic_t l_signal_release;
|
|
|
|
// Intended to be called in a loop until SIGNAL_NONE is returned.
|
|
|
|
int signals_flagged(void)
|
|
|
|
{
|
|
|
|
if (l_signal_exit) {
|
|
|
|
l_signal_exit = 0;
|
|
|
|
return SIGNAL_EXIT;
|
|
|
|
}
|
|
|
|
if (l_signal_renew) {
|
|
|
|
l_signal_renew = 0;
|
|
|
|
return SIGNAL_RENEW;
|
|
|
|
}
|
|
|
|
if (l_signal_release) {
|
|
|
|
l_signal_release = 0;
|
|
|
|
return SIGNAL_RELEASE;
|
|
|
|
}
|
|
|
|
return SIGNAL_NONE;
|
|
|
|
}
|
|
|
|
|
2021-04-25 05:26:19 -04:00
|
|
|
bool carrier_isup(void) { return cs.carrier_up; }
|
|
|
|
|
2022-01-11 22:35:19 -05:00
|
|
|
void set_client_addr(const char *v) { cs.clientAddr = inet_addr(v); }
|
2014-04-14 15:06:31 -04:00
|
|
|
|
|
|
|
void print_version(void)
|
|
|
|
{
|
|
|
|
printf("ndhc %s, dhcp client.\n", NDHC_VERSION);
|
2022-02-06 20:05:29 -05:00
|
|
|
printf("Copyright 2004-2022 Nicholas J. Kain\n\n"
|
|
|
|
"Permission is hereby granted, free of charge, to any person obtaining\n"
|
|
|
|
"a copy of this software and associated documentation files (the\n"
|
|
|
|
"\"Software\"), to deal in the Software without restriction, including\n"
|
|
|
|
"without limitation the rights to use, copy, modify, merge, publish,\n"
|
|
|
|
"distribute, sublicense, and/or sell copies of the Software, and to\n"
|
|
|
|
"permit persons to whom the Software is furnished to do so, subject to\n"
|
|
|
|
"the following conditions:\n\n"
|
|
|
|
"The above copyright notice and this permission notice shall be\n"
|
|
|
|
"included in all copies or substantial portions of the Software.\n\n"
|
|
|
|
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
|
|
|
|
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
|
|
|
|
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n"
|
|
|
|
"NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n"
|
|
|
|
"LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n"
|
|
|
|
"OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n"
|
|
|
|
"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
|
|
|
);
|
2014-04-14 15:06:31 -04:00
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void show_usage(void)
|
2010-11-12 04:02:18 -05:00
|
|
|
{
|
2010-11-12 14:33:17 -05:00
|
|
|
printf(
|
2022-02-06 20:05:29 -05:00
|
|
|
"ndhc " NDHC_VERSION ", dhcp client.\n"
|
|
|
|
"Copyright 2004-2022 Nicholas J. Kain\n"
|
2010-11-12 04:02:18 -05:00
|
|
|
"Usage: ndhc [OPTIONS]\n\n"
|
2014-04-14 15:06:31 -04:00
|
|
|
" -c, --config=FILE Path to ndhc configuration file\n"
|
|
|
|
" -I, --clientid=CLIENTID Client identifier\n"
|
2011-07-05 11:14:35 -04:00
|
|
|
" -h, --hostname=HOSTNAME Client hostname\n"
|
2011-07-05 11:18:28 -04:00
|
|
|
" -V, --vendorid=VENDORID Client vendor identification string\n"
|
2010-11-12 04:02:18 -05:00
|
|
|
" -i, --interface=INTERFACE Interface to use (default: eth0)\n"
|
|
|
|
" -n, --now Exit with failure if lease cannot be\n"
|
|
|
|
" immediately negotiated.\n"
|
|
|
|
" -r, --request=IP IP address to request (default: none)\n"
|
2020-10-20 06:44:31 -04:00
|
|
|
" -u, --user=USER ndhc runs as this user\n"
|
|
|
|
" -U, --ifch-user=USER ndhc-ifch runs as this user\n"
|
|
|
|
" -D, --sockd-user=USER ndhc-sockd runs as this user\n"
|
2011-07-05 11:18:28 -04:00
|
|
|
" -C, --chroot=DIR Chroot to this directory\n"
|
2014-03-19 00:46:54 -04:00
|
|
|
" -s, --state-dir=DIR State storage dir (default: /etc/ndhc)\n"
|
2011-07-05 13:03:55 -04:00
|
|
|
" -d, --relentless-defense Never back off in defending IP against\n"
|
|
|
|
" conflicting hosts (servers only)\n"
|
2013-02-09 00:30:19 -05:00
|
|
|
" -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 01:13:11 -04:00
|
|
|
" -t, --gw-metric Route metric for default gw (default: 0)\n"
|
2014-03-10 00:52:56 -04:00
|
|
|
" -R, --resolve-conf=FILE Path to resolv.conf or equivalent\n"
|
2014-04-14 15:06:31 -04:00
|
|
|
" -H, --dhcp-set-hostname Allow DHCP to set machine hostname\n"
|
2010-11-12 04:02:18 -05:00
|
|
|
" -v, --version Display version\n"
|
2010-11-12 14:33:17 -05:00
|
|
|
);
|
|
|
|
exit(EXIT_SUCCESS);
|
2010-11-12 04:02:18 -05:00
|
|
|
}
|
|
|
|
|
2020-10-20 01:12:59 -04:00
|
|
|
static void signal_handler(int signo)
|
|
|
|
{
|
2022-02-24 05:06:12 -05:00
|
|
|
int serrno = errno;
|
2020-10-20 01:12:59 -04:00
|
|
|
switch (signo) {
|
|
|
|
case SIGCHLD: {
|
2020-10-20 07:53:23 -04:00
|
|
|
static const char errstr[] = "ndhc-master: Subprocess terminated unexpectedly. Exiting.\n";
|
2020-10-20 01:12:59 -04:00
|
|
|
safe_write(STDOUT_FILENO, errstr, sizeof errstr - 1);
|
2020-10-21 09:49:22 -04:00
|
|
|
_exit(EXIT_FAILURE);
|
2020-10-20 01:12:59 -04:00
|
|
|
}
|
2020-10-20 04:23:55 -04:00
|
|
|
case SIGINT:
|
|
|
|
case SIGTERM: l_signal_exit = 1; break;
|
|
|
|
case SIGUSR1: l_signal_renew = 1; break;
|
|
|
|
case SIGUSR2: l_signal_release = 1; break;
|
|
|
|
default: break;
|
2020-10-20 01:12:59 -04:00
|
|
|
}
|
2022-02-24 05:06:12 -05:00
|
|
|
errno = serrno;
|
2020-10-20 01:12:59 -04:00
|
|
|
}
|
|
|
|
|
2020-10-20 04:23:55 -04:00
|
|
|
void signal_exit(int status)
|
|
|
|
{
|
|
|
|
log_line("Received terminal signal. Exiting.");
|
|
|
|
exit(status);
|
|
|
|
}
|
|
|
|
|
2014-03-12 16:51:10 -04:00
|
|
|
static void setup_signals_ndhc(void)
|
|
|
|
{
|
2020-10-20 04:23:55 -04:00
|
|
|
static const int ss[] = {
|
|
|
|
SIGCHLD, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGKILL
|
|
|
|
};
|
2014-03-12 16:51:10 -04:00
|
|
|
sigset_t mask;
|
2020-10-20 01:12:59 -04:00
|
|
|
|
2020-10-20 04:23:55 -04:00
|
|
|
if (sigprocmask(0, 0, &mask) < 0)
|
2020-10-20 01:12:59 -04:00
|
|
|
suicide("sigprocmask failed");
|
2020-10-20 04:23:55 -04:00
|
|
|
for (int i = 0; ss[i] != SIGKILL; ++i)
|
|
|
|
if (sigdelset(&mask, ss[i]))
|
|
|
|
suicide("sigdelset failed");
|
|
|
|
if (sigaddset(&mask, SIGPIPE))
|
|
|
|
suicide("sigaddset failed");
|
|
|
|
if (sigprocmask(SIG_SETMASK, &mask, (sigset_t *)0) < 0)
|
|
|
|
suicide("sigprocmask failed");
|
|
|
|
|
2020-10-20 01:12:59 -04:00
|
|
|
struct sigaction sa = {
|
|
|
|
.sa_handler = signal_handler,
|
|
|
|
.sa_flags = SA_RESTART,
|
|
|
|
};
|
2020-10-20 04:23:55 -04:00
|
|
|
if (sigemptyset(&sa.sa_mask))
|
|
|
|
suicide("sigemptyset failed");
|
|
|
|
for (int i = 0; ss[i] != SIGKILL; ++i)
|
|
|
|
if (sigaction(ss[i], &sa, NULL))
|
|
|
|
suicide("sigaction failed");
|
2010-12-01 12:35:13 -05:00
|
|
|
}
|
|
|
|
|
2022-01-11 22:35:19 -05:00
|
|
|
static int is_string_hwaddr(const char *str, size_t slen)
|
2011-07-03 05:36:47 -04:00
|
|
|
{
|
2011-07-03 05:45:05 -04:00
|
|
|
if (slen == 17 && str[2] == ':' && str[5] == ':' && str[8] == ':' &&
|
|
|
|
str[11] == ':' && str[14] == ':' &&
|
2011-07-03 05:36:47 -04:00
|
|
|
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 05:45:05 -04:00
|
|
|
isxdigit(str[13]) && isxdigit(str[15]) && isxdigit(str[16])
|
|
|
|
)
|
2011-07-03 05:36:47 -04:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-11 22:35:19 -05:00
|
|
|
int get_clientid_string(const char *str, size_t slen)
|
2011-07-03 05:45:05 -04:00
|
|
|
{
|
2014-03-18 03:13:51 -04:00
|
|
|
if (!slen)
|
|
|
|
return -1;
|
|
|
|
if (!is_string_hwaddr(str, slen)) {
|
|
|
|
client_config.clientid[0] = 0;
|
2014-04-15 14:55:50 -04:00
|
|
|
memcpy(client_config.clientid + 1, str,
|
2014-03-18 03:13:51 -04:00
|
|
|
min_size_t(slen, sizeof client_config.clientid - 1));
|
|
|
|
client_config.clientid_len = slen + 1;
|
2011-07-03 05:45:05 -04:00
|
|
|
return 0;
|
2014-03-18 03:13:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t mac[6];
|
|
|
|
for (size_t i = 0; i < sizeof mac; ++i)
|
2018-10-26 07:17:39 -04:00
|
|
|
mac[i] = strtol(str+i*3, (char **)0, 16);
|
2014-03-18 03:13:51 -04:00
|
|
|
client_config.clientid[0] = 1; // Ethernet MAC type
|
2014-04-15 14:55:50 -04:00
|
|
|
memcpy(client_config.clientid + 1, mac,
|
|
|
|
min_size_t(sizeof mac, sizeof client_config.clientid - 1));
|
2014-03-18 03:13:51 -04:00
|
|
|
client_config.clientid_len = 7;
|
2011-07-03 05:45:05 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-03-19 04:12:24 -04:00
|
|
|
static void fail_if_state_dir_dne(void)
|
|
|
|
{
|
2014-03-30 17:21:27 -04:00
|
|
|
if (strlen(state_dir) == 0)
|
|
|
|
suicide("state_dir path is empty; it must be specified");
|
2014-03-19 04:12:24 -04:00
|
|
|
struct stat st;
|
2014-03-30 17:21:27 -04:00
|
|
|
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 04:12:24 -04:00
|
|
|
}
|
|
|
|
|
2014-03-10 00:52:56 -04:00
|
|
|
static void do_ndhc_work(void)
|
2010-12-01 12:35:13 -05:00
|
|
|
{
|
2015-02-18 05:31:13 -05:00
|
|
|
static bool rfkill_set; // Is the rfkill switch set?
|
2017-01-12 06:05:00 -05:00
|
|
|
static bool rfkill_nl_carrier_wentup; // iface carrier changed to up during rfkill
|
2015-02-15 06:38:03 -05:00
|
|
|
struct dhcpmsg dhcp_packet;
|
2011-07-11 11:31:27 -04:00
|
|
|
long long nowts;
|
2015-05-27 12:23:16 -04:00
|
|
|
int timeout = 0;
|
|
|
|
bool had_event;
|
2010-11-12 14:33:17 -05:00
|
|
|
|
2014-03-12 16:51:10 -04:00
|
|
|
setup_signals_ndhc();
|
2015-02-13 19:08:50 -05:00
|
|
|
start_dhcp_listen(&cs);
|
2010-11-12 14:33:17 -05:00
|
|
|
|
2020-10-20 05:54:00 -04:00
|
|
|
struct pollfd pfds[6] = {0};
|
|
|
|
pfds[0].fd = cs.nlFd;
|
|
|
|
pfds[0].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
pfds[1].fd = ifchStream[0];
|
|
|
|
pfds[1].events = POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
pfds[2].fd = sockdStream[0];
|
|
|
|
pfds[2].events = POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
pfds[3].fd = cs.rfkillFd;
|
|
|
|
pfds[3].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
// These can change on the fly.
|
|
|
|
pfds[4].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
pfds[5].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;
|
|
|
|
|
2010-12-01 13:11:09 -05:00
|
|
|
for (;;) {
|
2020-10-20 05:54:00 -04:00
|
|
|
pfds[4].fd = cs.arpFd;
|
|
|
|
pfds[5].fd = cs.listenFd;
|
2015-05-27 12:23:16 -04:00
|
|
|
had_event = false;
|
2020-10-20 05:54:00 -04:00
|
|
|
if (poll(pfds, 6, timeout) < 0) {
|
2020-11-06 18:32:38 -05:00
|
|
|
if (errno != EINTR) suicide("poll failed");
|
2010-12-01 13:11:09 -05:00
|
|
|
}
|
2020-10-20 05:54:00 -04:00
|
|
|
|
2015-02-18 11:02:13 -05:00
|
|
|
bool sev_dhcp = false;
|
2015-02-15 06:48:49 -05:00
|
|
|
uint32_t dhcp_srcaddr;
|
|
|
|
uint8_t dhcp_msgtype;
|
2015-02-18 11:02:13 -05:00
|
|
|
bool sev_arp = false;
|
2015-02-15 06:48:49 -05:00
|
|
|
int sev_nl = IFS_NONE;
|
|
|
|
int sev_rfk = RFK_NONE;
|
2015-02-18 05:31:13 -05:00
|
|
|
bool force_fingerprint = false;
|
2020-10-20 05:54:00 -04:00
|
|
|
if (pfds[0].revents & POLLIN) {
|
|
|
|
had_event = true;
|
|
|
|
sev_nl = nl_event_get(&cs);
|
2022-01-11 22:16:44 -05:00
|
|
|
if (!cs.carrier_up)
|
|
|
|
cs.carrier_up = (sev_nl == IFS_UP);
|
2020-10-20 05:54:00 -04:00
|
|
|
}
|
|
|
|
if (pfds[0].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
suicide("nlfd closed unexpectedly");
|
|
|
|
}
|
|
|
|
if (pfds[1].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (pfds[2].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (pfds[3].revents & POLLIN) {
|
2015-05-27 12:23:16 -04:00
|
|
|
had_event = true;
|
2020-10-20 05:54:00 -04:00
|
|
|
sev_rfk = rfkill_get(&cs, 1, client_config.rfkillIdx);
|
|
|
|
}
|
|
|
|
if (pfds[3].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
suicide("rfkillfd closed unexpectedly");
|
|
|
|
}
|
|
|
|
if (pfds[4].revents & POLLIN) {
|
|
|
|
had_event = true;
|
|
|
|
// Make sure the fd is still the same.
|
|
|
|
if (pfds[4].fd == cs.arpFd)
|
|
|
|
sev_arp = arp_packet_get(&cs);
|
|
|
|
}
|
|
|
|
if (pfds[4].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
suicide("arpfd closed unexpectedly");
|
|
|
|
}
|
|
|
|
if (pfds[5].revents & POLLIN) {
|
|
|
|
had_event = true;
|
|
|
|
// Make sure the fd is still the same.
|
|
|
|
if (pfds[5].fd == cs.listenFd)
|
2015-02-15 06:48:49 -05:00
|
|
|
sev_dhcp = dhcp_packet_get(&cs, &dhcp_packet, &dhcp_msgtype,
|
|
|
|
&dhcp_srcaddr);
|
2020-10-20 05:54:00 -04:00
|
|
|
}
|
|
|
|
if (pfds[5].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
suicide("listenfd closed unexpectedly");
|
2017-01-12 06:05:00 -05:00
|
|
|
}
|
2015-02-18 05:31:13 -05:00
|
|
|
|
2017-01-12 06:05:00 -05:00
|
|
|
if (sev_rfk == RFK_ENABLED) {
|
|
|
|
rfkill_set = 1;
|
|
|
|
rfkill_nl_carrier_wentup = false;
|
|
|
|
log_line("rfkill: radio now blocked");
|
|
|
|
} else if (sev_rfk == RFK_DISABLED) {
|
|
|
|
rfkill_set = 0;
|
|
|
|
log_line("rfkill: radio now unblocked");
|
2022-03-07 13:59:52 -05:00
|
|
|
cs.carrier_up = ifchange_carrier_isup();
|
2017-01-12 06:05:00 -05:00
|
|
|
if (rfkill_nl_carrier_wentup && carrier_isup()) {
|
|
|
|
// We might have changed networks while the radio was down.
|
|
|
|
force_fingerprint = true;
|
2015-02-18 05:31:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-12 06:05:00 -05:00
|
|
|
if (sev_nl != IFS_NONE && nl_event_carrier_wentup(sev_nl)) {
|
|
|
|
if (!rfkill_set)
|
|
|
|
force_fingerprint = true;
|
|
|
|
else
|
|
|
|
rfkill_nl_carrier_wentup = true;
|
2015-02-18 05:31:13 -05:00
|
|
|
}
|
|
|
|
|
2017-01-12 06:05:00 -05:00
|
|
|
if (rfkill_set || !carrier_isup()) {
|
2015-02-18 05:31:13 -05:00
|
|
|
// We can't do anything while the iface is disabled, anyway.
|
2017-01-12 06:05:00 -05:00
|
|
|
// Suspend might cause link state change notifications to be
|
|
|
|
// missed, so we use a non-infinite timeout.
|
2018-02-09 02:39:46 -05:00
|
|
|
timeout = 2000 + (int)(nk_random_u32(&cs.rnd_state) % 3000);
|
2015-02-18 05:31:13 -05:00
|
|
|
continue;
|
2010-11-12 14:33:17 -05:00
|
|
|
}
|
2010-12-02 00:15:03 -05:00
|
|
|
|
2020-10-20 05:54:00 -04:00
|
|
|
// These two can change on the fly; make sure the event is current.
|
|
|
|
if (pfds[4].fd != cs.arpFd) sev_arp = false;
|
|
|
|
if (pfds[5].fd != cs.listenFd) sev_dhcp = false;
|
|
|
|
|
2015-02-18 05:31:13 -05:00
|
|
|
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,
|
2020-10-20 04:23:55 -04:00
|
|
|
arp_wake_ts <= nowts);
|
2015-02-18 05:31:13 -05:00
|
|
|
|
|
|
|
if (dhcp_ok == COR_ERROR) {
|
2018-02-09 02:39:46 -05:00
|
|
|
timeout = 2000 + (int)(nk_random_u32(&cs.rnd_state) % 3000);
|
2015-02-18 05:31:13 -05:00
|
|
|
continue;
|
2015-02-15 06:48:49 -05:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:23:16 -04:00
|
|
|
int prev_timeout = timeout;
|
2015-05-27 12:58:42 -04:00
|
|
|
long long tt;
|
2015-05-27 12:23:16 -04:00
|
|
|
|
2015-02-18 05:31:13 -05:00
|
|
|
arp_wake_ts = arp_get_wake_ts();
|
2015-05-27 12:23:16 -04:00
|
|
|
if (arp_wake_ts < 0 && cs.dhcp_wake_ts < 0) {
|
|
|
|
timeout = -1;
|
|
|
|
continue;
|
|
|
|
} else if (arp_wake_ts < 0) {
|
2015-05-27 12:58:42 -04:00
|
|
|
tt = cs.dhcp_wake_ts - nowts;
|
2015-05-27 12:23:16 -04:00
|
|
|
} else if (cs.dhcp_wake_ts < 0) {
|
2015-05-27 12:58:42 -04:00
|
|
|
tt = arp_wake_ts - nowts;
|
2015-02-18 05:31:13 -05:00
|
|
|
} else {
|
2015-05-27 12:58:42 -04:00
|
|
|
tt = (arp_wake_ts < cs.dhcp_wake_ts ?
|
|
|
|
arp_wake_ts : cs.dhcp_wake_ts) - nowts;
|
2010-12-23 11:34:57 -05:00
|
|
|
}
|
2015-05-27 12:58:42 -04:00
|
|
|
if (tt > INT_MAX) tt = INT_MAX;
|
2015-05-27 15:00:02 -04:00
|
|
|
if (tt < INT_MIN) tt = INT_MIN;
|
2015-05-27 12:58:42 -04:00
|
|
|
timeout = tt;
|
2015-05-27 12:23:16 -04:00
|
|
|
if (timeout < 0)
|
|
|
|
timeout = 0;
|
|
|
|
|
|
|
|
// Failsafe to prevent busy-spin.
|
2015-05-27 12:35:16 -04:00
|
|
|
if (timeout == 0 && prev_timeout == 0 && !had_event)
|
2015-05-27 12:29:46 -04:00
|
|
|
timeout = 10000;
|
2010-11-12 14:33:17 -05:00
|
|
|
}
|
2010-11-12 04:02:18 -05:00
|
|
|
}
|
|
|
|
|
2014-03-30 17:02:48 -04:00
|
|
|
char state_dir[PATH_MAX] = "/etc/ndhc";
|
|
|
|
char chroot_dir[PATH_MAX] = "";
|
|
|
|
char resolv_conf_d[PATH_MAX] = "";
|
2022-02-24 00:52:26 -05:00
|
|
|
char script_file[PATH_MAX] = "";
|
2014-04-14 15:06:31 -04:00
|
|
|
uid_t ndhc_uid = 0;
|
|
|
|
gid_t ndhc_gid = 0;
|
2014-04-07 03:44:02 -04:00
|
|
|
int ifchSock[2];
|
2014-04-15 23:19:24 -04:00
|
|
|
int ifchStream[2];
|
2022-02-24 00:52:26 -05:00
|
|
|
int sockdSock[2];
|
2014-04-15 23:19:24 -04:00
|
|
|
int sockdStream[2];
|
2022-02-24 00:52:26 -05:00
|
|
|
int scriptdSock[2];
|
|
|
|
int scriptdStream[2];
|
2014-04-04 04:12:25 -04:00
|
|
|
|
2014-04-15 18:01:01 -04:00
|
|
|
static void create_ifch_ipc_sockets(void) {
|
2014-04-07 03:44:02 -04:00
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, ifchSock) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/ifch socket: %s", strerror(errno));
|
2014-04-15 23:19:24 -04:00
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ifchStream) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/ifch socket: %s", strerror(errno));
|
2014-03-10 00:52:56 -04:00
|
|
|
}
|
|
|
|
|
2014-04-15 18:01:01 -04:00
|
|
|
static void create_sockd_ipc_sockets(void) {
|
2014-04-05 05:25:56 -04:00
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockdSock) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/sockd socket: %s", strerror(errno));
|
2014-04-15 23:19:24 -04:00
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockdStream) < 0)
|
2022-02-24 00:52:26 -05:00
|
|
|
suicide("FATAL - can't create ndhc/sockd socket: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_scriptd_ipc_sockets(void) {
|
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, scriptdSock) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/scriptd socket: %s", strerror(errno));
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, scriptdStream) < 0)
|
|
|
|
suicide("FATAL - can't create ndhc/scriptd socket: %s", strerror(errno));
|
2014-04-04 04:12:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void spawn_ifch(void)
|
|
|
|
{
|
2014-04-15 18:01:01 -04:00
|
|
|
create_ifch_ipc_sockets();
|
2014-04-04 04:12:25 -04:00
|
|
|
pid_t ifch_pid = fork();
|
|
|
|
if (ifch_pid == 0) {
|
2014-04-07 03:44:02 -04:00
|
|
|
close(ifchSock[0]);
|
2014-04-15 23:19:24 -04:00
|
|
|
close(ifchStream[0]);
|
2014-04-04 04:12:25 -04:00
|
|
|
// Don't share the RNG state with the master process.
|
2017-08-24 02:36:31 -04:00
|
|
|
nk_random_init(&cs.rnd_state);
|
2014-04-04 04:12:25 -04:00
|
|
|
ifch_main();
|
|
|
|
} else if (ifch_pid > 0) {
|
2014-04-07 03:44:02 -04:00
|
|
|
close(ifchSock[1]);
|
2014-04-15 23:19:24 -04:00
|
|
|
close(ifchStream[1]);
|
2014-04-04 04:12:25 -04:00
|
|
|
} else
|
|
|
|
suicide("failed to fork ndhc-ifch: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spawn_sockd(void)
|
|
|
|
{
|
2014-04-15 18:01:01 -04:00
|
|
|
create_sockd_ipc_sockets();
|
2014-04-04 04:12:25 -04:00
|
|
|
pid_t sockd_pid = fork();
|
|
|
|
if (sockd_pid == 0) {
|
2014-04-05 05:25:56 -04:00
|
|
|
close(sockdSock[0]);
|
2014-04-15 23:19:24 -04:00
|
|
|
close(sockdStream[0]);
|
2014-04-04 04:12:25 -04:00
|
|
|
// Don't share the RNG state with the master process.
|
2017-08-24 02:36:31 -04:00
|
|
|
nk_random_init(&cs.rnd_state);
|
2014-04-04 04:12:25 -04:00
|
|
|
sockd_main();
|
|
|
|
} else if (sockd_pid > 0) {
|
2014-04-05 05:25:56 -04:00
|
|
|
close(sockdSock[1]);
|
2014-04-15 23:19:24 -04:00
|
|
|
close(sockdStream[1]);
|
2014-04-04 04:12:25 -04:00
|
|
|
} else
|
|
|
|
suicide("failed to fork ndhc-sockd: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
2022-02-24 00:52:26 -05:00
|
|
|
static void spawn_scriptd(void)
|
|
|
|
{
|
|
|
|
valid_script_file = access(script_file, R_OK | X_OK) == 0;
|
|
|
|
if (!valid_script_file) return;
|
|
|
|
|
|
|
|
log_line("Found script file: '%s'", script_file);
|
|
|
|
|
|
|
|
create_scriptd_ipc_sockets();
|
|
|
|
pid_t scriptd_pid = fork();
|
|
|
|
if (scriptd_pid == 0) {
|
|
|
|
close(scriptdSock[0]);
|
|
|
|
close(scriptdStream[0]);
|
|
|
|
// Don't share the RNG state with the master process.
|
|
|
|
nk_random_init(&cs.rnd_state);
|
|
|
|
scriptd_main();
|
|
|
|
} else if (scriptd_pid > 0) {
|
|
|
|
close(scriptdSock[1]);
|
|
|
|
close(scriptdStream[1]);
|
|
|
|
} else
|
|
|
|
suicide("failed to fork ndhc-scriptd: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
|
2014-03-10 23:00:57 -04:00
|
|
|
static void ndhc_main(void) {
|
2014-03-10 14:44:12 -04:00
|
|
|
prctl(PR_SET_NAME, "ndhc: master");
|
2014-03-10 00:52:56 -04:00
|
|
|
log_line("ndhc client " NDHC_VERSION " started on interface [%s].",
|
|
|
|
client_config.interface);
|
|
|
|
|
2014-03-30 17:21:27 -04:00
|
|
|
if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &cs.nlPortId)) < 0)
|
|
|
|
suicide("%s: failed to open netlink socket", __func__);
|
2014-03-17 05:56:30 -04:00
|
|
|
|
2015-02-13 16:25:36 -05:00
|
|
|
cs.rfkillFd = rfkill_open(&client_config.enable_rfkill);
|
|
|
|
|
2014-03-10 00:52:56 -04:00
|
|
|
open_leasefile();
|
|
|
|
|
2014-03-30 17:02:48 -04:00
|
|
|
nk_set_chroot(chroot_dir);
|
2014-03-10 00:52:56 -04:00
|
|
|
memset(chroot_dir, '\0', sizeof chroot_dir);
|
2018-10-26 07:17:39 -04:00
|
|
|
nk_set_uidgid(ndhc_uid, ndhc_gid, (const unsigned char *)0, 0);
|
2014-03-10 00:52:56 -04:00
|
|
|
|
2021-04-25 05:26:19 -04:00
|
|
|
cs.carrier_up = ifchange_carrier_isup();
|
2017-01-12 06:05:00 -05:00
|
|
|
if (!carrier_isup()) {
|
2015-02-14 20:47:14 -05:00
|
|
|
if (ifchange_deconfig(&cs) < 0)
|
|
|
|
suicide("%s: can't deconfigure interface settings", __func__);
|
|
|
|
}
|
2014-03-10 00:52:56 -04:00
|
|
|
|
|
|
|
do_ndhc_work();
|
|
|
|
}
|
|
|
|
|
2015-02-13 19:08:50 -05:00
|
|
|
static void wait_for_rfkill()
|
|
|
|
{
|
2015-02-14 15:33:02 -05:00
|
|
|
cs.rfkillFd = rfkill_open(&client_config.enable_rfkill);
|
|
|
|
if (cs.rfkillFd < 0)
|
2015-02-13 19:08:50 -05:00
|
|
|
suicide("can't wait for rfkill to end if /dev/rfkill can't be opened");
|
2020-10-20 05:54:00 -04:00
|
|
|
|
|
|
|
struct pollfd pfds[1] = {0};
|
|
|
|
pfds[0].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;
|
2015-02-13 19:08:50 -05:00
|
|
|
for (;;) {
|
2020-10-20 05:54:00 -04:00
|
|
|
pfds[0].fd = cs.rfkillFd;
|
|
|
|
if (poll(pfds, 1, -1) < 0) {
|
2020-11-06 18:32:38 -05:00
|
|
|
if (errno != EINTR) suicide("poll failed");
|
2015-02-13 19:08:50 -05:00
|
|
|
}
|
2020-10-20 05:54:00 -04:00
|
|
|
if (pfds[0].revents & POLLIN) {
|
|
|
|
if (rfkill_get(&cs, 0, 0) == 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-15 06:38:03 -05:00
|
|
|
}
|
|
|
|
}
|
2015-02-13 19:08:50 -05:00
|
|
|
}
|
2020-10-20 05:54:00 -04:00
|
|
|
if (pfds[0].revents & (POLLHUP|POLLERR|POLLRDHUP)) {
|
|
|
|
suicide("rfkillFd closed unexpectedly");
|
|
|
|
}
|
2015-02-13 19:08:50 -05:00
|
|
|
}
|
|
|
|
rfkill_gone:
|
2015-02-14 15:33:02 -05:00
|
|
|
// We always close because ifchd and sockd shouldn't keep
|
|
|
|
// an rfkill fd open.
|
|
|
|
close(cs.rfkillFd);
|
|
|
|
cs.rfkillFd = -1;
|
2015-02-13 19:08:50 -05:00
|
|
|
}
|
|
|
|
|
2014-04-04 04:12:25 -04:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2014-04-14 15:06:31 -04:00
|
|
|
parse_cmdline(argc, argv);
|
2010-11-12 14:33:17 -05:00
|
|
|
|
2017-08-24 02:36:31 -04:00
|
|
|
nk_random_init(&cs.rnd_state);
|
2020-10-19 05:39:08 -04:00
|
|
|
cs.xid = nk_random_u32(&cs.rnd_state);
|
2014-03-19 00:42:32 -04:00
|
|
|
|
2014-03-10 00:52:56 -04:00
|
|
|
if (getuid())
|
2014-03-30 17:21:27 -04:00
|
|
|
suicide("I need to be started as root.");
|
2014-03-10 00:52:56 -04:00
|
|
|
if (!strncmp(chroot_dir, "", sizeof chroot_dir))
|
2014-03-30 17:21:27 -04:00
|
|
|
suicide("No chroot path is specified. Refusing to run.");
|
2014-03-19 04:12:24 -04:00
|
|
|
fail_if_state_dir_dne();
|
2014-03-10 00:52:56 -04:00
|
|
|
|
2014-03-30 17:21:27 -04:00
|
|
|
if (nl_getifdata() < 0)
|
|
|
|
suicide("failed to get interface MAC or index");
|
2014-03-17 05:56:30 -04:00
|
|
|
|
2014-03-19 00:42:32 -04:00
|
|
|
get_clientid(&cs, &client_config);
|
|
|
|
|
2014-03-17 05:56:30 -04:00
|
|
|
switch (perform_ifup()) {
|
2017-01-12 06:05:00 -05:00
|
|
|
case 1: case 0: break;
|
2015-02-13 19:08:50 -05:00
|
|
|
case -3: wait_for_rfkill(); break;
|
2014-04-04 04:12:25 -04:00
|
|
|
default: suicide("failed to set the interface to up state");
|
2014-03-12 15:07:37 -04:00
|
|
|
}
|
|
|
|
|
2014-04-15 15:02:20 -04:00
|
|
|
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-06 22:07:12 -04:00
|
|
|
|
2014-04-04 04:12:25 -04:00
|
|
|
spawn_ifch();
|
|
|
|
spawn_sockd();
|
2022-02-24 00:52:26 -05:00
|
|
|
spawn_scriptd();
|
2014-04-04 04:12:25 -04:00
|
|
|
ndhc_main();
|
2014-03-10 00:52:56 -04:00
|
|
|
exit(EXIT_SUCCESS);
|
2010-11-12 04:02:18 -05:00
|
|
|
}
|
2014-04-04 04:12:25 -04:00
|
|
|
|