From 30e35202c19e7e9e19578677d1bbca3dd554a285 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Tue, 15 Nov 2016 08:15:08 +0000 Subject: 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@ --- usr.sbin/switchctl/Makefile | 25 +++++- usr.sbin/switchctl/ofpclient.c | 180 +++++++++++++++++++++++++++++++++++++++++ usr.sbin/switchctl/parser.c | 34 +++++++- usr.sbin/switchctl/parser.h | 10 ++- usr.sbin/switchctl/switchctl.c | 55 ++++++------- 5 files changed, 266 insertions(+), 38 deletions(-) create mode 100644 usr.sbin/switchctl/ofpclient.c (limited to 'usr.sbin') 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 '' > $@ + @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 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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = ≻ + + /* + * 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 @@ -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 @@ -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 @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #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; -- cgit v1.2.3