diff options
author | 2016-10-12 19:07:42 +0000 | |
---|---|---|
committer | 2016-10-12 19:07:42 +0000 | |
commit | 60874843d5beb38ccd258f1fab55dddb4dff5fd5 (patch) | |
tree | d774bb00fe0a9f5d6274709946bbf1d7856e6ec2 /usr.sbin/switchd/switchd.c | |
parent | Compare to see if pane status line has actually changed, not just size, and (diff) | |
download | wireguard-openbsd-60874843d5beb38ccd258f1fab55dddb4dff5fd5.tar.xz wireguard-openbsd-60874843d5beb38ccd258f1fab55dddb4dff5fd5.zip |
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@
Diffstat (limited to 'usr.sbin/switchd/switchd.c')
-rw-r--r-- | usr.sbin/switchd/switchd.c | 246 |
1 files changed, 169 insertions, 77 deletions
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); } |