src/usr.sbin/dhcp6leasectl/dhcp6leasectl.c

240 lines
5.4 KiB
C

/* $OpenBSD: dhcp6leasectl.c,v 1.1 2024/06/06 15:16:57 florian Exp $ */
/*
* Copyright (c) 2021, 2024 Florian Obser <florian@openbsd.org>
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
*
* 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.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <imsg.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "dhcp6leased.h"
__dead void usage(void);
void show_interface_msg(struct ctl_engine_info *);
struct imsgbuf *ibuf;
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-l] [-s socket] [-w maxwait] interface\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
struct sockaddr_un sun;
struct imsg imsg;
struct ctl_engine_info *cei;
int ctl_sock;
int n, lFlag = 0, maxwait_set = 0, didot = 0;
int ch, if_index = 0, maxwait = 10, bound = 0;
char *sockname;
const char *errstr;
sockname = _PATH_CTRL_SOCKET;
while ((ch = getopt(argc, argv, "ls:w:")) != -1) {
switch (ch) {
case 'l':
lFlag = 1;
break;
case 's':
sockname = optarg;
break;
case 'w':
maxwait_set = 1;
maxwait = strtonum(optarg, 1, INT_MAX, &errstr);
if (errstr)
errx(1, "maxwait value is %s: %s",
errstr, optarg);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
if ((if_index = if_nametoindex(argv[0])) == 0)
errx(1, "unknown interface");
if (lFlag && !maxwait_set)
maxwait = 0;
/* Connect to control socket. */
if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
err(1, "connect: %s", sockname);
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
imsg_init(ibuf, ctl_sock);
if (!lFlag) {
imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
&if_index, sizeof(if_index));
while (ibuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
err(1, "write error");
}
for(;;) {
imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
&if_index, sizeof(if_index));
while (ibuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
err(1, "write error");
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
errx(1, "imsg_read error");
if (n == 0)
errx(1, "pipe closed");
if ((n = imsg_get(ibuf, &imsg)) == -1)
errx(1, "imsg_get error");
if (n == 0)
break;
if (imsg.hdr.type == IMSG_CTL_END) {
if (lFlag)
errx(1, "non-autoconf interface %s", argv[0]);
else if (--maxwait < 0)
break;
else
continue;
}
cei = imsg.data;
if (strcmp(cei->state, "Bound") == 0)
bound = 1;
if (bound || --maxwait < 0) {
if (didot)
putchar('\n');
show_interface_msg(cei);
break;
} else {
didot = 1;
putchar('.');
fflush(stdout);
}
imsg_free(&imsg);
sleep(1);
}
close(ctl_sock);
free(ibuf);
return (0);
}
void
show_interface_msg(struct ctl_engine_info *cei)
{
struct timespec now, diff;
time_t d, h, m, s;
int i, has_pd = 0;
char buf[IF_NAMESIZE], *bufp;
char ntopbuf[INET6_ADDRSTRLEN];
bufp = if_indextoname(cei->if_index, buf);
printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state);
for (i = 0; i < MAX_IA; i++) {
if (cei->pds[i].prefix_len == 0)
continue;
has_pd = 1;
printf ("\tIA_PD %d: %s/%d\n", i, inet_ntop(AF_INET6,
&cei->pds[i], ntopbuf, INET6_ADDRSTRLEN),
cei->pds[i].prefix_len);
}
if (has_pd) {
clock_gettime(CLOCK_MONOTONIC, &now);
timespecsub(&now, &cei->request_time, &diff);
s = cei->lease_time - diff.tv_sec;
if (s < 0)
s = 0;
if ( s > 86400 ) {
d = s / 86400;
/* round up */
if (s - d * 86400 > 43200)
d++;
printf("\tlease %lld day%s\n", d, d > 1 ? "s" : "");
} else if (s > 3600) {
h = s / 3600;
/* round up */
if (s - h * 3600 > 1800)
h++;
printf("\tlease %lld hour%s\n", h, h > 1 ? "s" : "");
} else if (s > 60) {
m = s / 60;
/* round up */
if (s - m * 60 > 30)
m++;
printf("\tlease %lld minute%s\n", m, m > 1 ? "s" : "");
} else
printf("\tlease %lld second%s\n", s, s > 1 ? "s" : "");
}
}