1 /*	$OpenBSD: dhcpleasectl.c,v 1.3 2021/03/23 17:46:20 florian Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <arpa/inet.h>
27 
28 #include <net/if.h>
29 #include <net/if_media.h>
30 #include <net/if_types.h>
31 
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <netinet6/nd6.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <event.h>
39 #include <imsg.h>
40 #include <netdb.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include "dhcpleased.h"
47 #include "frontend.h"
48 #include "parser.h"
49 
50 __dead void	 usage(void);
51 int		 show_interface_msg(struct imsg *);
52 
53 struct imsgbuf	*ibuf;
54 
55 __dead void
56 usage(void)
57 {
58 	extern char *__progname;
59 
60 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
61 	    __progname);
62 	exit(1);
63 }
64 
65 int
66 main(int argc, char *argv[])
67 {
68 	struct sockaddr_un	 sun;
69 	struct parse_result	*res;
70 	struct imsg		 imsg;
71 	int			 ctl_sock;
72 	int			 done = 0;
73 	int			 n, verbose = 0;
74 	int			 ch;
75 	char			*sockname;
76 
77 	sockname = _PATH_DHCPLEASED_SOCKET;
78 	while ((ch = getopt(argc, argv, "s:")) != -1) {
79 		switch (ch) {
80 		case 's':
81 			sockname = optarg;
82 			break;
83 		default:
84 			usage();
85 		}
86 	}
87 	argc -= optind;
88 	argv += optind;
89 
90 	if (pledge("stdio unix", NULL) == -1)
91 		err(1, "pledge");
92 
93 	/* Parse command line. */
94 	if ((res = parse(argc, argv)) == NULL)
95 		exit(1);
96 
97 	/* Connect to control socket. */
98 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
99 		err(1, "socket");
100 
101 	memset(&sun, 0, sizeof(sun));
102 	sun.sun_family = AF_UNIX;
103 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
104 
105 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
106 		err(1, "connect: %s", sockname);
107 
108 	if (pledge("stdio", NULL) == -1)
109 		err(1, "pledge");
110 
111 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
112 		err(1, NULL);
113 	imsg_init(ibuf, ctl_sock);
114 	done = 0;
115 
116 	/* Process user request. */
117 	switch (res->action) {
118 	case LOG_VERBOSE:
119 		verbose = 1;
120 		/* FALLTHROUGH */
121 	case LOG_BRIEF:
122 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
123 		    &verbose, sizeof(verbose));
124 		printf("logging request sent.\n");
125 		done = 1;
126 		break;
127 	case SHOW_INTERFACE:
128 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
129 		    &res->if_index, sizeof(res->if_index));
130 		break;
131 	case SEND_REQUEST:
132 		imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
133 		    &res->if_index, sizeof(res->if_index));
134 		done = 1;
135 		break;
136 	default:
137 		usage();
138 	}
139 
140 	while (ibuf->w.queued)
141 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
142 			err(1, "write error");
143 
144 	while (!done) {
145 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
146 			errx(1, "imsg_read error");
147 		if (n == 0)
148 			errx(1, "pipe closed");
149 
150 		while (!done) {
151 			if ((n = imsg_get(ibuf, &imsg)) == -1)
152 				errx(1, "imsg_get error");
153 			if (n == 0)
154 				break;
155 
156 			switch (res->action) {
157 			case SHOW_INTERFACE:
158 				done = show_interface_msg(&imsg);
159 				break;
160 			default:
161 				break;
162 			}
163 
164 			imsg_free(&imsg);
165 		}
166 	}
167 	close(ctl_sock);
168 	free(ibuf);
169 
170 	return (0);
171 }
172 
173 int
174 show_interface_msg(struct imsg *imsg)
175 {
176 	static int		 if_count = 0;
177 	struct ctl_engine_info	*cei;
178 	struct timespec		 now, diff;
179 	time_t			 y, d, h, m, s;
180 	int			 i;
181 	char			 buf[IF_NAMESIZE], *bufp;
182 	char			 ipbuf[INET_ADDRSTRLEN];
183 	char			 maskbuf[INET_ADDRSTRLEN];
184 	char			 routerbuf[INET_ADDRSTRLEN];
185 
186 	switch (imsg->hdr.type) {
187 	case IMSG_CTL_SHOW_INTERFACE_INFO:
188 		cei = imsg->data;
189 
190 		if (if_count++ > 0)
191 			printf("\n");
192 
193 		bufp = if_indextoname(cei->if_index, buf);
194 		printf("%s [%s]:\n", bufp != NULL ? bufp : "unknown",
195 		    cei->state);
196 		memset(ipbuf, 0, sizeof(ipbuf));
197 		if (cei->server_identifier.s_addr != INADDR_ANY) {
198 			if (inet_ntop(AF_INET, &cei->server_identifier, ipbuf,
199 			    sizeof(ipbuf)) == NULL)
200 				ipbuf[0] = '\0';
201 		} else if (cei->dhcp_server.s_addr != INADDR_ANY) {
202 			if (inet_ntop(AF_INET, &cei->dhcp_server, ipbuf,
203 			    sizeof(ipbuf)) == NULL)
204 				ipbuf[0] = '\0';
205 		}
206 		if (ipbuf[0] != '\0')
207 			printf("\tserver: %s\n", ipbuf);
208 		if (cei->requested_ip.s_addr != INADDR_ANY) {
209 			clock_gettime(CLOCK_MONOTONIC, &now);
210 			timespecsub(&now, &cei->request_time, &diff);
211 			memset(ipbuf, 0, sizeof(ipbuf));
212 			memset(maskbuf, 0, sizeof(maskbuf));
213 			memset(routerbuf, 0, sizeof(routerbuf));
214 			if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf,
215 			    sizeof(ipbuf)) == NULL)
216 				ipbuf[0] = '\0';
217 			if (inet_ntop(AF_INET, &cei->mask, maskbuf,
218 			    sizeof(maskbuf)) == NULL)
219 				maskbuf[0] = '\0';
220 			if (inet_ntop(AF_INET, &cei->router, routerbuf,
221 			    sizeof(routerbuf)) == NULL)
222 				routerbuf[0] = '\0';
223 			printf("\t    IP: %s/%s\n", ipbuf, maskbuf);
224 			if (cei->router.s_addr != INADDR_ANY)
225 				printf("\trouter: %s\n", routerbuf);
226 			if (cei->nameservers[0].s_addr != INADDR_ANY) {
227 				printf("\t   DNS:");
228 				for (i = 0; i < MAX_RDNS_COUNT &&
229 				    cei->nameservers[i].s_addr != INADDR_ANY;
230 				    i++) {
231 					if (inet_ntop(AF_INET,
232 					    &cei->nameservers[i], ipbuf,
233 					    sizeof(ipbuf)) == NULL)
234 						continue;
235 					printf(" %s", ipbuf);
236 				}
237 				printf("\n");
238 			}
239 			s = cei->lease_time - diff.tv_sec;
240 			if (s < 0)
241 				s = 0;
242 			y = s / 31556926; s -= y * 31556926;
243 			d = s / 86400; s -= d * 86400;
244 			h = s / 3600; s -= h * 3600;
245 			m = s / 60; s -= m * 60;
246 
247 			printf("\t lease: ");
248 			if (y > 0)
249 				printf("%lldy ", y);
250 			if (d > 0)
251 				printf("%lldd ", d);
252 			if (h > 0)
253 				printf("%lldh ", h);
254 			if (m > 0)
255 				printf("%lldm ", m);
256 			if (s > 0)
257 				printf("%llds ", s);
258 			printf("\n");
259 		}
260 		break;
261 	case IMSG_CTL_END:
262 		return (1);
263 	default:
264 		break;
265 	}
266 
267 	return (0);
268 }
269