summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2016-11-24 09:23:12 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2016-11-24 09:23:12 +0000
commitbc8c9b9d401565311ebf7cc9f3f08c65629f5bf5 (patch)
tree62a0d9e56628e98ab87a00b1e3ece3dc0de83e23 /usr.sbin
parent8234254a7aa847b32355f52fdc956e7d5dcab13f (diff)
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')
-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: