sortix-mirror/dhclient/dhclient.c

799 lines
23 KiB
C

/*
* 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
}