240 lines
5.4 KiB
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" : "");
|
|
|
|
}
|
|
}
|