--- /dev/null
+# $OpenBSD: Makefile,v 1.1 2016/07/19 16:54:26 reyk Exp $
+
+NAME= switch
+
+.PATH: ${.CURDIR}/../${NAME}d
+
+PROG= ${NAME}ctl
+MAN= ${NAME}ctl.8
+SRCS= log.c ${NAME}ctl.c parser.c util.c
+
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+CFLAGS+= -DOFD_NAME=\"${NAME}\"
+CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../${NAME}d
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: parser.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 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/tree.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+#include <netdb.h>
+
+#include "switchd.h"
+#include "parser.h"
+
+enum token_type {
+ NOTOKEN,
+ ENDTOKEN,
+ KEYWORD,
+ PATH,
+ ADDRESS,
+ FQDN,
+ DEVICE,
+ URI
+};
+
+struct token {
+ enum token_type type;
+ const char *keyword;
+ int value;
+ const struct token *next;
+};
+
+static const struct token t_main[];
+static const struct token t_reset[];
+static const struct token t_log[];
+static const struct token t_load[];
+static const struct token t_show[];
+static const struct token t_device[];
+static const struct token t_device_add[];
+static const struct token t_device_remove[];
+static const struct token t_connect_to[];
+static const struct token t_uri[];
+
+static const struct token t_main[] = {
+ { KEYWORD, "device", NONE, t_device },
+ { KEYWORD, "load", LOAD, t_load },
+ { KEYWORD, "log", NONE, t_log },
+ { KEYWORD, "monitor", MONITOR, NULL },
+ { KEYWORD, "reload", RELOAD, NULL },
+ { KEYWORD, "reset", NONE, t_reset },
+ { KEYWORD, "show", NONE, t_show },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_log[] = {
+ { KEYWORD, "verbose", LOG_VERBOSE, NULL },
+ { KEYWORD, "brief", LOG_BRIEF, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_reset[] = {
+ { KEYWORD, "all", RESETALL, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_load[] = {
+ { PATH, "", NONE, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_opt_path[] = {
+ { NOTOKEN, "", NONE, NULL },
+ { PATH, "", NONE, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_show[] = {
+ { KEYWORD, "summary", SHOW_SUM, NULL },
+ { KEYWORD, "switches", SHOW_SWITCHES, NULL },
+ { KEYWORD, "macs", SHOW_MACS, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+static const struct token t_device[] = {
+ { KEYWORD, "add", ADD_DEVICE, t_device_add },
+ { KEYWORD, "remove", REMOVE_DEVICE, t_device_remove },
+ { ENDTOKEN, "", NONE, NULL }
+};
+static const struct token t_device_add[] = {
+ { DEVICE, "", NONE, t_connect_to },
+ { ENDTOKEN, "", NONE, NULL }
+};
+static const struct token t_device_remove[] = {
+ { DEVICE, "", NONE, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+static const struct token t_connect_to[] = {
+ { KEYWORD, "connect-to", NONE, t_uri },
+ { ENDTOKEN, "", NONE, NULL }
+};
+static const struct token t_uri[] = {
+ { URI, "", NONE, NULL },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static struct parse_result res;
+
+const struct token *match_token(char *, const struct token []);
+void show_valid_args(const struct token []);
+int parse_addr(const char *);
+
+struct parse_result *
+parse(int argc, char *argv[])
+{
+ const struct token *table = t_main;
+ const struct token *match;
+
+ bzero(&res, sizeof(res));
+
+ while (argc >= 0) {
+ if ((match = match_token(argv[0], table)) == NULL) {
+ fprintf(stderr, "valid commands/args:\n");
+ show_valid_args(table);
+ return (NULL);
+ }
+
+ argc--;
+ argv++;
+
+ if (match->type == NOTOKEN || match->next == NULL)
+ break;
+
+ table = match->next;
+ }
+
+ if (argc > 0) {
+ fprintf(stderr, "superfluous argument: %s\n", argv[0]);
+ return (NULL);
+ }
+
+ return (&res);
+}
+
+int
+parse_addr(const char *word)
+{
+ struct addrinfo hints, *r;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM; /* dummy */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(word, "0", &hints, &r) == 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+
+const struct token *
+match_token(char *word, const struct token table[])
+{
+ unsigned int i, match = 0;
+ const struct token *t = NULL;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ if (word == NULL || strlen(word) == 0) {
+ match++;
+ t = &table[i];
+ }
+ break;
+ case KEYWORD:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ strlen(word)) == 0) {
+ match++;
+ t = &table[i];
+ if (t->value)
+ res.action = t->value;
+ }
+ break;
+ case DEVICE:
+ case PATH:
+ if (!match && word != NULL && strlen(word) > 0) {
+ res.path = strdup(word);
+ match++;
+ t = &table[i];
+ }
+ break;
+ case ADDRESS:
+ case FQDN:
+ if (!match && word != NULL && strlen(word) > 0) {
+ parse_addr(word);
+ res.host = strdup(word);
+ if (parse_addr(word) == 0)
+ res.htype = HOST_IPADDR;
+ else
+ res.htype = HOST_FQDN;
+ match++;
+ t = &table[i];
+ }
+ break;
+ case URI:
+ if (!match && word != NULL && strlen(word) > 0) {
+ res.uri = strdup(word);
+ match++;
+ t = &table[i];
+ }
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+
+ if (match != 1) {
+ if (word == NULL)
+ fprintf(stderr, "missing argument:\n");
+ else if (match > 1)
+ fprintf(stderr, "ambiguous argument: %s\n", word);
+ else if (match < 1)
+ fprintf(stderr, "unknown argument: %s\n", word);
+ return (NULL);
+ }
+
+ return (t);
+}
+
+void
+show_valid_args(const struct token table[])
+{
+ int i;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ fprintf(stderr, " <cr>\n");
+ break;
+ case KEYWORD:
+ fprintf(stderr, " %s\n", table[i].keyword);
+ break;
+ case PATH:
+ fprintf(stderr, " <path>\n");
+ break;
+ case ADDRESS:
+ fprintf(stderr, " <ipaddr>\n");
+ break;
+ case FQDN:
+ fprintf(stderr, " <fqdn>\n");
+ break;
+ case DEVICE:
+ fprintf(stderr, " <device>\n");
+ break;
+ case URI:
+ fprintf(stderr, " <uri>\n");
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+}
--- /dev/null
+/* $OpenBSD: parser.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007-2015 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _SWITCHCTL_PARSER_H
+#define _SWITCHCTL_PARSER_H
+
+enum actions {
+ NONE,
+ SHOW_SUM,
+ SHOW_SWITCHES,
+ SHOW_MACS,
+ LOAD,
+ RELOAD,
+ MONITOR,
+ LOG_VERBOSE,
+ LOG_BRIEF,
+ RESETALL,
+ ADD_DEVICE,
+ REMOVE_DEVICE
+};
+
+struct parse_result {
+ enum actions action;
+ struct imsgbuf *ibuf;
+ char *path;
+ char *caname;
+ char *pass;
+ char *host;
+ char *peer;
+ char *uri;
+ int htype;
+ int quiet;
+};
+
+#define HOST_IPADDR 1
+#define HOST_FQDN 2
+
+struct parse_result *parse(int, char *[]);
+
+#endif /* _SWITCHCTL_PARSER_H */
--- /dev/null
+.\" $OpenBSD: switchctl.8,v 1.1 2016/07/19 16:54:26 reyk Exp $
+.\"
+.\" Copyright (c) 2007-2015 Reyk Floeter <reyk@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.
+.\"
+.Dd $Mdocdate: July 19 2016 $
+.Dt SWITCHCTL 8
+.Os
+.Sh NAME
+.Nm switchctl
+.Nd control the SDN flow controller
+.Sh SYNOPSIS
+.Nm
+.Op Fl q
+.Op Fl s Ar socket
+.Ar command
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm
+program controls the
+.Xr switchd 8
+daemon.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl q
+Don't ask for confirmation of any default options.
+.It Fl s Ar socket
+Use
+.Ar socket
+instead of the default
+.Pa /var/run/switchd.sock
+to communicate with
+.Xr switchd 8 .
+.El
+.Pp
+The following commands are available to control
+.Xr switchd 8 :
+.Bl -tag -width Ds
+.It Cm device add Ar filename
+Add new
+.Xr switch 4
+control device, for example
+.Pa /dev/switch0 .
+.It Cm load Ar filename
+Reload the configuration from the specified file.
+.It Cm log brief
+Disable verbose logging.
+.It Cm log verbose
+Enable verbose logging.
+.It Cm monitor
+Monitor internal messages of the
+.Xr switchd 8
+subsystems.
+.It Cm show macs
+Display all known mac addresses.
+.It Cm show summary
+Display a list of all switches and mac addresses.
+.It Cm show switches
+Display all known switches.
+.It Cm reload
+Reload the configuration from the default configuration file.
+.It Cm reset all
+Reset the running state.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/switchd.sockXX" -compact
+.It /etc/switchd.conf
+Active configuration.
+.It /var/run/switchd.sock
+default
+.Ux Ns -domain
+socket used for communication with
+.Xr switchd 8
+.El
+.Sh SEE ALSO
+.Xr bridge 4
+.Xr switchd 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 6.1 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq Mt reyk@openbsd.org .
--- /dev/null
+/* $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);
+}
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 2016/07/19 16:54:26 reyk Exp $
+
+NAME= switch
+
+PROG= ${NAME}d
+MAN= ${NAME}d.8 ${NAME}d.conf.5
+
+SRCS= imsg_util.c log.c packet.c switch.c timer.c util.c
+SRCS+= switchd.c ofp.c ofp10.c ofp13.c control.c proc.c
+SRCS+= ${.OBJDIR}/ofp_map.c ${.OBJDIR}/ofp10_map.c
+SRCS+= parse.y ofcconn.c
+
+LDADD= -levent -lutil
+DPADD= ${LIBEVENT} ${LIBUTIL}
+
+CFLAGS+= -DSWITCHD_NAME=\"${NAME}\"
+CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../${NAME}d
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+
+GENERATED= ofp_map.c ofp10_map.c
+CLEANFILES+= ${GENERATED} *~
+
+ofp_map.c: genmap.sh ${.CURDIR}/ofp.h
+ /bin/sh ${.CURDIR}/genmap.sh -i ${.CURDIR}/ofp.h -t ofp \
+ -m ${.CURDIR}/ofp_map.h -h '"ofp.h"' > $@
+ @touch $@
+
+ofp10_map.c: genmap.sh ${.CURDIR}/ofp10.h
+ /bin/sh ${.CURDIR}/genmap.sh -i ${.CURDIR}/ofp10.h -t ofp10 \
+ -m ${.CURDIR}/ofp_map.h -h '"ofp10.h"' > $@
+ @touch $@
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: control.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010-2016 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 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/queue.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <imsg.h>
+
+#include "proc.h"
+#include "switchd.h"
+
+#define CONTROL_BACKLOG 5
+
+struct ctl_connlist ctl_conns;
+
+void
+ control_accept(int, short, void *);
+struct ctl_conn
+ *control_connbyfd(int);
+void control_close(int, struct control_sock *);
+void control_dispatch_imsg(int, short, void *);
+void control_imsg_forward(struct imsg *);
+
+int
+control_init(struct privsep *ps, struct control_sock *cs)
+{
+ struct switchd *env = ps->ps_env;
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask, mode;
+
+ if (cs->cs_name == NULL)
+ return (0);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, cs->cs_name,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
+ log_warn("%s: %s name too long", __func__, cs->cs_name);
+ close(fd);
+ return (-1);
+ }
+
+ if (unlink(cs->cs_name) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, cs->cs_name);
+ close(fd);
+ return (-1);
+ }
+
+ if (cs->cs_restricted) {
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ } else {
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+ }
+
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("%s: bind: %s", __func__, cs->cs_name);
+ close(fd);
+ (void)umask(old_umask);
+ return (-1);
+ }
+ (void)umask(old_umask);
+
+ if (chmod(cs->cs_name, mode) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(cs->cs_name);
+ return (-1);
+ }
+
+ socket_set_blockmode(fd, BM_NONBLOCK);
+ cs->cs_fd = fd;
+ cs->cs_env = env;
+
+ return (0);
+}
+
+int
+control_listen(struct control_sock *cs)
+{
+ if (cs->cs_name == NULL)
+ return (0);
+
+ if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
+ log_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
+ control_accept, cs);
+ event_add(&cs->cs_ev, NULL);
+ evtimer_set(&cs->cs_evt, control_accept, cs);
+
+ return (0);
+}
+
+void
+control_cleanup(struct control_sock *cs)
+{
+ if (cs->cs_name == NULL)
+ return;
+ event_del(&cs->cs_ev);
+ event_del(&cs->cs_evt);
+ (void)unlink(cs->cs_name);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *arg)
+{
+ struct control_sock *cs = arg;
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ event_add(&cs->cs_ev, NULL);
+ if ((event & EV_TIMEOUT))
+ return;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ /*
+ * Pause accept if we are out of file descriptors, or
+ * libevent will haunt us here too.
+ */
+ if (errno == ENFILE || errno == EMFILE) {
+ struct timeval evtpause = { 1, 0 };
+
+ event_del(&cs->cs_ev);
+ evtimer_add(&cs->cs_evt, &evtpause);
+ } else if (errno != EWOULDBLOCK && errno != EINTR &&
+ errno != ECONNABORTED)
+ log_warn("%s: accept", __func__);
+ return;
+ }
+
+ socket_set_blockmode(connfd, BM_NONBLOCK);
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn("%s", __func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler = control_dispatch_imsg;
+ c->iev.events = EV_READ;
+ c->iev.data = cs;
+ event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+ c->iev.handler, c->iev.data);
+ event_add(&c->iev.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd, struct control_sock *cs)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ msgbuf_clear(&c->iev.ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+
+ /* Some file descriptors are available again. */
+ if (evtimer_pending(&cs->cs_evt, NULL)) {
+ evtimer_del(&cs->cs_evt);
+ event_add(&cs->cs_ev, NULL);
+ }
+
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *arg)
+{
+ struct control_sock *cs = arg;
+ struct switchd *env = cs->cs_env;
+ struct ctl_conn *c;
+ struct imsg imsg;
+ int n, v;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
+ control_close(fd, cs);
+ return;
+ }
+ }
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
+ control_close(fd, cs);
+ return;
+ }
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+ control_close(fd, cs);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ control_imsg_forward(&imsg);
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_SHOW_SUM:
+ /* Forward request and use control fd as _id_ */
+ proc_compose(&env->sc_ps, PROC_OFP,
+ imsg.hdr.type, &fd, sizeof(fd));
+ break;
+ case IMSG_CTL_DEVICE_CONNECT:
+ case IMSG_CTL_DEVICE_DISCONNECT:
+ proc_compose(&env->sc_ps, PROC_PARENT,
+ imsg.hdr.type, imsg.data, IMSG_DATA_SIZE(&imsg));
+ break;
+ case IMSG_CTL_NOTIFY:
+ if (c->flags & CTL_CONN_NOTIFY) {
+ log_debug("%s: "
+ "client requested notify more than once",
+ __func__);
+ imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
+ 0, 0, -1, NULL, 0);
+ break;
+ }
+ c->flags |= CTL_CONN_NOTIFY;
+ break;
+ case IMSG_CTL_VERBOSE:
+ IMSG_SIZE_CHECK(&imsg, &v);
+
+ memcpy(&v, imsg.data, sizeof(v));
+ log_verbose(v);
+
+ proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1);
+ proc_forward_imsg(&env->sc_ps, &imsg, PROC_OFP, -1);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d",
+ __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->iev);
+}
+
+void
+control_imsg_forward(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry)
+ if (c->flags & CTL_CONN_NOTIFY)
+ imsg_compose(&c->iev.ibuf, imsg->hdr.type,
+ 0, imsg->hdr.pid, -1, imsg->data,
+ imsg->hdr.len - IMSG_HEADER_SIZE);
+}
+
+int control_dispatch_ofp(int, struct privsep_proc *, struct imsg *);
+
+static struct privsep_proc procs[] = {
+ { "ofp", PROC_OFP, control_dispatch_ofp },
+ { "parent", PROC_PARENT, NULL },
+ { "ofcconn", PROC_OFCCONN, NULL }
+};
+
+pid_t
+control(struct privsep *ps, struct privsep_proc *p)
+{
+ return (proc_run(ps, p, procs, nitems(procs), NULL, NULL));
+}
+
+int
+control_dispatch_ofp(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ int cfd;
+ struct ctl_conn *c;
+ uint8_t *d = imsg->data;
+ size_t s;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SWITCH:
+ case IMSG_CTL_MAC:
+ IMSG_SIZE_CHECK(imsg, &cfd);
+ memcpy(&cfd, d, sizeof(cfd));
+
+ if ((c = control_connbyfd(cfd)) == NULL)
+ fatalx("invalid control connection");
+
+ s = IMSG_DATA_SIZE(imsg) - sizeof(cfd);
+ d += sizeof(cfd);
+ imsg_compose_event(&c->iev, imsg->hdr.type, 0, 0, -1, d, s);
+ return (0);
+ case IMSG_CTL_END:
+ IMSG_SIZE_CHECK(imsg, &cfd);
+ memcpy(&cfd, d, sizeof(cfd));
+
+ if ((c = control_connbyfd(cfd)) == NULL)
+ fatalx("invalid control connection");
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
--- /dev/null
+#!/bin/sh
+# $OpenBSD: genmap.sh,v 1.1 2016/07/19 16:54:26 reyk Exp $
+
+# Copyright (c) 2010-2013 Reyk Floeter <reyk@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.
+
+TOKEN=""
+MAPFILE=""
+INPUT=""
+HEADER=""
+
+args=`getopt i:o:h:t:m: $*`
+
+if [ $? -ne 0 ]; then
+ echo "usage: $0 -i input -h header -t token [-m mapfile]"
+ exit 1
+fi
+
+set -- $args
+while [ $# -ne 0 ]; do
+ case "$1" in
+ -i)
+ INPUT="$2"; shift; shift;
+ ;;
+ -h)
+ HEADER="$2"; shift; shift;
+ ;;
+ -t)
+ TOKEN="$2"; shift; shift;
+ ;;
+ -m)
+ MAPFILE="$2"; shift; shift;
+ ;;
+ --)
+ shift;
+ break
+ ;;
+ esac
+done
+
+if [ -z "$MAPFILE" ]; then
+ MAPFILE=$INPUT
+fi
+
+TOK=$(echo ${TOKEN} | tr "[:lower:]" "[:upper:]")
+tok=$(echo ${TOKEN} | tr "[:upper:]" "[:lower:]")
+INC="#include ${HEADER}"
+
+MAP=$(grep "struct constmap ${tok}_" $MAPFILE |
+ sed -Ee "s/.*${tok}_([^_]+)_map.*/\1/g")
+
+# Print license/copyright notice and headers
+cat <<EOF
+/* Automatically generated from $1, do not edit */
+EOF
+sed -n '1,/^ \*\//p' $INPUT
+cat <<EOF
+
+#include <sys/types.h>
+
+#include "types.h"
+${INC}
+
+EOF
+
+for i in $MAP; do
+ lower=$(echo $i | tr "[:upper:]" "[:lower:]")
+ upper=$(echo $i | tr "[:lower:]" "[:upper:]")
+
+ echo "struct constmap ${tok}_${lower}_map[] = {"
+
+ X="${TOK}_${upper}_"
+ grep "$X" $INPUT | grep -v '\\' | sed -Ee \
+ "s/#define.*${X}([^[:blank:]]+).*\/\* (.+) \*\/$\
+/ { ${X}\1, \"\1\", \"\2\" },/" | grep -v '\#define'
+
+ echo " { 0 }"
+ echo "};"
+done
--- /dev/null
+/* $OpenBSD: imsg_util.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010-2016 Reyk Floeter <reyk@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/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <event.h>
+#include <imsg.h>
+
+#include "switchd.h"
+
+/*
+ * Extending the imsg buffer API for internal use
+ */
+
+int
+ibuf_cat(struct ibuf *dst, struct ibuf *src)
+{
+ return (ibuf_add(dst, src->buf, ibuf_size(src)));
+}
+
+void
+ibuf_zero(struct ibuf *buf)
+{
+ memset(buf->buf, 0, buf->wpos);
+}
+
+void
+ibuf_reset(struct ibuf *buf)
+{
+ ibuf_zero(buf);
+ buf->rpos = buf->wpos = 0;
+}
+
+struct ibuf *
+ibuf_new(void *data, size_t len)
+{
+ struct ibuf *buf;
+
+ if ((buf = ibuf_dynamic(len,
+ SWITCHD_MSGBUF_MAX)) == NULL)
+ return (NULL);
+
+ ibuf_zero(buf);
+
+ if (data == NULL && len) {
+ if (ibuf_advance(buf, len) == NULL) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ } else {
+ if (ibuf_add(buf, data, len) != 0) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ }
+
+ return (buf);
+}
+
+struct ibuf *
+ibuf_static(void)
+{
+ struct ibuf *buf;
+
+ if ((buf = ibuf_open(SWITCHD_MSGBUF_MAX)) == NULL)
+ return (NULL);
+
+ ibuf_zero(buf);
+
+ return (buf);
+}
+
+void *
+ibuf_advance(struct ibuf *buf, size_t len)
+{
+ void *ptr;
+
+ if ((ptr = ibuf_reserve(buf, len)) != NULL)
+ memset(ptr, 0, len);
+
+ return (ptr);
+}
+
+void
+ibuf_release(struct ibuf *buf)
+{
+ if (buf == NULL)
+ return;
+ if (buf->buf != NULL)
+ free(buf->buf);
+ free(buf);
+}
+
+size_t
+ibuf_length(struct ibuf *buf)
+{
+ if (buf == NULL || buf->buf == NULL)
+ return (0);
+ return (ibuf_size(buf));
+}
+
+uint8_t *
+ibuf_data(struct ibuf *buf)
+{
+ return (ibuf_seek(buf, 0, 0));
+}
+
+void *
+ibuf_getdata(struct ibuf *buf, size_t len)
+{
+ void *data;
+
+ if ((data = ibuf_seek(buf, buf->rpos, len)) == NULL)
+ return (NULL);
+ buf->rpos += len;
+
+ return (data);
+}
+
+ssize_t
+ibuf_dataleft(struct ibuf *buf)
+{
+ return (buf->wpos - buf->rpos);
+}
+
+size_t
+ibuf_dataoffset(struct ibuf *buf)
+{
+ return (buf->rpos);
+}
+
+struct ibuf *
+ibuf_get(struct ibuf *buf, size_t len)
+{
+ void *data;
+
+ if ((data = ibuf_getdata(buf, len)) == NULL)
+ return (NULL);
+
+ return (ibuf_new(data, len));
+}
+
+struct ibuf *
+ibuf_dup(struct ibuf *buf)
+{
+ if (buf == NULL)
+ return (NULL);
+ return (ibuf_new(ibuf_data(buf), ibuf_size(buf)));
+}
+
+struct ibuf *
+ibuf_random(size_t len)
+{
+ struct ibuf *buf;
+ void *ptr;
+
+ if ((buf = ibuf_open(len)) == NULL)
+ return (NULL);
+ if ((ptr = ibuf_reserve(buf, len)) == NULL) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ arc4random_buf(ptr, len);
+ return (buf);
+}
+
+int
+ibuf_setsize(struct ibuf *buf, size_t len)
+{
+ if (len > buf->size)
+ return (-1);
+ buf->wpos = len;
+ return (0);
+}
+
+int
+ibuf_prepend(struct ibuf *buf, void *data, size_t len)
+{
+ struct ibuf *new;
+
+ /* Swap buffers (we could also use memmove here) */
+ if ((new = ibuf_new(data, len)) == NULL)
+ return (-1);
+ if (ibuf_cat(new, buf) == -1) {
+ ibuf_release(new);
+ return (-1);
+ }
+ free(buf->buf);
+ memcpy(buf, new, sizeof(*buf));
+ free(new);
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: log.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+
+int debug;
+int verbose;
+const char *log_procname;
+
+void log_init(int, int);
+void log_procinit(const char *);
+void log_verbose(int);
+void log_warn(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_warnx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_info(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_debug(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void logit(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void vlog(int, const char *, va_list)
+ __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+
+void
+log_init(int n_debug, int facility)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+ verbose = n_debug;
+ log_procinit(__progname);
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, facility);
+
+ tzset();
+}
+
+void
+log_procinit(const char *procname)
+{
+ if (procname != NULL)
+ log_procname = procname;
+}
+
+void
+log_verbose(int v)
+{
+ verbose = v;
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose > 1) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+static void
+vfatal(const char *emsg, va_list ap)
+{
+ static char s[BUFSIZ];
+ const char *sep;
+
+ if (emsg != NULL) {
+ (void)vsnprintf(s, sizeof(s), emsg, ap);
+ sep = ": ";
+ } else {
+ s[0] = '\0';
+ sep = "";
+ }
+ if (errno)
+ logit(LOG_CRIT, "%s: %s%s%s",
+ log_procname, s, sep, strerror(errno));
+ else
+ logit(LOG_CRIT, "%s%s%s", log_procname, sep, s);
+}
+
+void
+fatal(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vfatal(emsg, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+fatalx(const char *emsg, ...)
+{
+ va_list ap;
+
+ errno = 0;
+ va_start(ap, emsg);
+ vfatal(emsg, ap);
+ va_end(ap);
+ exit(1);
+}
--- /dev/null
+/* $OpenBSD: ofcconn.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2016 YASUOKA Masahiko <yasuoka@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/queue.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include "ofp.h"
+#include "ofp10.h"
+#include "types.h"
+#include "switchd.h"
+
+int ofcconn_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+
+static struct privsep_proc procs[] = {
+ { "parent", PROC_PARENT, ofcconn_dispatch_parent },
+ { "control", PROC_CONTROL, NULL },
+ { "ofp", PROC_OFP, NULL }
+};
+
+/* OpenFlow Channel Connection */
+struct ofcconn {
+ char *oc_device;
+ struct sockaddr_storage oc_peer;
+ int oc_sock;
+ int oc_devf;
+ int oc_sock_write_ready;
+ int oc_devf_write_ready;
+ int oc_connected;
+ int oc_conn_fails;
+ struct ibuf *oc_buf;
+ TAILQ_ENTRY(ofcconn) oc_next;
+ struct event oc_evsock;
+ struct event oc_evdevf;
+ struct event oc_evtimer;
+};
+
+TAILQ_HEAD(, ofcconn) ofcconn_list = TAILQ_HEAD_INITIALIZER(ofcconn_list);
+
+struct ofcconn *ofcconn_create(const char *, struct switch_controller *, int);
+int ofcconn_connect(struct ofcconn *);
+void ofcconn_on_sockio(int, short, void *);
+void ofcconn_on_devfio(int, short, void *);
+int ofcconn_write(struct ofcconn *);
+void ofcconn_connect_again(struct ofcconn *);
+void ofcconn_on_timer(int, short, void *);
+void ofcconn_reset_evsock(struct ofcconn *);
+void ofcconn_reset_evdevf(struct ofcconn *);
+void ofcconn_close(struct ofcconn *);
+void ofcconn_free(struct ofcconn *);
+void ofcconn_shutdown_all(void);
+int ofcconn_say_hello(struct ofcconn *);
+
+pid_t
+ofcconn_proc_init(struct privsep *ps, struct privsep_proc *p)
+{
+ return (proc_run(ps, p, procs, nitems(procs), NULL, NULL));
+}
+
+void
+ofcconn_proc_shutdown(void)
+{
+ struct ofcconn *e, *t;
+
+ TAILQ_FOREACH_SAFE(e, &ofcconn_list, oc_next, t) {
+ ofcconn_close(e);
+ ofcconn_free(e);
+ }
+}
+
+int
+ofcconn_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct ofcconn *conn;
+ struct switch_device *sdv;
+ struct switch_controller *swc;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_DEVICE_CONNECT:
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) {
+ log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: "
+ "invalid message size", __func__);
+ return (0);
+ }
+ sdv = imsg->data;
+ swc = &sdv->sdv_swc;
+ if ((conn = ofcconn_create(sdv->sdv_device, swc,
+ imsg->fd)) != NULL)
+ ofcconn_connect(conn);
+ return (0);
+ case IMSG_CTL_DEVICE_DISCONNECT:
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) {
+ log_warnx("%s: IMSG_CTL_DEVICE_DISCONNECT: "
+ "invalid message size", __func__);
+ return (0);
+ }
+ sdv = imsg->data;
+ TAILQ_FOREACH(conn, &ofcconn_list, oc_next) {
+ if (!strcmp(conn->oc_device, sdv->sdv_device))
+ break;
+ }
+ if (conn) {
+ log_warnx("%s: closed by request",
+ conn->oc_device);
+ ofcconn_close(conn);
+ ofcconn_free(conn);
+ }
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+struct ofcconn *
+ofcconn_create(const char *name, struct switch_controller *swc, int fd)
+{
+ struct ofcconn *oc = NULL;
+
+ if ((oc = calloc(1, sizeof(struct ofcconn))) == NULL) {
+ log_warn("%s: calloc() failed", __func__);
+ goto on_error;
+ }
+ if ((oc->oc_device = strdup(name)) == NULL) {
+ log_warn("%s: calloc() failed", __func__);
+ goto on_error;
+ }
+ if ((oc->oc_buf = ibuf_new(NULL, 0)) == NULL) {
+ log_warn("%s: ibuf_new() failed", __func__);
+ goto on_error;
+ }
+
+ oc->oc_sock = -1;
+ oc->oc_devf = fd;
+ TAILQ_INSERT_TAIL(&ofcconn_list, oc, oc_next);
+
+ memcpy(&oc->oc_peer, &swc->swc_addr, sizeof(oc->oc_peer));
+
+ if (ntohs(((struct sockaddr_in *)&oc->oc_peer)->sin_port) == 0)
+ ((struct sockaddr_in *)&oc->oc_peer)->sin_port =
+ htons(SWITCHD_CTLR_PORT);
+
+ evtimer_set(&oc->oc_evtimer, ofcconn_on_timer, oc);
+
+ return (oc);
+
+on_error:
+ if (oc != NULL) {
+ free(oc->oc_device);
+ ibuf_release(oc->oc_buf);
+ }
+ free(oc);
+
+ return (NULL);
+}
+
+int
+ofcconn_connect(struct ofcconn *oc)
+{
+ int sock = -1;
+ char buf[256];
+ struct timeval tv;
+
+ if ((sock = socket(oc->oc_peer.ss_family, SOCK_STREAM | SOCK_NONBLOCK,
+ IPPROTO_TCP)) == -1) {
+ log_warn("%s: opening of channel with %s failed. "
+ "socket()", oc->oc_device,
+ print_host(&oc->oc_peer, buf, sizeof(buf)));
+ goto on_error;
+ }
+
+ if (connect(sock, (struct sockaddr *)&oc->oc_peer,
+ oc->oc_peer.ss_len) == -1) {
+ if (errno != EINPROGRESS) {
+ log_warn("%s: opening OpenFlow channel with %s "
+ "failed. connect()", oc->oc_device,
+ print_host(&oc->oc_peer, buf, sizeof(buf)));
+ goto on_error;
+ }
+ }
+
+ oc->oc_sock = sock;
+ event_set(&oc->oc_evsock, oc->oc_sock, EV_READ|EV_WRITE,
+ ofcconn_on_sockio, oc);
+ event_set(&oc->oc_evdevf, oc->oc_devf, EV_READ|EV_WRITE,
+ ofcconn_on_devfio, oc);
+ event_add(&oc->oc_evdevf, NULL);
+ event_add(&oc->oc_evsock, NULL);
+
+ tv.tv_sec = SWITCHD_OFCCONN_TIMEOUT;
+ tv.tv_usec = 0;
+ event_add(&oc->oc_evtimer, &tv);
+
+ return (0);
+
+on_error:
+ if (sock >= 0)
+ close(sock);
+
+ oc->oc_conn_fails++;
+ ofcconn_connect_again(oc);
+
+ return (-1);
+}
+
+void
+ofcconn_on_sockio(int fd, short evmask, void *ctx)
+{
+ struct ofcconn *oc = ctx;
+ ssize_t sz;
+ size_t wpos;
+ char buf[256];
+ int err;
+ socklen_t optlen;
+
+ if (evmask & EV_WRITE) {
+ if (oc->oc_connected == 0) {
+ optlen = sizeof(err);
+ getsockopt(oc->oc_sock, SOL_SOCKET, SO_ERROR, &err,
+ &optlen);
+ if (err != 0) {
+ log_warnx("%s: opening OpenFlow channel with "
+ "%s failed: %s",
+ oc->oc_device, print_host(&oc->oc_peer,
+ buf, sizeof(buf)), strerror(err));
+ oc->oc_conn_fails++;
+ ofcconn_close(oc);
+ ofcconn_connect_again(oc);
+ return;
+ }
+ log_info("%s: OpenFlow channel with %s connected",
+ oc->oc_device,
+ print_host(&oc->oc_peer, buf, sizeof(buf)));
+ event_del(&oc->oc_evtimer);
+ oc->oc_connected = 1;
+ oc->oc_conn_fails = 0;
+ if (ofcconn_say_hello(oc) != 0)
+ return;
+ } else {
+ oc->oc_sock_write_ready = 1;
+ /* schedule an event to reset the event handlers */
+ event_active(&oc->oc_evdevf, 0, 1);
+ }
+ }
+
+ if (evmask & EV_READ && ibuf_left(oc->oc_buf) > 0) {
+ wpos = ibuf_length(oc->oc_buf);
+
+ /* XXX temporally fix not to access unallocated area */
+ if (wpos + ibuf_left(oc->oc_buf) > oc->oc_buf->size) {
+ ibuf_reserve(oc->oc_buf, ibuf_left(oc->oc_buf));
+ ibuf_setsize(oc->oc_buf, wpos);
+ }
+
+ if ((sz = read(oc->oc_sock,
+ ibuf_data(oc->oc_buf) + wpos,
+ ibuf_left(oc->oc_buf))) <= 0) {
+ if (sz == 0)
+ log_warnx("%s: OpenFlow channel is closed by "
+ "peer",
+ oc->oc_device);
+ else
+ log_warn("%s: OpenFlow channel read error",
+ oc->oc_device);
+ goto on_fail;
+ }
+ if (ibuf_setsize(oc->oc_buf, wpos + sz) == -1)
+ goto on_fail;
+ if (oc->oc_devf_write_ready) {
+ if (ofcconn_write(oc) == -1)
+ goto on_fail;
+ event_active(&oc->oc_evdevf, 0, 1);
+ }
+ }
+ ofcconn_reset_evsock(oc);
+
+ return;
+on_fail:
+ ofcconn_close(oc);
+ ofcconn_connect_again(oc);
+}
+
+void
+ofcconn_on_devfio(int fd, short evmask, void *ctx)
+{
+ struct ofcconn *oc = ctx;
+ static char buf[65536];/* max size of OpenFlow message */
+ size_t sz, sz2;
+ struct ofp_header *hdr;
+
+ if (evmask & EV_WRITE) {
+ oc->oc_devf_write_ready = 1;
+ if (ofcconn_write(oc) == -1)
+ goto on_fail;
+ }
+
+ if (evmask & EV_READ && oc->oc_sock_write_ready) {
+ if ((sz = read(oc->oc_devf, buf, sizeof(buf))) <= 0) {
+ if (sz < 0)
+ log_warn("%s: %s read()", __func__,
+ oc->oc_device);
+ goto on_fail;
+ }
+ hdr = (struct ofp_header *)buf;
+ if (hdr->oh_type == OFP_T_HELLO)
+ goto dont_forward;
+ if ((sz2 = write(oc->oc_sock, buf, sz)) != sz) {
+ log_warn("%s: %s write()", __func__, oc->oc_device);
+ goto on_fail;
+ }
+ oc->oc_sock_write_ready = 0;
+ /* schedule an event to reset the event handlers */
+ event_active(&oc->oc_evsock, 0, 1);
+dont_forward: ;
+ }
+ ofcconn_reset_evdevf(oc);
+
+ return;
+on_fail:
+ ofcconn_close(oc);
+ ofcconn_connect_again(oc);
+}
+
+void
+ofcconn_connect_again(struct ofcconn *oc)
+{
+ struct timeval tv;
+ const int ofcconn_backoffs[] = { 1, 2, 4, 8, 16 };
+
+ tv.tv_sec = (oc->oc_conn_fails < (int)nitems(ofcconn_backoffs))
+ ? ofcconn_backoffs[oc->oc_conn_fails]
+ : ofcconn_backoffs[nitems(ofcconn_backoffs) - 1];
+ tv.tv_usec = 0;
+ event_add(&oc->oc_evtimer, &tv);
+}
+
+void
+ofcconn_on_timer(int fd, short evmask, void *ctx)
+{
+ struct ofcconn *oc = ctx;
+ char buf[256];
+
+ if (oc->oc_sock < 0)
+ ofcconn_connect(oc);
+ else if (!oc->oc_connected) {
+ log_warnx("%s: opening OpenFlow channel with %s failed: "
+ "timeout", oc->oc_device,
+ print_host(&oc->oc_peer, buf, sizeof(buf)));
+ ofcconn_close(oc);
+ oc->oc_conn_fails++;
+ ofcconn_connect_again(oc);
+ }
+}
+
+void
+ofcconn_reset_evsock(struct ofcconn *oc)
+{
+ short evmask = 0, oevmask;
+
+ oevmask = event_pending(&oc->oc_evsock, EV_READ|EV_WRITE, NULL);
+
+ if (ibuf_left(oc->oc_buf) > 0)
+ evmask |= EV_READ;
+ if (!oc->oc_sock_write_ready)
+ evmask |= EV_WRITE;
+
+ if (oevmask != evmask) {
+ if (oevmask)
+ event_del(&oc->oc_evsock);
+ event_set(&oc->oc_evsock, oc->oc_sock, evmask,
+ ofcconn_on_sockio, oc);
+ event_add(&oc->oc_evsock, NULL);
+ }
+}
+
+void
+ofcconn_reset_evdevf(struct ofcconn *oc)
+{
+ short evmask = 0, oevmask;
+
+ oevmask = event_pending(&oc->oc_evdevf, EV_READ|EV_WRITE, NULL);
+
+ if (oc->oc_sock_write_ready)
+ evmask |= EV_READ;
+ if (!oc->oc_devf_write_ready)
+ evmask |= EV_WRITE;
+
+ if (oevmask != evmask) {
+ if (oevmask)
+ event_del(&oc->oc_evdevf);
+ event_set(&oc->oc_evdevf, oc->oc_devf, evmask,
+ ofcconn_on_devfio, oc);
+ event_add(&oc->oc_evdevf, NULL);
+ }
+}
+
+int
+ofcconn_write(struct ofcconn *oc)
+{
+ struct ofp_header *hdr;
+ size_t sz, pktlen;
+ void *pkt;
+ /* XXX */
+ u_char buf[65535];
+ int remain = 0;
+
+ /* Try to write if the OFP header has arrived */
+ if (!oc->oc_devf_write_ready ||
+ (hdr = ibuf_seek(oc->oc_buf, 0, sizeof(*hdr))) == NULL)
+ return (0);
+
+ /* Check the length in the OFP header */
+ pktlen = ntohs(hdr->oh_length);
+
+ if ((pkt = ibuf_seek(oc->oc_buf, 0, pktlen)) != NULL) {
+ hdr = pkt;
+ if (hdr->oh_type == OFP_T_HELLO)
+ goto dont_forward;
+ /* Has entire packet already */
+ if ((sz = write(oc->oc_devf, pkt, pktlen)) != pktlen) {
+ log_warn("%s: %s(%d, %d)", __func__, oc->oc_device,
+ (int)sz, (int)pktlen);
+ return (-1);
+ }
+dont_forward:
+ /* XXX preserve the remaining part */
+ if ((remain = oc->oc_buf->wpos - pktlen) > 0)
+ memmove(buf, (caddr_t)pkt + pktlen, remain);
+ ibuf_reset(oc->oc_buf);
+ oc->oc_devf_write_ready = 0;
+ }
+ /* XXX put the remaining part again */
+ if (remain > 0)
+ ibuf_add(oc->oc_buf, buf, remain);
+
+ return (0);
+}
+
+void
+ofcconn_close(struct ofcconn *oc)
+{
+ if (oc->oc_sock >= 0) {
+ event_del(&oc->oc_evsock);
+ close(oc->oc_sock);
+ oc->oc_sock = -1;
+ oc->oc_sock_write_ready = 0;
+ }
+ event_del(&oc->oc_evdevf);
+ event_del(&oc->oc_evtimer);
+ oc->oc_connected = 0;
+}
+
+void
+ofcconn_free(struct ofcconn *oc)
+{
+ if (oc == NULL)
+ return;
+ close(oc->oc_devf);
+ TAILQ_REMOVE(&ofcconn_list, oc, oc_next);
+ ibuf_release(oc->oc_buf);
+ free(oc->oc_device);
+ free(oc);
+}
+
+int
+ofcconn_say_hello(struct ofcconn *oc)
+{
+ struct ofp_header hdr;
+ ssize_t sz;
+
+ hdr.oh_version = OFP_V_1_3;
+ hdr.oh_type = OFP_T_HELLO;
+ hdr.oh_length = htons(sizeof(hdr));
+ hdr.oh_xid = htonl(0xffffffffUL);
+
+ if ((sz = write(oc->oc_sock, &hdr, sizeof(hdr))) != sz) {
+ log_warn("%s: %s write()", __func__, oc->oc_device);
+ ofcconn_close(oc);
+ ofcconn_connect_again(oc);
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: ofp.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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/uio.h>
+#include <sys/un.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "ofp.h"
+#include "ofp10.h"
+#include "switchd.h"
+
+int ofp_dispatch_control(int, struct privsep_proc *, struct imsg *);
+int ofp_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+void ofp_init(struct privsep *, struct privsep_proc *, void *);
+int ofp_add_device(struct switchd *, int, const char *);
+
+static unsigned int id = 0;
+
+static struct privsep_proc procs[] = {
+ { "control", PROC_CONTROL, ofp_dispatch_control },
+ { "parent", PROC_PARENT, ofp_dispatch_parent },
+ { "ofcconn", PROC_OFCCONN, NULL }
+};
+
+static TAILQ_HEAD(, switch_connection) conn_head =
+ TAILQ_HEAD_INITIALIZER(conn_head);
+
+pid_t
+ofp(struct privsep *ps, struct privsep_proc *p)
+{
+ struct switchd *sc = ps->ps_env;
+ struct switch_server *srv = &sc->sc_server;
+ pid_t pid;
+
+ if ((sc->sc_tap = switchd_tap()) == -1)
+ fatal("tap");
+
+ log_info("listen on %s", print_host(&srv->srv_addr, NULL, 0));
+
+ if ((srv->srv_fd = switchd_listen((struct sockaddr *)
+ &srv->srv_addr)) == -1)
+ fatal("listen");
+
+ pid = proc_run(ps, p, procs, nitems(procs), ofp_init, NULL);
+ close(srv->srv_fd);
+
+ return (pid);
+}
+
+void
+ofp_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+ struct switchd *sc = ps->ps_env;
+ struct switch_server *srv = &sc->sc_server;
+
+ event_set(&srv->srv_ev, srv->srv_fd, EV_READ, ofp_accept, srv);
+ event_add(&srv->srv_ev, NULL);
+}
+
+int
+ofp_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_SUM:
+ return (switch_dispatch_control(fd, p, imsg));
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+ofp_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct switchd *sc = p->p_ps->ps_env;
+ struct switch_device *sdv;
+ struct switch_connection *c;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_DEVICE_CONNECT:
+ case IMSG_CTL_DEVICE_DISCONNECT:
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) {
+ log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: "
+ "message size is wrong", __func__);
+ return (0);
+ }
+ sdv = imsg->data;
+ if (imsg->hdr.type == IMSG_CTL_DEVICE_CONNECT)
+ ofp_add_device(sc, imsg->fd, sdv->sdv_device);
+ else {
+ TAILQ_FOREACH(c, &conn_head, con_next) {
+ if (c->con_peer.ss_family == AF_UNIX &&
+ strcmp(sdv->sdv_device,
+ ((struct sockaddr_un *)&c->con_peer)
+ ->sun_path) == 0)
+ break;
+ }
+ if (c)
+ ofp_close(c);
+ }
+ return (0);
+ default:
+
+ break;
+ }
+
+ return (-1);
+}
+
+void
+ofp_close(struct switch_connection *con)
+{
+ log_info("%s: connection %u closed", __func__, con->con_id);
+ switch_remove(con->con_sc, con->con_switch);
+ close(con->con_fd);
+ TAILQ_REMOVE(&conn_head, con, con_next);
+}
+
+void
+ofp_read(int fd, short event, void *arg)
+{
+ uint8_t buf[SWITCHD_READ_BUFFER];
+ struct switch_connection *con = arg;
+ struct switch_control *sw;
+ struct switchd *sc = con->con_sc;
+ struct ofp_header *oh;
+ ssize_t len;
+ struct ibuf *ibuf = NULL;
+
+ event_add(&con->con_ev, NULL);
+ if ((event & EV_TIMEOUT))
+ goto fail;
+
+ if ((len = read(fd, buf, sizeof(buf))) == -1)
+ goto fail;
+ if (len == 0)
+ return;
+
+ if ((ibuf = ibuf_new(buf, len)) == NULL)
+ goto fail;
+
+ sw = con->con_switch;
+ log_debug("%s: connection %d: %ld bytes from switch %u", __func__,
+ con->con_id, len, sw == NULL ? 0 : sw->sw_id);
+
+ if ((oh = ibuf_seek(ibuf, 0, sizeof(*oh))) == NULL) {
+ log_debug("short header");
+ goto fail;
+ }
+
+ switch (oh->oh_version) {
+ case OFP_V_1_0:
+ ofp10_input(sc, con, oh, ibuf);
+ break;
+ case OFP_V_1_3:
+ ofp13_input(sc, con, oh, ibuf);
+ break;
+ case OFP_V_1_1:
+ case OFP_V_1_2:
+ ofp10_debug(sc, &con->con_peer, &con->con_local, oh, ibuf);
+ /* FALLTHROUGH */
+ default:
+ ofp10_debug(sc, &con->con_peer, &con->con_local, oh, ibuf);
+ ofp10_hello(sc, con, oh, ibuf);
+ break;
+ }
+
+ return;
+
+ fail:
+ ibuf_release(ibuf);
+ ofp_close(con);
+}
+
+int
+ofp_send(struct switch_connection *con, struct ofp_header *oh,
+ struct ibuf *obuf)
+{
+ struct iovec iov[2];
+ int cnt = 0;
+ void *data;
+ size_t len;
+
+ if (oh != NULL) {
+ iov[cnt].iov_base = oh;
+ iov[cnt++].iov_len = sizeof(*oh);
+ }
+
+ if (ibuf_length(obuf)) {
+ if (oh != NULL && (ibuf_seek(obuf, 0, sizeof(*oh)) == NULL))
+ return (-1);
+ len = ibuf_dataleft(obuf);
+ if (len < 0) {
+ return (-1);
+ } else if (len > 0 &&
+ (data = ibuf_getdata(obuf, len)) != NULL) {
+ iov[cnt].iov_base = data;
+ iov[cnt++].iov_len = len;
+ }
+ }
+
+ if (cnt == 0)
+ return (-1);
+
+ /* XXX */
+ if (writev(con->con_fd, iov, cnt) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+ofp_add_device(struct switchd *sc, int fd, const char *name)
+{
+ struct switch_connection *con = NULL;
+ struct sockaddr_un *sun;
+ struct switch_control *sw;
+
+ if ((con = calloc(1, sizeof(*con))) == NULL) {
+ log_warn("calloc");
+ goto fail;
+ }
+ con->con_fd = fd;
+ con->con_sc = sc;
+ con->con_id = ++id;
+ sun = (struct sockaddr_un *)&con->con_peer;
+ sun->sun_family = AF_LOCAL;
+ strlcpy(sun->sun_path, name, sizeof(sun->sun_path));
+
+ /* Get associated switch, if it exists */
+ sw = switch_get(con);
+
+ log_info("%s: new device %u (%s) from switch %u",
+ __func__, con->con_id, name, sw == NULL ? 0 : sw->sw_id);
+
+ bzero(&con->con_ev, sizeof(con->con_ev));
+ event_set(&con->con_ev, con->con_fd, EV_READ, ofp_read, con);
+ event_add(&con->con_ev, NULL);
+
+ TAILQ_INSERT_TAIL(&conn_head, con, con_next);
+
+ return (0);
+fail:
+ if (fd != -1)
+ close(fd);
+ free(con);
+
+ return (-1);
+}
+
+void
+ofp_accept(int fd, short event, void *arg)
+{
+ struct switch_server *server = arg;
+ struct switch_connection *con = NULL;
+ struct switchd *sc = server->srv_sc;
+ struct switch_control *sw;
+ struct sockaddr_storage ss;
+ socklen_t slen;
+ int s;
+
+ event_add(&server->srv_ev, NULL);
+ if ((event & EV_TIMEOUT))
+ return;
+
+ /* XXX accept_reserve() */
+ slen = sizeof(ss);
+ if ((s = accept(fd, (struct sockaddr *)&ss, &slen)) == -1) {
+ log_warn("accept");
+ goto fail;
+ }
+
+ if ((con = calloc(1, sizeof(*con))) == NULL) {
+ log_warn("calloc");
+ goto fail;
+ }
+
+ slen = sizeof(con->con_local);
+ if (getsockname(s, (struct sockaddr *)&con->con_local, &slen) == -1) {
+ log_warn("getsockname");
+ goto fail;
+ }
+
+ con->con_fd = s;
+ con->con_sc = sc;
+ con->con_id = ++id;
+ con->con_port = htons(socket_getport(&ss));
+ memcpy(&con->con_peer, &ss, sizeof(ss));
+
+ /* Get associated switch, if it exists */
+ sw = switch_get(con);
+
+ log_info("%s: new connection %u from switch %u",
+ __func__, con->con_id, sw == NULL ? 0 : sw->sw_id);
+
+ bzero(&con->con_ev, sizeof(con->con_ev));
+ event_set(&con->con_ev, con->con_fd, EV_READ, ofp_read, con);
+ event_add(&con->con_ev, NULL);
+
+ TAILQ_INSERT_TAIL(&conn_head, con, con_next);
+
+ return;
+ fail:
+ if (s != -1)
+ close(s);
+ free(con);
+}
--- /dev/null
+/* $OpenBSD: ofp.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _NET_OFP_H_
+#define _NET_OFP_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#define OFP_IFNAMSIZ 16 /* on-wire (not IF_NAMSIZE) */
+
+struct ofp_header {
+ uint8_t oh_version; /* OpenFlow version */
+ uint8_t oh_type; /* message type */
+ uint16_t oh_length; /* message length */
+ uint32_t oh_xid; /* transaction Id */
+} __packed;
+
+/* OpenFlow version */
+#define OFP_V_0 0x00 /* OpenFlow 0.0 */
+#define OFP_V_1_0 0x01 /* OpenFlow 1.0 */
+#define OFP_V_1_1 0x02 /* OpenFlow 1.1 */
+#define OFP_V_1_2 0x03 /* OpenFlow 1.2 */
+#define OFP_V_1_3 0x04 /* OpenFlow 1.3 */
+#define OFP_V_1_4 0x05 /* OpenFlow 1.4 */
+#define OFP_V_1_5 0x06 /* OpenFlow 1.5 */
+
+/* OpenFlow message type */
+#define OFP_T_HELLO 0 /* Hello */
+#define OFP_T_ERROR 1 /* Error */
+#define OFP_T_ECHO_REQUEST 2 /* Echo Request */
+#define OFP_T_ECHO_REPLY 3 /* Echo Reply */
+#define OFP_T_EXPERIMENTER 4 /* Vendor/Experimenter */
+#define OFP_T_FEATURES_REQUEST 5 /* Features Request (switch) */
+#define OFP_T_FEATURES_REPLY 6 /* Features Reply (switch) */
+#define OFP_T_GET_CONFIG_REQUEST 7 /* Get Config Request (switch) */
+#define OFP_T_GET_CONFIG_REPLY 8 /* Get Config Reply (switch) */
+#define OFP_T_SET_CONFIG 9 /* Set Config (switch) */
+#define OFP_T_PACKET_IN 10 /* Packet In (async) */
+#define OFP_T_FLOW_REMOVED 11 /* Flow Removed (async) */
+#define OFP_T_PORT_STATUS 12 /* Port Status (async) */
+#define OFP_T_PACKET_OUT 13 /* Packet Out (controller) */
+#define OFP_T_FLOW_MOD 14 /* Flow Mod (controller) */
+#define OFP_T_GROUP_MOD 15 /* Group Mod (controller) */
+#define OFP_T_PORT_MOD 16 /* Port Mod (controller) */
+#define OFP_T_TABLE_MOD 17 /* Table Mod (controller) */
+#define OFP_T_MULTIPART_REQUEST 18 /* Multipart Message Request */
+#define OFP_T_MULTIPART_REPLY 19 /* Multipart Message Request */
+#define OFP_T_BARRIER_REQUEST 20 /* Barrier Request */
+#define OFP_T_BARRIER_REPLY 21 /* Barrier Reply */
+#define OFP_T_QUEUE_GET_CONFIG_REQUEST 22 /* Queue Get Config Request */
+#define OFP_T_QUEUE_GET_CONFIG_REPLY 23 /* Queue Get Config Reply */
+#define OFP_T_ROLE_REQUEST 24 /* Role Request */
+#define OFP_T_ROLE_REPLY 25 /* Role Reply */
+#define OFP_T_GET_ASYNC_REQUEST 26 /* Get Async Request */
+#define OFP_T_GET_ASYNC_REPLY 27 /* Get Async Reply */
+#define OFP_T_SET_ASYNC 28 /* Set Async */
+#define OFP_T_METER_MOD 29 /* Meter Mod */
+#define OFP_T_TYPE_MAX 30
+
+/* OpenFlow Hello Message */
+struct ofp_hello_element_header {
+ uint16_t he_type;
+ uint16_t he_length;
+};
+
+#define OPF_HELLO_T_VERSION_BITMAP 1 /* Supported version bitmap */
+
+struct ofp_hello_element_versionbitmap {
+ uint16_t hev_type;
+ uint16_t hev_length;
+};
+
+/* Ports */
+#define OFP_PORT_MAX 0xffffff00 /* Maximum number of physical ports */
+#define OFP_PORT_INPUT 0xfffffff8 /* Send back to input port */
+#define OFP_PORT_FLOWTABLE 0xfffffff9 /* Perform actions in flow table */
+#define OFP_PORT_NORMAL 0xfffffffa /* Let switch decide */
+#define OFP_PORT_FLOOD 0xfffffffb /* All non-block ports except input */
+#define OFP_PORT_ALL 0xfffffffc /* All ports except input */
+#define OFP_PORT_CONTROLLER 0xfffffffd /* Send to controller */
+#define OFP_PORT_LOCAL 0xfffffffe /* Local virtual OpenFlow port */
+#define OFP_PORT_ANY 0xffffffff /* No port */
+
+/* Switch Config Message (reply) */
+struct ofp_switch_config {
+ struct ofp_header cfg_oh; /* OpenFlow header */
+ uint16_t cfg_flags; /* Configuration flags */
+ uint16_t cfg_miss_send_len; /* Max bytes from datapath */
+} __packed;
+
+/* Switch Config */
+#define OFP_CONFIG_FRAG_NORMAL 0x0000 /* No special frag handling */
+#define OFP_CONFIG_FRAG_DROP 0x0001 /* Drop fragments */
+#define OFP_CONFIG_FRAG_REASM 0x0002 /* Reassemble fragments */
+#define OFP_CONFIG_FRAG_MASK 0x0003 /* Fragment mask */
+
+/* Switch port description */
+struct ofp_switch_port {
+ uint32_t swp_number; /* Switch port number */
+ uint8_t swp_pad[4]; /* Padding */
+ uint8_t swp_macaddr[ETHER_ADDR_LEN]; /* Port MAC address */
+ uint8_t swp_pad2[2]; /* Padding */
+ char swp_name[OFP_IFNAMSIZ]; /* Switch port name */
+ uint32_t swp_config; /* Configuration flags */
+ uint32_t swp_state; /* State flags */
+ uint32_t swp_cur; /* Current features */
+ uint32_t swp_advertised; /* Advertised by the port */
+ uint32_t swp_supported; /* Supported by the port */
+ uint32_t swp_peer; /* Advertised by peer */
+ uint32_t swp_cur_speed; /* Current port speed in Kbps*/
+ uint32_t swp_max_speed; /* Support port max speed in Kbps*/
+};
+
+/* Physical port configuration */
+#define OFP_PORTCONFIG_PORT_DOWN 0x1 /* Port is down */
+#define OFP_PORTCONFIG_NO_STP 0x2 /* Disable STP on port */
+#define OFP_PORTCONFIG_NO_RECV 0x4 /* Drop everything except STP */
+#define OFP_PORTCONFIG_NO_RECV_STP 0x8 /* Drop received STP */
+#define OFP_PORTCONFIG_NO_FLOOD 0x10 /* Do not flood to this port */
+#define OFP_PORTCONFIG_NO_FWD 0x20 /* Drop packets to port */
+#define OFP_PORTCONFIG_NO_PACKET_IN 0x40 /* NO PACKET_IN on port */
+
+/* Physical port state */
+#define OFP_PORTSTATE_LINK_DOWN 0x1 /* Link not active */
+#define OFP_PORTSTATE_STP_LISTEN 0x000 /* Not learning or forwarding */
+#define OFP_PORTSTATE_STP_LEARN 0x100 /* Learning but not forwarding */
+#define OFP_PORTSTATE_STP_FORWARD 0x200 /* Learning and forwarding */
+#define OFP_PORTSTATE_STP_BLOCK 0x300 /* Not part of spanning tree */
+#define OFP_PORTSTATE_STP_MASK 0x300 /* Spanning tree values */
+
+/* Physical port media types */
+#define OFP_PORTMEDIA_10MB_HD 0x1 /* 10 Mb half-duplex */
+#define OFP_PORTMEDIA_10MB_FD 0x2 /* 10 Mb full-duplex */
+#define OFP_PORTMEDIA_100MB_HD 0x4 /* 100 Mb half-duplex */
+#define OFP_PORTMEDIA_100MB_FD 0x8 /* 100 Mb full-duplex */
+#define OFP_PORTMEDIA_1GB_HD 0x10 /* 1 Gb half-duplex */
+#define OFP_PORTMEDIA_1GB_FD 0x20 /* 1 Gb full-duplex */
+#define OFP_PORTMEDIA_10GB_FD 0x40 /* 10 Gb full-duplex */
+#define OFP_PORTMEDIA_COPPER 0x80 /* Copper */
+#define OFP_PORTMEDIA_FIBER 0x100 /* Fiber */
+#define OFP_PORTMEDIA_AUTONEG 0x200 /* Auto-negotiation */
+#define OFP_PORTMEDIA_PAUSE 0x400 /* Pause */
+#define OFP_PORTMEDIA_PAUSE_ASYM 0x800 /* Asymmetric pause */
+
+/* Switch Features Message (reply) */
+struct ofp_switch_features {
+ struct ofp_header swf_oh; /* OpenFlow header */
+ uint64_t swf_datapath_id; /* Datapath unique ID */
+ uint32_t swf_nbuffers; /* Max packets buffered */
+ uint8_t swf_ntables; /* Number of supported tables */
+ uint8_t swf_aux_id; /* Identify auxiliary connections */
+ uint8_t swf_pad[2]; /* Align to 64 bits */
+ uint32_t swf_capabilities; /* Capability flags */
+ uint32_t swf_actions; /* Supported action flags */
+};
+
+/* Switch capabilities */
+#define OFP_SWCAP_FLOW_STATS 0x1 /* Flow statistics */
+#define OFP_SWCAP_TABLE_STATS 0x2 /* Table statistics */
+#define OFP_SWCAP_PORT_STATS 0x4 /* Port statistics */
+#define OFP_SWCAP_GROUP_STATS 0x8 /* Group statistics */
+#define OFP_SWCAP_IP_REASM 0x20 /* Can reassemble IP frags */
+#define OFP_SWCAP_QUEUE_STATS 0x40 /* Queue statistics */
+#define OFP_SWCAP_ARP_MATCH_IP 0x80 /* Match IP addresses in ARP pkts */
+#define OFP_SWCAP_PORT_BLOCKED 0x100 /* Switch will block ports */
+
+/* Flow matching */
+struct ofp_match {
+ uint16_t om_type;
+ uint16_t om_length;
+} __packed;
+
+/* Flow matching type type */
+#define OFP_MATCH_STANDARD 0 /* Standard match deprecated */
+#define OFP_MATCH_OXM 1 /* OpenFlow Extensible Match */
+
+/* Packet-In Message */
+struct ofp_packet_in {
+ struct ofp_header pin_oh; /* OpenFlow header */
+ uint32_t pin_buffer_id;
+ uint16_t pin_total_len;
+ uint8_t pin_reason;
+ uint8_t pin_table_id;
+ uint64_t pin_cookie;
+ struct ofp_match pin_match;
+} __packed;
+
+/* Reason */
+#define OFP_PKTIN_REASON_NO_MATCH 0 /* No matching flow */
+#define OFP_PKTIN_REASON_ACTION 1 /* Explicit output */
+#define OFP_PKTIN_REASON_TTL 2 /* Packet has invalid TTL */
+
+/* Flow Instruction */
+struct ofp_instruction {
+ uint16_t i_type;
+ uint16_t i_len;
+};
+
+/* Instruction types */
+#define OFP_INSTRUCTION_T_GOTO_TABLE 1 /* Goto-Table */
+#define OFP_INSTRUCTION_T_WRITE_META 2 /* Write-Metadata */
+#define OFP_INSTRUCTION_T_WRITE_ACTIONS 3 /* Write-Actions */
+#define OFP_INSTRUCTION_T_APPLY_ACTIONS 4 /* Apply-Actions */
+#define OFP_INSTRUCTION_T_CLEAR_ACTIONS 5 /* Clear-Actions */
+#define OFP_INSTRUCTION_T_METER 6 /* Meter */
+#define OFP_INSTRUCTION_T_EXPERIMENTER 0xffff /* Experimenter */
+
+/* Write-Metadata instruction */
+struct ofp_instruction_write_metadata {
+ uint16_t iwm_type;
+ uint16_t iwm_len;
+ uint8_t iwm_pad[4];
+ uint64_t iwm_metadata;
+ uint64_t iwm_metadata_mask;
+};
+
+/* Goto-Table instruction */
+struct ofp_instruction_goto_table {
+ uint16_t igt_type;
+ uint16_t igt_len;
+ uint8_t igt_table_id;
+ uint8_t igt_pad[3];
+};
+
+/* Apply-Actions instruction */
+struct ofp_instruction_actions {
+ uint16_t ia_type;
+ uint16_t ia_len;
+ uint8_t pad[4];
+};
+
+/* Meter instruction */
+struct ofp_instruction_meter {
+ uint16_t im_type;
+ uint16_t im_len;
+ uint32_t im_meter_id;
+};
+
+/* Experimenter instruction */
+struct ofp_instruction_experimenter {
+ uint16_t ie_type;
+ uint16_t ie_len;
+ uint32_t ie_experimenter;
+};
+
+/* Actions */
+#define OFP_ACTION_OUTPUT 0 /* Output to switch port */
+#define OFP_ACTION_COPY_TTL_OUT 11 /* */
+#define OFP_ACTION_COPY_TTL_IN 12 /* */
+#define OFP_ACTION_SET_MPLS_TTL 15 /* */
+#define OFP_ACTION_DEC_MPLS_TTL 16 /* */
+#define OFP_ACTION_PUSH_VLAN 17 /* */
+#define OFP_ACTION_POP_VLAN 18 /* */
+#define OFP_ACTION_PUSH_MPLS 19 /* */
+#define OFP_ACTION_POP_MPLS 20 /* */
+#define OFP_ACTION_SET_QUEUE 21 /* */
+#define OFP_ACTION_GROUP 22 /* */
+#define OFP_ACTION_SET_NW_TTL 23 /* */
+#define OFP_ACTION_DEC_NW_TTL 24 /* */
+#define OFP_ACTION_SET_FIELD 25 /* */
+#define OFP_ACTION_PUSH_PBB 26 /* */
+#define OFP_ACTION_POP_PBB 27 /* */
+#define OFP_ACTION_EXPERIMENTER 0xffff /* Vendor-specific action */
+
+/* Action Header */
+struct ofp_action_header {
+ uint16_t ah_type;
+ uint16_t ah_len;
+ uint32_t ah_pad;
+} __packed;
+
+#define OFP_CONTROLLER_MAXLEN_MAX 0xffe5
+#define OFP_CONTROLLER_MAXLEN_NO_BUFFER 0xffff
+
+/* Output Action */
+struct ofp_action_output {
+ uint16_t ao_type;
+ uint16_t ao_len;
+ uint32_t ao_port;
+ uint16_t ao_max_len;
+ uint8_t ao_pad[6];
+} __packed;
+
+struct ofp_action_mpls_ttl {
+ uint16_t amt_type;
+ uint16_t amt_len;
+ uint8_t amt_ttl;
+ uint8_t amt_pad[3];
+} __packed;
+
+struct ofp_action_push {
+ uint16_t ap_type;
+ uint16_t ap_len;
+ uint16_t ap_ethertype;
+ uint8_t pad[2];
+} __packed;
+
+struct ofp_action_pop_mpls {
+ uint16_t apm_type;
+ uint16_t apm_len;
+ uint16_t apm_ethertype;
+ uint8_t pad[2];
+} __packed;
+
+struct ofp_action_group {
+ uint16_t ag_type;
+ uint16_t ag_len;
+ uint32_t ag_group_id;
+} __packed;
+
+struct ofp_action_nw_ttl {
+ uint16_t ant_type;
+ uint16_t ant_len;
+ uint8_t ant_ttl;
+ uint8_t ant_pad[3];
+} __packed;
+
+struct ofp_action_set_field {
+ uint16_t asf_type;
+ uint16_t asf_len;
+ uint8_t asf_field[4];
+} __packed;
+
+/* Packet-Out Message */
+struct ofp_packet_out {
+ struct ofp_header pout_oh; /* OpenFlow header */
+ uint32_t pout_buffer_id;
+ uint32_t pout_in_port;
+ uint16_t pout_actions_len;
+ uint8_t pout_pad[6];
+ struct ofp_action_header pout_actions[0];
+ /* Followed by optional packet data if buffer_id == 0xffffffff */
+} __packed;
+
+/* Flow match fields for basic class */
+#define OFP_XM_T_IN_PORT 0
+#define OFP_XM_T_IN_PHY_PORT 1
+#define OFP_XM_T_META 2
+#define OFP_XM_T_ETH_DST 3
+#define OFP_XM_T_ETH_SRC 4
+#define OFP_XM_T_ETH_TYPE 5
+#define OFP_XM_T_VLAN_VID 6
+#define OFP_XM_T_VLAN_PCP 7
+#define OFP_XM_T_IP_DSCP 8
+#define OFP_XM_T_IP_ECN 9
+#define OFP_XM_T_IP_PROTO 10
+#define OFP_XM_T_IPV4_SRC 11
+#define OFP_XM_T_IPV4_DST 12
+#define OFP_XM_T_TCP_SRC 13
+#define OFP_XM_T_TCP_DST 14
+#define OFP_XM_T_UDP_SRC 15
+#define OFP_XM_T_UDP_DST 16
+#define OFP_XM_T_SCTP_SRC 17
+#define OFP_XM_T_SCTP_DST 18
+#define OFP_XM_T_ICMPV4_TYPE 19
+#define OFP_XM_T_ICMPV4_CODE 20
+#define OFP_XM_T_ARP_OP 21
+#define OFP_XM_T_ARP_SPA 22
+#define OFP_XM_T_ARP_TPA 23
+#define OFP_XM_T_ARP_SHA 24
+#define OFP_XM_T_ARP_THA 25
+#define OFP_XM_T_IPV6_SRC 26
+#define OFP_XM_T_IPV6_DST 27
+#define OFP_XM_T_IPV6_FLABEL 28
+#define OFP_XM_T_ICMPV6_TYPE 29
+#define OFP_XM_T_ICMPV6_CODE 30
+#define OFP_XM_T_IPV6_ND_TARGET 31
+#define OFP_XM_T_IPV6_ND_SLL 32
+#define OFP_XM_T_IPV6_ND_TLL 33
+#define OFP_XM_T_MPLS_LABEL 34
+#define OFP_XM_T_MPLS_TC 35
+#define OFP_XM_T_MPLS_BOS 36
+#define OFP_XM_T_PBB_ISID 37
+#define OFP_XM_T_TUNNEL_ID 38
+#define OFP_XM_T_IPV6_EXTHDR 39
+#define OFP_XM_T_MAX 40
+
+/* Flow match fields for nxm1 class */
+#define OFP_XM_NXMT_TUNNEL_ID 38
+#define OFP_XM_NXMT_TUNNEL_IPV4_SRC 31
+#define OFP_XM_NXMT_TUNNEL_IPV4_DST 32
+#define OFP_XM_NXMT_TUNNEL_IPV6_SRC 109
+#define OFP_XM_NXMT_TUNNEL_IPV6_DST 110
+
+/* OXM class */
+#define OFP_OXM_C_NXM_0 0x0000
+#define OFP_OXM_C_NXM_1 0x0001
+#define OFP_OXM_C_OPENFLOW_BASIC 0x8000
+#define OFP_OXM_C_OPENFLOW_EXPERIMENTER 0xffff
+
+/* VLAN matching flag */
+#define OFP_XM_VID_PRESENT 0x1000
+#define OFP_XM_VID_NONE 0x0000
+
+struct ofp_ox_match {
+ uint16_t oxm_class;
+ uint8_t oxm_fh;
+ uint8_t oxm_length;
+};
+
+#define OFP_OXM_GET_FIELD(o) (((o)->oxm_fh) >> 1)
+#define OFP_OXM_GET_HASMASK(o) (((o)->oxm_fh) & 0x1)
+#define OFP_OXM_SET_FIELD(o, t) (((o)->oxm_fh) = ((t) << 1))
+#define OFP_OXM_SET_HASMASK(o) (((o)->oxm_fh) |= 0x1)
+
+/* Flow modification commands */
+#define OFP_FLOWCMD_ADD 0 /* Add new flow */
+#define OFP_FLOWCMD_MODIFY 1 /* Modify flow */
+#define OFP_FLOWCMD_MODIFY_STRICT 2 /* Modify flow w/o wildcard */
+#define OFP_FLOWCMD_DELETE 3 /* Delete flow */
+#define OFP_FLOWCMD_DELETE_STRICT 4 /* Delete flow w/o wildcard */
+
+/* Flow modification flags */
+#define OFP_FLOWFLAG_SEND_FLOW_REMOVED 0x01 /* Send flow removed message */
+#define OFP_FLOWFLAG_CHECK_OVERLAP 0x02 /* Check flow overlap first */
+#define OFP_FLOWFLAG_RESET_COUNTS 0x04 /* Reset flow packet and byte counters */
+#define OFP_FLOWFLAG_NO_PACKET_COUNTS 0x08 /* Don't keep track of packet count */
+#define OFP_FLOWFLAG_NO_BYTE_COUNTS 0x10 /* Don't keep track of byte count */
+
+/* Flow modification message */
+struct ofp_flow_mod {
+ struct ofp_header fm_oh; /* OpenFlow header */
+ uint64_t fm_cookie;
+ uint64_t fm_cookie_mask;
+ uint8_t fm_table_id;
+ uint8_t fm_command;
+ uint16_t fm_idle_timeout;
+ uint16_t fm_hard_timeout;
+ uint16_t fm_priority;
+ uint32_t fm_buffer_id;
+ uint32_t fm_out_port;
+ uint32_t fm_out_group;
+ uint16_t fm_flags;
+ uint8_t fm_pad[2];
+ struct ofp_match fm_match;
+} __packed;
+
+/* Flow removed reasons */
+#define OFP_FLOWREM_REASON_IDLE_TIMEOUT 0 /* Flow idle time exceeded idle_timeout */
+#define OFP_FLOWREM_REASON_HARD_TIMEOUT 1 /* Time exceeded hard_timeout */
+#define OFP_FLOWREM_REASON_DELETE 2 /* Evicted by a DELETE flow mod */
+#define OFP_FLOWREM_REASON_GROUP_DELETE 3 /* Group was removed */
+
+/* Flow removed message */
+struct ofp_flow_removed {
+ struct ofp_header fr_oh;
+ uint64_t fr_cookie;
+ uint16_t fr_priority;
+ uint8_t fr_reason;
+ uint8_t fr_table_id;
+ uint32_t fr_duration_sec;
+ uint32_t fr_duration_nsec;
+ uint16_t fr_idle_timeout;
+ uint16_t fr_hard_timeout;
+ uint64_t fr_packet_count;
+ uint64_t fr_byte_count;
+ struct ofp_match fr_match;
+} __packed;
+
+/* Error message */
+struct ofp_error {
+ struct ofp_header err_oh;
+ uint16_t err_type;
+ uint16_t err_code;
+ uint8_t err_data[0];
+ /* Followed by optional data */
+} __packed;
+
+/* Error types */
+#define OFP_ERRTYPE_HELLO_FAILED 0 /* Hello protocol failed */
+#define OFP_ERRTYPE_BAD_REQUEST 1 /* Request was not understood */
+#define OFP_ERRTYPE_BAD_ACTION 2 /* Error in action */
+#define OFP_ERRTYPE_BAD_INSTRUCTION 3 /* Error in instruction list */
+#define OFP_ERRTYPE_BAD_MATCH 4 /* Error in match */
+#define OFP_ERRTYPE_FLOW_MOD_FAILED 5 /* Problem modifying flow */
+#define OFP_ERRTYPE_GROUP_MOD_FAILED 6 /* Problem modifying group */
+#define OFP_ERRTYPE_PORT_MOD_FAILED 7 /* Port mod request failed */
+#define OFP_ERRTYPE_TABLE_MOD_FAILED 8 /* Port mod request failed */
+#define OFP_ERRTYPE_QUEUE_OP_FAILED 9 /* Queue operation failed */
+#define OFP_ERRTYPE_SWITCH_CFG_FAILED 10 /* Switch Config request failed */
+#define OFP_ERRTYPE_ROLE_REQUEST_FAILED 11 /* Controller role request failed */
+#define OFP_ERRTYPE_METER_MOD_FAILED 12 /* Error in meter */
+#define OFP_ERRTYPE_TABLE_FEATURES_FAILED 13 /* Setting table features failed */
+#define OFP_ERRTYPE_EXPERIMENTER 0xffff /* Experimenter error message */
+
+/* HELLO error codes */
+#define OFP_ERRHELLO_INCOMPATIBLE 0 /* No compatible version */
+#define OFP_ERRHELLO_EPERM 1 /* Permissions error */
+
+/* REQUEST error codes */
+#define OFP_ERRREQ_VERSION 0 /* Version not supported */
+#define OFP_ERRREQ_TYPE 1 /* Type not supported */
+#define OFP_ERRREQ_MULTIPART 2 /* Multipart type not supported */
+#define OFP_ERRREQ_EXPERIMENTER 3 /* Experimenter id not supported */
+#define OFP_ERRREQ_EXP_TYPE 4 /* Experimenter type not supported */
+#define OFP_ERRREQ_EPERM 5 /* Permission error */
+#define OFP_ERRREQ_LEN 6 /* Wrong request length for type */
+#define OFP_ERRREQ_BUFFER_EMPTY 7 /* Specified buffer has already been used */
+#define OFP_ERRREQ_BUFFER_UNKNOWN 8 /* Specified buffer does not exist */
+#define OFP_ERRREQ_TABLE_ID 9 /* Specified table-id invalid or does not exit */
+#define OFP_ERRREQ_IS_SLAVE 10 /* Denied because controller is slave */
+#define OFP_ERRREQ_PORT 11 /* Invalid port */
+#define OFP_ERRREQ_PACKET 12 /* Invalid packet in packet-out */
+#define OFP_ERRREQ_MULTIPART_OVERFLOW 13 /* Multipart overflowed the assigned buffer */
+
+/* ACTION error codes */
+#define OFP_ERRACTION_TYPE 0 /* Unknown or unsupported action type */
+#define OFP_ERRACTION_LEN 1 /* Length problem in actions */
+#define OFP_ERRACTION_EXPERIMENTER 2 /* Unknown experimenter id specified */
+#define OFP_ERRACTION_EXP_TYPE 3 /* Unknown action for experimenter id */
+#define OFP_ERRACTION_OUT_PORT 4 /* Problem validating output port */
+#define OFP_ERRACTION_ARGUMENT 5 /* Bad action argument */
+#define OFP_ERRACTION_EPERM 6 /* Permission error */
+#define OFP_ERRACTION_TOO_MANY 7 /* Can't handle this many actions */
+#define OFP_ERRACTION_BAD_QUEUE 8 /* Problem validating output queue */
+#define OFP_ERRACTION_BAD_OUT_GROPU 9 /* Invalid group id in forward action */
+#define OFP_ERRACTION_MATCH_INCONSIST 10 /* Action can't apply or Set-Field failed */
+#define OFP_ERRACTION_UNSUPPORTED_ORDER 11 /* Action order is unsupported for Apply-Actions */
+#define OFP_ERRACTION_TAG 12 /* Actions uses an unsupported tag/encap */
+#define OFP_ERRACTION_SET_TYPE 13 /* Unsupported type in SET_FIELD action */
+#define OFP_ERRACTION_SET_LEN 14 /* Length problem in SET_FIELD action */
+#define OFP_ERRACTION_SET_ARGUMENT 15 /* Bad argument in SET_FIELD action */
+
+/* INSTRUCTION error codes */
+#define OFP_ERRINST_UNKNOWN_INST 0 /* Unknown instruction */
+#define OFP_ERRINST_UNSUPPORTED_INST 1 /* Switch or table does not support */
+#define OFP_ERRINST_TABLE_ID 2 /* Invalid Table-ID specified */
+#define OFP_ERRINST_UNSUPP_META 3 /* Metadata value unsupported by datapath */
+#define OFP_ERRINST_UNSUPP_META_MASK 4 /* Metadata mask value unsupported by datapath */
+#define OFP_ERRINST_BAD_EXPERIMENTER 5 /* Unknown experimenter id specified */
+#define OFP_ERRINST_BAD_EXPERIMENTER_TYPE 6 /* Unknown instruction for experimenter id */
+#define OFP_ERRINST_BAD_LEN 7 /* Length problem in instructions */
+#define OFP_ERRINST_EPERM 8 /* Permissions error */
+
+/* MATCH error codes */
+#define OFP_ERRMATCH_BAD_TYPE 0 /* Unsupported match type */
+#define OFP_ERRMATCH_BAD_LEN 1 /* Length problem in match */
+#define OFP_ERRMATCH_BAD_TAG 2 /* Match uses an unsupported tag/encap */
+#define OFP_ERRMATCH_BAD_DL_ADDR_MASK 3 /* Unsupported datalink addr mask */
+#define OFP_ERRMATCH_BAD_NW_ADDR_MASK 4 /* Unsupported network addr mask */
+#define OFP_ERRMATCH_BAD_WILDCARDS 5 /* Unsupported combination of fields */
+#define OFP_ERRMATCH_BAD_FIELD 6 /* Unsupported field type in the match */
+#define OFP_ERRMATCH_BAD_VALUE 7 /* Unsupported value in a match field */
+#define OFP_ERRMATCH_BAD_MASK 8 /* Unsupported mask specified in match */
+#define OFP_ERRMATCH_BAD_PREREQ 9 /* A prerequisite was not met */
+#define OFP_ERRMATCH_DUP_FIELD 10 /* A field type was duplicated */
+#define OFP_ERRMATCH_EPERM 11 /* Permissions error */
+
+/* FLOW MOD error codes */
+#define OFP_ERRFLOWMOD_UNKNOWN 0 /* Unknown */
+#define OFP_ERRFLOWMOD_ALL_TABLES_FULL 1 /* Not added, full tables */
+#define OFP_ERRFLOWMOD_TABLE_ID 2 /* Invalid table id */
+#define OFP_ERRFLOWMOD_OVERLAP 3 /* Overlapping flow */
+#define OFP_ERRFLOWMOD_EPERM 4 /* Permissions error */
+#define OFP_ERRFLOWMOD_BAD_TIMEOUT 5 /* non-zero idle/hard timeout */
+#define OFP_ERRFLOWMOD_BAD_COMMAND 6 /* Unsupported or Unknown command */
+#define OFP_ERRFLOWMOD_BAD_FLAGS 7 /* Unsupported or Unknown flags */
+
+/* GROUP MOD error codes */
+#define OFP_ERRGROUPMOD_GROUP_EXISTS 0 /* Already present group */
+#define OFP_ERRGROUPMOD_INVALID_GROUP 1 /* Group specified is invalid */
+#define OFP_ERRGROUPMOD_WEIGHT_UNSUPP 2 /* Switch does not support unequal load sharing */
+#define OFP_ERRGROUPMOD_OUT_OF_GROUPS 3 /* The Group table is full */
+#define OFP_ERRGROUPMOD_OUT_OF_BUCKETS 4 /* The maximum number of action buckets */
+#define OFP_ERRGROUPMOD_CHAINING_UNSUPP 5 /* Switch does not support groups forwarding to groups */
+#define OFP_ERRGROUPMOD_WATCH_UNSUPP 6 /* This group cannot watch the watch_port */
+#define OFP_ERRGROUPMOD_LOOP 7 /* Group entry would cause a loop */
+#define OFP_ERRGROUPMOD_UNKNOWN_GROUP 8 /* MODIFY attempted to modify a non-existent group */
+#define OFP_ERRGROUPMOD_CHAINED_GROUP 9 /* Group not deleted because another group is forwarding to it */
+#define OFP_ERRGROUPMOD_BAD_TYPE 10 /* Unsupported or unknown group type */
+#define OFP_ERRGROUPMOD_BAD_COMMAND 11 /* Unsupported or unknown command */
+#define OFP_ERRGROUPMOD_BAD_BUCKET 12 /* Error in bucket */
+#define OFP_ERRGROUPMOD_BAD_WATCH 13 /* Error in watch port/group */
+#define OFP_ERRGROUPMOD_EPERM 14 /* Permission error */
+
+/* GROUP MOD message */
+#define OFP_GROUPCMD_ADD 0 /* Add group */
+#define OFP_GROUPCMD_MODIFY 1 /* Modify group */
+#define OFP_GROUPCMD_DELETE 2 /* Delete group */
+
+/* Group types */
+#define OFP_GROUP_T_ALL 0
+#define OFP_GROUP_T_SELECT 1
+#define OFP_GROUP_T_INDIRECT 2
+#define OFP_GROUP_T_FAST_FAILOVER 3
+
+#define OFP_GROUP_MAX 0xffffff00
+#define OFP_GROUP_ALL 0xfffffffc
+#define OFP_GROUP_ANY 0xffffffff
+
+struct ofp_bucket {
+ uint16_t b_len;
+ uint16_t b_weight;
+ uint32_t b_watch_port;
+ uint32_t b_watch_group;
+ uint8_t b_pad[4];
+ struct ofp_action_header b_actions[0];
+} __packed;
+
+struct ofp_group_mod {
+ struct ofp_header gm_oh;
+ uint16_t gm_command;
+ uint8_t gm_type;
+ uint8_t gm_pad;
+ uint32_t gm_group_id;
+ struct ofp_bucket gm_buckets[0];
+} __packed;
+
+struct ofp_multipart {
+ struct ofp_header mp_oh;
+ uint16_t mp_type;
+ uint16_t mp_flags;
+ uint8_t mp_pad[4];
+} __packed;
+
+#define OFP_MP_FLAG_REQ_MORE 1
+#define OFP_MP_FLAG_REPLY_MORE 1
+
+/* Multipart types */
+#define OFP_MP_T_DESC 0
+#define OFP_MP_T_FLOW 1
+#define OFP_MP_T_AGGREGATE 2
+#define OFP_MP_T_TABLE 3
+#define OFP_MP_T_PORT_STATS 4
+#define OFP_MP_T_QUEUE 5
+#define OFP_MP_T_GROUP 6
+#define OFP_MP_T_GROUP_DESC 7
+#define OFP_MP_T_GROUP_FEATURES 8
+#define OFP_MP_T_METER 9
+#define OFP_MP_T_METER_CONFIG 10
+#define OFP_MP_T_METER_FEATURES 11
+#define OFP_MP_T_TABLE_FEATURES 12
+#define OFP_MP_T_PORT_DESC 13
+#define OFP_MP_T_EXPERIMENTER 0xffff
+
+#define OFP_DESC_STR_LEN 256
+#define OFP_SERIAL_NUM_LEN 32
+
+struct ofp_desc {
+ char d_mfr_desc[OFP_DESC_STR_LEN];
+ char d_hw_desc[OFP_DESC_STR_LEN];
+ char d_sw_desc[OFP_DESC_STR_LEN];
+ char d_serial_num[OFP_SERIAL_NUM_LEN];
+ char d_dp_desc[OFP_DESC_STR_LEN];
+} __packed;
+
+/* Flow stats request */
+struct ofp_flow_stats_request {
+ uint8_t fsr_table_id;
+ uint8_t fsr_pad[3];
+ uint32_t fsr_out_port;
+ uint32_t fsr_out_group;
+ uint8_t fsr_pad2[4];
+ uint64_t fsr_cookie;
+ uint64_t fsr_cookie_mask;
+ struct ofp_match fsr_match;
+} __packed;
+
+/* Flow stats */
+struct ofp_flow_stats {
+ uint16_t fs_length;
+ uint8_t fs_table_id;
+ uint8_t fs_pad;
+ uint32_t fs_duration_sec;
+ uint32_t fs_duration_nsec;
+ uint16_t fs_priority;
+ uint16_t fs_idle_timeout;
+ uint16_t fs_hard_timeout;
+ uint16_t fs_flags;
+ uint8_t fs_pad2[4];
+ uint64_t fs_cookie;
+ uint64_t fs_packet_count;
+ uint64_t fs_byte_count;
+ struct ofp_match fs_match;
+} __packed;
+
+/* Aggregate flow stats request */
+struct ofp_aggregate_stats_request {
+ uint8_t asr_table_id;
+ uint8_t asr_pad[3];
+ uint32_t asr_out_port;
+ uint32_t asr_out_group;
+ uint8_t asr_pad2[4];
+ uint64_t asr_cookie;
+ uint64_t asr_cookie_mask;
+ struct ofp_match asr_match;
+} __packed;
+
+struct ofp_aggregate_stats {
+ uint64_t as_packet_count;
+ uint64_t as_byte_count;
+ uint32_t as_flow_count;
+ uint8_t as_pad[4];
+} __packed;
+
+#define OFP_TABLE_ID_MAX 0xfe
+#define OFP_TABLE_ID_ALL 0xff
+
+struct ofp_table_stats {
+ uint8_t ts_table_id;
+ uint8_t ts_pad[3];
+ uint32_t ts_active_count;
+ uint64_t ts_lookup_count;
+ uint64_t ts_matched_count;
+} __packed;
+
+/* Table features */
+#define OFP_TABLE_FEATPROP_INSTRUCTION 0
+#define OFP_TABLE_FEATPROP_INSTRUCTION_MISS 1
+#define OFP_TABLE_FEATPROP_NEXT_TABLES 2
+#define OFP_TABLE_FEATPROP_NEXT_TABLES_MISS 3
+#define OFP_TABLE_FEATPROP_WRITE_ACTIONS 4
+#define OFP_TABLE_FEATPROP_WRITE_ACTIONS_MISS 5
+#define OFP_TABLE_FEATPROP_APPLY_ACTIONS 6
+#define OFP_TABLE_FEATPROP_APPLY_ACTIONS_MISS 7
+#define OFP_TABLE_FEATPROP_MATCH 8
+#define OFP_TABLE_FEATPROP_WILDCARDS 10
+#define OFP_TABLE_FEATPROP_WRITE_SETFIELD 12
+#define OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS 13
+#define OFP_TABLE_FEATPROP_APPLY_SETFIELD 14
+#define OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS 15
+#define OFP_TABLE_FEATPROP_EXPERIMENTER 0xfffe
+#define OFP_TABLE_FEATPROP_EXPERIMENTER_MISS 0xffff
+
+#define OFP_TABLE_MAX_NAME_LEN 32
+
+struct ofp_table_features {
+ uint16_t tf_length;
+ uint8_t tf_tableid;
+ uint8_t tf_pad[5];
+ char tf_name[OFP_TABLE_MAX_NAME_LEN];
+ uint64_t tf_metadata_match;
+ uint64_t tf_metadata_write;
+ uint32_t tf_config;
+ uint32_t tf_max_entries;
+} __packed;
+
+struct ofp_table_feature_property {
+ uint16_t tp_type;
+ uint16_t tp_length;
+} __packed;
+
+struct ofp_table_feature_property_instruction {
+ uint16_t tpi_type;
+ uint16_t tpi_length;
+ struct ofp_instruction tpi_instructions[0];
+} __packed;
+
+struct ofp_table_feature_property_next_tables {
+ uint16_t tpnt_type;
+ uint16_t tpnt_length;
+ uint8_t tpnt_tables[0];
+} __packed;
+
+struct ofp_table_feature_property_actions {
+ uint16_t tpa_type;
+ uint16_t tpa_length;
+ struct ofp_action_header tpa_actions[0];
+} __packed;
+
+struct ofp_table_feature_property_oxm {
+ uint16_t tpoxm_type;
+ uint16_t tpoxm_length;
+ uint32_t tpoxm_oxm[0];
+} __packed;
+
+struct ofp_table_feature_property_experimenter {
+ uint16_t tfpexp_type;
+ uint16_t tfpexp_length;
+ uint32_t tfpexp_experimenter;
+ uint32_t tfpexp_exp_type;
+ uint32_t tfpexp_experimenter_data[0];
+} __packed;
+
+struct ofp_port_stats {
+ uint32_t pt_port_no;
+ uint8_t pt_pad[4];
+ uint64_t pt_rx_packets;
+ uint64_t pt_tx_packets;
+ uint64_t pt_rx_bytes;
+ uint64_t pt_tx_bytes;
+ uint64_t pt_rx_dropped;
+ uint64_t pt_tx_dropped;
+ uint64_t pt_rx_errors;
+ uint64_t pt_tx_errors;
+ uint64_t pt_rx_frame_err;
+ uint64_t pt_rx_over_err;
+ uint64_t pt_rx_crc_err;
+ uint64_t pt_collision;
+ uint32_t pt_duration_sec;
+ uint32_t pt_duration_nsec;
+} __packed;
+
+/* Groups stats request */
+struct ofp_group_stats_request {
+ uint32_t gsr_group_id;
+ uint8_t gsr_pad[4];
+} __packed;
+
+struct ofp_bucket_counter {
+ uint64_t gs_packet_count;
+ uint64_t gs_byte_count;
+} __packed;
+
+/* Group stats */
+struct ofp_group_stats {
+ uint16_t gs_length;
+ uint8_t gs_pad[2];
+ uint32_t gs_group_id;
+ uint32_t gs_ref_count;
+ uint8_t gs_pad2[4];
+ uint64_t gs_packet_count;
+ uint64_t gs_byte_count;
+ uint32_t gs_duration_sec;
+ uint32_t gs_duration_nsec;
+ struct ofp_bucket_counter gs_bucket_stats[0];
+} __packed;
+
+/* Group description */
+struct ofp_group_desc {
+ uint16_t gd_length;
+ uint8_t gd_type;
+ uint8_t gd_pad;
+ uint32_t gd_group_id;
+ struct ofp_bucket gd_buckets[0];
+} __packed;
+
+#endif /* _NET_OPF_H_ */
--- /dev/null
+/* $OpenBSD: ofp10.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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/queue.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <event.h>
+
+#include "ofp.h"
+#include "ofp10.h"
+#include "switchd.h"
+#include "ofp_map.h"
+
+int ofp10_echo_request(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+int ofp10_packet_in(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+int ofp10_error(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+
+int ofp10_packet_match(struct packet *, struct ofp10_match *, unsigned int);
+
+int ofp10_debug_packet_in(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+int ofp10_debug_packet_out(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+int ofp10_debug_error(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+
+struct ofp_callback ofp10_callbacks[] = {
+ { OFP10_T_HELLO, ofp10_hello, NULL },
+ { OFP10_T_ERROR, NULL, ofp10_debug_error },
+ { OFP10_T_ECHO_REQUEST, ofp10_echo_request, NULL },
+ { OFP10_T_ECHO_REPLY, NULL, NULL },
+ { OFP10_T_EXPERIMENTER, NULL, NULL },
+ { OFP10_T_FEATURES_REQUEST, NULL, NULL },
+ { OFP10_T_FEATURES_REPLY, NULL, NULL },
+ { OFP10_T_GET_CONFIG_REQUEST, NULL, NULL },
+ { OFP10_T_GET_CONFIG_REPLY, NULL, NULL },
+ { OFP10_T_SET_CONFIG, NULL, NULL },
+ { OFP10_T_PACKET_IN, ofp10_packet_in, ofp10_debug_packet_in },
+ { OFP10_T_FLOW_REMOVED, NULL, NULL },
+ { OFP10_T_PORT_STATUS, NULL, NULL },
+ { OFP10_T_PACKET_OUT, NULL, ofp10_debug_packet_out },
+ { OFP10_T_FLOW_MOD, NULL, NULL },
+ { OFP10_T_PORT_MOD, NULL, NULL },
+ { OFP10_T_STATS_REQUEST, NULL, NULL },
+ { OFP10_T_STATS_REPLY, NULL, NULL },
+ { OFP10_T_BARRIER_REQUEST, NULL, NULL },
+ { OFP10_T_BARRIER_REPLY, NULL, NULL },
+ { OFP10_T_QUEUE_GET_CONFIG_REQUEST, NULL, NULL },
+ { OFP10_T_QUEUE_GET_CONFIG_REPLY, NULL, NULL }
+};
+
+void
+ofp10_debug_header(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh)
+{
+ log_debug("%s > %s: version %s type %s length %u xid %u",
+ print_host(src, NULL, 0),
+ print_host(dst, NULL, 0),
+ print_map(oh->oh_version, ofp_v_map),
+ print_map(oh->oh_type, ofp10_t_map),
+ ntohs(oh->oh_length), ntohl(oh->oh_xid));
+}
+
+void
+ofp10_debug(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ ofp10_debug_header(sc, src, dst, oh);
+
+ if (ibuf == NULL ||
+ oh->oh_version != OFP_V_1_0 ||
+ oh->oh_type >= OFP_T_TYPE_MAX ||
+ ofp10_callbacks[oh->oh_type].debug == NULL)
+ return;
+ if (ofp10_callbacks[oh->oh_type].debug(sc, src, dst, oh, ibuf) != 0)
+ goto fail;
+
+ return;
+ fail:
+ log_debug("\tinvalid packet");
+}
+
+int
+ofp10_debug_packet_in(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp10_packet_in *pin;
+ uint8_t *p;
+ size_t len;
+ off_t off;
+
+ off = 0;
+ if ((pin = ibuf_seek(ibuf, off, sizeof(*pin))) == NULL)
+ return (-1);
+ log_debug("\tbuffer %d port %s "
+ "length %u reason %u",
+ ntohl(pin->pin_buffer_id),
+ print_map(ntohs(pin->pin_port), ofp10_port_map),
+ ntohs(pin->pin_total_len),
+ pin->pin_reason);
+ len = ntohs(pin->pin_total_len);
+ off += sizeof(*pin);
+ if ((p = ibuf_seek(ibuf, off, len)) == NULL)
+ return (-1);
+ if (sc->sc_tap != -1)
+ (void)write(sc->sc_tap, p, len);
+ return (0);
+}
+
+int
+ofp10_debug_packet_out(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp10_packet_out *pout;
+ size_t len;
+ off_t off;
+ struct ofp_action_header *ah;
+ struct ofp10_action_output *ao;
+
+ off = 0;
+ if ((pout = ibuf_seek(ibuf, off, sizeof(*pout))) == NULL) {
+ log_debug("%s: seek failed: length %zd",
+ __func__, ibuf_length(ibuf));
+ return (-1);
+ }
+ log_debug("\tbuffer %d port %s "
+ "actions length %u",
+ ntohl(pout->pout_buffer_id),
+ print_map(ntohs(pout->pout_port), ofp10_port_map),
+ ntohs(pout->pout_actions_len));
+ len = ntohs(pout->pout_actions_len);
+
+ off += sizeof(*pout);
+ while ((ah = ibuf_seek(ibuf, off, len)) != NULL &&
+ ntohs(ah->ah_len) >= sizeof(*ah)) {
+ switch (ntohs(ah->ah_type)) {
+ case OFP10_ACTION_OUTPUT:
+ ao = (struct ofp10_action_output *)ah;
+ log_debug("\t\taction type %s length %d "
+ "port %s max length %d",
+ print_map(ntohs(ao->ao_type), ofp10_action_map),
+ ntohs(ao->ao_len),
+ print_map(ntohs(ao->ao_port), ofp10_port_map),
+ ntohs(ao->ao_max_len));
+ break;
+ default:
+ log_debug("\t\taction type %s length %d",
+ print_map(ntohs(ah->ah_type), ofp10_action_map),
+ ntohs(ah->ah_len));
+ break;
+ }
+ if (pout->pout_buffer_id == (uint32_t)-1)
+ break;
+ off += ntohs(ah->ah_len);
+ }
+
+ return (0);
+}
+
+int
+ofp10_debug_error(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp_error *err;
+ off_t off;
+ const char *code;
+
+ off = 0;
+ if ((err = ibuf_seek(ibuf, off, sizeof(*err))) == NULL) {
+ log_debug("%s: seek failed: length %zd",
+ __func__, ibuf_length(ibuf));
+ return (-1);
+ }
+
+ switch (ntohs(err->err_type)) {
+ case OFP10_ERRTYPE_FLOW_MOD_FAILED:
+ code = print_map(ntohs(err->err_code), ofp10_errflowmod_map);
+ break;
+ default:
+ code = NULL;
+ break;
+ }
+
+ log_debug("\terror type %s code %u%s%s",
+ print_map(ntohs(err->err_type), ofp10_errtype_map),
+ ntohs(err->err_code),
+ code == NULL ? "" : ": ",
+ code == NULL ? "" : code);
+
+ return (0);
+}
+
+int
+ofp10_input(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ ofp10_debug(sc, &con->con_peer, &con->con_local, oh, ibuf);
+
+ if (oh->oh_version != OFP_V_1_0 ||
+ oh->oh_type >= OFP_T_TYPE_MAX) {
+ log_debug("unsupported packet");
+ return (-1);
+ }
+
+ if (ofp10_callbacks[oh->oh_type].cb == NULL) {
+ log_debug("message not supported: %s",
+ print_map(oh->oh_type, ofp10_t_map));
+ return (-1);
+ }
+ if (ofp10_callbacks[oh->oh_type].cb(sc, con, oh, ibuf) != 0)
+ return (-1);
+
+ return (0);
+}
+
+int
+ofp10_hello(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ if (oh->oh_version == OFP_V_1_0 &&
+ switch_add(con) == NULL) {
+ log_debug("%s: failed to add switch", __func__);
+ ofp_close(con);
+ return (-1);
+ }
+
+ /* Echo back the received Hello packet */
+ oh->oh_version = OFP_V_1_0;
+ oh->oh_length = htons(sizeof(*oh));
+ oh->oh_xid = htonl(con->con_xidnxt++);
+ ofp_send(con, oh, NULL);
+ ofp10_debug(sc, &con->con_local, &con->con_peer, oh, NULL);
+
+#if 0
+ (void)write(fd, &oh, sizeof(oh));
+ ofd_debug(sc, &sname, &con->con_ss, &oh, buf, len);
+ oh.oh_xid = htonl(1);
+ oh.oh_type = OFP10_T_FEATURES_REQUEST;
+ (void)write(fd, &oh, sizeof(oh));
+ ofd_debug(sc, &sname, &con->con_ss, &oh, buf, len);
+ oh.oh_xid = htonl(2);
+ oh.oh_type = OFP10_T_GET_CONFIG_REQUEST;
+ (void)write(fd, &oh, sizeof(oh));
+#endif
+ return (0);
+}
+
+int
+ofp10_echo_request(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ /* Echo reply */
+ oh->oh_type = OFP10_T_ECHO_REPLY;
+ ofp10_debug(sc, &con->con_local, &con->con_peer, oh, NULL);
+ ofp_send(con, oh, NULL);
+
+ return (0);
+}
+
+int
+ofp10_packet_match(struct packet *pkt, struct ofp10_match *m, uint32_t flags)
+{
+ struct ether_header *eh = pkt->pkt_eh;
+
+ bzero(m, sizeof(*m));
+ m->m_wildcards = htonl(~flags);
+
+ if ((flags & (OFP10_WILDCARD_DL_SRC|OFP10_WILDCARD_DL_DST)) && (eh == NULL))
+ return (-1);
+
+ if (flags & OFP10_WILDCARD_DL_SRC)
+ memcpy(m->m_dl_src, eh->ether_shost, ETHER_ADDR_LEN);
+ if (flags & OFP10_WILDCARD_DL_DST)
+ memcpy(m->m_dl_dst, eh->ether_dhost, ETHER_ADDR_LEN);
+
+ return (0);
+}
+
+int
+ofp10_packet_in(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *ih, struct ibuf *ibuf)
+{
+ struct ofp10_packet_in *pin;
+ struct ofp10_packet_out *pout;
+ struct ofp10_action_output *ao;
+ struct ofp10_flow_mod *fm;
+ struct ofp_header *oh;
+ struct packet pkt;
+ struct ibuf *obuf = NULL;
+ int ret = -1;
+ size_t len;
+ long srcport, dstport;
+ int addflow = 0;
+ int addpacket = 0;
+
+ if ((pin = ibuf_getdata(ibuf, sizeof(*pin))) == NULL)
+ return (-1);
+
+ bzero(&pkt, sizeof(pkt));
+ len = ntohs(pin->pin_total_len);
+ srcport = ntohs(pin->pin_port);
+
+ if ((dstport = packet_input(sc, con->con_switch,
+ srcport, ibuf, len, &pkt)) == -1 ||
+ dstport > OFP10_PORT_MAX) {
+ /* fallback to flooding */
+ dstport = OFP10_PORT_FLOOD;
+ } else if (srcport == dstport) {
+ /*
+ * silently drop looping packet
+ * (don't use OFP10_PORT_INPUT here)
+ */
+ dstport = OFP10_PORT_ANY;
+ }
+
+ if (dstport <= OFP10_PORT_MAX)
+ addflow = 1;
+
+ if ((obuf = ibuf_static()) == NULL)
+ goto done;
+
+ again:
+ if (addflow) {
+ if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL)
+ goto done;
+
+ ofp10_packet_match(&pkt, &fm->fm_match, OFP10_WILDCARD_DL_DST);
+
+ oh = &fm->fm_oh;
+ fm->fm_cookie = 0; /* XXX should we set a cookie? */
+ fm->fm_command = htons(OFP_FLOWCMD_ADD);
+ fm->fm_idle_timeout = htons(sc->sc_cache_timeout);
+ fm->fm_hard_timeout = 0; /* permanent */
+ fm->fm_priority = 0;
+ fm->fm_buffer_id = pin->pin_buffer_id;
+ fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED);
+ if (pin->pin_buffer_id == (uint32_t)-1)
+ addpacket = 1;
+ } else {
+ if ((pout = ibuf_advance(obuf, sizeof(*pout))) == NULL)
+ goto done;
+
+ oh = &pout->pout_oh;
+ pout->pout_buffer_id = pin->pin_buffer_id;
+ pout->pout_port = pin->pin_port;
+ pout->pout_actions_len = htons(sizeof(*ao));
+
+ if (pin->pin_buffer_id == (uint32_t)-1)
+ addpacket = 1;
+ }
+
+ if ((ao = ibuf_advance(obuf, sizeof(*ao))) == NULL)
+ goto done;
+ ao->ao_type = htons(OFP_ACTION_OUTPUT);
+ ao->ao_len = htons(sizeof(*ao));
+ ao->ao_port = htons((uint16_t)dstport);
+ ao->ao_max_len = 0;
+
+ /* Add optional packet payload */
+ if (addpacket &&
+ imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1)
+ goto done;
+
+ /* Set output header */
+ memcpy(oh, ih, sizeof(*oh));
+ oh->oh_length = htons(ibuf_length(obuf));
+ oh->oh_type = addflow ? OFP10_T_FLOW_MOD : OFP10_T_PACKET_OUT;
+ oh->oh_xid = htonl(con->con_xidnxt++);
+
+ ofp10_debug(sc, &con->con_local, &con->con_peer, oh, obuf);
+
+ ofp_send(con, NULL, obuf);
+
+ if (addflow && addpacket) {
+ /* loop to output the packet again */
+ addflow = 0;
+ if ((obuf = ibuf_static()) == NULL)
+ goto done;
+ goto again;
+ }
+
+ ret = 0;
+ done:
+ ibuf_release(obuf);
+ return (ret);
+}
--- /dev/null
+/* $OpenBSD: ofp10.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _NET_OFP10_H_
+#define _NET_OFP10_H_
+
+#include <ofp.h>
+
+/* OpenFlow message type */
+#define OFP10_T_HELLO 0 /* Hello */
+#define OFP10_T_ERROR 1 /* Error */
+#define OFP10_T_ECHO_REQUEST 2 /* Echo Request */
+#define OFP10_T_ECHO_REPLY 3 /* Echo Reply */
+#define OFP10_T_EXPERIMENTER 4 /* Vendor/Experimenter */
+#define OFP10_T_FEATURES_REQUEST 5 /* Features Request (switch) */
+#define OFP10_T_FEATURES_REPLY 6 /* Features Reply (switch) */
+#define OFP10_T_GET_CONFIG_REQUEST 7 /* Get Config Request (switch) */
+#define OFP10_T_GET_CONFIG_REPLY 8 /* Get Config Reply (switch) */
+#define OFP10_T_SET_CONFIG 9 /* Set Config (switch) */
+#define OFP10_T_PACKET_IN 10 /* Packet In (async) */
+#define OFP10_T_FLOW_REMOVED 11 /* Flow Removed (async) */
+#define OFP10_T_PORT_STATUS 12 /* Port Status (async) */
+#define OFP10_T_PACKET_OUT 13 /* Packet Out (controller) */
+#define OFP10_T_FLOW_MOD 14 /* Flow Mod (controller) */
+#define OFP10_T_PORT_MOD 16 /* Port Mod (controller) */
+#define OFP10_T_STATS_REQUEST 17 /* Stats Request */
+#define OFP10_T_STATS_REPLY 18 /* Stats Reply */
+#define OFP10_T_BARRIER_REQUEST 19 /* Barrier Request */
+#define OFP10_T_BARRIER_REPLY 20 /* Barrier Reply */
+#define OFP10_T_QUEUE_GET_CONFIG_REQUEST 21 /* Queue Get Config Request */
+#define OFP10_T_QUEUE_GET_CONFIG_REPLY 22 /* Queue Get Config Reply */
+#define OFP10_T_TYPE_MAX 23
+
+/* Ports */
+#define OFP10_PORT_MAX 0xff00 /* Maximum number of physical ports */
+#define OFP10_PORT_INPUT 0xfff8 /* Send back to input port */
+#define OFP10_PORT_FLOWTABLE 0xfff9 /* Perform actions in flow table */
+#define OFP10_PORT_NORMAL 0xfffa /* Let switch decide */
+#define OFP10_PORT_FLOOD 0xfffb /* All non-block ports except input */
+#define OFP10_PORT_ALL 0xfffc /* All ports except input */
+#define OFP10_PORT_CONTROLLER 0xfffd /* Send to controller */
+#define OFP10_PORT_LOCAL 0xfffe /* Local virtual OpenFlow port */
+#define OFP10_PORT_ANY 0xffff /* No port */
+
+/* Switch port description */
+struct ofp10_phy_port {
+ uint16_t swp_number;
+ uint8_t swp_macaddr[ETHER_ADDR_LEN];
+ char swp_name[OFP_IFNAMSIZ];
+ uint32_t swp_config; /* Configuration flags */
+ uint32_t swp_state; /* State flags */
+ uint32_t swp_cur; /* Current features */
+ uint32_t swp_advertised; /* Advertised by the port */
+ uint32_t swp_supported; /* Supported by the port */
+ uint32_t swp_peer; /* Advertised by peer */
+};
+
+/* Packet-In Message */
+struct ofp10_packet_in {
+ struct ofp_header pin_oh; /* OpenFlow header */
+ uint32_t pin_buffer_id;
+ uint16_t pin_total_len;
+ uint16_t pin_port;
+ uint8_t pin_reason;
+ uint8_t pin_pad;
+ uint8_t pin_data[0];
+} __packed;
+
+/* Actions */
+#define OFP10_ACTION_OUTPUT 0 /* Output to switch port */
+#define OFP10_ACTION_SET_VLAN_VID 1 /* Set the 802.1q VLAN id */
+#define OFP10_ACTION_SET_VLAN_PCP 2 /* Set the 802.1q priority */
+#define OFP10_ACTION_STRIP_VLAN 3 /* Strip the 802.1q header */
+#define OFP10_ACTION_SET_DL_SRC 4 /* Ethernet src address */
+#define OFP10_ACTION_SET_DL_DST 5 /* Ethernet dst address */
+#define OFP10_ACTION_SET_NW_SRC 6 /* IP src address */
+#define OFP10_ACTION_SET_NW_DST 7 /* IP dst address */
+#define OFP10_ACTION_SET_NW_TOS 8 /* IP TOS */
+#define OFP10_ACTION_SET_TP_SRC 9 /* TCP/UDP src port */
+#define OFP10_ACTION_SET_TP_DST 10 /* TCP/UDP dst port */
+#define OFP10_ACTION_ENQUEUE 11 /* Output to queue */
+#define OFP10_ACTION_EXPERIMENTER 0xffff /* Vendor-specific action */
+
+/* Output Action */
+struct ofp10_action_output {
+ uint16_t ao_type;
+ uint16_t ao_len;
+ uint16_t ao_port;
+ uint16_t ao_max_len;
+} __packed;
+
+/* Packet-Out Message */
+struct ofp10_packet_out {
+ struct ofp_header pout_oh; /* OpenFlow header */
+ uint32_t pout_buffer_id;
+ uint16_t pout_port;
+ uint16_t pout_actions_len;
+ struct ofp_action_header pout_actions[0];
+ /* Followed by optional packet data if buffer_id == 0xffffffff */
+} __packed;
+
+/* Flow matching wildcards */
+#define OFP10_WILDCARD_IN_PORT 0x00000001 /* Switch input port */
+#define OFP10_WILDCARD_DL_VLAN 0x00000002 /* VLAN id */
+#define OFP10_WILDCARD_DL_SRC 0x00000004 /* Ethernet src address */
+#define OFP10_WILDCARD_DL_DST 0x00000008 /* Ethernet dst address */
+#define OFP10_WILDCARD_DL_TYPE 0x00000010 /* Ethernet frame type */
+#define OFP10_WILDCARD_NW_PROTO 0x00000020 /* IPv4 protocol */
+#define OFP10_WILDCARD_TP_SRC 0x00000040 /* TCP/UDP source port */
+#define OFP10_WILDCARD_TP_DST 0x00000080 /* TCP/UDP destination port */
+#define OFP10_WILDCARD_NW_SRC 0x00003f00 /* IPv4 source address */
+#define OFP10_WILDCARD_NW_SRC_S 8
+#define OFP10_WILDCARD_NW_DST 0x000fc000 /* IPv4 destination address */
+#define OFP10_WILDCARD_NW_DST_S 14
+#define OFP10_WILDCARD_DL_VLANPCP 0x00100000 /* VLAN prio */
+#define OFP10_WILDCARD_NW_TOS 0x00200000 /* IPv4 ToS/DSCP */
+#define OFP10_WILDCARD_MASK 0x003fffff /* All wildcard flags */
+
+/* Flow matching */
+struct ofp10_match {
+ uint32_t m_wildcards; /* Wildcard options */
+ uint16_t m_in_port; /* Switch port */
+ uint8_t m_dl_src[ETHER_ADDR_LEN]; /* Ether src addr */
+ uint8_t m_dl_dst[ETHER_ADDR_LEN]; /* Ether dst addr */
+ uint16_t m_dl_vlan; /* Input VLAN id */
+ uint8_t m_dl_vlan_pcp; /* Input VLAN prio */
+ uint8_t m_pad1[1];
+ uint16_t m_dl_type; /* Ether type */
+ uint8_t m_nw_tos; /* IPv4 ToS/DSCP */
+ uint8_t m_nw_proto; /* IPv4 Proto */
+ uint8_t m_pad2[2];
+ uint32_t m_nw_src; /* IPv4 source */
+ uint32_t m_nw_dst; /* IPv4 destination */
+ uint16_t m_tp_src; /* TCP/UDP src port */
+ uint16_t m_tp_dst; /* TCP/UDP dst port */
+} __packed;
+
+/* Flow modification message */
+struct ofp10_flow_mod {
+ struct ofp_header fm_oh; /* OpenFlow header */
+ struct ofp10_match fm_match;
+ uint64_t fm_cookie;
+ uint16_t fm_command;
+ uint16_t fm_idle_timeout;
+ uint16_t fm_hard_timeout;
+ uint16_t fm_priority;
+ uint32_t fm_buffer_id;
+ uint16_t fm_port;
+ uint16_t fm_flags;
+ struct ofp_action_header fm_actions[0];
+} __packed;
+
+/* Error types */
+#define OFP10_ERRTYPE_HELLO_FAILED 0 /* Hello protocol failed */
+#define OFP10_ERRTYPE_BAD_REQUEST 1 /* Request was not understood */
+#define OFP10_ERRTYPE_BAD_ACTION 2 /* Error in action */
+#define OFP10_ERRTYPE_FLOW_MOD_FAILED 3 /* Problem modifying flow */
+#define OFP10_ERRTYPE_PORT_MOD_FAILED 4 /* Port mod request failed */
+#define OFP10_ERRTYPE_QUEUE_OP_FAILED 5 /* Queue operation failed */
+
+/* FLOW MOD error codes */
+#define OFP10_ERRFLOWMOD_ALL_TABLES_FULL 0 /* Not added, full tables */
+#define OFP10_ERRFLOWMOD_OVERLAP 1 /* Overlapping flow */
+#define OFP10_ERRFLOWMOD_EPERM 2 /* Permissions error */
+#define OFP10_ERRFLOWMOD_BAD_TIMEOUT 3 /* non-zero idle/hardtimeout */
+#define OFP10_ERRFLOWMOD_BAD_COMMAND 4 /* Unknown command */
+#define OFP10_ERRFLOWMOD_UNSUPPORTED 5 /* Unsupported action list */
+
+#endif /* _NET_OPF_H_ */
--- /dev/null
+/* $OpenBSD: ofp13.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+/* XXX not implemented, this is just a stub */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <event.h>
+
+#include "ofp.h"
+#include "ofp10.h"
+#include "switchd.h"
+
+void
+ofp13_debug(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp_packet_in pin;
+#if 0
+ uint8_t *p;
+#endif
+ uint8_t *buf;
+ size_t len;
+
+ len = ibuf_length(ibuf);
+ buf = ibuf_data(ibuf);
+
+ ofp10_debug_header(sc, src, dst, oh);
+
+ if (oh->oh_version != OFP_V_1_3)
+ return;
+
+ switch (oh->oh_type) {
+ case OFP_T_PACKET_IN:
+ if (len < sizeof(pin))
+ goto fail;
+ memcpy(&pin, buf, sizeof(pin));
+#if 0
+ log_debug("\tbuffer %d port 0x%08x "
+ "phy port 0x%08x length %u "
+ "reason %u table id %u",
+ ntohl(pin13.pin_buffer_id),
+ ntohl(pin13.pin_port),
+ ntohl(pin13.pin_phy_port),
+ ntohs(pin13.pin_total_len),
+ pin13.pin_reason,
+ pin13.pin_table_id);
+ if ((len - sizeof(pin)) < ntohs(pin.pin_total_len))
+ goto fail;
+ if (sc->sc_tap != -1) {
+ p = (uint8_t *)&buf[sizeof(pin)];
+ (void)write(sc->sc_tap, p,
+ ntohs(pin.pin_total_len));
+ }
+#endif
+ break;
+ }
+ return;
+
+ fail:
+ log_debug("\tinvalid packet\n");
+}
+
+int
+ofp13_input(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ uint8_t *buf;
+ ssize_t len;
+
+ len = ibuf_length(ibuf);
+ buf = ibuf_data(ibuf);
+
+ ofp13_debug(sc, &con->con_peer, &con->con_local, oh, ibuf);
+
+ switch (oh->oh_type) {
+ case OFP_T_HELLO:
+ /* Echo back the received Hello packet */
+ ofp_send(con, oh, NULL);
+ break;
+ case OFP_T_ECHO_REQUEST:
+ /* Echo reply */
+ oh->oh_type = OFP_T_ECHO_REPLY;
+ ofp_send(con, oh, NULL);
+ break;
+ default:
+ /* not implemented */
+ break;
+ }
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: ofp_map.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _SWITCHD_OFP_MAP_H
+#define _SWITCHD_OFP_MAP_H
+
+/*
+ * Each map is generated from lists of #define's in ofp.h, using the format:
+ * #define OFP_{MAPNAME}_FLAG {value} / * COMMENT * /
+ *
+ * Please make sure that the flags in ofp.h match this style (incl. comment)
+ */
+
+/* OpenFlow 1.0 maps */
+extern struct constmap ofp10_t_map[];
+extern struct constmap ofp10_port_map[];
+extern struct constmap ofp10_action_map[];
+extern struct constmap ofp10_wildcard_map[];
+extern struct constmap ofp10_errtype_map[];
+extern struct constmap ofp10_errflowmod_map[];
+
+/* OpenFlow 1.3+ maps */
+extern struct constmap ofp_v_map[];
+extern struct constmap ofp_t_map[];
+extern struct constmap ofp_hi_map[];
+extern struct constmap ofp_port_map[];
+extern struct constmap ofp_config_map[];
+extern struct constmap ofp_portstate_map[];
+extern struct constmap ofp_portconfig_map[];
+extern struct constmap ofp_portmedia_map[];
+extern struct constmap ofp_swcap_map[];
+extern struct constmap ofp_match_map[];
+extern struct constmap ofp_action_map[];
+extern struct constmap ofp_flowcmd_map[];
+extern struct constmap ofp_flowflag_map[];
+extern struct constmap ofp_errtype_map[];
+extern struct constmap ofp_errflowmod_map[];
+
+#endif /* _SWITCHD_OFP_MAP_H */
--- /dev/null
+/* $OpenBSD: packet.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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 <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "switchd.h"
+
+const uint8_t etherbroadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+const uint8_t etherzeroaddr[] = { 0, 0, 0, 0, 0, 0 };
+
+int packet_ether_unicast(uint8_t *);
+
+int
+packet_ether_unicast(uint8_t *ea)
+{
+ if (memcmp(ea, etherbroadcastaddr, ETHER_ADDR_LEN) == 0 ||
+ memcmp(ea, etherzeroaddr, ETHER_ADDR_LEN) == 0 ||
+ ETHER_IS_MULTICAST(ea))
+ return (-1);
+ return (0);
+}
+
+long
+packet_input(struct switchd *sc, struct switch_control *sw, long port,
+ struct ibuf *ibuf, size_t len, struct packet *pkt)
+{
+ struct ether_header *eh;
+ struct macaddr *src, *dst;
+
+ if (sw == NULL)
+ return (-1);
+ if (len < sizeof(*eh))
+ return (-1);
+
+ pkt->pkt_len = ibuf_dataleft(ibuf);
+ if ((pkt->pkt_eh = eh = ibuf_getdata(ibuf, sizeof(*eh))) == NULL) {
+ log_debug("short packet");
+ return (-1);
+ }
+ len -= sizeof(*eh);
+
+ if ((packet_ether_unicast(eh->ether_shost) == -1) ||
+ (src = switch_learn(sc, sw, eh->ether_shost, port)) == NULL)
+ return (-1);
+
+ if (packet_ether_unicast(eh->ether_dhost) == -1)
+ dst = NULL;
+ else
+ dst = switch_cached(sw, eh->ether_dhost);
+
+ log_debug("%s: %s -> %s, port %ld -> %ld", __func__,
+ print_ether(eh->ether_shost),
+ print_ether(eh->ether_dhost),
+ src->mac_port,
+ dst == NULL ? -1 : dst->mac_port);
+
+ return (dst == NULL ? -1 : dst->mac_port);
+}
--- /dev/null
+/* $OpenBSD: parse.y,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * 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/queue.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "switchd.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+int host(const char *, struct sockaddr *, socklen_t);
+
+struct switchd *conf;
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ in_port_t port;
+ struct switch_device
+ *conn;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token INCLUDE ERROR LISTEN ON TLS PORT DEVICE FORWARD TO
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> opttls
+%type <v.conn> optofcconn
+%type <v.port> port
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar include '\n'
+ | grammar listen '\n'
+ | grammar device '\n'
+ | grammar varset '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+listen : LISTEN ON STRING opttls port {
+ if (host($3,
+ (struct sockaddr *)&conf->sc_server.srv_addr,
+ sizeof(conf->sc_server.srv_addr)) != 0) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ conf->sc_server.srv_tls = $4;
+ ((struct sockaddr_in *)&conf->sc_server.srv_addr)
+ ->sin_port = $5;
+ }
+ | LISTEN ON STRING opttls {
+ if (host($3,
+ (struct sockaddr *)&conf->sc_server.srv_addr,
+ sizeof(conf->sc_server.srv_addr)) != 0) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
+
+port : PORT NUMBER {
+ if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
+ yyerror("invalid port: %lld", $2);
+ YYERROR;
+ }
+ $$ = htons($2);
+ }
+ ;
+
+opttls : /* empty */ { $$ = 0; }
+ | TLS { $$ = 1; }
+ ;
+
+device : DEVICE STRING optofcconn {
+ struct switch_device *c;
+
+ TAILQ_FOREACH(c, &conf->sc_conns, sdv_next) {
+ if (strcmp(c->sdv_device, $2) == 0)
+ break;
+ }
+ if (c != NULL) {
+ yyerror("device name is duplicated");
+ YYERROR;
+ }
+ if (strlcpy($3->sdv_device, $2, sizeof($3->sdv_device))
+ >= sizeof($3->sdv_device)) {
+ yyerror("device name is too long");
+ YYERROR;
+ }
+ free($2);
+ TAILQ_INSERT_TAIL(&conf->sc_conns, $3, sdv_next);
+ }
+ ;
+
+optofcconn : /* empty */ {
+ if (($$ = calloc(1,
+ sizeof(struct switch_device))) == NULL)
+ fatal("calloc");
+ $$->sdv_swc.swc_type = SWITCH_CONN_LOCAL;
+ }
+ | FORWARD TO STRING {
+ if (($$ = calloc(1,
+ sizeof(struct switch_device))) == NULL)
+ fatal("calloc");
+ if (strncmp($3, "tcp:", 4) == 0)
+ $$->sdv_swc.swc_type = SWITCH_CONN_TCP;
+ else if (strncmp($3, "tls:", 4) == 0)
+ $$->sdv_swc.swc_type = SWITCH_CONN_TLS;
+ else {
+ yyerror("foward to proto is not supported");
+ free($$);
+ free($3);
+ YYERROR;
+ }
+ if (parsehostport($3 + 4,
+ (struct sockaddr *)&$$->sdv_swc.swc_addr,
+ sizeof($$->sdv_swc.swc_addr)) == -1) {
+ yyerror("could not parse host and port part "
+ "of connect-to");
+ free($$);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
+
+varset : STRING '=' STRING {
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatal("yyerror vasprintf");
+ va_end(ap);
+ log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "device", DEVICE },
+ { "forward", FORWARD },
+ { "include", INCLUDE },
+ { "listen", LISTEN },
+ { "on", ON },
+ { "port", PORT },
+ { "tls", TLS },
+ { "to", TO },
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+u_char *parsebuf;
+int parseindex;
+u_char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(file->stream);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, file->stream);
+ c = ' ';
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ u_char buf[8096];
+ u_char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return (findeol());
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ fatal("yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '/') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ fatal("yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ free(nfile);
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+int
+parse_config(const char *filename, struct switchd *sc)
+{
+ struct sym *sym;
+ int errors = 0;
+ struct sockaddr_in *sin4;
+
+ conf = sc;
+
+ /* Set the default 0.0.0.0 6633/tcp */
+ memset(&conf->sc_server.srv_addr, 0, sizeof(conf->sc_server.srv_addr));
+ sin4 = (struct sockaddr_in *)&conf->sc_server.srv_addr;
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = htons(SWITCHD_CTLR_PORT);
+ sin4->sin_len = sizeof(struct sockaddr_in);
+
+ if ((file = pushfile(filename, 0)) == NULL) {
+ log_warn("failed to open %s", filename);
+ return (0);
+ }
+ topfile = file;
+ setservent(1);
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ endservent();
+
+ /* Free macros and check which have not been used. */
+ while ((sym = TAILQ_FIRST(&symhead))) {
+ if (!sym->used)
+ log_debug("warning: macro '%s' not "
+ "used\n", sym->nam);
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+
+ return (errors ? -1 : 0);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = (val - s) + 1;
+ if ((sym = malloc(len)) == NULL)
+ fatal("cmdline_symset: malloc");
+
+ (void)strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+int
+host(const char *str, struct sockaddr *sa, socklen_t salen)
+{
+ struct addrinfo hints, *ai0;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
+
+ if ((error = getaddrinfo(str, NULL, &hints, &ai0)) != 0) {
+ yyerror("invalid listen address: %s: %s", str,
+ gai_strerror(error));
+ return (-1);
+ }
+ if (salen >= ai0->ai_addrlen)
+ memcpy(sa, ai0->ai_addr, ai0->ai_addrlen);
+ else {
+ yyerror("addrlen is invalid: %d", (int)ai0->ai_addrlen);
+ freeaddrinfo(ai0);
+ return (-1);
+ }
+ freeaddrinfo(ai0);
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: proc.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@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/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <event.h>
+#include <imsg.h>
+
+#include "proc.h"
+
+void proc_open(struct privsep *, struct privsep_proc *,
+ struct privsep_proc *, size_t);
+void proc_close(struct privsep *);
+int proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid);
+void proc_shutdown(struct privsep_proc *);
+void proc_sig_handler(int, short, void *);
+void proc_range(struct privsep *, enum privsep_procid, int *, int *);
+int proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
+
+int
+proc_ispeer(struct privsep_proc *procs, unsigned int nproc,
+ enum privsep_procid type)
+{
+ unsigned int i;
+
+ for (i = 0; i < nproc; i++)
+ if (procs[i].p_id == type)
+ return (1);
+ return (0);
+}
+
+void
+proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
+{
+ unsigned int i, j, src, dst;
+ struct privsep_pipes *pp;
+
+ /*
+ * Allocate pipes for all process instances (incl. parent)
+ *
+ * - ps->ps_pipes: N:M mapping
+ * N source processes connected to M destination processes:
+ * [src][instances][dst][instances], for example
+ * [PROC_RELAY][3][PROC_CA][3]
+ *
+ * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
+ * Each process instance has a destination array of socketpair fds:
+ * [dst][instances], for example
+ * [PROC_PARENT][0]
+ */
+ for (src = 0; src < PROC_MAX; src++) {
+ /* Allocate destination array for each process */
+ if ((ps->ps_pipes[src] = calloc(ps->ps_ninstances,
+ sizeof(struct privsep_pipes))) == NULL)
+ fatal("proc_init: calloc");
+
+ for (i = 0; i < ps->ps_ninstances; i++) {
+ pp = &ps->ps_pipes[src][i];
+
+ for (dst = 0; dst < PROC_MAX; dst++) {
+ /* Allocate maximum fd integers */
+ if ((pp->pp_pipes[dst] =
+ calloc(ps->ps_ninstances,
+ sizeof(int))) == NULL)
+ fatal("proc_init: calloc");
+
+ /* Mark fd as unused */
+ for (j = 0; j < ps->ps_ninstances; j++)
+ pp->pp_pipes[dst][j] = -1;
+ }
+ }
+ }
+
+ /*
+ * Setup and run the parent and its children
+ */
+ privsep_process = PROC_PARENT;
+ ps->ps_instances[PROC_PARENT] = 1;
+ ps->ps_title[PROC_PARENT] = "parent";
+ ps->ps_pid[PROC_PARENT] = getpid();
+ ps->ps_pp = &ps->ps_pipes[privsep_process][0];
+
+ for (i = 0; i < nproc; i++) {
+ /* Default to 1 process instance */
+ if (ps->ps_instances[procs[i].p_id] < 1)
+ ps->ps_instances[procs[i].p_id] = 1;
+ ps->ps_title[procs[i].p_id] = procs[i].p_title;
+ }
+
+ proc_open(ps, NULL, procs, nproc);
+
+ /* Engage! */
+ for (i = 0; i < nproc; i++)
+ ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]);
+}
+
+void
+proc_kill(struct privsep *ps)
+{
+ pid_t pid;
+ unsigned int i;
+
+ if (privsep_process != PROC_PARENT)
+ return;
+
+ for (i = 0; i < PROC_MAX; i++) {
+ if (ps->ps_pid[i] == 0)
+ continue;
+ killpg(ps->ps_pid[i], SIGTERM);
+ }
+
+ do {
+ pid = waitpid(WAIT_ANY, NULL, 0);
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ proc_close(ps);
+}
+
+void
+proc_open(struct privsep *ps, struct privsep_proc *p,
+ struct privsep_proc *procs, size_t nproc)
+{
+ struct privsep_pipes *pa, *pb;
+ int fds[2];
+ unsigned int i, j, src, proc;
+
+ if (p == NULL)
+ src = privsep_process; /* parent */
+ else
+ src = p->p_id;
+
+ /*
+ * Open socket pairs for our peers
+ */
+ for (proc = 0; proc < nproc; proc++) {
+ procs[proc].p_ps = ps;
+ procs[proc].p_env = ps->ps_env;
+ if (procs[proc].p_cb == NULL)
+ procs[proc].p_cb = proc_dispatch_null;
+
+ for (i = 0; i < ps->ps_instances[src]; i++) {
+ for (j = 0; j < ps->ps_instances[procs[proc].p_id];
+ j++) {
+ pa = &ps->ps_pipes[src][i];
+ pb = &ps->ps_pipes[procs[proc].p_id][j];
+
+ /* Check if fds are already set by peer */
+ if (pa->pp_pipes[procs[proc].p_id][j] != -1)
+ continue;
+
+ if (socketpair(AF_UNIX,
+ SOCK_STREAM | SOCK_NONBLOCK,
+ PF_UNSPEC, fds) == -1)
+ fatal("socketpair");
+
+ pa->pp_pipes[procs[proc].p_id][j] = fds[0];
+ pb->pp_pipes[src][i] = fds[1];
+ }
+ }
+ }
+}
+
+void
+proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
+{
+ unsigned int i, dst, src, n, m;
+ struct privsep_pipes *pp;
+
+ /*
+ * Close unused pipes
+ */
+ for (src = 0; src < PROC_MAX; src++) {
+ for (n = 0; n < ps->ps_instances[src]; n++) {
+ /* Ingore current process */
+ if (src == (unsigned int)privsep_process &&
+ n == ps->ps_instance)
+ continue;
+
+ pp = &ps->ps_pipes[src][n];
+
+ for (dst = 0; dst < PROC_MAX; dst++) {
+ if (src == dst)
+ continue;
+ for (m = 0; m < ps->ps_instances[dst]; m++) {
+ if (pp->pp_pipes[dst][m] == -1)
+ continue;
+
+ /* Close and invalidate fd */
+ close(pp->pp_pipes[dst][m]);
+ pp->pp_pipes[dst][m] = -1;
+ }
+ }
+ }
+ }
+
+ src = privsep_process;
+ ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
+
+ /*
+ * Listen on appropriate pipes
+ */
+ for (i = 0; i < nproc; i++) {
+ dst = procs[i].p_id;
+
+ if (src == dst)
+ fatal("proc_listen: cannot peer with oneself");
+
+ if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
+ sizeof(struct imsgev))) == NULL)
+ fatal("proc_open");
+
+ for (n = 0; n < ps->ps_instances[dst]; n++) {
+ if (pp->pp_pipes[dst][n] == -1)
+ continue;
+
+ imsg_init(&(ps->ps_ievs[dst][n].ibuf),
+ pp->pp_pipes[dst][n]);
+ ps->ps_ievs[dst][n].handler = proc_dispatch;
+ ps->ps_ievs[dst][n].events = EV_READ;
+ ps->ps_ievs[dst][n].proc = &procs[i];
+ ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
+ procs[i].p_instance = n;
+
+ event_set(&(ps->ps_ievs[dst][n].ev),
+ ps->ps_ievs[dst][n].ibuf.fd,
+ ps->ps_ievs[dst][n].events,
+ ps->ps_ievs[dst][n].handler,
+ ps->ps_ievs[dst][n].data);
+ event_add(&(ps->ps_ievs[dst][n].ev), NULL);
+ }
+ }
+}
+
+void
+proc_close(struct privsep *ps)
+{
+ unsigned int dst, n;
+ struct privsep_pipes *pp;
+
+ if (ps == NULL)
+ return;
+
+ pp = ps->ps_pp;
+
+ for (dst = 0; dst < PROC_MAX; dst++) {
+ if (ps->ps_ievs[dst] == NULL)
+ continue;
+
+ for (n = 0; n < ps->ps_instances[dst]; n++) {
+ if (pp->pp_pipes[dst][n] == -1)
+ continue;
+
+ /* Cancel the fd, close and invalidate the fd */
+ event_del(&(ps->ps_ievs[dst][n].ev));
+ imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
+ close(pp->pp_pipes[dst][n]);
+ pp->pp_pipes[dst][n] = -1;
+ }
+ free(ps->ps_ievs[dst]);
+ }
+}
+
+void
+proc_shutdown(struct privsep_proc *p)
+{
+ struct privsep *ps = p->p_ps;
+
+ if (p->p_id == PROC_CONTROL && ps)
+ control_cleanup(&ps->ps_csock);
+
+ if (p->p_shutdown != NULL)
+ (*p->p_shutdown)();
+
+ proc_close(ps);
+
+ log_info("%s exiting, pid %d", p->p_title, getpid());
+
+ _exit(0);
+}
+
+void
+proc_sig_handler(int sig, short event, void *arg)
+{
+ struct privsep_proc *p = arg;
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ proc_shutdown(p);
+ break;
+ case SIGCHLD:
+ case SIGHUP:
+ case SIGPIPE:
+ case SIGUSR1:
+ /* ignore */
+ break;
+ default:
+ fatalx("proc_sig_handler: unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+pid_t
+proc_run(struct privsep *ps, struct privsep_proc *p,
+ struct privsep_proc *procs, unsigned int nproc,
+ void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
+{
+ pid_t pid;
+ struct passwd *pw;
+ const char *root;
+ struct control_sock *rcs;
+ unsigned int n;
+
+ if (ps->ps_noaction)
+ return (0);
+
+ proc_open(ps, p, procs, nproc);
+
+ /* Fork child handlers */
+ switch (pid = fork()) {
+ case -1:
+ fatal("proc_run: cannot fork");
+ case 0:
+ log_procinit(p->p_title);
+
+ /* Set the process group of the current process */
+ setpgid(0, 0);
+ break;
+ default:
+ return (pid);
+ }
+
+ pw = ps->ps_pw;
+
+ if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
+ if (control_init(ps, &ps->ps_csock) == -1)
+ fatalx(__func__);
+ TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
+ if (control_init(ps, rcs) == -1)
+ fatalx(__func__);
+ }
+
+ /* Change root directory */
+ if (p->p_chroot != NULL)
+ root = p->p_chroot;
+ else
+ root = pw->pw_dir;
+
+ if (chroot(root) == -1)
+ fatal("proc_run: chroot");
+ if (chdir("/") == -1)
+ fatal("proc_run: chdir(\"/\")");
+
+ privsep_process = p->p_id;
+
+ setproctitle("%s", p->p_title);
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("proc_run: cannot drop privileges");
+
+ /* Fork child handlers */
+ for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
+ if (fork() == 0) {
+ ps->ps_instance = p->p_instance = n;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
+ ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
+#endif
+
+ event_init();
+
+ signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
+ signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
+ signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
+ signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
+ signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
+ signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
+
+ signal_add(&ps->ps_evsigint, NULL);
+ signal_add(&ps->ps_evsigterm, NULL);
+ signal_add(&ps->ps_evsigchld, NULL);
+ signal_add(&ps->ps_evsighup, NULL);
+ signal_add(&ps->ps_evsigpipe, NULL);
+ signal_add(&ps->ps_evsigusr1, NULL);
+
+ proc_listen(ps, procs, nproc);
+
+ if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
+ TAILQ_INIT(&ctl_conns);
+ if (control_listen(&ps->ps_csock) == -1)
+ fatalx(__func__);
+ TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
+ if (control_listen(rcs) == -1)
+ fatalx(__func__);
+ }
+
+ if (run != NULL)
+ run(ps, p, arg);
+
+ event_dispatch();
+
+ proc_shutdown(p);
+
+ return (0);
+}
+
+void
+proc_dispatch(int fd, short event, void *arg)
+{
+ struct imsgev *iev = arg;
+ struct privsep_proc *p = iev->proc;
+ struct privsep *ps = p->p_ps;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int verbose;
+ const char *title;
+
+ title = ps->ps_title[privsep_process];
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ fatal(__func__);
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ }
+
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+ fatal(__func__);
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal(__func__);
+ if (n == 0)
+ break;
+
+#if DEBUG > 1
+ log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
+ __func__, title, ps->ps_instance + 1,
+ imsg.hdr.type, imsg.hdr.peerid, p->p_title, p->p_instance);
+#endif
+
+ /*
+ * Check the message with the program callback
+ */
+ if ((p->p_cb)(fd, p, &imsg) == 0) {
+ /* Message was handled by the callback, continue */
+ imsg_free(&imsg);
+ continue;
+ }
+
+ /*
+ * Generic message handling
+ */
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_VERBOSE:
+ IMSG_SIZE_CHECK(&imsg, &verbose);
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ break;
+ default:
+ log_warnx("%s: %s %d got invalid imsg %d peerid %d "
+ "from %s %d",
+ __func__, title, ps->ps_instance + 1,
+ imsg.hdr.type, imsg.hdr.peerid,
+ p->p_title, p->p_instance);
+ fatalx(__func__);
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(iev);
+}
+
+int
+proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ return (-1);
+}
+
+/*
+ * imsg helper functions
+ */
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ if (iev->handler == NULL) {
+ imsg_flush(&iev->ibuf);
+ return;
+ }
+
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+ pid_t pid, int fd, void *data, uint16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+ pid, fd, data, datalen)) == -1)
+ return (ret);
+ imsg_event_add(iev);
+ return (ret);
+}
+
+int
+imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+ pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+ int ret;
+
+ if ((ret = imsg_composev(&iev->ibuf, type, peerid,
+ pid, fd, iov, iovcnt)) == -1)
+ return (ret);
+ imsg_event_add(iev);
+ return (ret);
+}
+
+void
+proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
+{
+ if (*n == -1) {
+ /* Use a range of all target instances */
+ *n = 0;
+ *m = ps->ps_instances[id];
+ } else {
+ /* Use only a single slot of the specified peer process */
+ *m = *n + 1;
+ }
+}
+
+int
+proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
+ uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
+{
+ int m;
+
+ proc_range(ps, id, &n, &m);
+ for (; n < m; n++) {
+ if (imsg_compose_event(&ps->ps_ievs[id][n],
+ type, peerid, 0, fd, data, datalen) == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+proc_compose(struct privsep *ps, enum privsep_procid id,
+ uint16_t type, void *data, uint16_t datalen)
+{
+ return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
+}
+
+int
+proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
+ uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
+{
+ int m;
+
+ proc_range(ps, id, &n, &m);
+ for (; n < m; n++)
+ if (imsg_composev_event(&ps->ps_ievs[id][n],
+ type, peerid, 0, fd, iov, iovcnt) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+proc_composev(struct privsep *ps, enum privsep_procid id,
+ uint16_t type, const struct iovec *iov, int iovcnt)
+{
+ return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
+}
+
+int
+proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
+ enum privsep_procid id, int n)
+{
+ return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
+ imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
+}
+
+struct imsgbuf *
+proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
+{
+ int m;
+
+ proc_range(ps, id, &n, &m);
+ return (&ps->ps_ievs[id][n].ibuf);
+}
+
+struct imsgev *
+proc_iev(struct privsep *ps, enum privsep_procid id, int n)
+{
+ int m;
+
+ proc_range(ps, id, &n, &m);
+ return (&ps->ps_ievs[id][n]);
+}
--- /dev/null
+/* $OpenBSD: proc.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010-2015 Reyk Floeter <reyk@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 <imsg.h>
+#include <event.h>
+#include "types.h"
+
+#ifndef _PROC_H
+#define _PROC_H
+
+struct control_sock {
+ const char *cs_name;
+ struct event cs_ev;
+ struct event cs_evt;
+ int cs_fd;
+ int cs_restricted;
+ void *cs_env;
+
+ TAILQ_ENTRY(control_sock) cs_entry;
+};
+TAILQ_HEAD(control_socks, control_sock);
+
+struct privsep_pipes {
+ int *pp_pipes[PROC_MAX];
+};
+
+struct privsep {
+ struct privsep_pipes *ps_pipes[PROC_MAX];
+ struct privsep_pipes *ps_pp;
+
+ struct imsgev *ps_ievs[PROC_MAX];
+ const char *ps_title[PROC_MAX];
+ pid_t ps_pid[PROC_MAX];
+ struct passwd *ps_pw;
+ int ps_noaction;
+
+ struct control_sock ps_csock;
+ struct control_socks ps_rcsocks;
+
+ unsigned int ps_instances[PROC_MAX];
+ unsigned int ps_ninstances;
+ unsigned int ps_instance;
+
+ /* Event and signal handlers */
+ struct event ps_evsigint;
+ struct event ps_evsigterm;
+ struct event ps_evsigchld;
+ struct event ps_evsighup;
+ struct event ps_evsigpipe;
+ struct event ps_evsigusr1;
+
+ void *ps_env;
+};
+
+struct privsep_proc {
+ const char *p_title;
+ enum privsep_procid p_id;
+ int (*p_cb)(int, struct privsep_proc *,
+ struct imsg *);
+ pid_t (*p_init)(struct privsep *,
+ struct privsep_proc *);
+ const char *p_chroot;
+ struct privsep *p_ps;
+ void *p_env;
+ void (*p_shutdown)(void);
+ unsigned int p_instance;
+};
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ struct privsep_proc *proc;
+ void *data;
+ short events;
+ const char *name;
+};
+
+#ifndef IMSG_DATA_SIZE
+#define IMSG_DATA_SIZE(_imsg) ((_imsg)->hdr.len - IMSG_HEADER_SIZE)
+#endif
+
+#ifndef IMSG_SIZE_CHECK
+#define IMSG_SIZE_CHECK(_imsg, _type) \
+ do { \
+ if (IMSG_DATA_SIZE(_imsg) < sizeof(*(_type))) \
+ fatal("received imsg size was wrong."); \
+ } while (0 /* CONSTCOND */)
+#endif
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ uint8_t flags;
+#define CTL_CONN_NOTIFY 0x01
+ struct imsgev iev;
+ int restricted;
+};
+TAILQ_HEAD(ctl_connlist, ctl_conn);
+extern struct ctl_connlist ctl_conns;
+
+/* proc.c */
+void proc_init(struct privsep *, struct privsep_proc *, unsigned int);
+void proc_kill(struct privsep *);
+void proc_listen(struct privsep *, struct privsep_proc *, size_t);
+void proc_dispatch(int, short event, void *);
+pid_t proc_run(struct privsep *, struct privsep_proc *,
+ struct privsep_proc *, unsigned int,
+ void (*)(struct privsep *, struct privsep_proc *, void *), void *);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
+ pid_t, int, void *, uint16_t);
+int imsg_composev_event(struct imsgev *, uint16_t, uint32_t,
+ pid_t, int, const struct iovec *, int);
+int proc_compose_imsg(struct privsep *, enum privsep_procid, int,
+ uint16_t, uint32_t, int, void *, uint16_t);
+int proc_compose(struct privsep *, enum privsep_procid,
+ uint16_t, void *data, uint16_t);
+int proc_composev_imsg(struct privsep *, enum privsep_procid, int,
+ uint16_t, uint32_t, int, const struct iovec *, int);
+int proc_composev(struct privsep *, enum privsep_procid,
+ uint16_t, const struct iovec *, int);
+int proc_forward_imsg(struct privsep *, struct imsg *,
+ enum privsep_procid, int);
+struct imsgbuf *
+ proc_ibuf(struct privsep *, enum privsep_procid, int);
+struct imsgev *
+ proc_iev(struct privsep *, enum privsep_procid, int);
+
+/* control.c */
+int control_init(struct privsep *, struct control_sock *);
+int control_listen(struct control_sock *);
+void control_cleanup(struct control_sock *);
+struct ctl_conn
+ *control_connbyfd(int);
+pid_t control(struct privsep *, struct privsep_proc *);
+
+/* log.c */
+void log_init(int, int);
+void log_procinit(const char *);
+void log_verbose(int);
+void log_warn(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_warnx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_info(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void log_debug(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void logit(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void vlog(int, const char *, va_list)
+ __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+
+void socket_set_blockmode(int, enum blockmodes);
+
+#endif /* _PROC_H */
--- /dev/null
+/* $OpenBSD: switch.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <imsg.h>
+#include <event.h>
+
+#include "switchd.h"
+
+void switch_timer(struct switchd *, void *);
+
+static __inline int
+ switch_cmp(struct switch_control *, struct switch_control *);
+static __inline int
+ switch_maccmp(struct macaddr *, struct macaddr *);
+
+void
+switch_init(struct switchd *sc)
+{
+ RB_INIT(&sc->sc_switches);
+}
+
+int
+switch_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ struct switchd *sc = p->p_env;
+ struct privsep *ps = p->p_ps;
+ struct switch_control *sw;
+ struct macaddr *mac;
+ struct iovec iov[2];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_SUM:
+ IMSG_SIZE_CHECK(imsg, &fd);
+
+ RB_FOREACH(sw, switch_head, &sc->sc_switches) {
+ iov[0].iov_base = imsg->data;
+ iov[0].iov_len = IMSG_DATA_SIZE(imsg);
+ iov[1].iov_base = sw;
+ iov[1].iov_len = sizeof(*sw);
+
+ proc_composev(ps, PROC_CONTROL,
+ IMSG_CTL_SWITCH, iov, 2);
+
+ RB_FOREACH(mac, macaddr_head, &sw->sw_addrcache) {
+ iov[0].iov_base = imsg->data;
+ iov[0].iov_len = IMSG_DATA_SIZE(imsg);
+ iov[1].iov_base = mac;
+ iov[1].iov_len = sizeof(*mac);
+
+ proc_composev(ps, PROC_CONTROL,
+ IMSG_CTL_MAC, iov, 2);
+ }
+ }
+
+ proc_compose(ps, PROC_CONTROL,
+ IMSG_CTL_END, imsg->data, IMSG_DATA_SIZE(imsg));
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+struct switch_control *
+switch_get(struct switch_connection *con)
+{
+ struct switchd *sc = con->con_sc;
+ struct switch_control key;
+
+ memcpy(&key.sw_addr, &con->con_peer, sizeof(key.sw_addr));
+
+ con->con_switch = RB_FIND(switch_head, &sc->sc_switches, &key);
+ return (con->con_switch);
+}
+
+struct switch_control *
+switch_add(struct switch_connection *con)
+{
+ struct switchd *sc = con->con_sc;
+ struct switch_control *sw, *oldsw;
+ static unsigned int id = 0;
+
+ /* Connection already has an associated switch */
+ if (con->con_switch != NULL)
+ return (NULL);
+
+ if ((sw = calloc(1, sizeof(*sw))) == NULL)
+ return (NULL);
+
+ memcpy(&sw->sw_addr, &con->con_peer, sizeof(sw->sw_addr));
+ sw->sw_id = ++id;
+ RB_INIT(&sw->sw_addrcache);
+
+ if ((oldsw =
+ RB_INSERT(switch_head, &sc->sc_switches, sw)) != NULL) {
+ free(sw);
+ sw = oldsw;
+ } else {
+ timer_set(sc, &sw->sw_timer, switch_timer, sw);
+ timer_add(sc, &sw->sw_timer, sc->sc_cache_timeout);
+ }
+
+ con->con_switch = sw;
+ return (con->con_switch);
+}
+
+void
+switch_timer(struct switchd *sc, void *arg)
+{
+ struct switch_control *sw = arg;
+ struct macaddr *mac, *next;
+ struct timeval tv;
+ unsigned int cnt = 0;
+
+ getmonotime(&tv);
+
+ for (mac = RB_MIN(macaddr_head, &sw->sw_addrcache);
+ mac != NULL; mac = next) {
+ next = RB_NEXT(macaddr_head, &sw->sw_addrcache, mac);
+
+ /* Simple monotonic timeout */
+ if ((tv.tv_sec - mac->mac_age) >= sc->sc_cache_timeout) {
+ RB_REMOVE(macaddr_head, &sw->sw_addrcache, mac);
+ sw->sw_cachesize--;
+ free(mac);
+ cnt++;
+ }
+ }
+ if (cnt)
+ log_debug("%s: flushed %d mac from switch %u after timeout",
+ __func__, cnt, sw->sw_id);
+
+ timer_add(sc, &sw->sw_timer, sc->sc_cache_timeout);
+}
+
+void
+switch_remove(struct switchd *sc, struct switch_control *sw)
+{
+ struct macaddr *mac, *next;
+
+ if (sw == NULL)
+ return;
+
+ timer_del(sc, &sw->sw_timer);
+
+ for (mac = RB_MIN(macaddr_head, &sw->sw_addrcache);
+ mac != NULL; mac = next) {
+ next = RB_NEXT(macaddr_head, &sw->sw_addrcache, mac);
+ RB_REMOVE(macaddr_head, &sw->sw_addrcache, mac);
+ sw->sw_cachesize--;
+ free(mac);
+ }
+ RB_REMOVE(switch_head, &sc->sc_switches, sw);
+
+ log_debug("%s: switch %u removed", __func__, sw->sw_id);
+
+ free(sw);
+}
+
+struct macaddr *
+switch_learn(struct switchd *sc, struct switch_control *sw,
+ uint8_t *ea, long port)
+{
+ struct macaddr *mac, *oldmac = NULL;
+ struct timeval tv;
+
+ if ((mac = oldmac = switch_cached(sw, ea)) != NULL)
+ goto update;
+
+ if (sw->sw_cachesize >= sc->sc_cache_max)
+ return (NULL);
+
+ if ((mac = calloc(1, sizeof(*mac))) == NULL)
+ return (NULL);
+
+ memcpy(&mac->mac_addr, ea, sizeof(mac->mac_addr));
+
+ if (RB_INSERT(macaddr_head, &sw->sw_addrcache, mac) != NULL)
+ fatalx("cache corrupted");
+ sw->sw_cachesize++;
+
+ update:
+ getmonotime(&tv);
+ mac->mac_port = port;
+ mac->mac_age = tv.tv_sec;
+
+ log_debug("%s: %s mac %s on switch %u port %ld",
+ __func__, oldmac == NULL ? "learned new" : "updated",
+ print_ether(ea), sw->sw_id, port);
+
+ return (mac);
+}
+
+struct macaddr *
+switch_cached(struct switch_control *sw, uint8_t *ea)
+{
+ struct macaddr key;
+ memcpy(&key.mac_addr, ea, sizeof(key.mac_addr));
+ return (RB_FIND(macaddr_head, &sw->sw_addrcache, &key));
+}
+
+static __inline int
+switch_cmp(struct switch_control *a, struct switch_control *b)
+{
+ int diff = 0;
+
+ diff = sockaddr_cmp((struct sockaddr *)&a->sw_addr,
+ (struct sockaddr *)&b->sw_addr, 128);
+ if (!diff)
+ diff = socket_getport(&a->sw_addr) -
+ socket_getport(&b->sw_addr);
+
+ return (diff);
+}
+
+static __inline int
+switch_maccmp(struct macaddr *a, struct macaddr *b)
+{
+ return (memcmp(a->mac_addr, b->mac_addr, sizeof(a->mac_addr)));
+}
+
+RB_GENERATE(switch_head, switch_control, sw_entry, switch_cmp);
+RB_GENERATE(macaddr_head, macaddr, mac_entry, switch_maccmp);
--- /dev/null
+.\" $OpenBSD: switchd.8,v 1.1 2016/07/19 16:54:26 reyk Exp $
+.\"
+.\" Copyright (c) 2016 Reyk Floeter <reyk@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.
+.\"
+.Dd $Mdocdate: July 19 2016 $
+.Dt SWITCHD 8
+.Os
+.Sh NAME
+.Nm switchd
+.Nd software-defined networking (SDN) sflow controller
+.Sh SYNOPSIS
+.Nm switchd
+.Op Fl 6dnSTtv
+.Op Fl D Ar macro Ns = Ns Ar value
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is an controller for software-defined networking (SDN) and is
+compatible with the OpenFlow protocol.
+.Sh STANDARDS
+.Rs
+.%A Open Networking Foundation (ONF)
+.%D December 31, 2009
+.%R Version 1.0.0 (Wire Protocol 0x01)
+.%T OpenFlow Switch Specification
+.Re
+.Pp
+.Rs
+.%A Open Networking Foundation (ONF)
+.%D March 26, 2015
+.%R Version 1.3.5 (Protocol version 0x04)
+.%T OpenFlow Switch Specification
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 6.1 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq Mt reyk@openbsd.org .
--- /dev/null
+/* $OpenBSD: switchd.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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/uio.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "switchd.h"
+
+void parent_shutdown(struct switchd *);
+void parent_sig_handler(int, short, void *);
+int parent_dispatch_ofp(int, struct privsep_proc *, struct imsg *);
+int parent_dispatch_control(int, struct privsep_proc *, struct imsg *);
+int parent_configure(struct switchd *);
+int parent_reload(struct switchd *);
+void parent_device_connect(struct privsep *, struct switch_device *);
+int switch_device_cmp(struct switch_device *, struct switch_device *);
+
+__dead void usage(void);
+
+static struct privsep_proc procs[] = {
+ { "ofp", PROC_OFP, NULL, ofp },
+ { "control", PROC_CONTROL, parent_dispatch_control, control },
+ { "ofcconn", PROC_OFCCONN, NULL, ofcconn_proc_init,
+ .p_shutdown = ofcconn_proc_shutdown }
+};
+
+__dead void
+usage(void)
+{
+ extern const char *__progname;
+ fprintf(stderr, "usage: %s [-dv] [-D macro=value] [-f file] "
+ "[-c mac-cache-size] [-t cache-timeout]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct switchd *sc = NULL;
+ struct privsep *ps = NULL;
+ struct switch_server *srv;
+ const char *errstr = NULL;
+ int c;
+ int debug = 0, verbose = 0;
+ unsigned int cache = SWITCHD_CACHE_MAX;
+ unsigned int timeout = SWITCHD_CACHE_TIMEOUT;
+ const char *conffile = SWITCHD_CONFIG;
+
+ log_init(1, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "c:dD:f:ht:v")) != -1) {
+ switch (c) {
+ case 'c':
+ cache = strtonum(optarg, 1, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warn("max cache size: %s", errstr);
+ usage();
+ }
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 't':
+ timeout = strtonum(optarg, 0, UINT32_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warn("cache timeout: %s", errstr);
+ usage();
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if ((sc = calloc(1, sizeof(*sc))) == NULL)
+ fatal("calloc");
+
+ if (strlcpy(sc->sc_conffile, conffile, PATH_MAX) >= PATH_MAX)
+ fatal("config file exceeds PATH_MAX");
+
+ sc->sc_cache_max = cache;
+ sc->sc_cache_timeout = timeout;
+
+ srv = &sc->sc_server;
+ srv->srv_sc = sc;
+
+ ps = &sc->sc_ps;
+ ps->ps_env = sc;
+ TAILQ_INIT(&ps->ps_rcsocks);
+ TAILQ_INIT(&sc->sc_conns);
+
+ if (parse_config(sc->sc_conffile, sc) == -1) {
+ proc_kill(&sc->sc_ps);
+ exit(1);
+ }
+
+ /* check for root privileges */
+ if (geteuid())
+ fatalx("need root privileges");
+
+ if ((ps->ps_pw = getpwnam(SWITCHD_USER)) == NULL)
+ fatalx("unknown user " SWITCHD_USER);
+
+ /* Configure the control socket */
+ ps->ps_csock.cs_name = SWITCHD_SOCKET;
+
+ log_init(debug, LOG_DAEMON);
+ log_verbose(verbose);
+
+ if (!debug && daemon(0, 0) == -1)
+ fatal("failed to daemonize");
+
+ ps->ps_ninstances = 1;
+ proc_init(ps, procs, nitems(procs));
+
+ setproctitle("parent");
+
+ event_init();
+
+ signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
+ signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
+
+ signal_add(&ps->ps_evsigint, NULL);
+ signal_add(&ps->ps_evsigterm, NULL);
+ signal_add(&ps->ps_evsigchld, NULL);
+ signal_add(&ps->ps_evsighup, NULL);
+ signal_add(&ps->ps_evsigpipe, NULL);
+ signal_add(&ps->ps_evsigusr1, NULL);
+
+ proc_listen(ps, procs, nitems(procs));
+
+ if (parent_configure(sc) == -1)
+ fatalx("configuration failed");
+
+ event_dispatch();
+
+ log_debug("%d parent exiting", getpid());
+
+ return (0);
+}
+
+int
+switchd_socket(struct sockaddr *sock, int reuseport)
+{
+ int s = -1, val;
+ struct linger lng;
+
+ if ((s = socket(sock->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
+ goto bad;
+
+ /*
+ * Socket options
+ */
+ bzero(&lng, sizeof(lng));
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
+ goto bad;
+ if (reuseport) {
+ val = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val,
+ sizeof(int)) == -1)
+ goto bad;
+ }
+ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
+ goto bad;
+
+ /*
+ * TCP options
+ */
+ val = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
+ &val, sizeof(val)) == -1)
+ goto bad;
+
+ return (s);
+
+ bad:
+ if (s != -1)
+ close(s);
+ return (-1);
+}
+
+int
+switchd_listen(struct sockaddr *sock)
+{
+ int s;
+
+ if ((s = switchd_socket(sock, 1)) == -1)
+ return (-1);
+
+ if (bind(s, sock, sock->sa_len) == -1)
+ goto bad;
+ if (listen(s, 10) == -1)
+ goto bad;
+
+ return (s);
+
+ bad:
+ close(s);
+ return (-1);
+}
+
+int
+switchd_tap(void)
+{
+ int fd;
+ if ((fd = open("/dev/tun0", O_WRONLY)) == -1)
+ return (-1);
+ return (fd);
+}
+
+
+void
+parent_sig_handler(int sig, short event, void *arg)
+{
+ struct privsep *ps = arg;
+ int die = 0, status, fail, id;
+ pid_t pid;
+ char *cause;
+
+ switch (sig) {
+ case SIGHUP:
+ log_info("%s: reload requested with SIGHUP", __func__);
+
+ /*
+ * This is safe because libevent uses async signal handlers
+ * that run in the event loop and not in signal context.
+ */
+ parent_reload(ps->ps_env);
+ break;
+ case SIGPIPE:
+ log_info("%s: ignoring SIGPIPE", __func__);
+ break;
+ case SIGUSR1:
+ log_info("%s: ignoring SIGUSR1", __func__);
+ break;
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ do {
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid <= 0)
+ continue;
+
+ fail = 0;
+ if (WIFSIGNALED(status)) {
+ fail = 1;
+ asprintf(&cause, "terminated; signal %d",
+ WTERMSIG(status));
+ } else if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ fail = 1;
+ asprintf(&cause, "exited abnormally");
+ } else
+ asprintf(&cause, "exited okay");
+ } else
+ fatalx("unexpected cause of SIGCHLD");
+
+ die = 1;
+
+ for (id = 0; id < PROC_MAX; id++)
+ if (pid == ps->ps_pid[id]) {
+ if (fail)
+ log_warnx("lost child: %s %s",
+ ps->ps_title[id], cause);
+ break;
+ }
+
+ free(cause);
+ } while (pid > 0 || (pid == -1 && errno == EINTR));
+
+ if (die)
+ parent_shutdown(ps->ps_env);
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+int
+parent_configure(struct switchd *sc)
+{
+ struct switch_device *c;
+
+ TAILQ_FOREACH(c, &sc->sc_conns, sdv_next) {
+ parent_device_connect(&sc->sc_ps, c);
+ }
+
+ return (0);
+}
+
+int
+parent_reload(struct switchd *sc)
+{
+ struct switchd newconf;
+ struct switch_device *sdv, *osdv, *sdvn;
+ enum privsep_procid procid;
+
+ memset(&newconf, 0, sizeof(newconf));
+ TAILQ_INIT(&newconf.sc_conns);
+
+ if (parse_config(sc->sc_conffile, &newconf) != -1) {
+ TAILQ_FOREACH_SAFE(sdv, &sc->sc_conns, sdv_next, sdvn) {
+ TAILQ_FOREACH(osdv, &newconf.sc_conns, sdv_next) {
+ if (switch_device_cmp(osdv, sdv) == 0) {
+ TAILQ_REMOVE(&newconf.sc_conns,
+ osdv, sdv_next);
+ break;
+ }
+ }
+ if (osdv == NULL) {
+ /* Removed */
+ TAILQ_REMOVE(&sc->sc_conns, sdv, sdv_next);
+ procid = (sdv->sdv_swc.swc_type ==
+ SWITCH_CONN_LOCAL)
+ ? PROC_OFP : PROC_OFCCONN;
+ proc_compose_imsg(&sc->sc_ps, procid, -1,
+ IMSG_CTL_DEVICE_DISCONNECT,
+ -1, -1, sdv, sizeof(*sdv));
+ } else {
+ /* Keep the existing one */
+ TAILQ_REMOVE(&newconf.sc_conns, osdv, sdv_next);
+ free(osdv);
+ }
+ }
+ TAILQ_FOREACH(sdv, &newconf.sc_conns, sdv_next) {
+ procid =
+ (sdv->sdv_swc.swc_type == SWITCH_CONN_LOCAL)
+ ? PROC_OFP : PROC_OFCCONN;
+ TAILQ_INSERT_TAIL(&sc->sc_conns, sdv, sdv_next);
+ parent_device_connect(&sc->sc_ps, sdv);
+ }
+ }
+
+ return (0);
+}
+
+int
+parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_DEVICE_CONNECT:
+ case IMSG_CTL_DEVICE_DISCONNECT:
+ if (IMSG_DATA_SIZE(imsg) <
+ sizeof(struct switch_device)) {
+ log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: "
+ "message size is wrong", __func__);
+ return (0);
+ }
+ if (imsg->hdr.type == IMSG_CTL_DEVICE_CONNECT)
+ parent_device_connect(p->p_ps, imsg->data);
+ else {
+ /*
+ * Since we don't know which the device was attached
+ * to, we send the message to the both.
+ */
+ proc_compose(p->p_ps, PROC_OFP,
+ imsg->hdr.type, imsg->data, IMSG_DATA_SIZE(imsg));
+ proc_compose(p->p_ps, PROC_OFCCONN,
+ imsg->hdr.type, imsg->data, IMSG_DATA_SIZE(imsg));
+ }
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+void
+parent_shutdown(struct switchd *sc)
+{
+ proc_kill(&sc->sc_ps);
+
+ free(sc);
+
+ log_warnx("parent terminating");
+ exit(0);
+}
+
+void
+parent_device_connect(struct privsep *ps, struct switch_device *sdv)
+{
+ int fd;
+
+ /* restrict the opening path to /dev/switch* */
+ if (strncmp(sdv->sdv_device, "/dev/switch", 11) != 0) {
+ log_warnx("%s: device path is wrong: %s", __func__,
+ sdv->sdv_device);
+ goto on_error;
+ }
+
+ if ((fd = open(sdv->sdv_device, O_RDWR | O_NONBLOCK)) == -1) {
+ log_warn("%s: open(%s) failed", __func__, sdv->sdv_device);
+ goto on_error;
+ }
+
+ switch (sdv->sdv_swc.swc_type) {
+ case SWITCH_CONN_LOCAL:
+ proc_compose_imsg(ps, PROC_OFP, -1, IMSG_CTL_DEVICE_CONNECT,
+ -1, fd, sdv, sizeof(*sdv));
+ break;
+ case SWITCH_CONN_TLS:
+ case SWITCH_CONN_TCP:
+ proc_compose_imsg(ps, PROC_OFCCONN, -1, IMSG_CTL_DEVICE_CONNECT,
+ -1, fd, sdv, sizeof(struct switch_device));
+ break;
+ default:
+ fatalx("not implemented");
+ }
+on_error:
+ return;
+}
+
+int
+switch_device_cmp(struct switch_device *a,
+ struct switch_device *b)
+{
+ struct switch_controller *ca = &a->sdv_swc;
+ struct switch_controller *cb = &b->sdv_swc;
+ int c;
+
+ if ((c = strcmp(a->sdv_device, b->sdv_device)) != 0)
+ return (c);
+ if ((c = cb->swc_type - ca->swc_type) != 0)
+ return (c);
+
+ return (sockaddr_cmp((struct sockaddr *)&ca->swc_addr,
+ (struct sockaddr *)&cb->swc_addr, -1));
+}
--- /dev/null
+.\" $OpenBSD: switchd.conf.5,v 1.1 2016/07/19 16:54:26 reyk Exp $
+.\"
+.\" Copyright (c) 2014, 2015, 2016 Reyk Floeter <reyk@openbsd.org>
+.\" Copyright (c) 2016 YASUOKA Masahiko <yasuoka@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.
+.\"
+.\" The following requests are required for all man pages.
+.\"
+.Dd $Mdocdate: July 19 2016 $
+.Dt SWITCHD.CONF 5
+.Os
+.Sh NAME
+.Nm switchd.conf
+.Nd Switch daemon configuration file
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the switch daemon,
+.Xr switchd 8 .
+.Sh SECTIONS
+.Nm
+files is divided into tw main sections:
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and user later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global runtime settings for
+.Xr switchd 8 .
+.El
+.Pp
+The current line can be extended over multiple lines using a backslash
+.Pq Sq \e .
+Comments can be put anywhere in the file using a hash mark
+.Pq Sq # ,
+and extend to the end of the current line.
+Care should be taken when commenting out multi-line text:
+the comment is effective until the end of the entire block.
+.Pp
+Argument names not beginning with a letter, digit, or underscore
+must be quoted.
+.Pp
+Additional configuration files can be included with the
+.Ic include
+keyword, for example:
+.Bd -literal -offset indent
+include "/etc/snmpd.conf.local"
+.Ed
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, digit, or underscore,
+and may contain any of those characters.
+Macro names may not be reserved words (for example,
+.Ic directory ,
+.Ic log ,
+or
+.Ic root ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+ext_ip="10.0.0.1"
+listen on $ext_ip
+.Ed
+.Sh GLOBAL CONFIGURATION
+The following options can be set globally:
+.Bl -tag -width Ds
+.It Ic listen on Ar address Oo Ic tls Oc Op Ic port Ar port
+Set the listen address and port to accept connections from remote
+OpenFlow switches.
+Secure connections can be enabled with the optional
+.Ic tls
+keyword.
+.It Ic device on Ar device-name Oo Ic forward to Ar uri Oc
+Attach to a
+.Xr switch 4
+device.
+When attached,
+.Xr switchd 8
+will accept OpenFlow messages from the connected kernel interface.
+The daemon either handles the requests locally or sends them to a remote
+controller if the
+.Ic forward to
+directive is set.
+The
+.Ar uri
+is the method and address to connect to the remote controller,
+with the format
+.Ar protocol:address:port
+where the
+.Ar protocol
+can be either
+.Dq tcp
+or
+.Dq tls .
+.El
+.Sh EXAMPLES
+The folowing example is a typical one.
+.Bd -literal -offset indent
+listen on 0.0.0.0 port 6633
+device "/dev/switch0"
+device "/dev/switch1" forward to tcp:192.168.0.1:6633
+.Ed
+.Sh SEE ALSO
+.Xr switchd 8
--- /dev/null
+/* $OpenBSD: switchd.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _SWITCHD_H
+#define _SWITCHD_H
+
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <imsg.h>
+
+#include "ofp.h"
+#include "ofp10.h"
+#include "types.h"
+#include "proc.h"
+
+struct switchd;
+
+struct timer {
+ struct event tmr_ev;
+ struct switchd *tmr_sc;
+ void (*tmr_cb)(struct switchd *, void *);
+ void *tmr_cbarg;
+};
+
+struct packet {
+ union {
+ struct ether_header *pkt_eh;
+ uint8_t *pkt_buf;
+ };
+ size_t pkt_len;
+};
+
+struct macaddr {
+ uint8_t mac_addr[ETHER_ADDR_LEN];
+ long mac_port;
+ time_t mac_age;
+ RB_ENTRY(macaddr) mac_entry;
+};
+RB_HEAD(macaddr_head, macaddr);
+
+struct switch_control {
+ unsigned int sw_id;
+ struct sockaddr_storage sw_addr;
+ struct macaddr_head sw_addrcache;
+ struct timer sw_timer;
+ unsigned int sw_cachesize;
+ RB_ENTRY(switch_control) sw_entry;
+};
+RB_HEAD(switch_head, switch_control);
+
+struct switch_connection {
+ unsigned int con_id;
+ int con_fd;
+ struct sockaddr_storage con_peer;
+ struct sockaddr_storage con_local;
+ struct switch_control *con_switch;
+ in_port_t con_port;
+ struct event con_ev;
+ struct switchd *con_sc;
+ uint32_t con_xidnxt;
+ TAILQ_ENTRY(switch_connection)
+ con_next;
+};
+
+struct switch_server {
+ int srv_fd;
+ int srv_tls;
+ struct sockaddr_storage srv_addr;
+ struct event srv_ev;
+ struct switchd *srv_sc;
+};
+
+struct switch_controller {
+ enum switch_conn_type swc_type;
+ struct sockaddr_storage swc_addr;
+};
+
+struct switch_device {
+ char sdv_device[PATH_MAX];
+ struct switch_controller sdv_swc;
+ TAILQ_ENTRY(switch_device)
+ sdv_next;
+};
+
+struct switchd {
+ struct privsep sc_ps;
+ struct switch_server sc_server;
+ int sc_tap;
+ struct switch_head sc_switches;
+ uint32_t sc_swid;
+ unsigned int sc_cache_max;
+ unsigned int sc_cache_timeout;
+ char sc_conffile[PATH_MAX];
+ TAILQ_HEAD(, switch_device)
+ sc_conns;
+};
+
+struct ofp_callback {
+ uint8_t cb_type;
+ int (*cb)(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+ int (*debug)(struct switchd *, struct sockaddr_storage *,
+ struct sockaddr_storage *, struct ofp_header *,
+ struct ibuf *);
+};
+
+/* switchd.c */
+int switchd_socket(struct sockaddr *, int);
+int switchd_listen(struct sockaddr *);
+int switchd_sockaddr(const char *, in_port_t, struct sockaddr_storage *);
+int switchd_tap(void);
+int switchd_open_device(struct privsep *, const char *, size_t);
+
+/* packet.c */
+long packet_input(struct switchd *, struct switch_control *, long,
+ struct ibuf *, size_t, struct packet *);
+
+/* switch.c */
+void switch_init(struct switchd *);
+int switch_dispatch_control(int, struct privsep_proc *,
+ struct imsg *);
+struct switch_control
+ *switch_add(struct switch_connection *);
+void switch_remove(struct switchd *, struct switch_control *);
+struct switch_control
+ *switch_get(struct switch_connection *);
+struct macaddr *switch_learn(struct switchd *, struct switch_control *,
+ uint8_t *, long);
+struct macaddr *switch_cached(struct switch_control *, uint8_t *);
+RB_PROTOTYPE(switch_head, switch_control, sw_entry, switch_cmp);
+RB_PROTOTYPE(macaddr_head, macaddr, mac_entry, switch_maccmp);
+
+/* timer.c */
+void timer_set(struct switchd *, struct timer *,
+ void (*)(struct switchd *, void *), void *);
+void timer_add(struct switchd *, struct timer *, int);
+void timer_del(struct switchd *, struct timer *);
+
+/* util.c */
+void socket_set_blockmode(int, enum blockmodes);
+in_port_t socket_getport(struct sockaddr_storage *);
+int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int);
+struct in6_addr *prefixlen2mask6(uint8_t, uint32_t *);
+uint32_t prefixlen2mask(uint8_t);
+const char *print_host(struct sockaddr_storage *, char *, size_t);
+const char *print_ether(const uint8_t *)
+ __attribute__ ((__bounded__(__minbytes__,1,ETHER_ADDR_LEN)));
+const char *print_map(unsigned int, struct constmap *);
+void print_verbose(const char *emsg, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void print_debug(const char *emsg, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+void getmonotime(struct timeval *);
+int parsehostport(const char *, struct sockaddr *, socklen_t);
+
+/* ofp.c */
+pid_t ofp(struct privsep *, struct privsep_proc *);
+void ofp_close(struct switch_connection *);
+void ofp_read(int, short, void *);
+int ofp_send(struct switch_connection *, struct ofp_header *,
+ struct ibuf *);
+void ofp_accept(int, short, void *);
+
+/* ofp10.c */
+int ofp10_hello(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+void ofp10_debug_header(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *);
+void ofp10_debug(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+int ofp10_input(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+
+/* ofp13.c */
+void ofp13_debug(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+int ofp13_input(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+
+/* ofcconn.c */
+pid_t ofcconn_proc_init(struct privsep *, struct privsep_proc *);
+void ofcconn_proc_shutdown(void);
+
+/* imsg_util.c */
+struct ibuf *ibuf_new(void *, size_t);
+struct ibuf *ibuf_static(void);
+int ibuf_cat(struct ibuf *, struct ibuf *);
+void ibuf_release(struct ibuf *);
+size_t ibuf_length(struct ibuf *);
+int ibuf_setsize(struct ibuf *, size_t);
+uint8_t *ibuf_data(struct ibuf *);
+void *ibuf_getdata(struct ibuf *, size_t);
+ssize_t ibuf_dataleft(struct ibuf *);
+size_t ibuf_dataoffset(struct ibuf *);
+struct ibuf *ibuf_get(struct ibuf *, size_t);
+struct ibuf *ibuf_dup(struct ibuf *);
+struct ibuf *ibuf_random(size_t);
+int ibuf_prepend(struct ibuf *, void *, size_t);
+void *ibuf_advance(struct ibuf *, size_t);
+void ibuf_zero(struct ibuf *);
+void ibuf_reset(struct ibuf *);
+
+/* parse.y */
+int cmdline_symset(char *);
+int parse_config(const char *, struct switchd *);
+
+#endif /* _SWITCHD_H */
--- /dev/null
+/* $OpenBSD: timer.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010-2016 Reyk Floeter <reyk@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/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <event.h>
+
+#include "switchd.h"
+
+void timer_callback(int, short, void *);
+
+void
+timer_set(struct switchd *sc, struct timer *tmr,
+ void (*cb)(struct switchd *, void *), void *arg)
+{
+ tmr->tmr_sc = sc;
+ tmr->tmr_cb = cb;
+ tmr->tmr_cbarg = arg;
+ evtimer_set(&tmr->tmr_ev, timer_callback, tmr);
+}
+
+void
+timer_add(struct switchd *sc, struct timer *tmr, int timeout)
+{
+ struct timeval tv = { timeout };
+
+ if (evtimer_initialized(&tmr->tmr_ev) &&
+ evtimer_pending(&tmr->tmr_ev, NULL))
+ evtimer_del(&tmr->tmr_ev);
+
+ evtimer_add(&tmr->tmr_ev, &tv);
+}
+
+void
+timer_del(struct switchd *sc, struct timer *tmr)
+{
+ if (tmr->tmr_sc == sc && tmr->tmr_cb &&
+ evtimer_initialized(&tmr->tmr_ev))
+ evtimer_del(&tmr->tmr_ev);
+}
+
+void
+timer_callback(int fd, short event, void *arg)
+{
+ struct timer *tmr = arg;
+
+ if (tmr->tmr_cb)
+ tmr->tmr_cb(tmr->tmr_sc, tmr->tmr_cbarg);
+}
--- /dev/null
+/* $OpenBSD: types.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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.
+ */
+
+#ifndef _SWITCHD_TYPES_H
+#define _SWITCHD_TYPES_H
+
+#ifndef SWITCHD_USER
+#define SWITCHD_USER "_hostapd"
+#endif
+
+#ifndef SWITCHD_NAME
+#define SWITCHD_NAME "switch"
+#endif
+
+#ifndef SWITCHD_CONFIG
+#define SWITCHD_CONFIG "/etc/" SWITCHD_NAME "d.conf"
+#endif
+#define SWITCHD_SOCKET "/var/run/" SWITCHD_NAME "d.sock"
+
+#define SWITCHD_CYCLE_BUFFERS 8 /* # of static buffers for mapping */
+#define SWITCHD_READ_BUFFER 0xffff
+#define SWITCHD_MSGBUF_MAX 0xffff
+
+#define SWITCHD_CTLR_PORT 6633 /* Previously used by OpenFlow */
+#define SWITCHD_CTLR_IANA_PORT 6653 /* Assigned by IANA for OpenFlow */
+
+#define SWITCHD_CACHE_MAX 4096 /* Default MAC address cache limit */
+#define SWITCHD_CACHE_TIMEOUT 240 /* t/o in seconds for learned MACs */
+
+#define SWITCHD_OFCCONN_TIMEOUT 20 /* connect timeout for OpenFlow ch. */
+
+#ifndef ETHER_ADDR_LEN
+#define ETHER_ADDR_LEN 6
+#endif
+
+struct constmap {
+ unsigned int cm_type;
+ const char *cm_name;
+ const char *cm_descr;
+};
+
+enum imsg_type {
+ IMSG_NONE = 0,
+ IMSG_CTL_VERBOSE,
+ IMSG_CTL_NOTIFY,
+ IMSG_CTL_OK,
+ IMSG_CTL_FAIL,
+ IMSG_CTL_END,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_RESET,
+ IMSG_CTL_SWITCH,
+ IMSG_CTL_MAC,
+ IMSG_CTL_SHOW_SUM,
+ IMSG_CTL_DEVICE_CONNECT,
+ IMSG_CTL_DEVICE_DISCONNECT
+};
+
+enum privsep_procid {
+ PROC_PARENT = 0,
+ PROC_OFP,
+ PROC_CONTROL,
+ PROC_OFCCONN,
+ PROC_MAX
+} privsep_process;
+
+enum blockmodes {
+ BM_NORMAL,
+ BM_NONBLOCK
+};
+
+enum flushmode {
+ RESET_RELOAD = 0,
+ RESET_ALL
+};
+
+enum switch_conn_type {
+ SWITCH_CONN_LOCAL,
+ SWITCH_CONN_TCP,
+ SWITCH_CONN_TLS
+};
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#endif /* _SWITCHD_TYPES_H */
--- /dev/null
+/* $OpenBSD: util.c,v 1.1 2016/07/19 16:54:26 reyk Exp $ */
+
+/*
+ * Copyright (c) 2013-2016 Reyk Floeter <reyk@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/un.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <event.h>
+
+#include "switchd.h"
+
+extern int debug;
+extern int verbose;
+
+void
+socket_set_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
+
+in_port_t
+socket_getport(struct sockaddr_storage *ss)
+{
+ switch (ss->ss_family) {
+ case AF_INET:
+ return (ntohs(((struct sockaddr_in *)ss)->sin_port));
+ case AF_INET6:
+ return (ntohs(((struct sockaddr_in6 *)ss)->sin6_port));
+ default:
+ return (0);
+ }
+
+ /* NOTREACHED */
+ return (0);
+}
+
+int
+sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)
+{
+ struct sockaddr_in *a4, *b4;
+ struct sockaddr_in6 *a6, *b6;
+ uint32_t av[4], bv[4], mv[4];
+
+ if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC)
+ return (0);
+ else if (a->sa_family > b->sa_family)
+ return (1);
+ else if (a->sa_family < b->sa_family)
+ return (-1);
+
+ if (prefixlen == -1)
+ memset(&mv, 0xff, sizeof(mv));
+
+ switch (a->sa_family) {
+ case AF_INET:
+ a4 = (struct sockaddr_in *)a;
+ b4 = (struct sockaddr_in *)b;
+
+ av[0] = a4->sin_addr.s_addr;
+ bv[0] = b4->sin_addr.s_addr;
+ if (prefixlen != -1)
+ mv[0] = prefixlen2mask(prefixlen);
+
+ if ((av[0] & mv[0]) > (bv[0] & mv[0]))
+ return (1);
+ if ((av[0] & mv[0]) < (bv[0] & mv[0]))
+ return (-1);
+ break;
+ case AF_INET6:
+ a6 = (struct sockaddr_in6 *)a;
+ b6 = (struct sockaddr_in6 *)b;
+
+ memcpy(&av, &a6->sin6_addr.s6_addr, 16);
+ memcpy(&bv, &b6->sin6_addr.s6_addr, 16);
+ if (prefixlen != -1)
+ prefixlen2mask6(prefixlen, mv);
+
+ if ((av[3] & mv[3]) > (bv[3] & mv[3]))
+ return (1);
+ if ((av[3] & mv[3]) < (bv[3] & mv[3]))
+ return (-1);
+ if ((av[2] & mv[2]) > (bv[2] & mv[2]))
+ return (1);
+ if ((av[2] & mv[2]) < (bv[2] & mv[2]))
+ return (-1);
+ if ((av[1] & mv[1]) > (bv[1] & mv[1]))
+ return (1);
+ if ((av[1] & mv[1]) < (bv[1] & mv[1]))
+ return (-1);
+ if ((av[0] & mv[0]) > (bv[0] & mv[0]))
+ return (1);
+ if ((av[0] & mv[0]) < (bv[0] & mv[0]))
+ return (-1);
+ break;
+ }
+
+ return (0);
+}
+
+uint32_t
+prefixlen2mask(uint8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ if (prefixlen > 32)
+ prefixlen = 32;
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+struct in6_addr *
+prefixlen2mask6(uint8_t prefixlen, uint32_t *mask)
+{
+ static struct in6_addr s6;
+ int i;
+
+ if (prefixlen > 128)
+ prefixlen = 128;
+
+ bzero(&s6, sizeof(s6));
+ for (i = 0; i < prefixlen / 8; i++)
+ s6.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ memcpy(mask, &s6, sizeof(s6));
+
+ return (&s6);
+}
+
+const char *
+print_ether(const uint8_t *ea)
+{
+ static char sbuf[SWITCHD_CYCLE_BUFFERS]
+ [ETHER_ADDR_LEN * 2 + 5 + 1];
+ static int idx = 0;
+ size_t len;
+ char *buf;
+
+ buf = sbuf[idx];
+ len = sizeof(sbuf[idx]);
+ if (++idx >= SWITCHD_CYCLE_BUFFERS)
+ idx = 0;
+
+ snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+
+ return (buf);
+}
+
+const char *
+print_host(struct sockaddr_storage *ss, char *buf, size_t len)
+{
+ static char sbuf[SWITCHD_CYCLE_BUFFERS][NI_MAXHOST + 7];
+ struct sockaddr_un *un;
+ static int idx = 0;
+ char pbuf[7];
+ in_port_t port;
+
+ if (buf == NULL) {
+ buf = sbuf[idx];
+ len = sizeof(sbuf[idx]);
+ if (++idx >= SWITCHD_CYCLE_BUFFERS)
+ idx = 0;
+ }
+
+ if (ss->ss_family == AF_UNSPEC) {
+ strlcpy(buf, "any", len);
+ return (buf);
+ } else if (ss->ss_family == AF_LOCAL) {
+ un = (struct sockaddr_un *)ss;
+ strlcpy(buf, un->sun_path, len);
+ return (buf);
+ }
+
+ if (getnameinfo((struct sockaddr *)ss, ss->ss_len,
+ buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
+ buf[0] = '\0';
+ return (NULL);
+ }
+
+ if ((port = socket_getport(ss)) != 0) {
+ snprintf(pbuf, sizeof(pbuf), ":%d", port);
+ (void)strlcat(buf, pbuf, len);
+ }
+
+ return (buf);
+}
+
+const char *
+print_map(unsigned int type, struct constmap *map)
+{
+ unsigned int i;
+ static char buf[SWITCHD_CYCLE_BUFFERS][32];
+ static int idx = 0;
+ const char *name = NULL;
+
+ if (idx >= SWITCHD_CYCLE_BUFFERS)
+ idx = 0;
+ bzero(buf[idx], sizeof(buf[idx]));
+
+ for (i = 0; map[i].cm_name != NULL; i++) {
+ if (map[i].cm_type == type) {
+ name = map[i].cm_name;
+ break;
+ }
+ }
+
+ if (name == NULL)
+ snprintf(buf[idx], sizeof(buf[idx]), "<%u>", type);
+ else
+ strlcpy(buf[idx], name, sizeof(buf[idx]));
+
+ return (buf[idx++]);
+}
+
+void
+getmonotime(struct timeval *tv)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ fatal("clock_gettime");
+
+ TIMESPEC_TO_TIMEVAL(tv, &ts);
+}
+
+void
+print_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug && verbose > 2) {
+ va_start(ap, emsg);
+ vfprintf(stderr, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+print_verbose(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose) {
+ va_start(ap, emsg);
+ vfprintf(stderr, emsg, ap);
+ va_end(ap);
+ }
+}
+
+int
+parsehostport(const char *str, struct sockaddr *sa, socklen_t salen)
+{
+ char buf[NI_MAXHOST + NI_MAXSERV + 8], *servp, *nodep;
+ struct addrinfo hints, *ai;
+
+ if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf))
+ return (-1);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = 0;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if (buf[0] == '[' &&
+ (servp = strchr(buf, ']')) != NULL &&
+ (*(servp + 1) == '\0' || *(servp + 1) == ':')) {
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_NUMERICHOST;
+ nodep = buf + 1;
+ *servp++ = '\0';
+ } else {
+ nodep = buf;
+ servp = strrchr(nodep, ':');
+ }
+ if (servp != NULL) {
+ *servp = '\0';
+ servp++;
+ } else
+ servp = NULL;
+
+ if (getaddrinfo(nodep, servp, &hints, &ai) != 0)
+ return (-1);
+
+ if (salen < ai->ai_addrlen) {
+ freeaddrinfo(ai);
+ return (-1);
+ }
+ memset(sa, 0, salen);
+ memcpy(sa, ai->ai_addr, ai->ai_addrlen);
+
+ return (0);
+}