summaryrefslogtreecommitdiffstats
path: root/usr.sbin/switchctl/switchctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/switchctl/switchctl.c')
-rw-r--r--usr.sbin/switchctl/switchctl.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/usr.sbin/switchctl/switchctl.c b/usr.sbin/switchctl/switchctl.c
new file mode 100644
index 00000000000..2ed852cd313
--- /dev/null
+++ b/usr.sbin/switchctl/switchctl.c
@@ -0,0 +1,348 @@
+/* $OpenBSD: switchctl.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "switchd.h"
+#include "parser.h"
+
+__dead void usage(void);
+
+struct imsgname {
+ int type;
+ char *name;
+ void (*func)(struct imsg *);
+};
+
+int show_summary_msg(struct imsg *, int);
+
+struct imsgname *monitor_lookup(uint8_t);
+void monitor_id(struct imsg *);
+int monitor(struct imsg *);
+
+int ca_opt(struct parse_result *);
+
+struct imsgname imsgs[] = {
+ { IMSG_CTL_OK, "ok", NULL },
+ { IMSG_CTL_FAIL, "fail", NULL },
+ { IMSG_CTL_VERBOSE, "verbose", NULL },
+ { IMSG_CTL_RELOAD, "reload", NULL },
+ { IMSG_CTL_RESET, "reset", NULL },
+ { 0, NULL, NULL }
+
+};
+struct imsgname imsgunknown = {
+ -1, "<unknown>", NULL
+};
+
+struct imsgbuf *ibuf;
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-q] [-s socket] command [arg ...]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_un sun;
+ struct parse_result *res;
+ struct imsg imsg;
+ struct switch_device sdv;
+ struct switch_controller *swc;
+ int ctl_sock;
+ int done = 1;
+ int n;
+ int ch;
+ int v = 0;
+ int quiet = 0;
+ const char *sock = SWITCHD_SOCKET;
+
+ while ((ch = getopt(argc, argv, "qs:")) != -1) {
+ switch (ch) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 's':
+ sock = optarg;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* parse options */
+ if ((res = parse(argc, argv)) == NULL)
+ exit(1);
+
+ res->quiet = quiet;
+
+ switch (res->action) {
+ case NONE:
+ usage();
+ break;
+ default:
+ goto connect;
+ }
+
+ return (0);
+
+ connect:
+ /* connect to sdnflowd control socket */
+ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, sock, sizeof(sun.sun_path));
+ reconnect:
+ if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ /* Keep retrying if running in monitor mode */
+ if (res->action == MONITOR &&
+ (errno == ENOENT || errno == ECONNREFUSED)) {
+ usleep(100);
+ goto reconnect;
+ }
+ err(1, "connect: %s", sock);
+ }
+
+ if (res->ibuf != NULL)
+ ibuf = res->ibuf;
+ else
+ if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
+ err(1, "malloc");
+ imsg_init(ibuf, ctl_sock);
+
+ /* process user request */
+ switch (res->action) {
+ case RESETALL:
+ v = RESET_ALL;
+ break;
+ case LOG_VERBOSE:
+ v = 2;
+ break;
+ case LOG_BRIEF:
+ default:
+ v = 0;
+ break;
+ }
+
+ switch (res->action) {
+ case NONE:
+ usage();
+ /* NOTREACHED */
+ break;
+ case SHOW_SUM:
+ case SHOW_SWITCHES:
+ case SHOW_MACS:
+ imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
+ printf("%-4s\t%-4s\t%-8s\t%-24s\t%s\n",
+ "Switch", "Port", "Type", "Name", "Info");
+ done = 0;
+ break;
+ case ADD_DEVICE:
+ case REMOVE_DEVICE:
+ memset(&sdv, 0, sizeof(sdv));
+ swc = &sdv.sdv_swc;
+ if (res->path[0] != '/')
+ strlcpy(sdv.sdv_device, "/dev/",
+ sizeof(sdv.sdv_device));
+ if (strlcat(sdv.sdv_device, res->path,
+ sizeof(sdv.sdv_device)) >= sizeof(sdv.sdv_device))
+ errx(1, "path is too long");
+ if (res->action == REMOVE_DEVICE) {
+ imsg_compose(ibuf, IMSG_CTL_DEVICE_DISCONNECT, 0, 0, -1,
+ &sdv, sizeof(sdv));
+ break;
+ }
+ if (res->uri == NULL || res->uri[0] == '\0')
+ swc->swc_type = SWITCH_CONN_LOCAL;
+ else {
+ if (strncmp(res->uri, "tcp:", 4) == 0)
+ swc->swc_type = SWITCH_CONN_TCP;
+ else if (strncmp(res->uri, "tls:", 4) == 0)
+ swc->swc_type = SWITCH_CONN_TLS;
+ else
+ errx(1, "protocol field is unknown");
+
+ if (parsehostport(res->uri + 4,
+ (struct sockaddr *)&swc->swc_addr,
+ sizeof(swc->swc_addr)) != 0)
+ errx(1,
+ "couldn't parse name-or-address and port");
+ warnx("%s", sdv.sdv_device);
+ }
+ imsg_compose(ibuf, IMSG_CTL_DEVICE_CONNECT, 0, 0, -1,
+ &sdv, sizeof(sdv));
+ break;
+ case RESETALL:
+ imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, &v, sizeof(v));
+ printf("reset request sent.\n");
+ break;
+ case LOAD:
+ imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
+ res->path, strlen(res->path));
+ break;
+ case RELOAD:
+ imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
+ break;
+ case MONITOR:
+ imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
+ done = 0;
+ break;
+ case LOG_VERBOSE:
+ case LOG_BRIEF:
+ imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, &v, sizeof(v));
+ printf("logging request sent.\n");
+ break;
+ default:
+ break;
+ }
+
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+ err(1, "write error");
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1)
+ errx(1, "imsg_read error");
+ if (n == 0)
+ errx(1, "pipe closed");
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ errx(1, "imsg_get error");
+ if (n == 0)
+ break;
+ switch (res->action) {
+ case SHOW_SUM:
+ case SHOW_SWITCHES:
+ case SHOW_MACS:
+ done = show_summary_msg(&imsg, res->action);
+ break;
+ case MONITOR:
+ done = monitor(&imsg);
+ break;
+ default:
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ }
+ close(ctl_sock);
+ free(ibuf);
+
+ return (0);
+}
+
+int
+show_summary_msg(struct imsg *imsg, int type)
+{
+ struct switch_control *sw;
+ struct macaddr *mac;
+ struct timeval tv;
+ static unsigned int sw_id = 0;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SWITCH:
+ IMSG_SIZE_CHECK(imsg, sw);
+ sw = imsg->data;
+ sw_id = sw->sw_id;
+
+ if (!(type == SHOW_SUM || type == SHOW_SWITCHES))
+ break;
+ printf("%-4u\t%-4s\t%-8s\t%-24s\n",
+ sw->sw_id, "", "switch",
+ print_host(&sw->sw_addr, NULL, 0));
+ break;
+ case IMSG_CTL_MAC:
+ IMSG_SIZE_CHECK(imsg, mac);
+ mac = imsg->data;
+
+ if (!(type == SHOW_SUM || type == SHOW_MACS))
+ break;
+
+ getmonotime(&tv);
+ printf("%-4u\t%-4ld\t%-8s\t%-24s\tage %llds\n",
+ sw_id, mac->mac_port, "mac",
+ print_ether(mac->mac_addr),
+ (long long)tv.tv_sec - mac->mac_age);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ errx(1, "wrong message in summary: %u", imsg->hdr.type);
+ break;
+ }
+ return (0);
+}
+
+struct imsgname *
+monitor_lookup(uint8_t type)
+{
+ int i;
+
+ for (i = 0; imsgs[i].name != NULL; i++)
+ if (imsgs[i].type == type)
+ return (&imsgs[i]);
+ return (&imsgunknown);
+}
+
+int
+monitor(struct imsg *imsg)
+{
+ time_t now;
+ int done = 0;
+ struct imsgname *imn;
+
+ now = time(NULL);
+
+ imn = monitor_lookup(imsg->hdr.type);
+ printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
+ imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
+ printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
+ if (imn->type == -1)
+ done = 1;
+ if (imn->func != NULL)
+ (*imn->func)(imsg);
+
+ return (done);
+}