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