1 /* $OpenBSD: relayctl.c,v 1.63 2024/11/21 13:38:15 claudio 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 if (imsgbuf_init(ibuf, ctl_sock) == -1)
152 err(1, NULL);
153 done = 0;
154
155 /* process user request */
156 switch (res->action) {
157 case NONE:
158 usage();
159 /* not reached */
160 case SHOW_SUM:
161 case SHOW_HOSTS:
162 case SHOW_RDRS:
163 case SHOW_RELAYS:
164 case SHOW_ROUTERS:
165 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
166 printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n",
167 "Id", "Type", "Name", "Avlblty");
168 break;
169 case SHOW_SESSIONS:
170 imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
171 break;
172 case RDR_ENABLE:
173 imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1,
174 &res->id, sizeof(res->id));
175 break;
176 case RDR_DISABLE:
177 imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1,
178 &res->id, sizeof(res->id));
179 break;
180 case TABLE_ENABLE:
181 imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1,
182 &res->id, sizeof(res->id));
183 break;
184 case TABLE_DISABLE:
185 imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1,
186 &res->id, sizeof(res->id));
187 break;
188 case HOST_ENABLE:
189 imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1,
190 &res->id, sizeof(res->id));
191 break;
192 case HOST_DISABLE:
193 imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1,
194 &res->id, sizeof(res->id));
195 break;
196 case SHUTDOWN:
197 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
198 break;
199 case POLL:
200 imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0);
201 break;
202 case LOAD:
203 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
204 res->path, strlen(res->path));
205 done = 1;
206 break;
207 case RELOAD:
208 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
209 done = 1;
210 break;
211 case MONITOR:
212 imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
213 break;
214 case LOG_VERBOSE:
215 verbose = 2;
216 /* FALLTHROUGH */
217 case LOG_BRIEF:
218 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
219 &verbose, sizeof(verbose));
220 printf("logging request sent.\n");
221 done = 1;
222 break;
223 }
224
225 if (imsgbuf_flush(ibuf) == -1)
226 err(1, "write error");
227
228 while (!done) {
229 if ((n = imsgbuf_read(ibuf)) == -1)
230 err(1, "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