summaryrefslogtreecommitdiffstats
path: root/usr.sbin/switchctl
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2016-11-24 09:23:11 +0000
committerreyk <reyk@openbsd.org>2016-11-24 09:23:11 +0000
commitb423f9d5571f6d10324c59d51a1c41277a699ef8 (patch)
tree16014384e6ba6168c076f488e0bb64fc234a068e /usr.sbin/switchctl
parentBetter cast for consistency (diff)
downloadwireguard-openbsd-b423f9d5571f6d10324c59d51a1c41277a699ef8.tar.xz
wireguard-openbsd-b423f9d5571f6d10324c59d51a1c41277a699ef8.zip
Add simple client to add flows from switchctl. Not finished yet, but
it is better for rzalamena and me to work on it in the tree. OK rzalamena@
Diffstat (limited to 'usr.sbin/switchctl')
-rw-r--r--usr.sbin/switchctl/ofpclient.c126
-rw-r--r--usr.sbin/switchctl/parser.c237
-rw-r--r--usr.sbin/switchctl/parser.h11
-rw-r--r--usr.sbin/switchctl/switchctl.c17
4 files changed, 339 insertions, 52 deletions
diff --git a/usr.sbin/switchctl/ofpclient.c b/usr.sbin/switchctl/ofpclient.c
index 2302355ebdd..179886c46ba 100644
--- a/usr.sbin/switchctl/ofpclient.c
+++ b/usr.sbin/switchctl/ofpclient.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofpclient.c,v 1.3 2016/11/18 22:15:52 tb Exp $ */
+/* $OpenBSD: ofpclient.c,v 1.4 2016/11/24 09:23:11 reyk Exp $ */
/*
* Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <stddef.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
@@ -45,17 +46,31 @@
#include "switchd.h"
#include "parser.h"
+void ofpclient_read(struct switch_connection *, int);
+int flowmod(struct switchd *, struct switch_connection *,
+ struct parse_result *);
+int flowmod_test(struct switchd *, struct switch_connection *);
+
void
ofpclient(struct parse_result *res, struct passwd *pw)
{
struct switch_connection con;
struct switchd sc;
struct ofp_header oh;
- int s;
+ int s, timeout;
memset(&sc, 0, sizeof(sc));
sc.sc_tap = -1;
+ /* If no uri has been specified, try to connect to localhost */
+ if (res->uri.swa_addr.ss_family == AF_UNSPEC) {
+ res->uri.swa_type = SWITCH_CONN_TCP;
+ if (parsehostport("127.0.0.1",
+ (struct sockaddr *)&res->uri.swa_addr,
+ sizeof(res->uri.swa_addr)) != 0)
+ fatal("could not parse address");
+ }
+
memset(&con, 0, sizeof(con));
memcpy(&con.con_peer, &res->uri.swa_addr, sizeof(res->uri.swa_addr));
con.con_sc = &sc;
@@ -87,15 +102,23 @@ ofpclient(struct parse_result *res, struct passwd *pw)
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
- log_verbose(0);
+ /* Set a default read timeout */
+ timeout = 3 * 1000;
+
+ log_verbose(res->verbose);
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);
+ oh.oh_length = htons(sizeof(oh));
+ oh.oh_xid = htonl(1);
+ if (ofp_validate(&sc, &con.con_local, &con.con_peer, &oh,
+ NULL, oh.oh_version) != 0)
+ fatal("ofp_validate");
+ ofp_output(&con, &oh, NULL);
+
+ ofpclient_read(&con, timeout);
- log_verbose(res->quiet ? 0 : 2);
+ log_verbose(res->quiet ? res->verbose : 2);
switch (res->action) {
case DUMP_DESC:
@@ -106,24 +129,58 @@ ofpclient(struct parse_result *res, struct passwd *pw)
break;
case DUMP_FLOWS:
ofp13_flow_stats(&sc, &con, OFP_PORT_ANY, OFP_GROUP_ID_ANY,
- OFP_TABLE_ID_ALL);
+ res->table);
break;
case DUMP_TABLES:
- ofp13_table_features(&sc, &con, 0);
+ ofp13_table_features(&sc, &con, res->table);
break;
- default:
+ case FLOW_ADD:
+ case FLOW_DELETE:
+ case FLOW_MODIFY:
+ timeout = 0;
+ flowmod(&sc, &con, res);
break;
- }
+ default:
+ fatalx("unsupported action");
+ }
+
+ /* XXX */
+ ofpclient_read(&con, timeout);
}
-/*
- * stubs for ofp*.c
- */
+int
+flowmod(struct switchd *sc, struct switch_connection *con,
+ struct parse_result *res)
+{
+ struct ofp_header *oh;
+ struct ofp_flow_mod *fm;
+
+ if (oflowmod_iclose(&res->fctx) == -1)
+ goto err;
+ if (oflowmod_close(&res->fctx) == -1)
+ goto err;
+
+ fm = res->fctx.ctx_fm;
+ fm->fm_table_id = res->table;
+ oh = &fm->fm_oh;
+
+ if (ofp_validate(sc, &con->con_local, &con->con_peer,
+ oh, res->fbuf, oh->oh_version) != 0)
+ goto err;
+
+ ofrelay_write(con, res->fbuf);
+
+ return (0);
+
+ err:
+ (void)oflowmod_err(&res->fctx, __func__, __LINE__);
+ log_warnx("invalid flow");
+ return (-1);
+}
void
-ofrelay_write(struct switch_connection *con, struct ibuf *buf)
+ofpclient_read(struct switch_connection *con, int timeout)
{
- struct msgbuf msgbuf;
uint8_t rbuf[0xffff];
ssize_t rlen;
struct ofp_header *oh;
@@ -131,20 +188,17 @@ ofrelay_write(struct switch_connection *con, struct ibuf *buf)
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);
+ nfds = poll(pfd, 1, timeout);
if (nfds == -1 || (pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)))
fatal("poll error");
- if (nfds == 0)
- fatal("time out");
+ if (nfds == 0) {
+ if (timeout)
+ fatal("time out");
+ return;
+ }
if ((rlen = read(con->con_fd, rbuf, sizeof(rbuf))) == -1)
fatal("read");
@@ -157,13 +211,29 @@ ofrelay_write(struct switch_connection *con, struct ibuf *buf)
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");
+ if (ofp_validate(con->con_sc,
+ &con->con_peer, &con->con_local, oh, ibuf, oh->oh_version) != 0)
+ fatal("ofp_validate");
ibuf_free(ibuf);
}
+/*
+ * stubs for ofp*.c
+ */
+
+void
+ofrelay_write(struct switch_connection *con, struct ibuf *buf)
+{
+ struct msgbuf msgbuf;
+
+ msgbuf_init(&msgbuf);
+ msgbuf.fd = con->con_fd;
+
+ ibuf_close(&msgbuf, buf);
+ ibuf_write(&msgbuf);
+}
+
struct switch_control *
switch_add(struct switch_connection *con)
{
diff --git a/usr.sbin/switchctl/parser.c b/usr.sbin/switchctl/parser.c
index 7826ae70b8b..fb496440de1 100644
--- a/usr.sbin/switchctl/parser.c
+++ b/usr.sbin/switchctl/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.5 2016/11/15 08:38:57 reyk Exp $ */
+/* $OpenBSD: parser.c,v 1.6 2016/11/24 09:23:11 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -34,6 +34,7 @@
#include <netdb.h>
#include "switchd.h"
+#include "ofp_map.h"
#include "parser.h"
enum token_type {
@@ -42,7 +43,16 @@ enum token_type {
KEYWORD,
PATH,
ADDRESS,
- URI
+ URI,
+ TABLE,
+ FLOWADD,
+ FLOWDELETE,
+ FLOWMODIFY,
+ FLOWAPPLY,
+ FLOWWRITE,
+ FLOWMATCH,
+ MATCHINPORT,
+ ACTIONOUTPUT,
};
struct token {
@@ -57,8 +67,16 @@ 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_switch[];
+static const struct token t_switchreq[];
+static const struct token t_table[];
static const struct token t_dump[];
-static const struct token t_dumpreq[];
+static const struct token t_flow[];
+static const struct token t_flowmod[];
+static const struct token t_flowmatch[];
+static const struct token t_matchinport[];
+static const struct token t_flowaction[];
+static const struct token t_actionoutput[];
static const struct token t_connect[];
static const struct token t_disconnect[];
static const struct token t_forward_to[];
@@ -68,12 +86,15 @@ static const struct token t_main[] = {
{ KEYWORD, "connect", CONNECT, t_connect },
{ KEYWORD, "disconnect", DISCONNECT, t_disconnect },
{ KEYWORD, "dump", NONE, t_dump },
+ { KEYWORD, "flow", NONE, t_flow },
{ 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 },
+ { KEYWORD, "switch", NONE, t_switch },
+ { KEYWORD, "table", NONE, t_table },
{ ENDTOKEN, "", NONE, NULL }
};
@@ -93,12 +114,23 @@ static const struct token t_load[] = {
{ ENDTOKEN, "", NONE, NULL }
};
-static const struct token t_dump[] = {
- { URI, "", NONE, t_dumpreq },
+static const struct token t_table[] = {
+ { TABLE, "", NONE, t_main },
{ ENDTOKEN, "", NONE, NULL }
};
-static const struct token t_dumpreq[] = {
+static const struct token t_switch[] = {
+ { URI, "", NONE, t_main },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_switchreq[] = {
+ { KEYWORD, "dump", NONE, t_dump },
+ { KEYWORD, "flow", NONE, t_flow },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_dump[] = {
{ KEYWORD, "desc", DUMP_DESC, NULL },
{ KEYWORD, "features", DUMP_FEATURES, NULL },
{ KEYWORD, "flows", DUMP_FLOWS, NULL },
@@ -106,6 +138,43 @@ static const struct token t_dumpreq[] = {
{ ENDTOKEN, "", NONE, NULL }
};
+static const struct token t_flow[] = {
+ { FLOWADD, "add", FLOW_ADD, t_flowmod },
+ { FLOWDELETE, "delete", FLOW_DELETE, t_flowmod },
+ { FLOWMODIFY, "modify", FLOW_MODIFY, t_flowmod },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_flowmod[] = {
+ { NOTOKEN, "", NONE, NULL },
+ { FLOWAPPLY, "apply", NONE, t_flowaction },
+ { FLOWWRITE, "write", NONE, t_flowaction },
+ { FLOWMATCH, "match", NONE, t_flowmatch },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_flowmatch[] = {
+ { NOTOKEN, "", NONE, t_flowmod },
+ { KEYWORD, "inport", NONE, t_matchinport },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_matchinport[] = {
+ { MATCHINPORT, "", NONE, t_flowmatch },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_flowaction[] = {
+ { NOTOKEN, "", NONE, t_flowmod },
+ { KEYWORD, "output", NONE, t_actionoutput },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_actionoutput[] = {
+ { ACTIONOUTPUT, "", NONE, t_flowaction },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
static const struct token t_show[] = {
{ KEYWORD, "summary", SHOW_SUM, NULL },
{ KEYWORD, "switches", SHOW_SWITCHES, NULL },
@@ -134,10 +203,10 @@ static const struct token t_uri[] = {
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 sockaddr_storage *);
+const struct token *match_token(char *, const struct token [], int);
+void show_valid_args(const struct token [], int);
+int parse_addr(const char *,
+ struct sockaddr_storage *);
struct parse_result *
parse(int argc, char *argv[])
@@ -147,10 +216,12 @@ parse(int argc, char *argv[])
bzero(&res, sizeof(res));
+ res.table = OFP_TABLE_ID_ALL;
+
while (argc >= 0) {
- if ((match = match_token(argv[0], table)) == NULL) {
+ if ((match = match_token(argv[0], table, 0)) == NULL) {
fprintf(stderr, "valid commands/args:\n");
- show_valid_args(table);
+ show_valid_args(table, 0);
return (NULL);
}
@@ -230,9 +301,12 @@ parse_addr(const char *word, struct sockaddr_storage *ss)
const struct token *
-match_token(char *word, const struct token table[])
+match_token(char *word, const struct token table[], int level)
{
- unsigned int i, match = 0;
+ unsigned int i, j, match = 0;
+ int64_t val;
+ struct constmap *cm;
+ const char *errstr = NULL;
const struct token *t = NULL;
size_t len;
@@ -245,12 +319,102 @@ match_token(char *word, const struct token table[])
}
break;
case KEYWORD:
+ case FLOWADD:
+ case FLOWDELETE:
+ case FLOWMODIFY:
+ case FLOWMATCH:
+ case FLOWAPPLY:
+ case FLOWWRITE:
if (word != NULL && strncmp(word, table[i].keyword,
strlen(word)) == 0) {
match++;
t = &table[i];
if (t->value)
res.action = t->value;
+ switch (table[i].type) {
+ case FLOWADD:
+ case FLOWDELETE:
+ case FLOWMODIFY:
+ if ((res.fbuf =
+ oflowmod_open(&res.fctx,
+ NULL, NULL, 0)) == NULL)
+ goto flowerr;
+
+ /* Update header */
+ if (table[i].type == FLOWDELETE)
+ res.fctx.ctx_fm->fm_command =
+ htons(OFP_FLOWCMD_DELETE);
+ else if (table[i].type == FLOWMODIFY)
+ res.fctx.ctx_fm->fm_command =
+ htons(OFP_FLOWCMD_MODIFY);
+ break;
+ case FLOWAPPLY:
+ val = OFP_INSTRUCTION_T_APPLY_ACTIONS;
+ if (oflowmod_instruction(&res.fctx,
+ val) == -1)
+ goto flowerr;
+ break;
+ case FLOWWRITE:
+ val = OFP_INSTRUCTION_T_WRITE_ACTIONS;
+ if (oflowmod_instruction(&res.fctx,
+ val) == -1)
+ goto flowerr;
+ break;
+ case FLOWMATCH:
+ if (oflowmod_mopen(&res.fctx) == -1)
+ goto flowerr;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case MATCHINPORT:
+ case ACTIONOUTPUT:
+ if (!match && word != NULL && strlen(word) > 0) {
+ match++;
+ t = &table[i];
+
+ val = -1;
+
+ /* Is the port a keyword? */
+ cm = ofp_port_map;
+ for (j = 0; cm[j].cm_name != NULL; j++) {
+ if (strcasecmp(cm[j].cm_name,
+ word) == 0) {
+ val = cm[j].cm_type;
+ break;
+ }
+ }
+
+ /* Is the port a number? */
+ if (val == -1) {
+ val = strtonum(word, 1,
+ UINT32_MAX, &errstr);
+ if (errstr != NULL)
+ val = -1;
+ }
+
+ if (val == -1) {
+ fprintf(stderr,
+ "could not parse port:"
+ " %s\n", word);
+ return (NULL);
+ }
+
+ switch (table[i].type) {
+ case MATCHINPORT:
+ if (oxm_inport(res.fbuf, val) == -1)
+ goto flowerr;
+ break;
+ case ACTIONOUTPUT:
+ if (action_output(res.fbuf, val,
+ OFP_CONTROLLER_MAXLEN_MAX) == -1)
+ goto flowerr;
+ break;
+ default:
+ break;
+ }
}
break;
case PATH:
@@ -267,6 +431,16 @@ match_token(char *word, const struct token table[])
t = &table[i];
}
break;
+ case TABLE:
+ if (word == NULL)
+ break;
+ res.table = strtonum(word, 0,
+ OFP_TABLE_ID_MAX, &errstr);
+ if (errstr)
+ res.table = OFP_TABLE_ID_ALL;
+ t = &table[i];
+ match++;
+ break;
case URI:
if (!match && word != NULL && strlen(word) > 0) {
len = 4;
@@ -301,33 +475,57 @@ match_token(char *word, const struct token table[])
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);
+ else if (match < 1) {
+ if (level == 0 &&
+ table[0].type == NOTOKEN && table[0].next)
+ return (match_token(word, table[0].next, 1));
+ else
+ fprintf(stderr, "unknown argument: %s\n", word);
+ }
return (NULL);
}
return (t);
+
+ flowerr:
+ (void)oflowmod_err(&res.fctx, __func__, __LINE__);
+ fprintf(stderr, "flow invalid\n");
+ return (NULL);
}
void
-show_valid_args(const struct token table[])
+show_valid_args(const struct token table[], int level)
{
int i;
for (i = 0; table[i].type != ENDTOKEN; i++) {
switch (table[i].type) {
case NOTOKEN:
- fprintf(stderr, " <cr>\n");
+ if (level == 0)
+ fprintf(stderr, " <cr>\n");
break;
case KEYWORD:
+ case FLOWADD:
+ case FLOWDELETE:
+ case FLOWMODIFY:
+ case FLOWMATCH:
+ case FLOWAPPLY:
+ case FLOWWRITE:
fprintf(stderr, " %s\n", table[i].keyword);
break;
+ case MATCHINPORT:
+ case ACTIONOUTPUT:
+ fprintf(stderr, " <port>\n");
+ break;
case PATH:
fprintf(stderr, " <path>\n");
break;
case ADDRESS:
fprintf(stderr, " <address>\n");
break;
+ case TABLE:
+ fprintf(stderr, " <table>\n");
+ break;
case URI:
fprintf(stderr, " <uri>\n");
break;
@@ -335,4 +533,7 @@ show_valid_args(const struct token table[])
break;
}
}
+
+ if (level == 0 && table[0].type == NOTOKEN && table[0].next)
+ return (show_valid_args(table[0].next, 1));
}
diff --git a/usr.sbin/switchctl/parser.h b/usr.sbin/switchctl/parser.h
index 6f5feb5fd40..83fb4896ba9 100644
--- a/usr.sbin/switchctl/parser.h
+++ b/usr.sbin/switchctl/parser.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.h,v 1.3 2016/11/15 08:15:07 reyk Exp $ */
+/* $OpenBSD: parser.h,v 1.4 2016/11/24 09:23:11 reyk Exp $ */
/*
* Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
@@ -21,6 +21,9 @@
enum actions {
NONE,
+ FLOW_ADD,
+ FLOW_DELETE,
+ FLOW_MODIFY,
DUMP_DESC,
DUMP_FEATURES,
DUMP_FLOWS,
@@ -42,11 +45,13 @@ struct parse_result {
enum actions action;
struct imsgbuf *ibuf;
char *path;
- char *caname;
- char *pass;
struct switch_address uri;
struct sockaddr_storage addr;
+ struct oflowmod_ctx fctx;
+ struct ibuf *fbuf;
+ int table;
int quiet;
+ int verbose;
};
#define HOST_IPADDR 1
diff --git a/usr.sbin/switchctl/switchctl.c b/usr.sbin/switchctl/switchctl.c
index 1bb085ae231..144ae131ea6 100644
--- a/usr.sbin/switchctl/switchctl.c
+++ b/usr.sbin/switchctl/switchctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: switchctl.c,v 1.5 2016/11/15 08:15:07 reyk Exp $ */
+/* $OpenBSD: switchctl.c,v 1.6 2016/11/24 09:23:11 reyk Exp $ */
/*
* Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org>
@@ -94,9 +94,10 @@ main(int argc, char *argv[])
int ch;
int v = 0;
int quiet = 0;
+ int verbose = 0;
const char *sock = SWITCHD_SOCKET;
- while ((ch = getopt(argc, argv, "qs:")) != -1) {
+ while ((ch = getopt(argc, argv, "qs:v")) != -1) {
switch (ch) {
case 'q':
quiet = 1;
@@ -104,6 +105,9 @@ main(int argc, char *argv[])
case 's':
sock = optarg;
break;
+ case 'v':
+ verbose = 2;
+ break;
default:
usage();
/* NOTREACHED */
@@ -125,13 +129,17 @@ main(int argc, char *argv[])
if (pledge("stdio dns inet unix", NULL) == -1)
err(1, "pledge");
+ log_init(quiet ? 0 : 2, LOG_USER);
+
/* parse options */
if ((res = parse(argc, argv)) == NULL)
exit(1);
res->quiet = quiet;
+ res->verbose = verbose;
- log_init(quiet ? 0 : 2, LOG_USER);
+ if (res->quiet && res->verbose)
+ fatal("conflicting -v and -q options");
switch (res->action) {
case NONE:
@@ -141,6 +149,9 @@ main(int argc, char *argv[])
case DUMP_FEATURES:
case DUMP_FLOWS:
case DUMP_TABLES:
+ case FLOW_ADD:
+ case FLOW_DELETE:
+ case FLOW_MODIFY:
ofpclient(res, pw);
break;
default: