xref: /openbsd/usr.sbin/relayctl/relayctl.c (revision ec4c1254)
1 /*	$OpenBSD: relayctl.c,v 1.58 2017/11/29 15:24:50 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
7  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <sys/queue.h>
27 #include <sys/un.h>
28 
29 #include <arpa/inet.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <imsg.h>
40 
41 #include "relayd.h"
42 #include "parser.h"
43 
44 __dead void	 usage(void);
45 int		 show_summary_msg(struct imsg *, int);
46 int		 show_session_msg(struct imsg *);
47 int		 show_command_output(struct imsg *);
48 char		*print_rdr_status(int);
49 char		*print_host_status(int, int);
50 char		*print_table_status(int, int);
51 char		*print_relay_status(int);
52 void		 print_statistics(struct ctl_stats[PROC_MAX_INSTANCES + 1]);
53 
54 struct imsgname {
55 	int type;
56 	char *name;
57 	void (*func)(struct imsg *);
58 };
59 
60 struct imsgname *monitor_lookup(u_int8_t);
61 void		 monitor_host_status(struct imsg *);
62 void		 monitor_id(struct imsg *);
63 int		 monitor(struct imsg *);
64 
65 struct imsgname imsgs[] = {
66 	{ IMSG_HOST_STATUS,		"host_status",	monitor_host_status },
67 	{ IMSG_CTL_RDR_DISABLE,		"ctl_rdr_disable",	monitor_id },
68 	{ IMSG_CTL_RDR_ENABLE,		"ctl_rdr_enable",	monitor_id },
69 	{ IMSG_CTL_TABLE_DISABLE,	"ctl_table_disable",	monitor_id },
70 	{ IMSG_CTL_TABLE_ENABLE,	"ctl_table_enable",	monitor_id },
71 	{ IMSG_CTL_HOST_DISABLE,	"ctl_host_disable",	monitor_id },
72 	{ IMSG_CTL_HOST_ENABLE,		"ctl_host_enable",	monitor_id },
73 	{ IMSG_CTL_TABLE_CHANGED,	"ctl_table_changed",	monitor_id },
74 	{ IMSG_CTL_PULL_RULESET,	"ctl_pull_ruleset",	monitor_id },
75 	{ IMSG_CTL_PUSH_RULESET,	"ctl_push_ruleset",	monitor_id },
76 	{ IMSG_SYNC,			"sync",			NULL },
77 	{ 0,				NULL,			NULL }
78 };
79 struct imsgname imsgunknown = {
80 	-1,				"<unknown>",		NULL
81 };
82 
83 struct imsgbuf	*ibuf;
84 int error = 0;
85 
86 __dead void
usage(void)87 usage(void)
88 {
89 	extern char *__progname;
90 
91 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
92 	    __progname);
93 	exit(1);
94 }
95 
96 int
main(int argc,char * argv[])97 main(int argc, char *argv[])
98 {
99 	struct sockaddr_un	 sun;
100 	struct parse_result	*res;
101 	struct imsg		 imsg;
102 	int			 ctl_sock;
103 	int			 done = 0;
104 	int			 n, verbose = 0;
105 	int			 ch;
106 	const char		*sockname;
107 
108 	sockname = RELAYD_SOCKET;
109 	while ((ch = getopt(argc, argv, "s:")) != -1) {
110 		switch (ch) {
111 		case 's':
112 			sockname = optarg;
113 			break;
114 		default:
115 			usage();
116 			/* NOTREACHED */
117 		}
118 	}
119 	argc -= optind;
120 	argv += optind;
121 
122 	/* parse options */
123 	if ((res = parse(argc, argv)) == NULL)
124 		exit(1);
125 
126 	/* connect to relayd control socket */
127 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
128 		err(1, "socket");
129 
130 	bzero(&sun, sizeof(sun));
131 	sun.sun_family = AF_UNIX;
132 	if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
133 	    sizeof(sun.sun_path))
134 		errx(1, "socket `%s' too long", sockname);
135  reconnect:
136 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
137 		/* Keep retrying if running in monitor mode */
138 		if (res->action == MONITOR &&
139 		    (errno == ENOENT || errno == ECONNREFUSED)) {
140 			usleep(100);
141 			goto reconnect;
142 		}
143 		err(1, "connect: %s", sockname);
144 	}
145 
146 	if (pledge("stdio", NULL) == -1)
147 		err(1, "pledge");
148 
149 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
150 		err(1, NULL);
151 	imsg_init(ibuf, ctl_sock);
152 	done = 0;
153 
154 	/* process user request */
155 	switch (res->action) {
156 	case NONE:
157 		usage();
158 		/* not reached */
159 	case SHOW_SUM:
160 	case SHOW_HOSTS:
161 	case SHOW_RDRS:
162 	case SHOW_RELAYS:
163 	case SHOW_ROUTERS:
164 		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
165 		printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n",
166 		    "Id", "Type", "Name", "Avlblty");
167 		break;
168 	case SHOW_SESSIONS:
169 		imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
170 		break;
171 	case RDR_ENABLE:
172 		imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1,
173 		    &res->id, sizeof(res->id));
174 		break;
175 	case RDR_DISABLE:
176 		imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1,
177 		    &res->id, sizeof(res->id));
178 		break;
179 	case TABLE_ENABLE:
180 		imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1,
181 		    &res->id, sizeof(res->id));
182 		break;
183 	case TABLE_DISABLE:
184 		imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1,
185 		    &res->id, sizeof(res->id));
186 		break;
187 	case HOST_ENABLE:
188 		imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1,
189 		    &res->id, sizeof(res->id));
190 		break;
191 	case HOST_DISABLE:
192 		imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1,
193 		    &res->id, sizeof(res->id));
194 		break;
195 	case SHUTDOWN:
196 		imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
197 		break;
198 	case POLL:
199 		imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0);
200 		break;
201 	case LOAD:
202 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
203 		    res->path, strlen(res->path));
204 		done = 1;
205 		break;
206 	case RELOAD:
207 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
208 		done = 1;
209 		break;
210 	case MONITOR:
211 		imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
212 		break;
213 	case LOG_VERBOSE:
214 		verbose = 2;
215 		/* FALLTHROUGH */
216 	case LOG_BRIEF:
217 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
218 		    &verbose, sizeof(verbose));
219 		printf("logging request sent.\n");
220 		done = 1;
221 		break;
222 	}
223 
224 	while (ibuf->w.queued)
225 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
226 			err(1, "write error");
227 
228 	while (!done) {
229 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
230 			errx(1, "imsg_read error");
231 		if (n == 0)
232 			errx(1, "pipe closed");
233 
234 		while (!done) {
235 			if ((n = imsg_get(ibuf, &imsg)) == -1)
236 				errx(1, "imsg_get error");
237 			if (n == 0)
238 				break;
239 			switch (res->action) {
240 			case SHOW_SUM:
241 			case SHOW_HOSTS:
242 			case SHOW_RDRS:
243 			case SHOW_RELAYS:
244 			case SHOW_ROUTERS:
245 				done = show_summary_msg(&imsg, res->action);
246 				break;
247 			case SHOW_SESSIONS:
248 				done = show_session_msg(&imsg);
249 				break;
250 			case RDR_DISABLE:
251 			case RDR_ENABLE:
252 			case TABLE_DISABLE:
253 			case TABLE_ENABLE:
254 			case HOST_DISABLE:
255 			case HOST_ENABLE:
256 			case POLL:
257 			case SHUTDOWN:
258 				done = show_command_output(&imsg);
259 				break;
260 			case NONE:
261 			case LOG_VERBOSE:
262 			case LOG_BRIEF:
263 			case RELOAD:
264 			case LOAD:
265 				break;
266 			case MONITOR:
267 				done = monitor(&imsg);
268 				break;
269 			}
270 			imsg_free(&imsg);
271 		}
272 	}
273 	close(ctl_sock);
274 	free(ibuf);
275 
276 	return (error ? 1 : 0);
277 }
278 
279 struct imsgname *
monitor_lookup(u_int8_t type)280 monitor_lookup(u_int8_t type)
281 {
282 	int i;
283 
284 	for (i = 0; imsgs[i].name != NULL; i++)
285 		if (imsgs[i].type == type)
286 			return (&imsgs[i]);
287 	return (&imsgunknown);
288 }
289 
290 void
monitor_host_status(struct imsg * imsg)291 monitor_host_status(struct imsg *imsg)
292 {
293 	struct ctl_status	 cs;
294 
295 	memcpy(&cs, imsg->data, sizeof(cs));
296 	printf("\tid: %u\n", cs.id);
297 	printf("\tstate: ");
298 	switch (cs.up) {
299 	case HOST_UP:
300 		printf("up\n");
301 		break;
302 	case HOST_DOWN:
303 		printf("down\n");
304 		break;
305 	default:
306 		printf("unknown\n");
307 		break;
308 	}
309 }
310 
311 void
monitor_id(struct imsg * imsg)312 monitor_id(struct imsg *imsg)
313 {
314 	struct ctl_id		 id;
315 
316 	memcpy(&id, imsg->data, sizeof(id));
317 	printf("\tid: %u\n", id.id);
318 	if (strlen(id.name))
319 		printf("\tname: %s\n", id.name);
320 }
321 
322 int
monitor(struct imsg * imsg)323 monitor(struct imsg *imsg)
324 {
325 	time_t			 now;
326 	int			 done = 0;
327 	struct imsgname		*imn;
328 
329 	now = time(NULL);
330 
331 	imn = monitor_lookup(imsg->hdr.type);
332 	printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
333 	    imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
334 	printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
335 	if (imn->type == -1)
336 		done = 1;
337 	if (imn->func != NULL)
338 		(*imn->func)(imsg);
339 
340 	return (done);
341 }
342 
343 int
show_summary_msg(struct imsg * imsg,int type)344 show_summary_msg(struct imsg *imsg, int type)
345 {
346 	struct rdr		*rdr;
347 	struct table		*table;
348 	struct host		*host;
349 	struct relay		*rlay;
350 	struct router		*rt;
351 	struct netroute		*nr;
352 	struct ctl_stats	 stats[PROC_MAX_INSTANCES];
353 	char			 name[HOST_NAME_MAX+1];
354 
355 	switch (imsg->hdr.type) {
356 	case IMSG_CTL_RDR:
357 		if (!(type == SHOW_SUM || type == SHOW_RDRS))
358 			break;
359 		rdr = imsg->data;
360 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
361 		    rdr->conf.id, "redirect", rdr->conf.name, "",
362 		    print_rdr_status(rdr->conf.flags));
363 		break;
364 	case IMSG_CTL_TABLE:
365 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
366 			break;
367 		table = imsg->data;
368 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
369 		    table->conf.id, "table", table->conf.name, "",
370 		    print_table_status(table->up, table->conf.flags));
371 		break;
372 	case IMSG_CTL_HOST:
373 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
374 			break;
375 		host = imsg->data;
376 		if (host->conf.parentid)
377 			snprintf(name, sizeof(name), "%s parent %u",
378 			    host->conf.name, host->conf.parentid);
379 		else
380 			strlcpy(name, host->conf.name, sizeof(name));
381 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
382 		    host->conf.id, "host", name,
383 		    print_availability(host->check_cnt, host->up_cnt),
384 		    print_host_status(host->up, host->flags));
385 		if (type == SHOW_HOSTS && host->check_cnt) {
386 			printf("\t%8s\ttotal: %lu/%lu checks",
387 			    "", host->up_cnt, host->check_cnt);
388 			if (host->retry_cnt)
389 				printf(", %d retries", host->retry_cnt);
390 			if (host->he && host->up == HOST_DOWN)
391 				printf(", error: %s", host_error(host->he));
392 			printf("\n");
393 		}
394 		break;
395 	case IMSG_CTL_RELAY:
396 		if (!(type == SHOW_SUM || type == SHOW_RELAYS))
397 			break;
398 		rlay = imsg->data;
399 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
400 		    rlay->rl_conf.id, "relay", rlay->rl_conf.name, "",
401 		    print_relay_status(rlay->rl_conf.flags));
402 		break;
403 	case IMSG_CTL_RDR_STATS:
404 		if (type != SHOW_RDRS)
405 			break;
406 		bcopy(imsg->data, &stats[0], sizeof(stats[0]));
407 		stats[1].id = EMPTY_ID;
408 		print_statistics(stats);
409 		break;
410 	case IMSG_CTL_RELAY_STATS:
411 		if (type != SHOW_RELAYS)
412 			break;
413 		bcopy(imsg->data, &stats, sizeof(stats));
414 		print_statistics(stats);
415 		break;
416 	case IMSG_CTL_ROUTER:
417 		if (!(type == SHOW_SUM || type == SHOW_ROUTERS))
418 			break;
419 		rt = imsg->data;
420 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
421 		    rt->rt_conf.id, "router", rt->rt_conf.name, "",
422 		    print_relay_status(rt->rt_conf.flags));
423 		if (type != SHOW_ROUTERS)
424 			break;
425 		if (rt->rt_conf.rtable)
426 			printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable);
427 		if (strlen(rt->rt_conf.label))
428 			printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label);
429 		break;
430 	case IMSG_CTL_NETROUTE:
431 		if (type != SHOW_ROUTERS)
432 			break;
433 		nr = imsg->data;
434 		(void)print_host(&nr->nr_conf.ss, name, sizeof(name));
435 		printf("\t%8s\troute: %s/%d\n",
436 		    "", name, nr->nr_conf.prefixlen);
437 		break;
438 	case IMSG_CTL_END:
439 		return (1);
440 	default:
441 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
442 		break;
443 	}
444 	return (0);
445 }
446 
447 int
show_session_msg(struct imsg * imsg)448 show_session_msg(struct imsg *imsg)
449 {
450 	struct rsession		*con;
451 	char			 a[128], b[128];
452 	struct timeval		 tv_now;
453 
454 	switch (imsg->hdr.type) {
455 	case IMSG_CTL_SESSION:
456 		con = imsg->data;
457 
458 		(void)print_host(&con->se_in.ss, a, sizeof(a));
459 		(void)print_host(&con->se_out.ss, b, sizeof(b));
460 		printf("session %u:%u %s:%u -> %s:%u\t%s\n",
461 		    imsg->hdr.peerid, con->se_id,
462 		    a, ntohs(con->se_in.port), b, ntohs(con->se_out.port),
463 		    con->se_done ? "DONE" : "RUNNING");
464 
465 		getmonotime(&tv_now);
466 		print_time(&tv_now, &con->se_tv_start, a, sizeof(a));
467 		print_time(&tv_now, &con->se_tv_last, b, sizeof(b));
468 		printf("\tage %s, idle %s, relay %u, pid %u",
469 		    a, b, con->se_relayid, con->se_pid);
470 		/* XXX grab tagname instead of tag id */
471 		if (con->se_tag)
472 			printf(", tag (id) %u", con->se_tag);
473 		printf("\n");
474 		break;
475 	case IMSG_CTL_END:
476 		return (1);
477 	default:
478 		errx(1, "wrong message in session: %u", imsg->hdr.type);
479 		break;
480 	}
481 	return (0);
482 }
483 
484 int
show_command_output(struct imsg * imsg)485 show_command_output(struct imsg *imsg)
486 {
487 	switch (imsg->hdr.type) {
488 	case IMSG_CTL_OK:
489 		printf("command succeeded\n");
490 		break;
491 	case IMSG_CTL_FAIL:
492 		printf("command failed\n");
493 		error++;
494 		break;
495 	default:
496 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
497 	}
498 	return (1);
499 }
500 
501 char *
print_rdr_status(int flags)502 print_rdr_status(int flags)
503 {
504 	if (flags & F_DISABLE) {
505 		return ("disabled");
506 	} else if (flags & F_DOWN) {
507 		return ("down");
508 	} else if (flags & F_BACKUP) {
509 		return ("active (using backup table)");
510 	} else
511 		return ("active");
512 }
513 
514 char *
print_table_status(int up,int fl)515 print_table_status(int up, int fl)
516 {
517 	static char buf[1024];
518 
519 	bzero(buf, sizeof(buf));
520 
521 	if (fl & F_DISABLE) {
522 		snprintf(buf, sizeof(buf) - 1, "disabled");
523 	} else if (!up) {
524 		snprintf(buf, sizeof(buf) - 1, "empty");
525 	} else
526 		snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up);
527 	return (buf);
528 }
529 
530 char *
print_host_status(int status,int fl)531 print_host_status(int status, int fl)
532 {
533 	if (fl & F_DISABLE)
534 		return ("disabled");
535 
536 	switch (status) {
537 	case HOST_DOWN:
538 		return ("down");
539 	case HOST_UNKNOWN:
540 		return ("unknown");
541 	case HOST_UP:
542 		return ("up");
543 	default:
544 		errx(1, "invalid status: %d", status);
545 	}
546 }
547 
548 char *
print_relay_status(int flags)549 print_relay_status(int flags)
550 {
551 	if (flags & F_DISABLE) {
552 		return ("disabled");
553 	} else
554 		return ("active");
555 }
556 
557 void
print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES+1])558 print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES + 1])
559 {
560 	struct ctl_stats	 crs;
561 	int			 i;
562 
563 	bzero(&crs, sizeof(crs));
564 	crs.interval = stats[0].interval;
565 	for (i = 0; stats[i].id != EMPTY_ID; i++) {
566 		crs.cnt += stats[i].cnt;
567 		crs.last += stats[i].last;
568 		crs.avg += stats[i].avg;
569 		crs.last_hour += stats[i].last_hour;
570 		crs.avg_hour += stats[i].avg_hour;
571 		crs.last_day += stats[i].last_day;
572 		crs.avg_day += stats[i].avg_day;
573 	}
574 	if (crs.cnt == 0)
575 		return;
576 	printf("\t%8s\ttotal: %llu sessions\n"
577 	    "\t%8s\tlast: %u/%llus %u/h %u/d sessions\n"
578 	    "\t%8s\taverage: %u/%llus %u/h %u/d sessions\n",
579 	    "", crs.cnt,
580 	    "", crs.last, crs.interval,
581 	    crs.last_hour, crs.last_day,
582 	    "", crs.avg, crs.interval,
583 	    crs.avg_hour, crs.avg_day);
584 }
585