diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-11-24 09:23:12 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-11-24 09:23:12 +0000 |
commit | bc8c9b9d401565311ebf7cc9f3f08c65629f5bf5 (patch) | |
tree | 62a0d9e56628e98ab87a00b1e3ece3dc0de83e23 /usr.sbin | |
parent | 8234254a7aa847b32355f52fdc956e7d5dcab13f (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.c | 126 | ||||
-rw-r--r-- | usr.sbin/switchctl/parser.c | 237 | ||||
-rw-r--r-- | usr.sbin/switchctl/parser.h | 11 | ||||
-rw-r--r-- | usr.sbin/switchctl/switchctl.c | 17 |
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 = ≻ @@ -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: |