-/* $OpenBSD: dhcpleasectl.c,v 1.5 2021/07/26 09:26:36 florian Exp $ */
+/* $OpenBSD: dhcpleasectl.c,v 1.6 2021/09/15 15:18:23 florian Exp $ */
/*
* Copyright (c) 2021 Florian Obser <florian@openbsd.org>
*/
#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 <net/if_media.h>
-#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
-#include <netinet6/nd6.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <imsg.h>
-#include <netdb.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include "dhcpleased.h"
-#include "frontend.h"
-#include "parser.h"
__dead void usage(void);
-int show_interface_msg(struct imsg *);
+void show_interface_msg(struct ctl_engine_info *);
struct imsgbuf *ibuf;
{
extern char *__progname;
- fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
+ fprintf(stderr, "usage: %s [-l] [-s socket] [-w seconds] interface\n",
__progname);
exit(1);
}
main(int argc, char *argv[])
{
struct sockaddr_un sun;
- struct parse_result *res;
struct imsg imsg;
+ struct ctl_engine_info *cei;
int ctl_sock;
- int done = 0;
- int n, verbose = 0;
- int ch;
+ 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, "s:")) != -1) {
+ 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 (pledge("stdio unix", NULL) == -1)
- err(1, "pledge");
+ if (argc != 1)
+ usage();
- /* Parse command line. */
- if ((res = parse(argc, argv)) == NULL)
- exit(1);
+ 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)
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
imsg_init(ibuf, ctl_sock);
- done = 0;
-
- /* Process user request. */
- switch (res->action) {
- case RELOAD:
- imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
- printf("reload request sent.\n");
- done = 1;
- break;
- case LOG_VERBOSE:
- verbose = 1;
- /* FALLTHROUGH */
- case LOG_BRIEF:
- imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
- &verbose, sizeof(verbose));
- printf("logging request sent.\n");
- done = 1;
- break;
- case SHOW_INTERFACE:
- imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
- &res->if_index, sizeof(res->if_index));
- break;
- case SEND_REQUEST:
+
+ if (!lFlag) {
imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
- &res->if_index, sizeof(res->if_index));
- done = 1;
- break;
- default:
- usage();
+ &if_index, sizeof(if_index));
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+ err(1, "write error");
+
}
- 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");
+
- while (!done) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
errx(1, "imsg_read error");
if (n == 0)
errx(1, "pipe closed");
- while (!done) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- errx(1, "imsg_get error");
- if (n == 0)
- break;
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ errx(1, "imsg_get error");
+ if (n == 0)
+ break;
- switch (res->action) {
- case SHOW_INTERFACE:
- done = show_interface_msg(&imsg);
- break;
- default:
+ 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;
- imsg_free(&imsg);
+ 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);
}
-int
-show_interface_msg(struct imsg *imsg)
+void
+show_interface_msg(struct ctl_engine_info *cei)
{
- static int if_count = 0;
- struct ctl_engine_info *cei;
struct timespec now, diff;
- time_t y, d, h, m, s;
+ 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];
- switch (imsg->hdr.type) {
- case IMSG_CTL_SHOW_INTERFACE_INFO:
- cei = imsg->data;
-
- if (if_count++ > 0)
- printf("\n");
-
- bufp = if_indextoname(cei->if_index, buf);
- printf("%s [%s]:\n", bufp != NULL ? bufp : "unknown",
- cei->state);
+ 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));
- 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("\tserver: %s\n", 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,
+ 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->mask, maskbuf,
+ if (inet_ntop(AF_INET, &cei->routes[i].mask, maskbuf,
sizeof(maskbuf)) == NULL)
maskbuf[0] = '\0';
- printf("\t IP: %s/%s\n", ipbuf, maskbuf);
- for (i = 0; i < cei->routes_len; i++) {
- if (inet_ntop(AF_INET, &cei->routes[i].dst,
+ 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)
- 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';
-
- printf("\t%s\t%s/%s - %s\n", i == 0 ? "routes:"
- : "", ipbuf, maskbuf, gwbuf);
- }
- if (cei->nameservers[0].s_addr != INADDR_ANY) {
- printf("\t DNS:");
- 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");
+ continue;
+ printf(" %s", ipbuf);
}
- s = cei->lease_time - diff.tv_sec;
- if (s < 0)
- s = 0;
- y = s / 31556926; s -= y * 31556926;
- d = s / 86400; s -= d * 86400;
- h = s / 3600; s -= h * 3600;
- m = s / 60; s -= m * 60;
-
- printf("\t lease: ");
- if (y > 0)
- printf("%lldy ", y);
- if (d > 0)
- printf("%lldd ", d);
- if (h > 0)
- printf("%lldh ", h);
- if (m > 0)
- printf("%lldm ", m);
- if (s > 0)
- printf("%llds ", s);
printf("\n");
}
- break;
- case IMSG_CTL_END:
- return (1);
- default:
- break;
+ 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);
}
-
- return (0);
+ 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);
}
+++ /dev/null
-/* $OpenBSD: parser.c,v 1.2 2021/07/26 09:26:36 florian Exp $ */
-
-/*
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 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/queue.h>
-#include <sys/socket.h>
-#include <netinet/in.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 "dhcpleased.h"
-#include "parser.h"
-
-enum token_type {
- NOTOKEN,
- ENDTOKEN,
- INTERFACENAME,
- KEYWORD
-};
-
-struct token {
- enum token_type type;
- const char *keyword;
- int value;
- const struct token *next;
-};
-
-static const struct token t_main[];
-static const struct token t_log[];
-static const struct token t_show[];
-static const struct token t_show_interface[];
-static const struct token t_send[];
-static const struct token t_send_request[];
-
-static const struct token t_main[] = {
- {KEYWORD, "reload", RELOAD, NULL},
- {KEYWORD, "show", SHOW, t_show},
- {KEYWORD, "log", NONE, t_log},
- {KEYWORD, "send", NONE, t_send},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_log[] = {
- {KEYWORD, "verbose", LOG_VERBOSE, NULL},
- {KEYWORD, "brief", LOG_BRIEF, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_show[] = {
- {KEYWORD, "interface", SHOW_INTERFACE, t_show_interface},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_send[] = {
- {KEYWORD, "request", SEND_REQUEST, t_send_request},
- {ENDTOKEN, "", NONE, NULL}
-};
-static const struct token t_send_request[] = {
- {INTERFACENAME, "", SEND_REQUEST, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token t_show_interface[] = {
- {NOTOKEN, "", NONE, NULL},
- {INTERFACENAME, "", SHOW_INTERFACE, NULL},
- {ENDTOKEN, "", NONE, NULL}
-};
-
-static const struct token *match_token(const char *, const struct token *,
- struct parse_result *);
-static void show_valid_args(const struct token *);
-
-struct parse_result *
-parse(int argc, char *argv[])
-{
- static struct parse_result res;
- const struct token *table = t_main;
- const struct token *match;
-
- memset(&res, 0, sizeof(res));
-
- while (argc >= 0) {
- if ((match = match_token(argv[0], table, &res)) == NULL) {
- fprintf(stderr, "valid commands/args:\n");
- show_valid_args(table);
- return (NULL);
- }
-
- argc--;
- argv++;
-
- if (match->type == NOTOKEN || match->next == NULL)
- break;
-
- table = match->next;
- }
-
- if (argc > 0) {
- fprintf(stderr, "superfluous argument: %s\n", argv[0]);
- return (NULL);
- }
-
- return (&res);
-}
-
-static const struct token *
-match_token(const char *word, const struct token *table,
- struct parse_result *res)
-{
- u_int i, match;
- const struct token *t = NULL;
-
- match = 0;
-
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- if (word == NULL || strlen(word) == 0) {
- match++;
- t = &table[i];
- }
- break;
- case INTERFACENAME:
- if (!match && word != NULL && strlen(word) > 0) {
- if ((res->if_index = if_nametoindex(word)) == 0)
- errx(1, "unknown interface");
- match++;
- t = &table[i];
- if (t->value)
- res->action = t->value;
- }
- break;
- case KEYWORD:
- if (word != NULL && strncmp(word, table[i].keyword,
- strlen(word)) == 0) {
- match++;
- t = &table[i];
- if (t->value)
- res->action = t->value;
- }
- break;
- case ENDTOKEN:
- break;
- }
- }
-
- if (match != 1) {
- if (word == NULL)
- fprintf(stderr, "missing argument:\n");
- else if (match > 1)
- fprintf(stderr, "ambiguous argument: %s\n", word);
- else if (match < 1)
- fprintf(stderr, "unknown argument: %s\n", word);
- return (NULL);
- }
-
- return (t);
-}
-
-static void
-show_valid_args(const struct token *table)
-{
- int i;
-
- for (i = 0; table[i].type != ENDTOKEN; i++) {
- switch (table[i].type) {
- case NOTOKEN:
- fprintf(stderr, " <cr>\n");
- break;
- case INTERFACENAME:
- fprintf(stderr, " <interface>\n");
- break;
- case KEYWORD:
- fprintf(stderr, " %s\n", table[i].keyword);
- break;
- case ENDTOKEN:
- break;
- }
- }
-}