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