summaryrefslogtreecommitdiffstats
path: root/usr.sbin/switchd/ofp.c
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2016-09-30 11:57:57 +0000
committerreyk <reyk@openbsd.org>2016-09-30 11:57:57 +0000
commit5c0cb926a5ed35b0e9346c80e018c71f8041fd90 (patch)
tree4770cd6a6b87abbb2633fbe76a357d33beb3a304 /usr.sbin/switchd/ofp.c
parentIn ssh tests set REGRESS_FAIL_EARLY with ?= so that the environment (diff)
downloadwireguard-openbsd-5c0cb926a5ed35b0e9346c80e018c71f8041fd90.tar.xz
wireguard-openbsd-5c0cb926a5ed35b0e9346c80e018c71f8041fd90.zip
Implement socket server code that properly handles async I/O, partial
messages, multiple messages per buffer and important things like connection limits and file descriptor accounting. It works with TCP connections as well as switch(4). The ofrelay.c part replaces networking that was in ofp.c and will soon handle all socket connections of switchd. It is called "ofrelay" because it will be used as client, server, and forwarder. OK rzalamena@
Diffstat (limited to 'usr.sbin/switchd/ofp.c')
-rw-r--r--usr.sbin/switchd/ofp.c254
1 files changed, 58 insertions, 196 deletions
diff --git a/usr.sbin/switchd/ofp.c b/usr.sbin/switchd/ofp.c
index 6bd4944bad4..ac1bb57f3d6 100644
--- a/usr.sbin/switchd/ofp.c
+++ b/usr.sbin/switchd/ofp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofp.c,v 1.9 2016/09/29 18:25:54 reyk Exp $ */
+/* $OpenBSD: ofp.c,v 1.10 2016/09/30 11:57:57 reyk Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -38,45 +38,27 @@
#include "switchd.h"
#include "ofp_map.h"
-int ofp_dispatch_control(int, struct privsep_proc *, struct imsg *);
int ofp_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+int ofp_dispatch_control(int, struct privsep_proc *, struct imsg *);
void ofp_run(struct privsep *, struct privsep_proc *, void *);
int ofp_add_device(struct switchd *, int, const char *);
-static unsigned int id = 0;
-
static struct privsep_proc procs[] = {
{ "control", PROC_CONTROL, ofp_dispatch_control },
- { "parent", PROC_PARENT, ofp_dispatch_parent },
- { "ofcconn", PROC_OFCCONN, NULL }
+ { "parent", PROC_PARENT, ofp_dispatch_parent }
};
-static TAILQ_HEAD(, switch_connection) conn_head =
- TAILQ_HEAD_INITIALIZER(conn_head);
-
void
ofp(struct privsep *ps, struct privsep_proc *p)
{
- struct switchd *sc = ps->ps_env;
- struct switch_server *srv = &sc->sc_server;
-
- if ((sc->sc_tap = switchd_tap()) == -1)
- fatal("tap");
-
- log_info("listen on %s", print_host(&srv->srv_addr, NULL, 0));
-
- if ((srv->srv_fd = switchd_listen((struct sockaddr *)
- &srv->srv_addr)) == -1)
- fatal("listen");
-
+ ofrelay(ps, p);
proc_run(ps, p, procs, nitems(procs), ofp_run, NULL);
}
void
ofp_run(struct privsep *ps, struct privsep_proc *p, void *arg)
{
- struct switchd *sc = ps->ps_env;
- struct switch_server *srv = &sc->sc_server;
+ struct switchd *sc = ps->ps_env;
/*
* pledge in the ofp process:
@@ -87,8 +69,10 @@ ofp_run(struct privsep *ps, struct privsep_proc *p, void *arg)
if (pledge("stdio inet recvfd", NULL) == -1)
fatal("pledge");
- event_set(&srv->srv_ev, srv->srv_fd, EV_READ, ofp_accept, srv);
- event_add(&srv->srv_ev, NULL);
+ TAILQ_INIT(&sc->sc_conns);
+ sc->sc_tap = -1;
+
+ ofrelay_run(ps, p, NULL);
}
int
@@ -107,51 +91,45 @@ ofp_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
int
ofp_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
- struct switchd *sc = p->p_ps->ps_env;
+ struct privsep *ps = p->p_ps;
+ struct switchd *sc = ps->ps_env;
+ struct sockaddr_un un;
struct switch_device *sdv;
- struct switch_connection *c;
+ struct switch_connection *con;
switch (imsg->hdr.type) {
+ case IMSG_TAPFD:
+ if (sc->sc_tap != -1)
+ close(sc->sc_tap);
+ sc->sc_tap = imsg->fd;
+ return (0);
case IMSG_CTL_DEVICE_CONNECT:
case IMSG_CTL_DEVICE_DISCONNECT:
- if (IMSG_DATA_SIZE(imsg) < sizeof(*sdv)) {
- log_warnx("%s: IMSG_CTL_DEVICE_CONNECT: "
- "message size is wrong", __func__);
+ 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);
}
- sdv = imsg->data;
+ un.sun_family = AF_UNIX;
+ un.sun_len = sizeof(un);
+
if (imsg->hdr.type == IMSG_CTL_DEVICE_CONNECT)
- ofp_add_device(sc, imsg->fd, sdv->sdv_device);
- else {
- TAILQ_FOREACH(c, &conn_head, con_next) {
- if (c->con_peer.ss_family == AF_UNIX &&
- strcmp(sdv->sdv_device,
- ((struct sockaddr_un *)&c->con_peer)
- ->sun_path) == 0)
- break;
- }
- if (c)
- ofp_close(c);
- }
+ ofrelay_attach(&sc->sc_server,
+ imsg->fd, (struct sockaddr *)&un);
+ else if ((con =
+ switchd_connbyaddr(sc, (struct sockaddr *)&un)) != NULL)
+ ofp_close(con);
return (0);
default:
-
break;
}
return (-1);
}
-void
-ofp_close(struct switch_connection *con)
-{
- log_info("%s: connection %u closed", __func__, con->con_id);
- event_del(&con->con_ev);
- switch_remove(con->con_sc, con->con_switch);
- close(con->con_fd);
- TAILQ_REMOVE(&conn_head, con, con_next);
-}
-
int
ofp_validate_header(struct switchd *sc,
struct sockaddr_storage *src, struct sockaddr_storage *dst,
@@ -186,46 +164,25 @@ ofp_validate_header(struct switchd *sc,
return (0);
}
-void
-ofp_read(int fd, short event, void *arg)
+int
+ofp_input(struct switch_connection *con, struct ibuf *ibuf)
{
- uint8_t buf[SWITCHD_READ_BUFFER];
- struct switch_connection *con = arg;
- struct switch_control *sw;
struct switchd *sc = con->con_sc;
struct ofp_header *oh;
- ssize_t len;
- struct ibuf *ibuf = NULL;
-
- event_add(&con->con_ev, NULL);
- if ((event & EV_TIMEOUT))
- goto fail;
-
- if ((len = read(fd, buf, sizeof(buf))) == -1)
- goto fail;
- if (len == 0)
- goto fail;
-
- if ((ibuf = ibuf_new(buf, len)) == NULL)
- goto fail;
-
- sw = con->con_switch;
- log_debug("%s: connection %d: %ld bytes from switch %u", __func__,
- con->con_id, len, sw == NULL ? 0 : sw->sw_id);
if ((oh = ibuf_seek(ibuf, 0, sizeof(*oh))) == NULL) {
log_debug("short header");
- goto fail;
+ return (-1);
}
switch (oh->oh_version) {
case OFP_V_1_0:
if (ofp10_input(sc, con, oh, ibuf) != 0)
- goto fail;
+ return (-1);
break;
case OFP_V_1_3:
if (ofp13_input(sc, con, oh, ibuf) != 0)
- goto fail;
+ return (-1);
break;
case OFP_V_1_1:
case OFP_V_1_2:
@@ -234,147 +191,52 @@ ofp_read(int fd, short event, void *arg)
(void)ofp10_validate(sc,
&con->con_peer, &con->con_local, oh, ibuf);
ofp10_hello(sc, con, oh, ibuf);
- goto fail;
+ return (-1);
}
- ibuf_release(ibuf);
- return;
-
- fail:
- ibuf_release(ibuf);
- ofp_close(con);
+ return (0);
}
int
ofp_output(struct switch_connection *con, struct ofp_header *oh,
struct ibuf *obuf)
{
- struct iovec iov[2];
- int cnt = 0;
- void *data;
- ssize_t len;
-
- if (oh != NULL) {
- iov[cnt].iov_base = oh;
- iov[cnt++].iov_len = sizeof(*oh);
- }
+ struct ibuf *buf;
- if (ibuf_length(obuf)) {
- if (oh != NULL && (ibuf_seek(obuf, 0, sizeof(*oh)) == NULL))
- return (-1);
- len = ibuf_dataleft(obuf);
- if (len < 0) {
- return (-1);
- } else if (len > 0 &&
- (data = ibuf_getdata(obuf, len)) != NULL) {
- iov[cnt].iov_base = data;
- iov[cnt++].iov_len = len;
- }
- }
-
- if (cnt == 0)
+ if ((buf = ibuf_static()) == NULL)
return (-1);
-
- /* XXX */
- if (writev(con->con_fd, iov, cnt) == -1)
+ if ((oh != NULL) &&
+ (ibuf_add(buf, oh, sizeof(*oh)) == -1)) {
+ ibuf_release(buf);
return (-1);
+ }
+ if ((obuf != NULL) &&
+ (ibuf_cat(buf, obuf) == -1)) {
+ ibuf_release(buf);
+ return (-1);
+ }
+ ibuf_close(&con->con_wbuf, buf);
return (0);
}
int
-ofp_add_device(struct switchd *sc, int fd, const char *name)
+ofp_open(struct privsep *ps, struct switch_connection *con)
{
- struct switch_connection *con = NULL;
- struct sockaddr_un *sun;
- struct switch_control *sw;
-
- if ((con = calloc(1, sizeof(*con))) == NULL) {
- log_warn("calloc");
- goto fail;
- }
- con->con_fd = fd;
- con->con_sc = sc;
- con->con_id = ++id;
- sun = (struct sockaddr_un *)&con->con_peer;
- sun->sun_family = AF_LOCAL;
- strlcpy(sun->sun_path, name, sizeof(sun->sun_path));
+ struct switch_control *sw;
/* Get associated switch, if it exists */
sw = switch_get(con);
- log_info("%s: new device %u (%s) from switch %u",
- __func__, con->con_id, name, sw == NULL ? 0 : sw->sw_id);
-
- bzero(&con->con_ev, sizeof(con->con_ev));
- event_set(&con->con_ev, con->con_fd, EV_READ, ofp_read, con);
- event_add(&con->con_ev, NULL);
-
- TAILQ_INSERT_TAIL(&conn_head, con, con_next);
+ log_info("%s: new connection %u.%u from switch %u",
+ __func__, con->con_id, con->con_instance,
+ sw == NULL ? 0 : sw->sw_id);
return (0);
-fail:
- if (fd != -1)
- close(fd);
- free(con);
-
- return (-1);
}
void
-ofp_accept(int fd, short event, void *arg)
+ofp_close(struct switch_connection *con)
{
- struct switch_server *server = arg;
- struct switch_connection *con = NULL;
- struct switchd *sc = server->srv_sc;
- struct switch_control *sw;
- struct sockaddr_storage ss;
- socklen_t slen;
- int s;
-
- event_add(&server->srv_ev, NULL);
- if ((event & EV_TIMEOUT))
- return;
-
- /* XXX accept_reserve() */
- slen = sizeof(ss);
- if ((s = accept(fd, (struct sockaddr *)&ss, &slen)) == -1) {
- log_warn("accept");
- goto fail;
- }
-
- if ((con = calloc(1, sizeof(*con))) == NULL) {
- log_warn("calloc");
- goto fail;
- }
-
- slen = sizeof(con->con_local);
- if (getsockname(s, (struct sockaddr *)&con->con_local, &slen) == -1) {
- log_warn("getsockname");
- goto fail;
- }
-
- con->con_fd = s;
- con->con_sc = sc;
- con->con_id = ++id;
- con->con_port = htons(socket_getport(&ss));
- memcpy(&con->con_peer, &ss, sizeof(ss));
-
- /* Get associated switch, if it exists */
- sw = switch_get(con);
-
- log_info("%s: new connection %u from switch %u",
- __func__, con->con_id, sw == NULL ? 0 : sw->sw_id);
-
- bzero(&con->con_ev, sizeof(con->con_ev));
- event_set(&con->con_ev, con->con_fd, EV_READ, ofp_read, con);
- event_add(&con->con_ev, NULL);
-
- TAILQ_INSERT_TAIL(&conn_head, con, con_next);
-
- return;
- fail:
- if (s != -1)
- close(s);
- free(con);
+ ofrelay_close(con);
}