Import switchd(8), a basic WIP OpenFlow implementation for OpenBSD.
authorreyk <reyk@openbsd.org>
Tue, 19 Jul 2016 16:54:26 +0000 (16:54 +0000)
committerreyk <reyk@openbsd.org>
Tue, 19 Jul 2016 16:54:26 +0000 (16:54 +0000)
switchd consists of two parts:
1. switchd(8) and switchctl(8), an OpenFlow controller or "vswitch".
2. switch(4), an OpenFlow-aware kernel "bridge".

This the 1st part, the driver will be imported later.  The code will
remain disabled for a while, but it helps development to have it in
the tree.  switchd currently supports partial OpenFlow 1.0, but the
goal is to use OpenFlow 1.3.5 instead (switch(4) already does 1.3.5).

For more background information see:
http://www.openbsd.org/papers/bsdcan2016-switchd.pdf
https://youtu.be/Cuo0qT-lqig

With help from yasuoka@ goda@
Import discussed with deraadt@

29 files changed:
usr.sbin/switchctl/Makefile [new file with mode: 0644]
usr.sbin/switchctl/parser.c [new file with mode: 0644]
usr.sbin/switchctl/parser.h [new file with mode: 0644]
usr.sbin/switchctl/switchctl.8 [new file with mode: 0644]
usr.sbin/switchctl/switchctl.c [new file with mode: 0644]
usr.sbin/switchd/Makefile [new file with mode: 0644]
usr.sbin/switchd/control.c [new file with mode: 0644]
usr.sbin/switchd/genmap.sh [new file with mode: 0644]
usr.sbin/switchd/imsg_util.c [new file with mode: 0644]
usr.sbin/switchd/log.c [new file with mode: 0644]
usr.sbin/switchd/ofcconn.c [new file with mode: 0644]
usr.sbin/switchd/ofp.c [new file with mode: 0644]
usr.sbin/switchd/ofp.h [new file with mode: 0644]
usr.sbin/switchd/ofp10.c [new file with mode: 0644]
usr.sbin/switchd/ofp10.h [new file with mode: 0644]
usr.sbin/switchd/ofp13.c [new file with mode: 0644]
usr.sbin/switchd/ofp_map.h [new file with mode: 0644]
usr.sbin/switchd/packet.c [new file with mode: 0644]
usr.sbin/switchd/parse.y [new file with mode: 0644]
usr.sbin/switchd/proc.c [new file with mode: 0644]
usr.sbin/switchd/proc.h [new file with mode: 0644]
usr.sbin/switchd/switch.c [new file with mode: 0644]
usr.sbin/switchd/switchd.8 [new file with mode: 0644]
usr.sbin/switchd/switchd.c [new file with mode: 0644]
usr.sbin/switchd/switchd.conf.5 [new file with mode: 0644]
usr.sbin/switchd/switchd.h [new file with mode: 0644]
usr.sbin/switchd/timer.c [new file with mode: 0644]
usr.sbin/switchd/types.h [new file with mode: 0644]
usr.sbin/switchd/util.c [new file with mode: 0644]

diff --git a/usr.sbin/switchctl/Makefile b/usr.sbin/switchctl/Makefile
new file mode 100644 (file)
index 0000000..e894ddd
--- /dev/null
@@ -0,0 +1,21 @@
+# $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>
diff --git a/usr.sbin/switchctl/parser.c b/usr.sbin/switchctl/parser.c
new file mode 100644 (file)
index 0000000..ee671c8
--- /dev/null
@@ -0,0 +1,284 @@
+/*     $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;
+               }
+       }
+}
diff --git a/usr.sbin/switchctl/parser.h b/usr.sbin/switchctl/parser.h
new file mode 100644 (file)
index 0000000..8b88588
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $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 */
diff --git a/usr.sbin/switchctl/switchctl.8 b/usr.sbin/switchctl/switchctl.8
new file mode 100644 (file)
index 0000000..d4c1ece
--- /dev/null
@@ -0,0 +1,100 @@
+.\" $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 .
diff --git a/usr.sbin/switchctl/switchctl.c b/usr.sbin/switchctl/switchctl.c
new file mode 100644 (file)
index 0000000..2ed852c
--- /dev/null
@@ -0,0 +1,348 @@
+/*     $OpenBSD: switchctl.c,v 1.1 2016/07/19 16:54:26 reyk Exp $      */
+
+/*
+ * Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "switchd.h"
+#include "parser.h"
+
+__dead void     usage(void);
+
+struct imsgname {
+       int type;
+       char *name;
+       void (*func)(struct imsg *);
+};
+
+int             show_summary_msg(struct imsg *, int);
+
+struct imsgname *monitor_lookup(uint8_t);
+void            monitor_id(struct imsg *);
+int             monitor(struct imsg *);
+
+int             ca_opt(struct parse_result *);
+
+struct imsgname imsgs[] = {
+       { IMSG_CTL_OK,                  "ok",                   NULL },
+       { IMSG_CTL_FAIL,                "fail",                 NULL },
+       { IMSG_CTL_VERBOSE,             "verbose",              NULL },
+       { IMSG_CTL_RELOAD,              "reload",               NULL },
+       { IMSG_CTL_RESET,               "reset",                NULL },
+       { 0,                            NULL,                   NULL }
+
+};
+struct imsgname imsgunknown = {
+       -1,                             "<unknown>",            NULL
+};
+
+struct imsgbuf *ibuf;
+
+__dead void
+usage(void)
+{
+       extern char *__progname;
+
+       fprintf(stderr, "usage: %s [-q] [-s socket] command [arg ...]\n",
+           __progname);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct sockaddr_un       sun;
+       struct parse_result     *res;
+       struct imsg              imsg;
+       struct switch_device     sdv;
+       struct switch_controller *swc;
+       int                      ctl_sock;
+       int                      done = 1;
+       int                      n;
+       int                      ch;
+       int                      v = 0;
+       int                      quiet = 0;
+       const char              *sock = SWITCHD_SOCKET;
+
+       while ((ch = getopt(argc, argv, "qs:")) != -1) {
+               switch (ch) {
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 's':
+                       sock = optarg;
+                       break;
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       /* parse options */
+       if ((res = parse(argc, argv)) == NULL)
+               exit(1);
+
+       res->quiet = quiet;
+
+       switch (res->action) {
+       case NONE:
+               usage();
+               break;
+       default:
+               goto connect;
+       }
+
+       return (0);
+
+ connect:
+       /* connect to sdnflowd control socket */
+       if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+               err(1, "socket");
+
+       bzero(&sun, sizeof(sun));
+       sun.sun_family = AF_UNIX;
+       strlcpy(sun.sun_path, sock, sizeof(sun.sun_path));
+ reconnect:
+       if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+               /* Keep retrying if running in monitor mode */
+               if (res->action == MONITOR &&
+                   (errno == ENOENT || errno == ECONNREFUSED)) {
+                       usleep(100);
+                       goto reconnect;
+               }
+               err(1, "connect: %s", sock);
+       }
+
+       if (res->ibuf != NULL)
+               ibuf = res->ibuf;
+       else
+               if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
+                       err(1, "malloc");
+       imsg_init(ibuf, ctl_sock);
+
+       /* process user request */
+       switch (res->action) {
+       case RESETALL:
+               v = RESET_ALL;
+               break;
+       case LOG_VERBOSE:
+               v = 2;
+               break;
+       case LOG_BRIEF:
+       default:
+               v = 0;
+               break;
+       }
+
+       switch (res->action) {
+       case NONE:
+               usage();
+               /* NOTREACHED */
+               break;
+       case SHOW_SUM:
+       case SHOW_SWITCHES:
+       case SHOW_MACS:
+               imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
+               printf("%-4s\t%-4s\t%-8s\t%-24s\t%s\n",
+                   "Switch", "Port", "Type", "Name", "Info");
+               done = 0;
+               break;
+       case ADD_DEVICE:
+       case REMOVE_DEVICE:
+               memset(&sdv, 0, sizeof(sdv));
+               swc = &sdv.sdv_swc;
+               if (res->path[0] != '/')
+                       strlcpy(sdv.sdv_device, "/dev/",
+                           sizeof(sdv.sdv_device));
+               if (strlcat(sdv.sdv_device, res->path,
+                   sizeof(sdv.sdv_device)) >= sizeof(sdv.sdv_device))
+                       errx(1, "path is too long");
+               if (res->action == REMOVE_DEVICE) {
+                       imsg_compose(ibuf, IMSG_CTL_DEVICE_DISCONNECT, 0, 0, -1,
+                           &sdv, sizeof(sdv));
+                       break;
+               }
+               if (res->uri == NULL || res->uri[0] == '\0')
+                       swc->swc_type = SWITCH_CONN_LOCAL;
+               else {
+                       if (strncmp(res->uri, "tcp:", 4) == 0)
+                               swc->swc_type = SWITCH_CONN_TCP;
+                       else if (strncmp(res->uri, "tls:", 4) == 0)
+                               swc->swc_type = SWITCH_CONN_TLS;
+                       else
+                               errx(1, "protocol field is unknown");
+
+                       if (parsehostport(res->uri + 4,
+                           (struct sockaddr *)&swc->swc_addr,
+                           sizeof(swc->swc_addr)) != 0)
+                               errx(1,
+                                   "couldn't parse name-or-address and port");
+                       warnx("%s", sdv.sdv_device);
+               }
+               imsg_compose(ibuf, IMSG_CTL_DEVICE_CONNECT, 0, 0, -1,
+                   &sdv, sizeof(sdv));
+               break;
+       case RESETALL:
+               imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, &v, sizeof(v));
+               printf("reset request sent.\n");
+               break;
+       case LOAD:
+               imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
+                   res->path, strlen(res->path));
+               break;
+       case RELOAD:
+               imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
+               break;
+       case MONITOR:
+               imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
+               done = 0;
+               break;
+       case LOG_VERBOSE:
+       case LOG_BRIEF:
+               imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, &v, sizeof(v));
+               printf("logging request sent.\n");
+               break;
+       default:
+               break;
+       }
+
+       while (ibuf->w.queued)
+               if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+                       err(1, "write error");
+
+       while (!done) {
+               if ((n = imsg_read(ibuf)) == -1)
+                       errx(1, "imsg_read error");
+               if (n == 0)
+                       errx(1, "pipe closed");
+
+               while (!done) {
+                       if ((n = imsg_get(ibuf, &imsg)) == -1)
+                               errx(1, "imsg_get error");
+                       if (n == 0)
+                               break;
+                       switch (res->action) {
+                       case SHOW_SUM:
+                       case SHOW_SWITCHES:
+                       case SHOW_MACS:
+                               done = show_summary_msg(&imsg, res->action);
+                               break;
+                       case MONITOR:
+                               done = monitor(&imsg);
+                               break;
+                       default:
+                               break;
+                       }
+                       imsg_free(&imsg);
+               }
+       }
+       close(ctl_sock);
+       free(ibuf);
+
+       return (0);
+}
+
+int
+show_summary_msg(struct imsg *imsg, int type)
+{
+       struct switch_control   *sw;
+       struct macaddr          *mac;
+       struct timeval           tv;
+       static unsigned int      sw_id = 0;
+
+       switch (imsg->hdr.type) {
+       case IMSG_CTL_SWITCH:
+               IMSG_SIZE_CHECK(imsg, sw);
+               sw = imsg->data;
+               sw_id = sw->sw_id;
+
+               if (!(type == SHOW_SUM || type == SHOW_SWITCHES))
+                       break;
+               printf("%-4u\t%-4s\t%-8s\t%-24s\n",
+                   sw->sw_id, "", "switch",
+                   print_host(&sw->sw_addr, NULL, 0));
+               break;
+       case IMSG_CTL_MAC:
+               IMSG_SIZE_CHECK(imsg, mac);
+               mac = imsg->data;
+
+               if (!(type == SHOW_SUM || type == SHOW_MACS))
+                       break;
+
+               getmonotime(&tv);
+               printf("%-4u\t%-4ld\t%-8s\t%-24s\tage %llds\n",
+                   sw_id, mac->mac_port, "mac",
+                   print_ether(mac->mac_addr),
+                   (long long)tv.tv_sec - mac->mac_age);
+               break;
+       case IMSG_CTL_END:
+               return (1);
+       default:
+               errx(1, "wrong message in summary: %u", imsg->hdr.type);
+               break;
+       }
+       return (0);
+}
+
+struct imsgname *
+monitor_lookup(uint8_t type)
+{
+       int i;
+
+       for (i = 0; imsgs[i].name != NULL; i++)
+               if (imsgs[i].type == type)
+                       return (&imsgs[i]);
+       return (&imsgunknown);
+}
+
+int
+monitor(struct imsg *imsg)
+{
+       time_t                   now;
+       int                      done = 0;
+       struct imsgname         *imn;
+
+       now = time(NULL);
+
+       imn = monitor_lookup(imsg->hdr.type);
+       printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
+           imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
+       printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
+       if (imn->type == -1)
+               done = 1;
+       if (imn->func != NULL)
+               (*imn->func)(imsg);
+
+       return (done);
+}
diff --git a/usr.sbin/switchd/Makefile b/usr.sbin/switchd/Makefile
new file mode 100644 (file)
index 0000000..2f54653
--- /dev/null
@@ -0,0 +1,36 @@
+# $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>
diff --git a/usr.sbin/switchd/control.c b/usr.sbin/switchd/control.c
new file mode 100644 (file)
index 0000000..bd97943
--- /dev/null
@@ -0,0 +1,373 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/genmap.sh b/usr.sbin/switchd/genmap.sh
new file mode 100644 (file)
index 0000000..76ecee1
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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
diff --git a/usr.sbin/switchd/imsg_util.c b/usr.sbin/switchd/imsg_util.c
new file mode 100644 (file)
index 0000000..9274276
--- /dev/null
@@ -0,0 +1,226 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/log.c b/usr.sbin/switchd/log.c
new file mode 100644 (file)
index 0000000..ecd94e4
--- /dev/null
@@ -0,0 +1,206 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/ofcconn.c b/usr.sbin/switchd/ofcconn.c
new file mode 100644 (file)
index 0000000..8a90fc0
--- /dev/null
@@ -0,0 +1,509 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/ofp.c b/usr.sbin/switchd/ofp.c
new file mode 100644 (file)
index 0000000..1efe9c8
--- /dev/null
@@ -0,0 +1,336 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/ofp.h b/usr.sbin/switchd/ofp.h
new file mode 100644 (file)
index 0000000..ba9ec71
--- /dev/null
@@ -0,0 +1,848 @@
+/*     $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_ */
diff --git a/usr.sbin/switchd/ofp10.c b/usr.sbin/switchd/ofp10.c
new file mode 100644 (file)
index 0000000..2a27e06
--- /dev/null
@@ -0,0 +1,425 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/ofp10.h b/usr.sbin/switchd/ofp10.h
new file mode 100644 (file)
index 0000000..04ac946
--- /dev/null
@@ -0,0 +1,184 @@
+/*     $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_ */
diff --git a/usr.sbin/switchd/ofp13.c b/usr.sbin/switchd/ofp13.c
new file mode 100644 (file)
index 0000000..0747d92
--- /dev/null
@@ -0,0 +1,119 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/ofp_map.h b/usr.sbin/switchd/ofp_map.h
new file mode 100644 (file)
index 0000000..3f2fe51
--- /dev/null
@@ -0,0 +1,54 @@
+/*     $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 */
diff --git a/usr.sbin/switchd/packet.c b/usr.sbin/switchd/packet.c
new file mode 100644 (file)
index 0000000..39df7a9
--- /dev/null
@@ -0,0 +1,88 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/parse.y b/usr.sbin/switchd/parse.y
new file mode 100644 (file)
index 0000000..7193ceb
--- /dev/null
@@ -0,0 +1,708 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/proc.c b/usr.sbin/switchd/proc.c
new file mode 100644 (file)
index 0000000..fb3dfcf
--- /dev/null
@@ -0,0 +1,648 @@
+/*     $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]);
+}
diff --git a/usr.sbin/switchd/proc.h b/usr.sbin/switchd/proc.h
new file mode 100644 (file)
index 0000000..26e82cf
--- /dev/null
@@ -0,0 +1,175 @@
+/*     $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 */
diff --git a/usr.sbin/switchd/switch.c b/usr.sbin/switchd/switch.c
new file mode 100644 (file)
index 0000000..a8cedb5
--- /dev/null
@@ -0,0 +1,248 @@
+/*     $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);
diff --git a/usr.sbin/switchd/switchd.8 b/usr.sbin/switchd/switchd.8
new file mode 100644 (file)
index 0000000..39aa8e8
--- /dev/null
@@ -0,0 +1,55 @@
+.\" $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 .
diff --git a/usr.sbin/switchd/switchd.c b/usr.sbin/switchd/switchd.c
new file mode 100644 (file)
index 0000000..3bc8415
--- /dev/null
@@ -0,0 +1,481 @@
+/*     $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));
+}
diff --git a/usr.sbin/switchd/switchd.conf.5 b/usr.sbin/switchd/switchd.conf.5
new file mode 100644 (file)
index 0000000..a72982b
--- /dev/null
@@ -0,0 +1,115 @@
+.\"    $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
diff --git a/usr.sbin/switchd/switchd.h b/usr.sbin/switchd/switchd.h
new file mode 100644 (file)
index 0000000..28b3abd
--- /dev/null
@@ -0,0 +1,228 @@
+/*     $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 */
diff --git a/usr.sbin/switchd/timer.c b/usr.sbin/switchd/timer.c
new file mode 100644 (file)
index 0000000..a217260
--- /dev/null
@@ -0,0 +1,73 @@
+/*     $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);
+}
diff --git a/usr.sbin/switchd/types.h b/usr.sbin/switchd/types.h
new file mode 100644 (file)
index 0000000..7dba07e
--- /dev/null
@@ -0,0 +1,101 @@
+/*     $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 */
diff --git a/usr.sbin/switchd/util.c b/usr.sbin/switchd/util.c
new file mode 100644 (file)
index 0000000..56e712a
--- /dev/null
@@ -0,0 +1,334 @@
+/*     $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);
+}