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