[BACK]Return to dhcpleasectl.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / dhcpleasectl

File: [local] / src / usr.sbin / dhcpleasectl / dhcpleasectl.c (download)

Revision 1.7, Thu Sep 16 06:23:01 2021 UTC (2 years, 8 months ago) by jmc
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, HEAD
Changes since 1.6: +2 -2 lines

sync SYNOPSIS and usage(); ok florian

/*	$OpenBSD: dhcpleasectl.c,v 1.7 2021/09/16 06:23:01 jmc Exp $	*/

/*
 * Copyright (c) 2021 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 "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);
}