summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2016-11-15 08:15:08 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2016-11-15 08:15:08 +0000
commit30e35202c19e7e9e19578677d1bbca3dd554a285 (patch)
tree29f4120bff332b30bef3547d0f4d1634100bf3db /usr.sbin
parent96a2eb214016d7cb10507c1cdab290df415d2236 (diff)
Add a simple openflow client to dump switch information. The
implementation re-uses the openflow code from switchd. Not finished yet, but it is better to continue working on it in the tree. Later versions will also include a way to add flows from the command line - this is being worked on for switchd and some code of the "ASCII-to-flow" parser will be shared here as well. As discussed with and lightly tested by rzalamena@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/switchctl/Makefile25
-rw-r--r--usr.sbin/switchctl/ofpclient.c180
-rw-r--r--usr.sbin/switchctl/parser.c34
-rw-r--r--usr.sbin/switchctl/parser.h10
-rw-r--r--usr.sbin/switchctl/switchctl.c55
5 files changed, 266 insertions, 38 deletions
diff --git a/usr.sbin/switchctl/Makefile b/usr.sbin/switchctl/Makefile
index 892c4c19cb3..4563831c148 100644
--- a/usr.sbin/switchctl/Makefile
+++ b/usr.sbin/switchctl/Makefile
@@ -1,18 +1,35 @@
-# $OpenBSD: Makefile,v 1.2 2016/07/19 18:14:08 reyk Exp $
+# $OpenBSD: Makefile,v 1.3 2016/11/15 08:15:07 reyk Exp $
-.PATH: ${.CURDIR}/../switchd
+SWITCHD= ${.CURDIR}/../switchd
+
+.PATH: ${SWITCHD}
PROG= switchctl
MAN= switchctl.8
-SRCS= log.c switchctl.c parser.c util.c
+SRCS= log.c ofpclient.c parser.c util.c imsg_util.c switchctl.c
+SRCS+= ofp13.c ofp_common.c packet.c
LDADD= -lutil
DPADD= ${LIBUTIL}
-CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../switchd
+CFLAGS+= -Wall -I${.CURDIR} -I${SWITCHD}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
CFLAGS+= -Wsign-compare
+GENERATED= ofp_map.c ofp10_map.c
+SRCS+= ${GENERATED}
+CLEANFILES+= ${GENERATED} *~
+
+ofp_map.c: genmap.sh ${SWITCHD}/ofp_map.h ${SWITCHD}/../../sys/net/ofp.h
+ /bin/sh ${SWITCHD}/genmap.sh -i ${SWITCHD}/../../sys/net/ofp.h -t ofp \
+ -m ${SWITCHD}/ofp_map.h -h '<net/ofp.h>' > $@
+ @touch $@
+
+ofp10_map.c: genmap.sh ${SWITCHD}/ofp_map.h ${SWITCHD}/ofp10.h
+ /bin/sh ${SWITCHD}/genmap.sh -i ${SWITCHD}/ofp10.h -t ofp10 \
+ -m ${SWITCHD}/ofp_map.h -h '"ofp10.h"' > $@
+ @touch $@
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/switchctl/ofpclient.c b/usr.sbin/switchctl/ofpclient.c
new file mode 100644
index 00000000000..48f4174b869
--- /dev/null
+++ b/usr.sbin/switchctl/ofpclient.c
@@ -0,0 +1,180 @@
+/* $OpenBSD: ofpclient.c,v 1.1 2016/11/15 08:15:07 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.
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+#include <sys/un.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <poll.h>
+#include <err.h>
+#include <pwd.h>
+#include <vis.h>
+
+#include "switchd.h"
+#include "parser.h"
+
+void
+ofpclient(struct parse_result *res, struct passwd *pw)
+{
+ struct switch_connection con;
+ struct switchd sc;
+ struct ofp_header oh;
+ int s;
+
+ memset(&sc, 0, sizeof(sc));
+ sc.sc_tap = -1;
+
+ memset(&con, 0, sizeof(con));
+ memcpy(&con.con_peer, &res->uri.swa_addr, sizeof(res->uri.swa_addr));
+ con.con_sc = &sc;
+
+ /*
+ * Connect and send the request
+ */
+ switch (res->uri.swa_type) {
+ case SWITCH_CONN_TCP:
+ if ((s = socket(res->uri.swa_addr.ss_family, SOCK_STREAM,
+ IPPROTO_TCP)) == -1)
+ fatal("socket");
+ if (connect(s, (struct sockaddr *)&con.con_peer,
+ con.con_peer.ss_len) == -1)
+ fatal("connect");
+
+ con.con_fd = s;
+ break;
+ default:
+ fatalx("connect type not supported");
+ }
+
+ /* Drop privileges */
+ if (pledge("stdio", NULL) == -1)
+ err(1, "pledge");
+
+ log_verbose(0);
+
+ oh.oh_version = OFP_V_1_3;
+ oh.oh_type = OFP_T_HELLO;
+ oh.oh_length = 0;
+ oh.oh_xid = 0;
+ ofp13_hello(&sc, &con, &oh, NULL);
+
+ log_verbose(res->quiet ? 0 : 2);
+
+ switch (res->action) {
+ case DUMP_DESC:
+ ofp13_desc(&sc, &con);
+ break;
+ case DUMP_FEATURES:
+ ofp13_featuresrequest(&sc, &con);
+ break;
+ case DUMP_FLOWS:
+ ofp13_flow_stats(&sc, &con, OFP_PORT_ANY, OFP_GROUP_ANY,
+ OFP_TABLE_ID_ALL);
+ break;
+ case DUMP_TABLES:
+ ofp13_table_features(&sc, &con, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * stubs for ofp*.c
+ */
+
+void
+ofrelay_write(struct switch_connection *con, struct ibuf *buf)
+{
+ struct msgbuf msgbuf;
+ uint8_t rbuf[0xffff];
+ ssize_t rlen;
+ struct ofp_header *oh;
+ struct ibuf *ibuf;
+ struct pollfd pfd[1];
+ int nfds;
+
+ msgbuf_init(&msgbuf);
+ msgbuf.fd = con->con_fd;
+
+ ibuf_close(&msgbuf, buf);
+ ibuf_write(&msgbuf);
+
+ /* Wait for response */
+ pfd[0].fd = con->con_fd;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, 3 * 1000);
+ if (nfds == -1 || (pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)))
+ fatal("poll error");
+ if (nfds == 0)
+ fatal("time out");
+
+ if ((rlen = read(con->con_fd, rbuf, sizeof(rbuf))) == -1)
+ fatal("read");
+ if (rlen == 0)
+ fatal("connection closed");
+
+ if ((ibuf = ibuf_new(rbuf, rlen)) == NULL)
+ fatal("ibuf_new");
+
+ if ((oh = ibuf_seek(ibuf, 0, sizeof(*oh))) == NULL)
+ fatal("short header");
+
+ if (ofp13_validate(con->con_sc,
+ &con->con_peer, &con->con_local, oh, ibuf) != 0)
+ fatal("ofp13_validate");
+
+ ibuf_free(ibuf);
+}
+
+struct switch_control *
+switch_add(struct switch_connection *con)
+{
+ static struct switch_control sw;
+ con->con_switch = &sw;
+ return (&sw);
+}
+
+struct macaddr *
+switch_learn(struct switchd *sc, struct switch_control *sw,
+ uint8_t *ea, uint32_t port)
+{
+ return (NULL);
+}
+
+struct macaddr *
+switch_cached(struct switch_control *sw, uint8_t *ea)
+{
+ return (NULL);
+}
diff --git a/usr.sbin/switchctl/parser.c b/usr.sbin/switchctl/parser.c
index 39fa77414d0..57ced7b967e 100644
--- a/usr.sbin/switchctl/parser.c
+++ b/usr.sbin/switchctl/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.3 2016/10/12 19:07:42 reyk Exp $ */
+/* $OpenBSD: parser.c,v 1.4 2016/11/15 08:15:07 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -57,6 +57,7 @@ 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_dump[];
static const struct token t_connect[];
static const struct token t_disconnect[];
static const struct token t_forward_to[];
@@ -65,6 +66,7 @@ static const struct token t_uri[];
static const struct token t_main[] = {
{ KEYWORD, "connect", CONNECT, t_connect },
{ KEYWORD, "disconnect", DISCONNECT, t_disconnect },
+ { KEYWORD, "dump", NONE, t_dump },
{ KEYWORD, "load", LOAD, t_load },
{ KEYWORD, "log", NONE, t_log },
{ KEYWORD, "monitor", MONITOR, NULL },
@@ -90,12 +92,21 @@ static const struct token t_load[] = {
{ ENDTOKEN, "", NONE, NULL }
};
+static const struct token t_dump[] = {
+ { KEYWORD, "desc", DUMP_DESC, t_uri },
+ { KEYWORD, "features", DUMP_FEATURES, t_uri },
+ { KEYWORD, "flows", DUMP_FLOWS, t_uri },
+ { KEYWORD, "tables", DUMP_TABLES, t_uri },
+ { 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_connect[] = {
{ ADDRESS, "", NONE, t_forward_to },
{ ENDTOKEN, "", NONE, NULL }
@@ -109,6 +120,7 @@ static const struct token t_forward_to[] = {
{ KEYWORD, "forward-to", NONE, t_uri },
{ ENDTOKEN, "", NONE, NULL }
};
+
static const struct token t_uri[] = {
{ URI, "", NONE, NULL },
{ ENDTOKEN, "", NONE, NULL }
@@ -216,6 +228,7 @@ match_token(char *word, const struct token table[])
{
unsigned int i, match = 0;
const struct token *t = NULL;
+ size_t len;
for (i = 0; table[i].type != ENDTOKEN; i++) {
switch (table[i].type) {
@@ -250,7 +263,24 @@ match_token(char *word, const struct token table[])
break;
case URI:
if (!match && word != NULL && strlen(word) > 0) {
- res.uri = strdup(word);
+ len = 4;
+ if (strncmp(word, "tcp:", len) == 0)
+ res.uri.swa_type = SWITCH_CONN_TCP;
+ else if (strncmp(word, "tls:", len) == 0)
+ res.uri.swa_type = SWITCH_CONN_TLS;
+ else {
+ /* set the default */
+ res.uri.swa_type = SWITCH_CONN_TCP;
+ len = 0;
+ }
+ if (parsehostport(word + len,
+ (struct sockaddr *)&res.uri.swa_addr,
+ sizeof(res.uri.swa_addr)) != 0) {
+ fprintf(stderr,
+ "could not parse address: %s\n",
+ word);
+ return (NULL);
+ }
match++;
t = &table[i];
}
diff --git a/usr.sbin/switchctl/parser.h b/usr.sbin/switchctl/parser.h
index 07cfe350fd0..6f5feb5fd40 100644
--- a/usr.sbin/switchctl/parser.h
+++ b/usr.sbin/switchctl/parser.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.h,v 1.2 2016/10/12 19:07:42 reyk Exp $ */
+/* $OpenBSD: parser.h,v 1.3 2016/11/15 08:15:07 reyk Exp $ */
/*
* Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
@@ -21,6 +21,10 @@
enum actions {
NONE,
+ DUMP_DESC,
+ DUMP_FEATURES,
+ DUMP_FLOWS,
+ DUMP_TABLES,
SHOW_SUM,
SHOW_SWITCHES,
SHOW_MACS,
@@ -40,8 +44,7 @@ struct parse_result {
char *path;
char *caname;
char *pass;
- char *peer;
- char *uri;
+ struct switch_address uri;
struct sockaddr_storage addr;
int quiet;
};
@@ -50,5 +53,6 @@ struct parse_result {
#define HOST_FQDN 2
struct parse_result *parse(int, char *[]);
+void ofpclient(struct parse_result *, struct passwd *);
#endif /* _SWITCHCTL_PARSER_H */
diff --git a/usr.sbin/switchctl/switchctl.c b/usr.sbin/switchctl/switchctl.c
index ed73eaea210..1bb085ae231 100644
--- a/usr.sbin/switchctl/switchctl.c
+++ b/usr.sbin/switchctl/switchctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: switchctl.c,v 1.4 2016/10/12 19:07:42 reyk Exp $ */
+/* $OpenBSD: switchctl.c,v 1.5 2016/11/15 08:15:07 reyk Exp $ */
/*
* Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
@@ -30,8 +30,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include <event.h>
+#include <pwd.h>
#include "switchd.h"
#include "parser.h"
@@ -85,6 +87,7 @@ main(int argc, char *argv[])
struct imsg imsg;
struct switch_client swc;
struct switch_address *to;
+ struct passwd *pw;
int ctl_sock;
int done = 1;
int n;
@@ -92,7 +95,6 @@ main(int argc, char *argv[])
int v = 0;
int quiet = 0;
const char *sock = SWITCHD_SOCKET;
- size_t len;
while ((ch = getopt(argc, argv, "qs:")) != -1) {
switch (ch) {
@@ -110,16 +112,37 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if ((pw = getpwnam(SWITCHD_USER)) == NULL)
+ fatal("switchctl: getpwnam");
+
+ /*
+ * pledge in switchctl:
+ * stdio - for malloc and basic I/O including events.
+ * dns - for parsehostport() in the device spec.
+ * inet - for handling tcp connections with OpenFlow peers.
+ * unix - for opening the control socket.
+ */
+ if (pledge("stdio dns inet unix", NULL) == -1)
+ err(1, "pledge");
+
/* parse options */
if ((res = parse(argc, argv)) == NULL)
exit(1);
res->quiet = quiet;
+ log_init(quiet ? 0 : 2, LOG_USER);
+
switch (res->action) {
case NONE:
usage();
break;
+ case DUMP_DESC:
+ case DUMP_FEATURES:
+ case DUMP_FLOWS:
+ case DUMP_TABLES:
+ ofpclient(res, pw);
+ break;
default:
goto connect;
}
@@ -145,14 +168,6 @@ main(int argc, char *argv[])
err(1, "connect: %s", sock);
}
- /*
- * pledge in switchctl:
- * stdio - for malloc and basic I/O including events.
- * dns - for parsehostport() in the device spec.
- */
- if (pledge("stdio dns", NULL) == -1)
- err(1, "pledge");
-
if (res->ibuf != NULL)
ibuf = res->ibuf;
else
@@ -201,26 +216,8 @@ main(int argc, char *argv[])
}
to = &swc.swc_target;
- if (res->uri == NULL || res->uri[0] == '\0')
- to->swa_type = SWITCH_CONN_LOCAL;
- else {
- len = 4;
- if (strncmp(res->uri, "tcp:", len) == 0)
- to->swa_type = SWITCH_CONN_TCP;
- else if (strncmp(res->uri, "tls:", len) == 0)
- to->swa_type = SWITCH_CONN_TLS;
- else {
- /* set the default */
- to->swa_type = SWITCH_CONN_TCP;
- len = 0;
- }
+ memcpy(&to, &res->uri, sizeof(to));
- if (parsehostport(res->uri + len,
- (struct sockaddr *)&to->swa_addr,
- sizeof(to->swa_addr)) != 0)
- errx(1,
- "couldn't parse name-or-address and port");
- }
imsg_compose(ibuf, IMSG_CTL_CONNECT, 0, 0, -1,
&swc, sizeof(swc));
break;