summaryrefslogtreecommitdiff
path: root/usr.sbin/ldomd/ds.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ldomd/ds.c')
-rw-r--r--usr.sbin/ldomd/ds.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/usr.sbin/ldomd/ds.c b/usr.sbin/ldomd/ds.c
new file mode 100644
index 00000000000..c287bf66098
--- /dev/null
+++ b/usr.sbin/ldomd/ds.c
@@ -0,0 +1,725 @@
+/* $OpenBSD: ds.c,v 1.1 2012/10/27 18:34:03 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2012 Mark Kettenis
+ *
+ * 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 <sys/types.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ds.h"
+#include "util.h"
+
+void ldc_rx_ctrl_vers(struct ldc_conn *, struct ldc_pkt *);
+void ldc_rx_ctrl_rtr(struct ldc_conn *, struct ldc_pkt *);
+void ldc_rx_ctrl_rts(struct ldc_conn *, struct ldc_pkt *);
+void ldc_rx_ctrl_rdx(struct ldc_conn *, struct ldc_pkt *);
+
+void ldc_send_ack(struct ldc_conn *);
+void ldc_send_rtr(struct ldc_conn *);
+void ldc_send_rts(struct ldc_conn *);
+void ldc_send_rdx(struct ldc_conn *);
+
+void
+ldc_rx_ctrl(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ switch (lp->ctrl) {
+ case LDC_VERS:
+ ldc_rx_ctrl_vers(lc, lp);
+ break;
+
+ case LDC_RTS:
+ ldc_rx_ctrl_rts(lc, lp);
+ break;
+
+ case LDC_RTR:
+ ldc_rx_ctrl_rtr(lc, lp);
+ break;
+
+ case LDC_RDX:
+ ldc_rx_ctrl_rdx(lc, lp);
+ break;
+
+ default:
+ DPRINTF(("CTRL/0x%02x/0x%02x\n", lp->stype, lp->ctrl));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ldc_rx_ctrl_vers(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ struct ldc_pkt *lvp = (struct ldc_pkt *)lp;
+
+ switch (lp->stype) {
+ case LDC_INFO:
+ if (lc->lc_state == LDC_RCV_VERS) {
+ DPRINTF(("Spurious CTRL/INFO/VERS: state %d\n",
+ lc->lc_state));
+ return;
+ }
+ DPRINTF(("CTRL/INFO/VERS\n"));
+ if (lvp->major == LDC_VERSION_MAJOR &&
+ lvp->minor == LDC_VERSION_MINOR)
+ ldc_send_ack(lc);
+ else
+ /* XXX do nothing for now. */
+ ;
+ break;
+
+ case LDC_ACK:
+ if (lc->lc_state != LDC_SND_VERS) {
+ DPRINTF(("Spurious CTRL/ACK/VERS: state %d\n",
+ lc->lc_state));
+ ldc_reset(lc);
+ return;
+ }
+ DPRINTF(("CTRL/ACK/VERS\n"));
+ ldc_send_rts(lc);
+ break;
+
+ case LDC_NACK:
+ DPRINTF(("CTRL/NACK/VERS\n"));
+ ldc_reset(lc);
+ break;
+
+ default:
+ DPRINTF(("CTRL/0x%02x/VERS\n", lp->stype));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ldc_rx_ctrl_rts(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ switch (lp->stype) {
+ case LDC_INFO:
+ if (lc->lc_state != LDC_RCV_VERS) {
+ DPRINTF(("Spurious CTRL/INFO/RTS: state %d\n",
+ lc->lc_state));
+ ldc_reset(lc);
+ return;
+ }
+ DPRINTF(("CTRL/INFO/RTS\n"));
+ if (lp->env != LDC_MODE_RELIABLE) {
+ ldc_reset(lc);
+ return;
+ }
+ ldc_send_rtr(lc);
+ break;
+
+ case LDC_ACK:
+ DPRINTF(("CTRL/ACK/RTS\n"));
+ ldc_reset(lc);
+ break;
+
+ case LDC_NACK:
+ DPRINTF(("CTRL/NACK/RTS\n"));
+ ldc_reset(lc);
+ break;
+
+ default:
+ DPRINTF(("CTRL/0x%02x/RTS\n", lp->stype));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ldc_rx_ctrl_rtr(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ switch (lp->stype) {
+ case LDC_INFO:
+ if (lc->lc_state != LDC_SND_RTS) {
+ DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
+ lc->lc_state));
+ ldc_reset(lc);
+ return;
+ }
+ DPRINTF(("CTRL/INFO/RTR\n"));
+ if (lp->env != LDC_MODE_RELIABLE) {
+ ldc_reset(lc);
+ return;
+ }
+ ldc_send_rdx(lc);
+#if 0
+ lc->lc_start(lc);
+#endif
+ break;
+
+ case LDC_ACK:
+ DPRINTF(("CTRL/ACK/RTR\n"));
+ ldc_reset(lc);
+ break;
+
+ case LDC_NACK:
+ DPRINTF(("CTRL/NACK/RTR\n"));
+ ldc_reset(lc);
+ break;
+
+ default:
+ DPRINTF(("CTRL/0x%02x/RTR\n", lp->stype));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ldc_rx_ctrl_rdx(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ switch (lp->stype) {
+ case LDC_INFO:
+ if (lc->lc_state != LDC_SND_RTR) {
+ DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
+ lc->lc_state));
+ ldc_reset(lc);
+ return;
+ }
+ DPRINTF(("CTRL/INFO/RDX\n"));
+#if 0
+ lc->lc_start(lc);
+#endif
+ break;
+
+ case LDC_ACK:
+ DPRINTF(("CTRL/ACK/RDX\n"));
+ ldc_reset(lc);
+ break;
+
+ case LDC_NACK:
+ DPRINTF(("CTRL/NACK/RDX\n"));
+ ldc_reset(lc);
+ break;
+
+ default:
+ DPRINTF(("CTRL/0x%02x/RDX\n", lp->stype));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ldc_rx_data(struct ldc_conn *lc, struct ldc_pkt *lp)
+{
+ size_t len;
+
+ if (lp->stype != LDC_INFO && lp->stype != LDC_ACK) {
+ DPRINTF(("DATA/0x%02x\n", lp->stype));
+ ldc_reset(lc);
+ return;
+ }
+
+ if (lc->lc_state != LDC_SND_RTR &&
+ lc->lc_state != LDC_SND_RDX) {
+ DPRINTF(("Spurious DATA/INFO: state %d\n", lc->lc_state));
+ ldc_reset(lc);
+ return;
+ }
+
+#if 0
+ if (lp->ackid) {
+ int i;
+
+ for (i = 0; ds_service[i].ds_svc_id; i++) {
+ if (ds_service[i].ds_ackid &&
+ lp->ackid >= ds_service[i].ds_ackid) {
+ ds_service[i].ds_ackid = 0;
+ ds_service[i].ds_start(lc, ds_service[i].ds_svc_handle);
+ }
+ }
+ }
+#endif
+ if (lp->stype == LDC_ACK)
+ return;
+
+ if (lp->env & LDC_FRAG_START) {
+ lc->lc_len = (lp->env & LDC_LEN_MASK);
+ memcpy((uint8_t *)lc->lc_msg, &lp->data, lc->lc_len);
+ } else {
+ len = (lp->env & LDC_LEN_MASK);
+ if (lc->lc_len + len > sizeof(lc->lc_msg)) {
+ DPRINTF(("Buffer overrun\n"));
+ ldc_reset(lc);
+ return;
+ }
+ memcpy((uint8_t *)lc->lc_msg + lc->lc_len, &lp->data, len);
+ lc->lc_len += len;
+ }
+
+ if (lp->env & LDC_FRAG_STOP) {
+ ldc_ack(lc, lp->seqid);
+ lc->lc_rx_data(lc, lc->lc_msg, lc->lc_len);
+ }
+}
+
+void
+ldc_send_vers(struct ldc_conn *lc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_CTRL;
+ lp.stype = LDC_INFO;
+ lp.ctrl = LDC_VERS;
+ lp.major = 1;
+ lp.minor = 0;
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+
+ lc->lc_state = LDC_SND_VERS;
+}
+
+void
+ldc_send_ack(struct ldc_conn *lc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_CTRL;
+ lp.stype = LDC_ACK;
+ lp.ctrl = LDC_VERS;
+ lp.major = 1;
+ lp.minor = 0;
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+
+ lc->lc_state = LDC_RCV_VERS;
+}
+
+void
+ldc_send_rts(struct ldc_conn *lc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_CTRL;
+ lp.stype = LDC_INFO;
+ lp.ctrl = LDC_RTS;
+ lp.env = LDC_MODE_RELIABLE;
+ lp.seqid = lc->lc_tx_seqid++;
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+
+ lc->lc_state = LDC_SND_RTS;
+}
+
+void
+ldc_send_rtr(struct ldc_conn *lc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_CTRL;
+ lp.stype = LDC_INFO;
+ lp.ctrl = LDC_RTR;
+ lp.env = LDC_MODE_RELIABLE;
+ lp.seqid = lc->lc_tx_seqid++;
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+
+ lc->lc_state = LDC_SND_RTR;
+}
+
+void
+ldc_send_rdx(struct ldc_conn *lc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_CTRL;
+ lp.stype = LDC_INFO;
+ lp.ctrl = LDC_RDX;
+ lp.env = LDC_MODE_RELIABLE;
+ lp.seqid = lc->lc_tx_seqid++;
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+
+ lc->lc_state = LDC_SND_RDX;
+}
+
+void
+ldc_reset(struct ldc_conn *lc)
+{
+ lc->lc_tx_seqid = 0;
+ lc->lc_state = 0;
+#if 0
+ lc->lc_reset(lc);
+#endif
+}
+
+void
+ldc_ack(struct ldc_conn *lc, uint32_t ackid)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_DATA;
+ lp.stype = LDC_ACK;
+ lp.seqid = lc->lc_tx_seqid++;
+ lp.ackid = ackid;
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+}
+
+void
+ds_rx_msg(struct ldc_conn *lc, void *data, size_t len)
+{
+ struct ds_conn *dc = lc->lc_cookie;
+ struct ds_msg *dm = data;
+
+ switch(dm->msg_type) {
+ case DS_INIT_REQ:
+ {
+ struct ds_init_req *dr = data;
+
+ DPRINTF(("DS_INIT_REQ %d.%d\n", dr->major_vers,
+ dr->minor_vers));
+ if (dr->major_vers != 1 || dr->minor_vers != 0){
+ ldc_reset(lc);
+ return;
+ }
+ ds_init_ack(lc);
+ break;
+ }
+
+ case DS_REG_REQ:
+ {
+ struct ds_reg_req *dr = data;
+ struct ds_conn *dc = lc->lc_cookie;
+ struct ds_conn_svc *dcs;
+
+ DPRINTF(("DS_REG_REQ %s %d.%d 0x%016llx\n", dr->svc_id,
+ dr->major_vers, dr->minor_vers, dr->svc_handle));
+ TAILQ_FOREACH(dcs, &dc->services, link) {
+ if (strcmp(dr->svc_id, dcs->service->ds_svc_id) == 0) {
+ dcs->svc_handle = dr->svc_handle;
+ dcs->ackid = lc->lc_tx_seqid;
+ ds_reg_ack(lc, dr->svc_handle);
+ return;
+ }
+ }
+
+ ds_reg_nack(lc, dr->svc_handle);
+ break;
+ }
+
+ case DS_UNREG:
+ {
+ struct ds_unreg *du = data;
+
+ DPRINTF(("DS_UNREG 0x%016llx\n", du->svc_handle));
+ ds_unreg_ack(lc, du->svc_handle);
+ break;
+ }
+
+ case DS_DATA:
+ {
+ struct ds_data *dd = data;
+ struct ds_conn *dc = lc->lc_cookie;
+ struct ds_conn_svc *dcs;
+
+ DPRINTF(("DS_DATA 0x%016llx\n", dd->svc_handle));
+ TAILQ_FOREACH(dcs, &dc->services, link) {
+ if (dcs->svc_handle == dd->svc_handle)
+ dcs->service->ds_rx_data(lc, dd->svc_handle,
+ data, len);
+ }
+ break;
+ }
+
+ default:
+ DPRINTF(("Unknown DS message type 0x%x\n", dm->msg_type));
+ ldc_reset(lc);
+ break;
+ }
+}
+
+void
+ds_init_ack(struct ldc_conn *lc)
+{
+ struct ds_init_ack da;
+
+ DPRINTF((" DS_INIT_ACK\n"));
+ bzero(&da, sizeof(da));
+ da.msg_type = DS_INIT_ACK;
+ da.payload_len = sizeof(da) - 8;
+ da.minor_vers = 0;
+ ds_send_msg(lc, &da, sizeof(da));
+}
+
+void
+ds_reg_ack(struct ldc_conn *lc, uint64_t svc_handle)
+{
+ struct ds_reg_ack da;
+
+ DPRINTF((" DS_REG_ACK 0x%016llx\n", svc_handle));
+ bzero(&da, sizeof(da));
+ da.msg_type = DS_REG_ACK;
+ da.payload_len = sizeof(da) - 8;
+ da.svc_handle = svc_handle;
+ da.minor_vers = 0;
+ ds_send_msg(lc, &da, sizeof(da));
+}
+
+void
+ds_reg_nack(struct ldc_conn *lc, uint64_t svc_handle)
+{
+ struct ds_reg_nack dn;
+
+ DPRINTF((" DS_REG_NACK 0x%016llx\n", svc_handle));
+ bzero(&dn, sizeof(dn));
+ dn.msg_type = DS_REG_NACK;
+ dn.payload_len = sizeof(dn) - 8;
+ dn.svc_handle = svc_handle;
+ dn.result = DS_REG_VER_NACK;
+ dn.major_vers = 0;
+ ds_send_msg(lc, &dn, sizeof(dn));
+}
+
+void
+ds_unreg_ack(struct ldc_conn *lc, uint64_t svc_handle)
+{
+ struct ds_unreg du;
+
+ DPRINTF((" DS_UNREG_ACK 0x%016llx\n", svc_handle));
+ bzero(&du, sizeof(du));
+ du.msg_type = DS_UNREG_ACK;
+ du.payload_len = sizeof(du) - 8;
+ du.svc_handle = svc_handle;
+ ds_send_msg(lc, &du, sizeof(du));
+}
+
+void
+ds_unreg_nack(struct ldc_conn *lc, uint64_t svc_handle)
+{
+ struct ds_unreg du;
+
+ DPRINTF((" DS_UNREG_NACK 0x%016llx\n", svc_handle));
+ bzero(&du, sizeof(du));
+ du.msg_type = DS_UNREG_NACK;
+ du.payload_len = sizeof(du) - 8;
+ du.svc_handle = svc_handle;
+ ds_send_msg(lc, &du, sizeof(du));
+}
+
+void
+ds_receive_msg(struct ldc_conn *lc, void *buf, size_t len)
+{
+ int env = LDC_FRAG_START;
+ struct ldc_pkt lp;
+ uint8_t *p = buf;
+ ssize_t nbytes;
+
+ while (len > 0) {
+ nbytes = read(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "read");
+
+ if (lp.type != LDC_DATA &&
+ lp.stype != LDC_INFO) {
+ ldc_reset(lc);
+ return;
+ }
+
+ if ((lp.env & LDC_FRAG_START) != env) {
+ ldc_reset(lc);
+ return;
+ }
+
+ bcopy(&lp.data, p, (lp.env & LDC_LEN_MASK));
+ p += (lp.env & LDC_LEN_MASK);
+ len -= (lp.env & LDC_LEN_MASK);
+
+ if (lp.env & LDC_FRAG_STOP)
+ ldc_ack(lc, lp.seqid);
+
+ env = (lp.env & LDC_FRAG_STOP) ? LDC_FRAG_START : 0;
+ }
+}
+
+void
+ldc_send_msg(struct ldc_conn *lc, void *buf, size_t len)
+{
+ struct ldc_pkt lp;
+ uint8_t *p = buf;
+ ssize_t nbytes;
+
+ while (len > 0) {
+ bzero(&lp, sizeof(lp));
+ lp.type = LDC_DATA;
+ lp.stype = LDC_INFO;
+ lp.env = min(len, LDC_PKT_PAYLOAD);
+ if (p == buf)
+ lp.env |= LDC_FRAG_START;
+ if (len <= LDC_PKT_PAYLOAD)
+ lp.env |= LDC_FRAG_STOP;
+ lp.seqid = lc->lc_tx_seqid++;
+ bcopy(p, &lp.data, min(len, LDC_PKT_PAYLOAD));
+
+ nbytes = write(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "write");
+ p += min(len, LDC_PKT_PAYLOAD);
+ len -= min(len, LDC_PKT_PAYLOAD);
+ }
+}
+
+void
+ds_send_msg(struct ldc_conn *lc, void *buf, size_t len)
+{
+ uint8_t *p = buf;
+#if 0
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+#endif
+
+ while (len > 0) {
+ ldc_send_msg(lc, p, min(len, LDC_MSG_MAX));
+ p += min(len, LDC_MSG_MAX);
+ len -= min(len, LDC_MSG_MAX);
+
+#if 0
+ /* Consume ACK. */
+ nbytes = read(lc->lc_fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp))
+ err(1, "read");
+
+ {
+ uint64_t *msg = (uint64_t *)&lp;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ printf("%02x: %016llx\n", i, msg[i]);
+ }
+#endif
+ }
+}
+
+TAILQ_HEAD(ds_conn_head, ds_conn) ds_conns =
+ TAILQ_HEAD_INITIALIZER(ds_conns);
+int num_ds_conns;
+
+struct ds_conn *
+ds_conn_open(const char *path, void *cookie)
+{
+ struct ds_conn *dc;
+
+ dc = xmalloc(sizeof(*dc));
+ dc->path = xstrdup(path);
+ dc->cookie = cookie;
+
+ dc->fd = open(path, O_RDWR, 0);
+ if (dc->fd == -1)
+ err(1, "open");
+
+ memset(&dc->lc, 0, sizeof(dc->lc));
+ dc->lc.lc_fd = dc->fd;
+ dc->lc.lc_cookie = dc;
+ dc->lc.lc_rx_data = ds_rx_msg;
+
+ TAILQ_INIT(&dc->services);
+ TAILQ_INSERT_TAIL(&ds_conns, dc, link);
+ dc->id = num_ds_conns++;
+ return dc;
+}
+
+void
+ds_conn_register_service(struct ds_conn *dc, struct ds_service *ds)
+{
+ struct ds_conn_svc *dcs;
+
+ dcs = xzalloc(sizeof(*dcs));
+ dcs->service = ds;
+
+ TAILQ_INSERT_TAIL(&dc->services, dcs, link);
+}
+
+void
+ds_conn_handle(struct ds_conn *dc)
+{
+ struct ldc_pkt lp;
+ ssize_t nbytes;
+
+ nbytes = read(dc->fd, &lp, sizeof(lp));
+ if (nbytes != sizeof(lp)) {
+ ldc_reset(&dc->lc);
+ return;
+ }
+
+ switch (lp.type) {
+ case LDC_CTRL:
+ ldc_rx_ctrl(&dc->lc, &lp);
+ break;
+ case LDC_DATA:
+ ldc_rx_data(&dc->lc, &lp);
+ break;
+ default:
+ DPRINTF(("0x%02x/0x%02x/0x%02x\n", lp.type, lp.stype,
+ lp.ctrl));
+ ldc_reset(&dc->lc);
+ break;
+ }
+}
+
+void
+ds_conn_serve(void)
+{
+ struct ds_conn *dc;
+ struct pollfd *pfd;
+ int nfds;;
+
+ pfd = xmalloc(num_ds_conns * sizeof(*pfd));
+ TAILQ_FOREACH(dc, &ds_conns, link) {
+ pfd[dc->id].fd = dc->fd;
+ pfd[dc->id].events = POLLIN;
+ }
+
+ while (1) {
+ nfds = poll(pfd, num_ds_conns, -1);
+ if (nfds == -1 || nfds == 0)
+ errx(1, "poll");
+
+ TAILQ_FOREACH(dc, &ds_conns, link) {
+ if (pfd[dc->id].revents)
+ ds_conn_handle(dc);
+ }
+ }
+}