/* $OpenBSD: dhcpleasectl.c,v 1.7 2021/09/16 06:23:01 jmc Exp $ */ /* * Copyright (c) 2021 Florian Obser * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2005 Esben Norby * Copyright (c) 2003 Henning Brauer * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcpleased.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_DHCPLEASED_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) { struct ifreq ifr, ifr_x; int ioctl_sock; if ((ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) err(1, NULL); strlcpy(ifr.ifr_name, argv[0], sizeof(ifr.ifr_name)); strlcpy(ifr_x.ifr_name, argv[0], sizeof(ifr.ifr_name)); if (ioctl(ioctl_sock, SIOCGIFFLAGS, &ifr) == -1) err(1, "SIOCGIFFLAGS"); if (ioctl(ioctl_sock, SIOCGIFXFLAGS, &ifr_x) == -1) err(1, "SIOCGIFFLAGS"); if (!(ifr.ifr_flags & IFF_UP) || !(ifr_x.ifr_flags & IFXF_AUTOCONF4)) { if (geteuid()) errx(1, "need root privileges"); } if (!(ifr.ifr_flags & IFF_UP)) { ifr.ifr_flags |= IFF_UP; if (ioctl(ioctl_sock, SIOCSIFFLAGS, &ifr) == -1) err(1, "SIOCSIFFLAGS"); } if (!(ifr_x.ifr_flags & IFXF_AUTOCONF4)) { ifr_x.ifr_flags |= IFXF_AUTOCONF4; if (ioctl(ioctl_sock, SIOCSIFXFLAGS, &ifr_x) == -1) err(1, "SIOCSIFFLAGS"); } } 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; char buf[IF_NAMESIZE], *bufp; char ipbuf[INET_ADDRSTRLEN]; char maskbuf[INET_ADDRSTRLEN]; char gwbuf[INET_ADDRSTRLEN]; bufp = if_indextoname(cei->if_index, buf); printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state); memset(ipbuf, 0, sizeof(ipbuf)); if (cei->requested_ip.s_addr != INADDR_ANY) { clock_gettime(CLOCK_MONOTONIC, &now); timespecsub(&now, &cei->request_time, &diff); memset(ipbuf, 0, sizeof(ipbuf)); memset(maskbuf, 0, sizeof(maskbuf)); memset(gwbuf, 0, sizeof(gwbuf)); if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf, sizeof(ipbuf)) == NULL) ipbuf[0] = '\0'; if (inet_ntop(AF_INET, &cei->mask, maskbuf, sizeof(maskbuf)) == NULL) maskbuf[0] = '\0'; printf("\tinet %s netmask %s\n", ipbuf, maskbuf); for (i = 0; i < cei->routes_len; i++) { if (inet_ntop(AF_INET, &cei->routes[i].dst, ipbuf, sizeof(ipbuf)) == NULL) ipbuf[0] = '\0'; if (inet_ntop(AF_INET, &cei->routes[i].mask, maskbuf, sizeof(maskbuf)) == NULL) maskbuf[0] = '\0'; if (inet_ntop(AF_INET, &cei->routes[i].gw, gwbuf, sizeof(gwbuf)) == NULL) gwbuf[0] = '\0'; if (cei->routes[i].dst.s_addr == INADDR_ANY && cei->routes[i].mask.s_addr == INADDR_ANY) printf("\tdefault gateway %s\n", gwbuf); else printf("\troute %s/%d gateway %s\n", ipbuf, 33 - ffs(ntohl(cei->routes[i].mask.s_addr)), gwbuf); } if (cei->nameservers[0].s_addr != INADDR_ANY) { printf("\tnameservers"); for (i = 0; i < MAX_RDNS_COUNT && cei->nameservers[i].s_addr != INADDR_ANY; i++) { if (inet_ntop(AF_INET, &cei->nameservers[i], ipbuf, sizeof(ipbuf)) == NULL) continue; printf(" %s", ipbuf); } printf("\n"); } 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 days\n", d); } else if (s > 3600) { h = s / 3600; /* round up */ if (s - h * 3600 > 1800) h++; printf("\tlease %lld hours\n", h); } else if (s > 60) { m = s / 60; /* round up */ if (s - m * 60 > 30) m++; printf("\tlease %lld minutes\n", m); } else printf("\tlease %lld seconds\n", s); } if (cei->server_identifier.s_addr != INADDR_ANY) { if (inet_ntop(AF_INET, &cei->server_identifier, ipbuf, sizeof(ipbuf)) == NULL) ipbuf[0] = '\0'; } else if (cei->dhcp_server.s_addr != INADDR_ANY) { if (inet_ntop(AF_INET, &cei->dhcp_server, ipbuf, sizeof(ipbuf)) == NULL) ipbuf[0] = '\0'; } if (ipbuf[0] != '\0') printf("\tdhcp server %s\n", ipbuf); }