diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-10-12 19:07:43 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-10-12 19:07:43 +0000 |
commit | 2c8b4a5d06668df9dd4ca308364599ed7bd90ce6 (patch) | |
tree | 1957e11b32cbb7f59e980e7bed52f779522b2dc9 | |
parent | 9a8144695bfe9da05620d6691336173b6178623c (diff) |
Start reworking the "device" support in switchd: Once connected, a
device is just an fd that is connected to a switch, either via TCP or
via /dev/switch. Change the switchctl from "device add" to "connect"
etc. This change is an intermediate step towards other changes,
including the configuration grammar, so a few things will be left
undocumented for now.
switchctl(8) examples,
switchctl connect /dev/switch0
switchctl connect /dev/switch0 forward-to 10.1.1.1
switchctl connect 127.0.0.1
switchctl connect 127.0.0.1 forward-to 10.1.1.1
switchctl disconnect /dev/switch0
Discussed with rzalamena@
-rw-r--r-- | usr.sbin/switchctl/parser.c | 104 | ||||
-rw-r--r-- | usr.sbin/switchctl/parser.h | 25 | ||||
-rw-r--r-- | usr.sbin/switchctl/switchctl.8 | 22 | ||||
-rw-r--r-- | usr.sbin/switchctl/switchctl.c | 63 | ||||
-rw-r--r-- | usr.sbin/switchd/control.c | 6 | ||||
-rw-r--r-- | usr.sbin/switchd/ofcconn.c | 42 | ||||
-rw-r--r-- | usr.sbin/switchd/ofp.c | 33 | ||||
-rw-r--r-- | usr.sbin/switchd/ofrelay.c | 4 | ||||
-rw-r--r-- | usr.sbin/switchd/parse.y | 77 | ||||
-rw-r--r-- | usr.sbin/switchd/switchd.c | 246 | ||||
-rw-r--r-- | usr.sbin/switchd/switchd.h | 28 | ||||
-rw-r--r-- | usr.sbin/switchd/types.h | 9 |
12 files changed, 405 insertions, 254 deletions
diff --git a/usr.sbin/switchctl/parser.c b/usr.sbin/switchctl/parser.c index 2ec8153b0b6..39fa77414d0 100644 --- a/usr.sbin/switchctl/parser.c +++ b/usr.sbin/switchctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.2 2016/07/19 18:09:39 reyk Exp $ */ +/* $OpenBSD: parser.c,v 1.3 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -22,6 +22,7 @@ #include <sys/socket.h> #include <sys/queue.h> #include <sys/tree.h> +#include <sys/un.h> #include <err.h> #include <errno.h> @@ -41,8 +42,6 @@ enum token_type { KEYWORD, PATH, ADDRESS, - FQDN, - DEVICE, URI }; @@ -58,14 +57,14 @@ 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_device[]; -static const struct token t_device_add[]; -static const struct token t_device_remove[]; -static const struct token t_connect_to[]; +static const struct token t_connect[]; +static const struct token t_disconnect[]; +static const struct token t_forward_to[]; static const struct token t_uri[]; static const struct token t_main[] = { - { KEYWORD, "device", NONE, t_device }, + { KEYWORD, "connect", CONNECT, t_connect }, + { KEYWORD, "disconnect", DISCONNECT, t_disconnect }, { KEYWORD, "load", LOAD, t_load }, { KEYWORD, "log", NONE, t_log }, { KEYWORD, "monitor", MONITOR, NULL }, @@ -97,21 +96,17 @@ static const struct token t_show[] = { { KEYWORD, "macs", SHOW_MACS, NULL }, { ENDTOKEN, "", NONE, NULL } }; -static const struct token t_device[] = { - { KEYWORD, "add", ADD_DEVICE, t_device_add }, - { KEYWORD, "remove", REMOVE_DEVICE, t_device_remove }, +static const struct token t_connect[] = { + { ADDRESS, "", NONE, t_forward_to }, { ENDTOKEN, "", NONE, NULL } }; -static const struct token t_device_add[] = { - { DEVICE, "", NONE, t_connect_to }, +static const struct token t_disconnect[] = { + { ADDRESS, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; -static const struct token t_device_remove[] = { - { DEVICE, "", NONE, NULL }, - { ENDTOKEN, "", NONE, NULL } -}; -static const struct token t_connect_to[] = { - { KEYWORD, "connect-to", NONE, t_uri }, +static const struct token t_forward_to[] = { + { NOTOKEN, "", NONE, NULL }, + { KEYWORD, "forward-to", NONE, t_uri }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_uri[] = { @@ -123,7 +118,8 @@ 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 *); +int parse_addr(const char *, + struct sockaddr_storage *); struct parse_result * parse(int argc, char *argv[]) @@ -158,19 +154,60 @@ parse(int argc, char *argv[]) } int -parse_addr(const char *word) +parse_addr(const char *word, struct sockaddr_storage *ss) { - struct addrinfo hints, *r; + struct addrinfo hints, *ai; + struct sockaddr_un *un; + + memset(ss, 0, sizeof(*ss)); + + /* device */ + if (*word == '/') { + un = (struct sockaddr_un *)ss; + if (strlcpy(un->sun_path, word, sizeof(un->sun_path)) >= + sizeof(un->sun_path)) { + warnx("invalid path"); + return (-1); + } + un->sun_family = AF_LOCAL; + un->sun_len = sizeof(*un); + return (0); + } - bzero(&hints, sizeof(hints)); + /* address */ + memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; /* dummy */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(word, "0", &hints, &r) == 0) { + if (getaddrinfo(word, "0", &hints, &ai) == 0) { + if (ai->ai_addrlen > sizeof(*ss)) { + warnx("invalid address length"); + return (-1); + } + memcpy(ss, ai->ai_addr, ai->ai_addrlen); + ss->ss_len = ai->ai_addrlen; + freeaddrinfo(ai); + return (0); + } + + /* FQDN */ + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; /* dummy */ + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + if (getaddrinfo(word, "0", &hints, &ai) == 0) { + /* Pick first name only */ + if (ai->ai_addrlen > sizeof(*ss)) { + warnx("invalid address length"); + return (-1); + } + memcpy(ss, ai->ai_addr, ai->ai_addrlen); + ss->ss_len = ai->ai_addrlen; + freeaddrinfo(ai); return (0); } - return (1); + return (-1); } @@ -197,7 +234,6 @@ match_token(char *word, const struct token table[]) res.action = t->value; } break; - case DEVICE: case PATH: if (!match && word != NULL && strlen(word) > 0) { res.path = strdup(word); @@ -206,14 +242,8 @@ match_token(char *word, const struct token table[]) } break; case ADDRESS: - case FQDN: if (!match && word != NULL && strlen(word) > 0) { - parse_addr(word); - res.host = strdup(word); - if (parse_addr(word) == 0) - res.htype = HOST_IPADDR; - else - res.htype = HOST_FQDN; + parse_addr(word, &res.addr); match++; t = &table[i]; } @@ -260,13 +290,7 @@ show_valid_args(const struct token table[]) fprintf(stderr, " <path>\n"); break; case ADDRESS: - fprintf(stderr, " <ipaddr>\n"); - break; - case FQDN: - fprintf(stderr, " <fqdn>\n"); - break; - case DEVICE: - fprintf(stderr, " <device>\n"); + fprintf(stderr, " <address>\n"); break; case URI: fprintf(stderr, " <uri>\n"); diff --git a/usr.sbin/switchctl/parser.h b/usr.sbin/switchctl/parser.h index 8b885886b8e..07cfe350fd0 100644 --- a/usr.sbin/switchctl/parser.h +++ b/usr.sbin/switchctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.1 2016/07/19 16:54:26 reyk Exp $ */ +/* $OpenBSD: parser.h,v 1.2 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org> @@ -30,21 +30,20 @@ enum actions { LOG_VERBOSE, LOG_BRIEF, RESETALL, - ADD_DEVICE, - REMOVE_DEVICE + CONNECT, + DISCONNECT }; struct parse_result { - enum actions action; - struct imsgbuf *ibuf; - char *path; - char *caname; - char *pass; - char *host; - char *peer; - char *uri; - int htype; - int quiet; + enum actions action; + struct imsgbuf *ibuf; + char *path; + char *caname; + char *pass; + char *peer; + char *uri; + struct sockaddr_storage addr; + int quiet; }; #define HOST_IPADDR 1 diff --git a/usr.sbin/switchctl/switchctl.8 b/usr.sbin/switchctl/switchctl.8 index d4c1ecefa35..e9543ba1d2f 100644 --- a/usr.sbin/switchctl/switchctl.8 +++ b/usr.sbin/switchctl/switchctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: switchctl.8,v 1.1 2016/07/19 16:54:26 reyk Exp $ +.\" $OpenBSD: switchctl.8,v 1.2 2016/10/12 19:07:42 reyk Exp $ .\" .\" Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 19 2016 $ +.Dd $Mdocdate: October 12 2016 $ .Dt SWITCHCTL 8 .Os .Sh NAME @@ -49,11 +49,25 @@ to communicate with The following commands are available to control .Xr switchd 8 : .Bl -tag -width Ds -.It Cm device add Ar filename -Add new +.It Cm connect Ar address Op Cm forward-to Ar address +Connect to a new switch by +.Ar address , +for example +.Ar 10.1.1.1 , +or a .Xr switch 4 control device, for example .Pa /dev/switch0 . +.Nm switchd +will forward all OpenFlow requests of the switch to a remote controller +if the optional +.Cm forward-to +.Ar address +is specified. +.It Cm disconnect Ar address +Close the client connection to a remote switch or a +.Xr switch 4 +control device. .It Cm load Ar filename Reload the configuration from the specified file. .It Cm log brief diff --git a/usr.sbin/switchctl/switchctl.c b/usr.sbin/switchctl/switchctl.c index 6ecc015e8e7..ed73eaea210 100644 --- a/usr.sbin/switchctl/switchctl.c +++ b/usr.sbin/switchctl/switchctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: switchctl.c,v 1.3 2016/09/28 09:13:30 reyk Exp $ */ +/* $OpenBSD: switchctl.c,v 1.4 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org> @@ -83,8 +83,8 @@ main(int argc, char *argv[]) struct sockaddr_un sun; struct parse_result *res; struct imsg imsg; - struct switch_device sdv; - struct switch_controller *swc; + struct switch_client swc; + struct switch_address *to; int ctl_sock; int done = 1; int n; @@ -92,6 +92,7 @@ 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) { @@ -186,40 +187,42 @@ main(int argc, char *argv[]) "Switch", "Port", "Type", "Name", "Info"); done = 0; break; - case ADD_DEVICE: - case REMOVE_DEVICE: - memset(&sdv, 0, sizeof(sdv)); - swc = &sdv.sdv_swc; - if (res->path[0] != '/') - strlcpy(sdv.sdv_device, "/dev/", - sizeof(sdv.sdv_device)); - if (strlcat(sdv.sdv_device, res->path, - sizeof(sdv.sdv_device)) >= sizeof(sdv.sdv_device)) - errx(1, "path is too long"); - if (res->action == REMOVE_DEVICE) { - imsg_compose(ibuf, IMSG_CTL_DEVICE_DISCONNECT, 0, 0, -1, - &sdv, sizeof(sdv)); + case CONNECT: + case DISCONNECT: + memset(&swc, 0, sizeof(swc)); + if (res->addr.ss_family == AF_UNSPEC) + errx(1, "invalid address"); + + memcpy(&swc.swc_addr.swa_addr, &res->addr, sizeof(res->addr)); + if (res->action == DISCONNECT) { + imsg_compose(ibuf, IMSG_CTL_DISCONNECT, 0, 0, -1, + &swc, sizeof(swc)); break; } + + to = &swc.swc_target; if (res->uri == NULL || res->uri[0] == '\0') - swc->swc_type = SWITCH_CONN_LOCAL; + to->swa_type = SWITCH_CONN_LOCAL; else { - if (strncmp(res->uri, "tcp:", 4) == 0) - swc->swc_type = SWITCH_CONN_TCP; - else if (strncmp(res->uri, "tls:", 4) == 0) - swc->swc_type = SWITCH_CONN_TLS; - else - errx(1, "protocol field is unknown"); - - if (parsehostport(res->uri + 4, - (struct sockaddr *)&swc->swc_addr, - sizeof(swc->swc_addr)) != 0) + 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; + } + + 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"); - warnx("%s", sdv.sdv_device); } - imsg_compose(ibuf, IMSG_CTL_DEVICE_CONNECT, 0, 0, -1, - &sdv, sizeof(sdv)); + imsg_compose(ibuf, IMSG_CTL_CONNECT, 0, 0, -1, + &swc, sizeof(swc)); break; case RESETALL: imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, &v, sizeof(v)); diff --git a/usr.sbin/switchd/control.c b/usr.sbin/switchd/control.c index 291e060bce6..b1b23d3ca48 100644 --- a/usr.sbin/switchd/control.c +++ b/usr.sbin/switchd/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.4 2016/09/14 13:46:51 rzalamena Exp $ */ +/* $OpenBSD: control.c,v 1.5 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2010-2016 Reyk Floeter <reyk@openbsd.org> @@ -340,8 +340,8 @@ control_dispatch_imsg(int fd, short event, void *arg) proc_compose(&env->sc_ps, PROC_OFP, imsg.hdr.type, &fd, sizeof(fd)); break; - case IMSG_CTL_DEVICE_CONNECT: - case IMSG_CTL_DEVICE_DISCONNECT: + case IMSG_CTL_CONNECT: + case IMSG_CTL_DISCONNECT: proc_compose(&env->sc_ps, PROC_PARENT, imsg.hdr.type, imsg.data, IMSG_DATA_SIZE(&imsg)); break; diff --git a/usr.sbin/switchd/ofcconn.c b/usr.sbin/switchd/ofcconn.c index a26159b47dd..059f2f4fdd9 100644 --- a/usr.sbin/switchd/ofcconn.c +++ b/usr.sbin/switchd/ofcconn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ofcconn.c,v 1.11 2016/09/30 12:48:27 reyk Exp $ */ +/* $OpenBSD: ofcconn.c,v 1.12 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2016 YASUOKA Masahiko <yasuoka@openbsd.org> @@ -20,6 +20,7 @@ #include <sys/queue.h> #include <sys/uio.h> #include <sys/socket.h> +#include <sys/un.h> #include <net/ofp.h> @@ -78,7 +79,7 @@ void ofsw_on_io(int, short, void *); int ofsw_write(struct ofsw *, struct ofcconn *); int ofsw_ofc_write_ready(struct ofsw *); void ofsw_reset_event_handlers(struct ofsw *); -int ofsw_new_ofcconn(struct ofsw *, struct switch_controller *); +int ofsw_new_ofcconn(struct ofsw *, struct switch_address *); int ofcconn_connect(struct ofcconn *); void ofcconn_on_sockio(int, short, void *); void ofcconn_connect_again(struct ofcconn *); @@ -126,30 +127,33 @@ int ofcconn_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) { struct ofsw *os; - struct switch_device *sdv; - struct switch_controller *swc; + struct switch_client swc; + struct sockaddr_un *un; switch (imsg->hdr.type) { - case IMSG_CTL_DEVICE_CONNECT: - if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) { - log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: " + case IMSG_CTL_CONNECT: + if (IMSG_DATA_SIZE(imsg) < sizeof(swc)) { + log_warnx("%s: IMSG_CTL_CONNECT: " "invalid message size", __func__); return (0); } - sdv = imsg->data; - swc = &sdv->sdv_swc; - if ((os = ofsw_create(sdv->sdv_device, imsg->fd)) != NULL) - ofsw_new_ofcconn(os, swc); + memcpy(&swc, imsg->data, sizeof(swc)); + un = (struct sockaddr_un *)&swc.swc_addr.swa_addr; + + if ((os = ofsw_create(un->sun_path, imsg->fd)) != NULL) + ofsw_new_ofcconn(os, &swc.swc_target); return (0); - case IMSG_CTL_DEVICE_DISCONNECT: - if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) { + case IMSG_CTL_DISCONNECT: + if (IMSG_DATA_SIZE(imsg) < sizeof(swc)) { log_warnx("%s: IMSG_CTL_DEVICE_DISCONNECT: " "invalid message size", __func__); return (0); } - sdv = imsg->data; + memcpy(&swc, imsg->data, sizeof(swc)); + un = (struct sockaddr_un *)&swc.swc_addr.swa_addr; + TAILQ_FOREACH(os, &ofsw_list, os_next) { - if (!strcmp(os->os_name, sdv->sdv_device)) + if (!strcmp(os->os_name, un->sun_path)) break; } if (os) { @@ -375,7 +379,7 @@ ofsw_reset_event_handlers(struct ofsw *os) } int -ofsw_new_ofcconn(struct ofsw *os, struct switch_controller *swc) +ofsw_new_ofcconn(struct ofsw *os, struct switch_address *swa) { struct ofcconn *oc = NULL; char buf[128]; @@ -386,7 +390,7 @@ ofsw_new_ofcconn(struct ofsw *os, struct switch_controller *swc) } if (asprintf(&oc->oc_name, "tcp:%s", - print_host(&swc->swc_addr, buf, sizeof(buf))) == -1) { + print_host(&swa->swa_addr, buf, sizeof(buf))) == -1) { log_warn("%s: strdup failed", __func__); goto fail; } @@ -396,7 +400,7 @@ ofsw_new_ofcconn(struct ofsw *os, struct switch_controller *swc) } oc->oc_sw = os; oc->oc_sock = -1; - memcpy(&oc->oc_peer, &swc->swc_addr, sizeof(oc->oc_peer)); + memcpy(&oc->oc_peer, &swa->swa_addr, sizeof(oc->oc_peer)); if (ntohs(((struct sockaddr_in *)&oc->oc_peer)->sin_port) == 0) ((struct sockaddr_in *)&oc->oc_peer)->sin_port = @@ -444,7 +448,7 @@ ofcconn_connect(struct ofcconn *oc) ofcconn_on_sockio, oc); event_add(&oc->oc_evsock, NULL); - tv.tv_sec = SWITCHD_OFCCONN_TIMEOUT; + tv.tv_sec = SWITCHD_CONNECT_TIMEOUT; tv.tv_usec = 0; event_add(&oc->oc_evtimer, &tv); diff --git a/usr.sbin/switchd/ofp.c b/usr.sbin/switchd/ofp.c index 76585a72a87..5c77deeef9f 100644 --- a/usr.sbin/switchd/ofp.c +++ b/usr.sbin/switchd/ofp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ofp.c,v 1.13 2016/10/05 16:40:55 reyk Exp $ */ +/* $OpenBSD: ofp.c,v 1.14 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -93,8 +93,7 @@ ofp_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) { struct privsep *ps = p->p_ps; struct switchd *sc = ps->ps_env; - struct sockaddr_un un; - struct switch_device *sdv; + struct switch_client swc; struct switch_connection *con; switch (imsg->hdr.type) { @@ -103,24 +102,16 @@ ofp_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) close(sc->sc_tap); sc->sc_tap = imsg->fd; return (0); - case IMSG_CTL_DEVICE_CONNECT: - case IMSG_CTL_DEVICE_DISCONNECT: - IMSG_SIZE_CHECK(imsg, sdv); - sdv = imsg->data; - - if (strlcpy(un.sun_path, sdv->sdv_device, - sizeof(un.sun_path)) >= sizeof(un.sun_path)) { - log_warnx("invalid device: %s", sdv->sdv_device); - return (0); - } - un.sun_family = AF_UNIX; - un.sun_len = sizeof(un); - - if (imsg->hdr.type == IMSG_CTL_DEVICE_CONNECT) - ofrelay_attach(&sc->sc_server, - imsg->fd, (struct sockaddr *)&un); - else if ((con = - switchd_connbyaddr(sc, (struct sockaddr *)&un)) != NULL) + case IMSG_CTL_CONNECT: + case IMSG_CTL_DISCONNECT: + IMSG_SIZE_CHECK(imsg, &swc); + memcpy(&swc, imsg->data, sizeof(swc)); + + if (imsg->hdr.type == IMSG_CTL_CONNECT) + ofrelay_attach(&sc->sc_server, imsg->fd, + (struct sockaddr *)&swc.swc_addr.swa_addr); + else if ((con = switchd_connbyaddr(sc, + (struct sockaddr *)&swc.swc_addr.swa_addr)) != NULL) ofp_close(con); return (0); default: diff --git a/usr.sbin/switchd/ofrelay.c b/usr.sbin/switchd/ofrelay.c index 381b3c648aa..0a29941f40b 100644 --- a/usr.sbin/switchd/ofrelay.c +++ b/usr.sbin/switchd/ofrelay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ofrelay.c,v 1.5 2016/10/07 08:31:08 rzalamena Exp $ */ +/* $OpenBSD: ofrelay.c,v 1.6 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org> @@ -76,7 +76,7 @@ ofrelay_run(struct privsep *ps, struct privsep_proc *p, void *arg) struct switch_server *srv = &sc->sc_server; TAILQ_INIT(&sc->sc_conns); - TAILQ_INIT(&sc->sc_devs); + TAILQ_INIT(&sc->sc_clients); srv->srv_sc = sc; event_set(&srv->srv_ev, srv->srv_fd, EV_READ, ofrelay_accept, srv); diff --git a/usr.sbin/switchd/parse.y b/usr.sbin/switchd/parse.y index a1c338f2c1c..8f051d7a8bb 100644 --- a/usr.sbin/switchd/parse.y +++ b/usr.sbin/switchd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.2 2016/09/30 11:57:57 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.3 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org> @@ -26,6 +26,7 @@ #include <sys/types.h> #include <sys/queue.h> #include <sys/stat.h> +#include <sys/un.h> #include <ctype.h> #include <err.h> @@ -79,7 +80,7 @@ typedef struct { int64_t number; char *string; in_port_t port; - struct switch_device + struct switch_client *conn; } v; int lineno; @@ -157,51 +158,73 @@ opttls : /* empty */ { $$ = 0; } ; device : DEVICE STRING optofcconn { - struct switch_device *c; + struct switch_client *c; + struct switch_address s; + struct sockaddr_un *un; - TAILQ_FOREACH(c, &conf->sc_devs, sdv_next) { - if (strcmp(c->sdv_device, $2) == 0) + memset(&s, 0, sizeof(s)); + un = (struct sockaddr_un *)&s.swa_addr; + + if (*$2 != '/') { + yyerror("not an absolute path: %s", $2); + free($2); + YYERROR; + } + + un->sun_family = AF_LOCAL; + un->sun_len = sizeof(*un); + if (strlcpy(un->sun_path, $2, + sizeof(un->sun_path)) >= sizeof(un->sun_path)) { + yyerror("device name is too long: %s", $2); + free($2); + YYERROR; + } + free($2); + + TAILQ_FOREACH(c, &conf->sc_clients, swc_next) { + if (sockaddr_cmp((struct sockaddr *) + &c->swc_addr.swa_addr, + (struct sockaddr *)&s.swa_addr, -1) == 0) break; } if (c != NULL) { yyerror("device name is duplicated"); YYERROR; } - if (strlcpy($3->sdv_device, $2, sizeof($3->sdv_device)) - >= sizeof($3->sdv_device)) { - yyerror("device name is too long"); - YYERROR; - } - free($2); - TAILQ_INSERT_TAIL(&conf->sc_devs, $3, sdv_next); + + memcpy(&$3->swc_addr, &s, sizeof(s)); + + TAILQ_INSERT_TAIL(&conf->sc_clients, $3, swc_next); } ; optofcconn : /* empty */ { if (($$ = calloc(1, - sizeof(struct switch_device))) == NULL) + sizeof(struct switch_client))) == NULL) fatal("calloc"); - $$->sdv_swc.swc_type = SWITCH_CONN_LOCAL; + $$->swc_addr.swa_type = $$->swc_target.swa_type = + SWITCH_CONN_LOCAL; } | FORWARD TO STRING { + size_t len; + if (($$ = calloc(1, - sizeof(struct switch_device))) == NULL) + sizeof(struct switch_client))) == NULL) fatal("calloc"); - if (strncmp($3, "tcp:", 4) == 0) - $$->sdv_swc.swc_type = SWITCH_CONN_TCP; - else if (strncmp($3, "tls:", 4) == 0) - $$->sdv_swc.swc_type = SWITCH_CONN_TLS; + len = 4; + if (strncmp($3, "tcp:", len) == 0) + $$->swc_target.swa_type = SWITCH_CONN_TCP; + else if (strncmp($3, "tls:", len) == 0) + $$->swc_target.swa_type = SWITCH_CONN_TLS; else { - yyerror("foward to proto is not supported"); - free($$); - free($3); - YYERROR; + len = 0; + $$->swc_target.swa_type = SWITCH_CONN_TCP; } - if (parsehostport($3 + 4, - (struct sockaddr *)&$$->sdv_swc.swc_addr, - sizeof($$->sdv_swc.swc_addr)) == -1) { + if (parsehostport($3 + len, + (struct sockaddr *)&$$->swc_target.swa_addr, + sizeof($$->swc_target.swa_addr)) == -1) { yyerror("could not parse host and port part " - "of connect-to"); + "of forward target"); free($$); free($3); YYERROR; diff --git a/usr.sbin/switchd/switchd.c b/usr.sbin/switchd/switchd.c index 72e62fb35da..64b26fbf452 100644 --- a/usr.sbin/switchd/switchd.c +++ b/usr.sbin/switchd/switchd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: switchd.c,v 1.13 2016/09/30 12:32:31 reyk Exp $ */ +/* $OpenBSD: switchd.c,v 1.14 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -19,6 +19,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> +#include <sys/un.h> #include <sys/queue.h> #include <arpa/inet.h> @@ -45,8 +46,9 @@ int parent_dispatch_ofp(int, struct privsep_proc *, struct imsg *); int parent_dispatch_control(int, struct privsep_proc *, struct imsg *); int parent_configure(struct switchd *); int parent_reload(struct switchd *); -void parent_device_connect(struct privsep *, struct switch_device *); -int switch_device_cmp(struct switch_device *, struct switch_device *); +void parent_connect(struct privsep *, struct switch_client *); +void parent_connected(int, short, void *); +void parent_disconnect(struct privsep *, struct switch_client *); __dead void usage(void); @@ -152,7 +154,7 @@ main(int argc, char *argv[]) ps = &sc->sc_ps; ps->ps_env = sc; TAILQ_INIT(&ps->ps_rcsocks); - TAILQ_INIT(&sc->sc_devs); + TAILQ_INIT(&sc->sc_clients); if (parse_config(sc->sc_conffile, sc) == -1) { proc_kill(&sc->sc_ps); @@ -362,7 +364,7 @@ parent_sig_handler(int sig, short event, void *arg) int parent_configure(struct switchd *sc) { - struct switch_device *c; + struct switch_client *swc, *swcn; int fd; if ((fd = switchd_tap()) == -1) @@ -370,8 +372,8 @@ parent_configure(struct switchd *sc) proc_compose_imsg(&sc->sc_ps, PROC_OFP, -1, IMSG_TAPFD, -1, fd, NULL, 0); - TAILQ_FOREACH(c, &sc->sc_devs, sdv_next) { - parent_device_connect(&sc->sc_ps, c); + TAILQ_FOREACH_SAFE(swc, &sc->sc_clients, swc_next, swcn) { + parent_connect(&sc->sc_ps, swc); } return (0); @@ -381,43 +383,39 @@ int parent_reload(struct switchd *sc) { struct switchd newconf; - struct switch_device *sdv, *osdv, *sdvn; - enum privsep_procid procid; + struct switch_client *swc, *oswc, *swcn; memset(&newconf, 0, sizeof(newconf)); - TAILQ_INIT(&newconf.sc_devs); + TAILQ_INIT(&newconf.sc_clients); TAILQ_INIT(&newconf.sc_conns); if (parse_config(sc->sc_conffile, &newconf) != -1) { - TAILQ_FOREACH_SAFE(sdv, &sc->sc_devs, sdv_next, sdvn) { - TAILQ_FOREACH(osdv, &newconf.sc_devs, sdv_next) { - if (switch_device_cmp(osdv, sdv) == 0) { - TAILQ_REMOVE(&newconf.sc_devs, - osdv, sdv_next); + TAILQ_FOREACH_SAFE(swc, &sc->sc_clients, swc_next, swcn) { + TAILQ_FOREACH(oswc, &newconf.sc_clients, swc_next) { + if (sockaddr_cmp((struct sockaddr *) + &oswc->swc_addr.swa_addr, + (struct sockaddr *) + &swc->swc_addr.swa_addr, -1) == 0) { + TAILQ_REMOVE(&newconf.sc_clients, + oswc, swc_next); break; } } - if (osdv == NULL) { + if (oswc == NULL) { /* Removed */ - TAILQ_REMOVE(&sc->sc_devs, sdv, sdv_next); - procid = (sdv->sdv_swc.swc_type == - SWITCH_CONN_LOCAL) - ? PROC_OFP : PROC_OFCCONN; - proc_compose_imsg(&sc->sc_ps, procid, -1, - IMSG_CTL_DEVICE_DISCONNECT, - -1, -1, sdv, sizeof(*sdv)); + parent_disconnect(&sc->sc_ps, swc); } else { /* Keep the existing one */ - TAILQ_REMOVE(&newconf.sc_devs, osdv, sdv_next); - free(osdv); + TAILQ_REMOVE(&newconf.sc_clients, + oswc, swc_next); + free(oswc); } } - TAILQ_FOREACH(sdv, &newconf.sc_devs, sdv_next) { - procid = - (sdv->sdv_swc.swc_type == SWITCH_CONN_LOCAL) - ? PROC_OFP : PROC_OFCCONN; - TAILQ_INSERT_TAIL(&sc->sc_devs, sdv, sdv_next); - parent_device_connect(&sc->sc_ps, sdv); + TAILQ_FOREACH_SAFE(swc, &newconf.sc_clients, swc_next, swcn) { + TAILQ_REMOVE(&newconf.sc_clients, swc, swc_next); + TAILQ_INSERT_TAIL(&sc->sc_clients, swc, swc_next); + + parent_connect(&sc->sc_ps, swc); } } @@ -427,26 +425,41 @@ parent_reload(struct switchd *sc) int parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) { + struct switch_client *swc, *oswc; + struct privsep *ps = p->p_ps; + struct switchd *sc = ps->ps_env; + switch (imsg->hdr.type) { - case IMSG_CTL_DEVICE_CONNECT: - case IMSG_CTL_DEVICE_DISCONNECT: - if (IMSG_DATA_SIZE(imsg) < - sizeof(struct switch_device)) { - log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: " - "message size is wrong", __func__); + case IMSG_CTL_CONNECT: + case IMSG_CTL_DISCONNECT: + IMSG_SIZE_CHECK(imsg, swc); + + /* Need to allocate it in case it is reused */ + if ((swc = calloc(1, sizeof(*swc))) == NULL) { + log_warnx("%s: calloc", __func__); return (0); } - if (imsg->hdr.type == IMSG_CTL_DEVICE_CONNECT) - parent_device_connect(p->p_ps, imsg->data); - else { - /* - * Since we don't know which the device was attached - * to, we send the message to the both. - */ - proc_compose(p->p_ps, PROC_OFP, - imsg->hdr.type, imsg->data, IMSG_DATA_SIZE(imsg)); - proc_compose(p->p_ps, PROC_OFCCONN, - imsg->hdr.type, imsg->data, IMSG_DATA_SIZE(imsg)); + memcpy(swc, imsg->data, sizeof(*swc)); + memset(&swc->swc_ev, 0, sizeof(swc->swc_ev)); + + if (imsg->hdr.type == IMSG_CTL_CONNECT) { + TAILQ_INSERT_TAIL(&sc->sc_clients, swc, swc_next); + parent_connect(p->p_ps, swc); + } else { + TAILQ_FOREACH(oswc, &sc->sc_clients, swc_next) { + if (sockaddr_cmp((struct sockaddr *) + &oswc->swc_addr.swa_addr, + (struct sockaddr *) + &swc->swc_addr.swa_addr, -1) == 0) { + parent_disconnect(ps, oswc); + break; + } + } + if (oswc == NULL) + log_warnx("client %s is not connected", + print_host(&swc->swc_addr.swa_addr, + NULL, 0)); + free(swc); } return (0); default: @@ -468,52 +481,131 @@ parent_shutdown(struct switchd *sc) } void -parent_device_connect(struct privsep *ps, struct switch_device *sdv) +parent_connect(struct privsep *ps, struct switch_client *swc) { - int fd; + struct switchd *sc = ps->ps_env; + struct sockaddr_storage *ss; + struct sockaddr_un *un; + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; + int fd = -1; + struct timeval tv; + + ss = &swc->swc_addr.swa_addr; + + if (ss->ss_len == 0) { + log_warnx("%s: invalid address", __func__); + goto fail; + } + swc->swc_arg = ps; + memset(&swc->swc_ev, 0, sizeof(swc->swc_ev)); + + switch (ss->ss_family) { + case AF_LOCAL: + un = (struct sockaddr_un *)ss; + + /* restrict the opening path to /dev/switch* */ + if (strncmp(un->sun_path, "/dev/switch", + strlen("/dev/switch")) != 0) { + log_warnx("%s: device path is wrong: %s", __func__, + un->sun_path); + goto fail; + } - /* restrict the opening path to /dev/switch* */ - if (strncmp(sdv->sdv_device, "/dev/switch", 11) != 0) { - log_warnx("%s: device path is wrong: %s", __func__, - sdv->sdv_device); - goto on_error; + if ((fd = open(un->sun_path, O_RDWR | O_NONBLOCK)) == -1) { + log_warn("%s: failed to open %s", + __func__, un->sun_path); + goto fail; + } + break; + case AF_INET: + case AF_INET6: + if (ss->ss_family == AF_INET) { + sin4 = (struct sockaddr_in *)ss; + if (sin4->sin_port == 0) + sin4->sin_port = htons(SWITCHD_CTLR_PORT); + } else if (ss->ss_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)ss; + if (sin6->sin6_port == 0) + sin6->sin6_port = htons(SWITCHD_CTLR_PORT); + } + + if ((fd = switchd_socket((struct sockaddr *)ss, 0)) == -1) { + log_debug("%s: failed to get socket for %s", __func__, + print_host(ss, NULL, 0)); + goto fail; + } + + retry: + if (connect(fd, (struct sockaddr *)ss, ss->ss_len) == -1) { + if (errno == EINTR) + goto retry; + if (errno == EINPROGRESS) { + tv.tv_sec = SWITCHD_CONNECT_TIMEOUT; + tv.tv_usec = 0; + event_set(&swc->swc_ev, fd, EV_WRITE|EV_TIMEOUT, + parent_connected, swc); + event_add(&swc->swc_ev, &tv); + return; + } + + log_warn("%s: failed to connect to %s, fd %d", __func__, + print_host(ss, NULL, 0), fd); + goto fail; + } + + break; } - if ((fd = open(sdv->sdv_device, O_RDWR | O_NONBLOCK)) == -1) { - log_warn("%s: open(%s) failed", __func__, sdv->sdv_device); - goto on_error; + parent_connected(fd, 0, swc); + return; + + fail: + TAILQ_REMOVE(&sc->sc_clients, swc, swc_next); + free(swc); +} + +void +parent_connected(int fd, short event, void *arg) +{ + struct switch_client *swc = arg; + struct privsep *ps = swc->swc_arg; + struct switchd *sc = ps->ps_env; + + if (event & EV_TIMEOUT) { + log_debug("%s: failed to connect to %s", __func__, + print_host(&swc->swc_addr.swa_addr, NULL, 0)); + TAILQ_REMOVE(&sc->sc_clients, swc, swc_next); + free(swc); + return; } - switch (sdv->sdv_swc.swc_type) { + switch (swc->swc_target.swa_type) { case SWITCH_CONN_LOCAL: - proc_compose_imsg(ps, PROC_OFP, -1, IMSG_CTL_DEVICE_CONNECT, - -1, fd, sdv, sizeof(*sdv)); + proc_compose_imsg(ps, PROC_OFP, -1, IMSG_CTL_CONNECT, + -1, fd, swc, sizeof(*swc)); break; case SWITCH_CONN_TLS: case SWITCH_CONN_TCP: - proc_compose_imsg(ps, PROC_OFCCONN, -1, IMSG_CTL_DEVICE_CONNECT, - -1, fd, sdv, sizeof(struct switch_device)); + proc_compose_imsg(ps, PROC_OFCCONN, -1, IMSG_CTL_CONNECT, + -1, fd, swc, sizeof(*swc)); break; default: fatalx("not implemented"); } -on_error: - return; } -int -switch_device_cmp(struct switch_device *a, - struct switch_device *b) +void +parent_disconnect(struct privsep *ps, struct switch_client *swc) { - struct switch_controller *ca = &a->sdv_swc; - struct switch_controller *cb = &b->sdv_swc; - int c; + struct switchd *sc = ps->ps_env; + enum privsep_procid target; + + TAILQ_REMOVE(&sc->sc_clients, swc, swc_next); - if ((c = strcmp(a->sdv_device, b->sdv_device)) != 0) - return (c); - if ((c = cb->swc_type - ca->swc_type) != 0) - return (c); + target = swc->swc_target.swa_type == SWITCH_CONN_LOCAL ? + PROC_OFP : PROC_OFCCONN; + proc_compose(ps, target, IMSG_CTL_DISCONNECT, swc, sizeof(*swc)); - return (sockaddr_cmp((struct sockaddr *)&ca->swc_addr, - (struct sockaddr *)&cb->swc_addr, -1)); + free(swc); } diff --git a/usr.sbin/switchd/switchd.h b/usr.sbin/switchd/switchd.h index 92a7533a4ad..ad94ee56306 100644 --- a/usr.sbin/switchd/switchd.h +++ b/usr.sbin/switchd/switchd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: switchd.h,v 1.15 2016/10/07 08:49:53 reyk Exp $ */ +/* $OpenBSD: switchd.h,v 1.16 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -76,6 +76,11 @@ struct multipart_message { }; SLIST_HEAD(multipart_list, multipart_message); +struct switch_address { + enum switch_conn_type swa_type; + struct sockaddr_storage swa_addr; +}; + struct switch_connection { unsigned int con_id; unsigned int con_instance; @@ -113,18 +118,15 @@ struct switch_server { struct switchd *srv_sc; }; -struct switch_controller { - enum switch_conn_type swc_type; - struct sockaddr_storage swc_addr; -}; - -struct switch_device { - char sdv_device[PATH_MAX]; - struct switch_controller sdv_swc; - TAILQ_ENTRY(switch_device) - sdv_next; +struct switch_client { + struct switch_address swc_addr; + struct switch_address swc_target; + struct event swc_ev; + void *swc_arg; + TAILQ_ENTRY(switch_client) + swc_next; }; -TAILQ_HEAD(switch_devices, switch_device); +TAILQ_HEAD(switch_clients, switch_client); struct switchd { struct privsep sc_ps; @@ -136,7 +138,7 @@ struct switchd { unsigned int sc_cache_timeout; char sc_conffile[PATH_MAX]; uint8_t sc_opts; - struct switch_devices sc_devs; + struct switch_clients sc_clients; struct switch_connections sc_conns; }; diff --git a/usr.sbin/switchd/types.h b/usr.sbin/switchd/types.h index a8300b92bcf..b9d8b81c95b 100644 --- a/usr.sbin/switchd/types.h +++ b/usr.sbin/switchd/types.h @@ -1,4 +1,4 @@ -/* $OpenBSD: types.h,v 1.6 2016/10/06 20:27:44 reyk Exp $ */ +/* $OpenBSD: types.h,v 1.7 2016/10/12 19:07:42 reyk Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -43,8 +43,7 @@ #define SWITCHD_CACHE_MAX 4096 /* Default MAC address cache limit */ #define SWITCHD_CACHE_TIMEOUT 240 /* t/o in seconds for learned MACs */ -#define SWITCHD_OFCCONN_TIMEOUT 20 /* connect timeout for OpenFlow ch. */ - +#define SWITCHD_CONNECT_TIMEOUT 5 #ifndef ETHER_ADDR_LEN #define ETHER_ADDR_LEN 6 @@ -69,8 +68,8 @@ enum imsg_type { IMSG_CTL_SWITCH, IMSG_CTL_MAC, IMSG_CTL_SHOW_SUM, - IMSG_CTL_DEVICE_CONNECT, - IMSG_CTL_DEVICE_DISCONNECT, + IMSG_CTL_CONNECT, + IMSG_CTL_DISCONNECT, IMSG_TAPFD }; |