Add dhclient(8).

This commit is contained in:
Jonas 'Sortie' Termansen 2016-07-24 22:12:22 +02:00
parent c35a856091
commit 946b10df27
16 changed files with 968 additions and 5 deletions

View File

@ -12,6 +12,7 @@ libmount \
bench \
carray \
checksum \
dhclient \
disked \
dnsconfig \
editor \

View File

@ -118,6 +118,12 @@ system_initrd=$(maybe_compressed boot/system.initrd)
ports=$(ls repository | sed 's/\.tix\.tar\.xz//')
mkdir -p boot/grub
mkdir -p boot/grub/init
echo "furthermore" > boot/grub/init/furthermore
# TODO: Possibly use a 'try' feature to not warn in case it was already unset.
echo "unset require dhclient" > boot/grub/init/network-no-dhclient
exec > boot/grub/grub.cfg
for hook in \
@ -184,6 +190,7 @@ else
fi
set enable_src=true
set enable_network_drivers=
set enable_dhclient=true
export version
export machine
@ -194,6 +201,7 @@ export default
export no_random_seed
export enable_src
export enable_network_drivers
export enable_dhclient
EOF
if [ -n "$ports" ]; then
@ -283,6 +291,12 @@ cat << EOF
multiboot /$kernel \$no_random_seed \$enable_network_drivers "\$@"
echo done
hook_kernel_post
if ! \$enable_dhclient; then
echo -n "Disabling dhclient ... "
module /boot/grub/init/furthermore --create-to /etc/init/network
module /boot/grub/init/network-no-dhclient --append-to /etc/init/network
echo done
fi
if [ \$no_random_seed != --no-random-seed ]; then
echo -n "Loading /boot/random.seed (256) ... "
module /boot/random.seed --random-seed
@ -430,6 +444,18 @@ else
}
fi
if \$enable_dhclient; then
menuentry "Disable DHCP client" {
enable_dhclient=false
configfile /boot/grub/advanced.cfg
}
else
menuentry "Enable DHCP client" {
enable_dhclient=true
configfile /boot/grub/advanced.cfg
}
fi
menuentry "Select binary packages..." {
configfile /boot/grub/tix.cfg
}

1
dhclient/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dhclient

29
dhclient/Makefile Normal file
View File

@ -0,0 +1,29 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
BINARIES = dhclient
#MANPAGES8 = dhclient.8
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
#mkdir -p $(DESTDIR)$(MANDIR)/man8
#install $(MANPAGES8) $(DESTDIR)$(MANDIR)/man8
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
clean:
rm -f $(BINARIES)

798
dhclient/dhclient.c Normal file
View File

@ -0,0 +1,798 @@
/*
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* dhclient.c
* Dynamic Host Configuration Protocol client.
*/
#if defined(__sortix__)
#include <sys/dnsconfig.h>
#endif
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <dirent.h>
#include <endian.h>
#include <err.h>
#include <fcntl.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#define PORT_DHCP_SERVER 67
#define PORT_DHCP_CLIENT 68
struct dhcp
{
uint8_t op;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
uint32_t xid;
uint16_t secs;
uint16_t flags;
uint8_t ciaddr[4];
uint8_t yiaddr[4];
uint8_t siaddr[4];
uint8_t giaddr[4];
uint8_t chaddr[16];
uint8_t sname[64];
uint8_t file[128];
};
#define DHCP_OP_BOOTREQUEST 1
#define DHCP_OP_BOOTREPLY 2
#define DHCP_FLAGS_BROADCAST (1 << 15)
#define DHCP_HTYPE_ETHERNET 1
#define DHCP_HLEN_ETHERNET 6
#define DHCP_MAGIC_0 99
#define DHCP_MAGIC_1 130
#define DHCP_MAGIC_2 83
#define DHCP_MAGIC_3 99
#define OPTION_PAD 0
#define OPTION_SUBNET 1
#define OPTION_TIME_OFFSET 2
#define OPTION_ROUTERS 3
#define OPTION_DNS 6
#define OPTION_DOMAIN_NAME 12
#define OPTION_INTERFACE_MTU 26
#define OPTION_BROADCAST_ADDRESS 28
#define OPTION_NTP 42
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OPTION_OVERLOAD 52
#define OPTION_DHCP_MSGTYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_PARAMETER_REQUEST 55
#define OPTION_RENEWAL_TIME 58
#define OPTION_REBINDING_TIME 59
#define OPTION_END 255
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 9
struct dhcp_message
{
struct dhcp hdr;
unsigned char magic[4];
unsigned char options[65536 - (sizeof(struct dhcp) + 4)];
};
enum option_state
{
OPTION_STATE_OPTIONS,
OPTION_STATE_FILE,
OPTION_STATE_SNAME,
OPTION_STATE_DONE,
};
struct option_iterate
{
struct dhcp* hdr;
unsigned char* options;
size_t offset;
size_t length;
enum option_state state;
bool has_sname_options;
bool has_file_options;
};
static void option_iterate_begin(struct option_iterate* iter,
struct dhcp* hdr,
unsigned char* options,
size_t length)
{
memset(iter, 0, sizeof(*iter));
iter->hdr = hdr;
iter->options = options;
iter->length = length;
}
static void option_iterate_begin_msg(struct option_iterate* iter,
struct dhcp_message* msg,
size_t length)
{
size_t offset = offsetof(struct dhcp_message, options);
assert(offset <= length);
option_iterate_begin(iter, &msg->hdr, msg->options, length - offset);
}
static bool option_iterate_array(struct option_iterate* iter,
unsigned char* options,
size_t length,
unsigned char* out_option,
unsigned char* out_optlen,
unsigned char** out_data)
{
while ( iter->offset < length )
{
unsigned char option = options[iter->offset++];
if ( option == OPTION_PAD )
continue;
if ( option == OPTION_END )
break;
if ( iter->offset == length )
return false;
unsigned char optlen = options[iter->offset++];
if ( length - iter->offset < optlen )
return false;
unsigned char* data = iter->options + iter->offset;
*out_option = option;
*out_optlen = optlen;
*out_data = data;
iter->offset += optlen;
if ( option == OPTION_OPTION_OVERLOAD )
{
if ( optlen != 1 )
return false;
if ( iter->state == OPTION_STATE_OPTIONS )
{
if ( data[0] & 1 << 0 )
iter->has_sname_options = true;
if ( data[0] & 1 << 1 )
iter->has_file_options = true;
}
continue;
}
return true;
}
return false;
}
static bool option_iterate(struct option_iterate* iter,
unsigned char* out_option,
unsigned char* out_optlen,
unsigned char** out_data)
{
if ( iter->state == OPTION_STATE_OPTIONS )
{
if ( option_iterate_array(iter, iter->options, iter->length,
out_option, out_optlen, out_data) )
return true;
iter->state = OPTION_STATE_SNAME;
iter->offset = 0;
}
if ( iter->state == OPTION_STATE_SNAME )
{
if ( iter->has_sname_options &&
option_iterate_array(iter, iter->hdr->sname,
sizeof(iter->hdr->sname), out_option,
out_optlen, out_data) )
return true;
iter->state = OPTION_STATE_FILE;
iter->offset = 0;
}
if ( iter->state == OPTION_STATE_FILE )
{
if ( iter->has_file_options &&
option_iterate_array(iter, iter->hdr->file,
sizeof(iter->hdr->file), out_option,
out_optlen, out_data) )
return true;
iter->state = OPTION_STATE_DONE;
iter->offset = 0;
}
return false;
}
static bool option_search(struct option_iterate* iter,
unsigned char search_option,
unsigned char* out_optlen,
unsigned char** out_data)
{
enum option_state saved_state = iter->state;
size_t saved_offset = iter->offset;
iter->state = OPTION_STATE_OPTIONS;
iter->offset = 0;
bool result = false;
unsigned char option;
unsigned char optlen;
unsigned char* data;
while ( option_iterate(iter, &option, &optlen, &data) )
{
if ( option == search_option )
{
result = true;
*out_optlen = optlen;
*out_data = data;
break;
}
}
iter->state = saved_state;
iter->offset = saved_offset;
return result;
}
static const unsigned char requests[] =
{
OPTION_SUBNET,
OPTION_TIME_OFFSET,
OPTION_ROUTERS,
OPTION_DNS,
OPTION_DOMAIN_NAME,
OPTION_INTERFACE_MTU,
OPTION_NTP,
};
static size_t add_option_byte(unsigned char* options,
size_t optsmax,
size_t offset,
unsigned char byte)
{
if ( optsmax <= offset )
errx(1, "too many dhcp options");
options[offset++] = byte;
return offset;
}
static size_t add_option(unsigned char* options,
size_t optsmax,
size_t offset,
unsigned char option,
unsigned char optlen,
const unsigned char* data)
{
offset = add_option_byte(options, optsmax, offset, option);
offset = add_option_byte(options, optsmax, offset, optlen);
for ( size_t i = 0; i < optlen; i++ )
offset = add_option_byte(options, optsmax, offset, data[i]);
return offset;
}
static bool check_dchp_message(struct dhcp_message* msg,
size_t amount,
unsigned char* chaddr,
uint32_t xid)
{
if ( (size_t) amount < sizeof(msg->hdr) )
return false;
if ( (size_t) amount < sizeof(msg->hdr) + sizeof(msg->magic) )
return false;
if ( msg->hdr.op != DHCP_OP_BOOTREPLY )
return false;
if ( msg->hdr.htype != DHCP_HTYPE_ETHERNET ||
msg->hdr.hlen != DHCP_HLEN_ETHERNET )
return false;
if ( memcmp(msg->hdr.chaddr, chaddr, sizeof(msg->hdr.chaddr)) != 0 )
return false;
if ( msg->hdr.xid != htobe32(xid) )
return false;
if ( msg->magic[0] != DHCP_MAGIC_0 ||
msg->magic[1] != DHCP_MAGIC_1 ||
msg->magic[2] != DHCP_MAGIC_2 ||
msg->magic[3] != DHCP_MAGIC_3 )
return false;
return true;
}
static void ready(void)
{
const char* readyfd_env = getenv("READYFD");
if ( !readyfd_env )
return;
int readyfd = atoi(readyfd_env);
char c = '\n';
write(readyfd, &c, 1);
close(readyfd);
unsetenv("READYFD");
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
int main(int argc, char* argv[])
{
setvbuf(stdout, NULL, _IOLBF, 0);
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
exit(1);
}
}
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
exit(1);
}
}
compact_arguments(&argc, &argv);
#if defined(__sortix__)
if ( argc <= 1 )
{
printf("Usage: %s <interface>\n", argv[0]);
return 0;
}
const char* if_name = argv[1];
if ( 3 <= argc )
errx(1, "unexpected extra operand `%s'", argv[2]);
int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
if ( dev_fd < 0 )
err(1, "/dev");
int if_fd = openat(dev_fd, if_name, O_RDWR);
if ( if_fd < 0 )
err(1, "%s", if_name);
close(dev_fd);
int type = ioctl(if_fd, IOCGETTYPE);
if ( type < 0 )
err(1, "%s: ioctl: IOCGETTYPE", if_name);
if ( IOC_TYPE(type) != IOC_TYPE_NETWORK_INTERFACE )
errx(1, "%s: Not a network interface", if_name);
struct if_info info;
if ( ioctl(if_fd, NIOC_GETINFO, &info) < 0 )
err(1, "%s: ioctl: NIOC_GETINFO", if_name);
if ( info.type == IF_TYPE_LOOPBACK )
{
printf("%s: Loopback interface does not need to be configured\n",
if_name);
return 0;
}
if ( info.type != IF_TYPE_ETHERNET )
errx(1, "%s: ioctl: NIOC_GETINFO: unknown device type", if_name);
if ( info.addrlen != 6 )
errx(1, "%s: ioctl: NIOC_GETINFO: bogus address length", if_name);
// TODO: struct ether_addr
unsigned char ethaddr[6];
memcpy(ethaddr, info.addr, 6);
#else
unsigned char ethaddr[6] = { 00, 25, 22, 04, 95, 83 };
#endif
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if ( fd < 0 )
err(1, "socket");
#if defined(__sortix__)
if ( setsockopt(fd, SOL_SOCKET, SO_BINDTOINDEX, &info.linkid,
sizeof(info.linkid)) < 0 )
err(1, "setsockopt: SO_BINDTOINDEX");
#endif
int enable = 1;
if ( setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)) < 0 )
err(1, "setsockopt: SO_BROADCAST");
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htobe16(PORT_DHCP_CLIENT);
local.sin_addr.s_addr = htobe32(INADDR_ANY);
if ( bind(fd, (const struct sockaddr*) &local, sizeof(local)) < 0 )
err(1, "bind");
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htobe16(PORT_DHCP_SERVER);
dest.sin_addr.s_addr = htobe32(INADDR_BROADCAST);
struct sockaddr_in remote;
socklen_t remote_len = sizeof(remote);
#if defined(__sortix__)
printf("%s: Waiting for interface to come up\n", if_name);
// TODO: This can block indefinitely. Run ready() if this step times out.
if ( ioctl(if_fd, NIOC_WAITLINKSTATUS, 1) < 0 )
err(1, "%s: ioctl: NIOC_WAITLINKSTATUS", if_name);
printf("%s: Interface is up\n", if_name);
#endif
// TODO: "The client SHOULD wait a random time between one and ten seconds
// to desynchronize the use of DHCP at startup."
// Ten seconds seems excessive in today's world, but a small random
// delay seems reasonable. There are randomness in retransmissions so
// maybe this doesn't benefit so much.
#if defined(__sortix__)
uint32_t xid = arc4random();
#else
uint32_t xid = rand();
#endif
struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
unsigned int retransmissions = 0;
unsigned char chaddr[16];
memset(chaddr, 0, sizeof(chaddr));
memcpy(chaddr, ethaddr, 6);
static struct dhcp_message msg;
unsigned char* opts = msg.options;
size_t msgsize;
size_t optsoff;
size_t optsmax = sizeof(msg.options);
unsigned char option;
unsigned char optlen = 0;
unsigned char* optdata = NULL;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec begun = now;
struct timespec last_sent;
struct timespec timeout;
last_sent = timespec_make(-1, 0);
timeout = timespec_make(0, 0);
unsigned char yiaddr[4];
unsigned char server_identifier[4];
char remote_host_str[NI_MAXHOST];
char remote_serv_str[NI_MAXSERV];
char yiaddr_str[INET_ADDRSTRLEN];
while ( true )
{
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec since_sent = timespec_sub(now, last_sent);
if ( timespec_ge(since_sent, timeout) )
{
struct timespec since_begun = timespec_sub(now, begun);
memset(&msg, 0, sizeof(msg));
msg.hdr.op = DHCP_OP_BOOTREQUEST;
msg.hdr.htype = DHCP_HTYPE_ETHERNET;
msg.hdr.hlen = DHCP_HLEN_ETHERNET;
msg.hdr.xid = htobe32(xid);
msg.hdr.secs = htobe16((uint16_t) since_begun.tv_sec);
msg.hdr.flags = htobe16(DHCP_FLAGS_BROADCAST);
memcpy(msg.hdr.chaddr, chaddr, sizeof(chaddr));
msg.magic[0] = DHCP_MAGIC_0;
msg.magic[1] = DHCP_MAGIC_1;
msg.magic[2] = DHCP_MAGIC_2;
msg.magic[3] = DHCP_MAGIC_3;
optsoff = 0;
option = DHCPDISCOVER;
optsoff = add_option(opts, optsmax, optsoff, OPTION_DHCP_MSGTYPE,
1, &option);
optsoff = add_option(opts, optsmax, optsoff,
OPTION_PARAMETER_REQUEST, sizeof(requests),
requests);
// TODO: Maybe send hostname.
optsoff = add_option_byte(opts, optsmax, optsoff, OPTION_END);
if ( retransmissions == 0 )
printf("%s: Broadcasting DHCPDISCOVER\n", if_name);
else
printf("%s: Broadcasting DHCPDISCOVER (attempt %i)\n", if_name,
retransmissions + 1);
msgsize = sizeof(msg.hdr) + sizeof(msg.magic) + optsoff;
// TODO: This can fail temporarily like with ENOBUFS.
if ( sendto(fd, &msg, msgsize, 0, (const struct sockaddr*) &dest,
sizeof(dest)) < 0 )
err(1, "send");
last_sent = now;
timeout = timespec_make(1 << retransmissions,
arc4random_uniform(1000000000));
if ( retransmissions < 6 )
retransmissions++;
else
{
fprintf(stderr, "%s: error: DHCPDISCOVER timed out\n", if_name);
ready();
// TODO: Technically restart the initialization.
retransmissions = 0;
}
}
struct timespec left =
timespec_sub(timespec_add(last_sent, timeout), now);
struct pollfd pfd = { 0 };
pfd.fd = fd;
pfd.events = POLLIN;
int num_events = ppoll(&pfd, 1, &left, NULL);
if ( num_events < 0 )
err(1, "ppoll");
if ( num_events == 0 )
continue;
ssize_t amount = recvfrom(fd, &msg, sizeof(msg), 0,
(struct sockaddr*) &remote, &remote_len);
if ( amount < 0 )
err(1, "recv");
// TODO: Check the remote port is correct?
if ( !check_dchp_message(&msg, amount, chaddr, xid) )
continue;
struct option_iterate iter;
option_iterate_begin_msg(&iter, &msg, amount);
if ( !option_search(&iter, OPTION_DHCP_MSGTYPE, &optlen, &optdata) ||
optlen != 1 ||
optdata[0] != DHCPOFFER )
continue;
if ( !option_search(&iter, OPTION_SERVER_IDENTIFIER, &optlen,
&optdata) || optlen != 4 )
continue;
memcpy(server_identifier, optdata, 4);
memcpy(yiaddr, msg.hdr.yiaddr, 4);
getnameinfo((const struct sockaddr*) &remote, remote_len,
remote_host_str, sizeof(remote_host_str),
remote_serv_str, sizeof(remote_serv_str),
NI_NUMERICHOST | NI_NUMERICSERV);
inet_ntop(AF_INET, yiaddr, yiaddr_str, sizeof(yiaddr_str));
printf("%s: DHCPOFFER of %s from %s:%s\n",
if_name, yiaddr_str, remote_host_str, remote_serv_str);
break;
}
last_sent = timespec_make(-1, 0);
timeout = timespec_make(0, 0);
retransmissions = 0;
unsigned char subnet[4];
unsigned char router[4];
#if defined(__sortix__)
size_t dns_count = 0;
unsigned char dns[DNSCONFIG_MAX_SERVERS][4];
#endif
uint32_t lease_time = 0;
while ( true )
{
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec since_sent = timespec_sub(now, last_sent);
if ( timespec_le(timeout, since_sent) )
{
struct timespec since_begun = timespec_sub(now, begun);
memset(&msg, 0, sizeof(msg));
msg.hdr.op = DHCP_OP_BOOTREQUEST;
msg.hdr.htype = DHCP_HTYPE_ETHERNET;
msg.hdr.hlen = DHCP_HLEN_ETHERNET;
msg.hdr.xid = htobe32(xid);
msg.hdr.secs = htobe16((uint16_t) since_begun.tv_sec);
msg.hdr.flags = htobe16(DHCP_FLAGS_BROADCAST);
memcpy(msg.hdr.chaddr, chaddr, sizeof(chaddr));
msg.magic[0] = DHCP_MAGIC_0;
msg.magic[1] = DHCP_MAGIC_1;
msg.magic[2] = DHCP_MAGIC_2;
msg.magic[3] = DHCP_MAGIC_3;
optsoff = 0;
option = DHCPREQUEST;
optsoff = add_option(opts, optsmax, optsoff, OPTION_DHCP_MSGTYPE,
1, &option);
optsoff = add_option(opts, optsmax, optsoff,
OPTION_PARAMETER_REQUEST, sizeof(requests),
requests);
optsoff = add_option(opts, optsmax, optsoff,
OPTION_SERVER_IDENTIFIER,
sizeof(server_identifier), server_identifier);
optsoff = add_option(opts, optsmax, optsoff, OPTION_REQUESTED_IP,
sizeof(yiaddr), yiaddr);
// TODO: Maybe send hostname.
optsoff = add_option_byte(opts, optsmax, optsoff, OPTION_END);
if ( retransmissions == 0 )
printf("%s: Broadcasting DHCPREQUEST\n", if_name);
else
printf("%s: Broadcasting DHCPREQUEST (attempt %i)\n", if_name,
retransmissions + 1);
msgsize = sizeof(msg.hdr) + sizeof(msg.magic) + optsoff;
if ( sendto(fd, &msg, msgsize, 0, (const struct sockaddr*) &dest,
sizeof(dest)) < 0 )
err(1, "send");
last_sent = now;
timeout = timespec_make(1 << retransmissions,
arc4random_uniform(1000000000));
if ( retransmissions < 6 )
retransmissions++;
else
{
fprintf(stderr, "%s: error: DHCPDISCOVER timed out\n", if_name);
ready();
// TODO: Technically restart the initialization.
retransmissions = 0;
}
}
struct timespec left =
timespec_sub(timespec_add(last_sent, timeout), now);
struct pollfd pfd = { 0 };
pfd.fd = fd;
pfd.events = POLLIN;
int num_events = ppoll(&pfd, 1, &left, NULL);
if ( num_events < 0 )
err(1, "ppoll");
if ( num_events == 0 )
continue;
struct sockaddr_in peer;
socklen_t peer_len = sizeof(peer);
ssize_t amount = recvfrom(fd, &msg, sizeof(msg), 0,
(struct sockaddr*) &peer, &peer_len);
if ( amount < 0 )
err(1, "recv");
if ( memcmp(&peer, &remote, remote_len) != 0 )
continue;
if ( !check_dchp_message(&msg, amount, chaddr, xid) )
continue;
struct option_iterate iter;
option_iterate_begin_msg(&iter, &msg, amount);
// TODO: Log proper errors for the below conditions.
if ( !option_search(&iter, OPTION_DHCP_MSGTYPE, &optlen, &optdata) ||
optlen != 1 ||
optdata[0] != DHCPACK )
continue;
if ( !option_search(&iter, OPTION_SERVER_IDENTIFIER, &optlen,
&optdata) || optlen != 4 )
continue;
memcpy(server_identifier, optdata, 4);
if ( !option_search(&iter, OPTION_SUBNET, &optlen, &optdata) ||
optlen != 4 )
continue;
memcpy(subnet, optdata, 4);
if ( !option_search(&iter, OPTION_ROUTERS, &optlen, &optdata) ||
optlen < 4 )
continue;
memcpy(router, optdata, 4);
if ( !option_search(&iter, OPTION_LEASE_TIME, &optlen, &optdata) ||
optlen != 4 )
continue;
lease_time = (uint32_t) optdata[0] << 24 |
(uint32_t) optdata[1] << 16 |
(uint32_t) optdata[2] << 8 |
(uint32_t) optdata[3] << 0;
// TODO: Verify yiaddr is what we requested (bait and switch).
memcpy(yiaddr, msg.hdr.yiaddr, 4);
#if defined(__sortix__)
if ( option_search(&iter, OPTION_DNS, &optlen, &optdata) )
{
size_t offset = 0;
for ( ; dns_count < DNSCONFIG_MAX_SERVERS && 4 <= optlen - offset;
dns_count++ )
{
dns[dns_count][0] = optdata[offset++];
dns[dns_count][1] = optdata[offset++];
dns[dns_count][2] = optdata[offset++];
dns[dns_count][3] = optdata[offset++];
}
}
#endif
printf("%s: DHCPACK of %s from %s:%s\n",
if_name, yiaddr_str, remote_host_str, remote_serv_str);
break;
}
char router_str[INET_ADDRSTRLEN];
char subnet_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, router, router_str, sizeof(router_str));
inet_ntop(AF_INET, subnet, subnet_str, sizeof(subnet_str));
printf("%s: Leased %s from %s:%s for %u seconds\n",
if_name,
yiaddr_str,
remote_host_str,
remote_serv_str,
lease_time);
printf("%s: Router is %s\n", if_name, router_str);
printf("%s: Subnet is %s\n", if_name, subnet_str);
if ( dns_count == 0 )
printf("%s: No DNS servers were offered\n", if_name);
else for ( size_t i = 0; i < dns_count; i++ )
{
char dns_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, dns[i], dns_str, sizeof(dns_str));
printf("%s: DNS server %zu is %s\n", if_name, i + 1, dns_str);
}
#if defined(__sortix__)
struct if_config_inet inet_cfg;
memcpy(&inet_cfg.address, yiaddr, sizeof(inet_cfg.address));
memcpy(&inet_cfg.router, router, sizeof(inet_cfg.router));
memcpy(&inet_cfg.subnet, subnet, sizeof(inet_cfg.subnet));
if ( ioctl(if_fd, NIOC_SETCONFIG_INET, &inet_cfg) < 0 )
err(1, "%s: ioctl: NIOC_SETCONFIG_INET", if_name);
// TODO: This configuration should set a bit that marks the interface as
// configured (and not in the configuration phase) and wake anything
// waiting for the interface to come up.
printf("%s: Configured network interface\n", if_name);
struct dnsconfig dnsconfig;
memset(&dnsconfig, 0, sizeof(dnsconfig));
dnsconfig.servers_count = dns_count;
for ( size_t i = 0; i < dns_count; i++ )
{
dnsconfig.servers[i].family = AF_INET;
dnsconfig.servers[i].addrsize = 4;
memcpy(&dnsconfig.servers[i].addr, dns[i], 4);
}
if ( setdnsconfig(&dnsconfig) < 0 )
err(1, "setdnsconfig");
printf("%s: Configured DNS\n", if_name);
#endif
ready();
while ( true )
{
sleep(lease_time);
printf("%s: Lease for %s has expired after %u seconds\n",
if_name, yiaddr_str, lease_time);
// TODO: Attempt to renew lease and expire it and such.
fprintf(stderr, "error: Lease renewal is not implemented");
}
close(fd);
#if defined(__sortix__)
close(if_fd);
#endif
}

View File

@ -76,7 +76,8 @@ Delete a resolver:
.Sh SEE ALSO
.Xr getdnsconfig 2 ,
.Xr setdnsconfig 2 ,
.Xr inet 4
.Xr inet 4 ,
.Xr dhclient 8
.Sh HISTORY
.Nm
originally appeared in Sortix 1.1.
@ -85,3 +86,6 @@ The kernel DNS resolver list is global state.
Changes made with
.Nm
may be overwritten by other programs.
In particular
.Xr dhclient 8
sets the resolver list automatically unless otherwise configured.

4
share/init/dhclient Normal file
View File

@ -0,0 +1,4 @@
per if
#program=dhclient
#exec $program $if
exec dhclient

View File

@ -0,0 +1 @@
require dhclient

View File

@ -332,7 +332,8 @@ temporarily fail with
.Xr inet 4 ,
.Xr ip 4 ,
.Xr lo 4 ,
.Xr kernel 7
.Xr kernel 7 ,
.Xr dhclient 8
.Sh STANDARDS
.St -p1003.1-2008
only specifies a minimal

View File

@ -143,6 +143,13 @@ It depends on the
and
.Sy time
daemons.
.It Sy dhclient
.\" TODO: The daemon becomes ready when an attempt has been made, rather than
.\" when it succeeded.
Daemon that starts
.Xr dhclient 8
on each network interface and becomes ready when each network interface has
been configured.
.It Sy local
Virtual daemon that starts daemons pertinent to the local system.
The system provides a default implementation that does nothing.
@ -154,6 +161,9 @@ system.
.\" TODO: The daemon becomes ready when an attempt has been made, rather than
.\" when it succeeded.
Virtual daemon that becomes ready when the network has been configured.
It depends on the
.Sy dhclient
deamon.
Daemons can depend on this daemon if they need the network to have been
established before they start.
.It Sy time
@ -482,6 +492,23 @@ The
.Sy optional
flag should only be omitted if a local daemon is critical and the boot should
fail if the daemon fails.
.Ss Disable network auto-configuration (DHCP)
The
.Sy network
daemon depends by default on
.Sy dhclient ,
which does DHCP configuration of the network.
This dependency can be removed by creating
.Pa /etc/init/network
with the following contents:
.Bd -literal
furthermore
unset require dhclient
.Ed
.Pp
This example extends the existing configuration in
.Pa /share/init/network
by removing a dependency.
.Ss Creating a new virtual daemon
The
.Sy exampled

View File

@ -82,7 +82,8 @@ per the instructions in
The release modification procedure lets you customize aspects such as the
default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, and so on.
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, and so on.
.Pp
Warning: The live environment does not come with any random entropy and entropy
gathering is not yet implemented.
@ -143,6 +144,29 @@ Only the selected ports are loaded into the live environment and installed onto
the new installation.
If upgrading an existing installation, then any ports not loaded will be removed
from the installation being upgraded.
.Pp
Ports can additionally be loaded as binary packages in the
.Pa /repository
directory by navigating to the advanced menu and then the select binary packages
submenu and then selecting which ports.
.Pp
The network drivers can be disabled by navigating to the advanced menu and
selecting
.Sy Disable network drivers .
It can be useful to disable the network drivers if it's undesirable to put the
system on the network for security reasons.
You can disable network drivers by default by editing the bootloader
configuration as described below after completing the installation.
.Pp
By default
.Xr dhclient 8
will automatically configure
.Xr ether 4
network interfaces with DHCP and bring up network connectivity.
The DHCP client can be disabled by navigating to the advanced menu and selecting
.Sy Disable DHCP client ,
which is useful if you want to manually configure the network or not expose the
system until you are ready.
.Ss Installer
This guide assumes you selected the operating system installation option in the
bootloader.

View File

@ -262,6 +262,8 @@ If the selected menu option itself is a submenu, it can be appended with a
.Sy '>'
and another selection to pick a default menu option in that submenu, and so on.
(Default: 0)
.It Sy enable_dhclient
TODO: Document this.
.It Sy enable_network_drivers
An additional
.Xr kernel 7

View File

@ -16,7 +16,8 @@ configuration as described in section 5 of the manual.
The release modification procedure lets you customize aspects such as the
default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, and so on.
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, and so on.
.Ss Prerequisites
.Bl -bullet -compact
.It
@ -399,6 +400,16 @@ security reasons or to work around driver issues:
tix-iso-bootconfig --disable-network-drivers bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Ss Disable DHCP Auto-Configuration By Default
To customize a release so
.Xr dhclient 8
doesn't automatically configure network interfaces using DHCP, useful if one
wants to manually configure network interfaces with
.Xr ifconfig 8 .
.Bd -literal
tix-iso-bootconfig --disable-dhclient bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Sh SEE ALSO
.Xr xorriso 1 ,
.Xr development 7 ,

View File

@ -29,7 +29,8 @@ per the instructions in
The release modification procedure lets you customize aspects such as the
default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, and so on.
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, and so on.
.Pp
Warning: The live environment does not come with any random entropy and entropy
gathering is not yet implemented.

View File

@ -22,6 +22,7 @@ append_title="modified by $(id -un)@$(hostname)"
default=
directory=
enable_append_title=true
enable_dhclient=
enable_network_drivers=
enable_src=
liveconfig=
@ -51,9 +52,11 @@ for argument do
--default=*) default=$parameter ;;
--default) previous_option=default ;;
--disable-append-title) enable_append_title=false ;;
--disable-dhclient) enable_dhclient=false ;;
--disable-network-drivers) enable_network_drivers=false ;;
--disable-src) enable_src=false ;;
--enable-append-title) enable_append_title=true ;;
--enable-dhclient) enable_dhclient=true ;;
--enable-network-drivers) enable_network_drivers=true ;;
--enable-src) enable_src=true ;;
--liveconfig=*) liveconfig=$parameter ;;
@ -135,6 +138,7 @@ mkdir -p -- "$directory/boot/grub"
if [ -n "$timeout" ]; then
printf 'timeout="%s"\n' "$timeout"
fi
print_enable_default_bool "$enable_dhclient" dhclient dhclient
print_enable_default "$enable_network_drivers" network_drivers network-drivers
print_enable_default_bool "$enable_src" src src
if $enable_append_title; then

View File

@ -9,9 +9,11 @@
.Op Fl \-append-title Ns = Ns Ar text
.Op Fl \-default Ns = Ns Ar default-boot-menu-option
.Op Fl \-disable-append-title
.Op Fl \-disable-dhclient
.Op Fl \-disable-network-drivers
.Op Fl \-disable-src
.Op Fl \-enable-append-title
.Op Fl \-enable-dhclient
.Op Fl \-enable-network-drivers
.Op Fl \-enable-src
.Op Fl \-liveconfig Ns = Ns Ar liveconfig-directory
@ -90,6 +92,14 @@ GRUB variable.
Don't append anything to the bootloader menu title by appending to the
.Sy base_menu_title
GRUB variable.
.It Fl \-disable-dhclient
Disable automatic DHCP configuration by setting the
.Sy enable_dhclient
GRUB variable to
.Sy false ,
causing the bootloader to load additional configuration that turns off the
.Xr dhclient 8
daemon on boot.
.It Fl \-disable-network-drivers
Disable network drivers by setting the
.Sy enable_network_drivers
@ -113,6 +123,14 @@ to the bootloader menu title by appending to the
GRUB variable.
This option is on by default and can be disabled with
.Fl \-disable-append-title .
.It Fl \-enable-dhclient
Enable automatic DHCP configuration by setting the
.Sy enable_dhclient
GRUB variable to
.Sy true ,
selecting the default behavior of starting the
.Xr dhclient 8
daemon.
.It Fl \-enable-network-drivers
Enable network drivers by setting the
.Sy enable_network_drivers
@ -254,10 +272,21 @@ security reasons or to work around driver issues:
tix-iso-bootconfig --disable-network-drivers bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Ss Disable DHCP Auto-Configuration By Default
To customize a release so
.Xr dhclient 8
doesn't automatically configure network interfaces using DHCP, useful if one
wants to manually configure network interfaces with
.Xr ifconfig 8 .
.Bd -literal
tix-iso-bootconfig --disable-dhclient bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Sh SEE ALSO
.Xr xorriso 1 ,
.Xr kernel 7 ,
.Xr release-iso-bootconfig 7 ,
.Xr release-iso-modification 7 ,
.Xr init 8 ,
.Xr tix-iso-add 8 ,
.Xr tix-iso-liveconfig 8