laforge has uploaded this change for review.
WIP: osmo_io: SCTP support
Add support osmo_io operations resembling sctp_send() and sctp_recvmsg()
that is provided by libsctp of lk-sctp.
Change-Id: I89eb519b22d21011d61a7855b2364bc3c295df82
Related: OS#5751
---
M include/osmocom/core/osmo_io.h
M src/core/osmo_io.c
M src/core/osmo_io_internal.h
M src/core/osmo_io_poll.c
M src/core/osmo_io_uring.c
5 files changed, 158 insertions(+), 7 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/68/35068/1
diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h
index 46e1a53..f144bef 100644
--- a/include/osmocom/core/osmo_io.h
+++ b/include/osmocom/core/osmo_io.h
@@ -14,6 +14,19 @@
#define LOGPIO(iofd, level, fmt, args...) \
LOGP(DLIO, level, "iofd(%s)" fmt, iofd->name, ## args)
+// FIXME: This must avoid clashing with the existing definitions in libosmo-netif!!!
+//
+/*! \brief Access SCTP flags from the msgb control buffer */
+#define OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION 0x80 /* sctp_recvmsg() flags=MSG_NOTIFICATION, msgb_data() contains "union sctp_notification*" */
+#define msgb_sctp_msg_flags(msg) (msg)->cb[2]
+
+/*! \brief Access the SCTP PPID from the msgb control buffer */
+#define msgb_sctp_ppid(msg) (msg)->cb[3]
+/*! \brief Access the SCTP Stream ID from the msgb control buffer */
+#define msgb_sctp_stream(msg) (msg)->cb[4]
+// END FIXME
+
+struct sctp_sndrcvinfo;
struct osmo_io_fd;
enum osmo_io_fd_mode {
@@ -66,6 +79,17 @@
struct msgb *msg,
const struct osmo_sockaddr *daddr);
};
+ /* mode OSMO_IO_FD_MODE_SCTP_RECVMSG_SENDMSG: */
+ struct {
+ /* call-back function emulating sctp_recvmsg */
+ int (*sctp_recvmsg_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *saddr,
+ struct sctp_sndrcvinfo *sinfo);
+
+ /* call-back function emulating sctp_send */
+ int (*sctp_send_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg);
+ };
};
};
diff --git a/src/core/osmo_io.c b/src/core/osmo_io.c
index c1a639a..5ff540f 100644
--- a/src/core/osmo_io.c
+++ b/src/core/osmo_io.c
@@ -329,6 +329,8 @@
* \param[in] hdr serialized msghdr containing state of completed I/O */
void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *hdr)
{
+ struct cmsghdr *cmsg = NULL;
+
talloc_steal(iofd->msgb_alloc.ctx, msg);
switch (iofd->mode) {
case OSMO_IO_FD_MODE_READ_WRITE:
@@ -338,7 +340,25 @@
iofd->io_ops.recvfrom_cb(iofd, rc, msg, &hdr->osa);
break;
case OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND:
- /* TODO Implement */
+ msgb_sctp_msg_flags(msg) = 0;
+ if (hdr->hdr.msg_flags & MSG_NOTIFICATION) {
+ msgb_sctp_msg_flags(msg) = OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION;
+ } else {
+ for (cmsg = CMSG_FIRSTHDR(&hdr->hdr); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&hdr->hdr, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) {
+ struct sctp_sndrcvinfo *sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ msgb_sctp_ppid(msg) = htonl(sinfo->sinfo_ppid);
+ msgb_sctp_stream(msg) = sinfo->sinfo_stream;
+ break;
+ }
+ }
+ if (rc > 0 && !cmsg)
+ printf("sctp_recvmsg without SNDRCV cmsg?!?\n");
+ }
+ iofd->io_ops.sctp_recvmsg_cb(iofd, rc, msg, &hdr->osa, cmsg ? (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg) : NULL);
+ break;
+ default:
OSMO_ASSERT(false);
break;
}
@@ -432,6 +452,63 @@
return 0;
}
+/*! Send a message through a connected SCTP socket, similar to sctp_sendmsg().
+ *
+ * Appends the message to the internal transmit queue.
+ * If the function returns success (0), it will take ownership of the msgb and
+ * internally call msgb_free() after the write request completes.
+ * In case of an error the msgb needs to be freed by the caller.
+ * \param[in] iofd file descriptor to write to
+ * \param[in] msg message buffer to send; uses msgb_sctp_ppid/msg_sctp_stream
+ * \param[in] sendmsg_flags Flags to pass to the send call
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_sctp_send_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags)
+{
+ int rc;
+ struct cmsghdr *cmsg;
+ struct sctp_sndrcvinfo *sinfo;
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND);
+ if (OSMO_UNLIKELY(!iofd->io_ops.sctp_send_cb)) {
+ LOGPIO(iofd, LOGL_ERROR, "sctp_send_cb not set, Rejecting msgb\n");
+ return -EINVAL;
+ }
+
+ struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SCTP_SEND, msg);
+ if (!msghdr)
+ return -ENOMEM;
+
+ msghdr->flags = sendmsg_flags;
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ /* put together sctp_sndrcvinfo, just like libsctp's sctp_sensmsg() */
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = sizeof(msghdr->cmsg);
+
+ cmsg = CMSG_FIRSTHDR(&msghdr->hdr);
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ msghdr->hdr.msg_controllen = cmsg->cmsg_len;
+ sinfo = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg);
+ sinfo->sinfo_ppid = msgb_sctp_ppid(msg);
+ sinfo->sinfo_stream = msgb_sctp_stream(msg);
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+
+}
+
/*! Allocate and setup a new iofd.
* \param[in] ctx the parent context from which to allocate
* \param[in] fd the underlying system file descriptor
@@ -452,6 +529,7 @@
switch (mode) {
case OSMO_IO_FD_MODE_READ_WRITE:
case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ case OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND:
break;
default:
return NULL;
@@ -697,6 +775,11 @@
osmo_iofd_ops.read_disable(iofd);
break;
case OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND:
+ if (iofd->io_ops.sctp_recvmsg_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
default:
OSMO_ASSERT(0);
}
@@ -707,7 +790,8 @@
* \param[in] iofd the file descriptor */
void osmo_iofd_notify_connected(struct osmo_io_fd *iofd)
{
- OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE ||
+ iofd->mode == OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND);
osmo_iofd_ops.write_enable(iofd);
}
diff --git a/src/core/osmo_io_internal.h b/src/core/osmo_io_internal.h
index 73a81e1..ccb3dfc 100644
--- a/src/core/osmo_io_internal.h
+++ b/src/core/osmo_io_internal.h
@@ -4,6 +4,7 @@
#include <unistd.h>
#include <stdbool.h>
+#include <netinet/sctp.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/linuxlist.h>
@@ -107,7 +108,8 @@
IOFD_ACT_WRITE,
IOFD_ACT_RECVFROM,
IOFD_ACT_SENDTO,
- // TODO: SCTP_*
+ IOFD_ACT_SCTP_RECVMSG,
+ IOFD_ACT_SCTP_SEND,
};
@@ -123,6 +125,8 @@
/*! io-vector we need to pass as argument to sendmsg/recvmsg; is set up
* to point into msg below */
struct iovec iov[1];
+ /*! control message buffer for passing sctp_sndrcvinfo along */
+ char cmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
/*! flags we pass as argument to sendmsg / recvmsg */
int flags;
diff --git a/src/core/osmo_io_poll.c b/src/core/osmo_io_poll.c
index 2c1e422..0d10033 100644
--- a/src/core/osmo_io_poll.c
+++ b/src/core/osmo_io_poll.c
@@ -64,6 +64,10 @@
.msg_name = &hdr.osa.u.sa,
.msg_namelen = sizeof(struct osmo_sockaddr),
};
+ if (iofd->mode == OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND) {
+ hdr.hdr.msg_control = hdr.cmsg;
+ hdr.hdr.msg_controllen = sizeof(hdr.cmsg);
+ }
rc = recvmsg(ofd->fd, &hdr.hdr, flags);
if (rc > 0)
@@ -99,7 +103,7 @@
iofd->io_ops.sendto_cb(iofd, rc, msg, &msghdr->osa);
break;
case OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND:
- OSMO_ASSERT(false);
+ iofd->io_ops.sctp_send_cb(iofd, rc, msg);
break;
}
diff --git a/src/core/osmo_io_uring.c b/src/core/osmo_io_uring.c
index c96d430..1fc11de 100644
--- a/src/core/osmo_io_uring.c
+++ b/src/core/osmo_io_uring.c
@@ -3,6 +3,7 @@
*
* (C) 2022-2023 by sysmocom s.f.m.c.
* Author: Daniel Willmann <daniel@sysmocom.de>
+ * (C) 2023 by Harald Welte <laforge@osmocom.org>
*
* All Rights Reserved.
*
@@ -35,6 +36,8 @@
#include <stdbool.h>
#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
#include <sys/eventfd.h>
#include <liburing.h>
@@ -126,6 +129,10 @@
switch (action) {
case IOFD_ACT_READ:
break;
+ case IOFD_ACT_SCTP_RECVMSG:
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = sizeof(msghdr->cmsg);
+ /* fall-through */
case IOFD_ACT_RECVFROM:
msghdr->hdr.msg_iov = &msghdr->iov[0];
msghdr->hdr.msg_iovlen = 1;
@@ -146,6 +153,7 @@
case IOFD_ACT_READ:
io_uring_prep_readv(sqe, iofd->fd, msghdr->iov, 1, 0);
break;
+ case IOFD_ACT_SCTP_RECVMSG:
case IOFD_ACT_RECVFROM:
io_uring_prep_recvmsg(sqe, iofd->fd, &msghdr->hdr, msghdr->flags);
break;
@@ -211,12 +219,20 @@
goto out;
}
- if (msghdr->action == IOFD_ACT_WRITE)
+ switch (msghdr->action) {
+ case IOFD_ACT_WRITE:
iofd->io_ops.write_cb(iofd, rc, msg);
- else if (msghdr->action == IOFD_ACT_SENDTO)
+ break;
+ case IOFD_ACT_SENDTO:
iofd->io_ops.sendto_cb(iofd, rc, msg, &msghdr->osa);
- else
+ break;
+ case IOFD_ACT_SCTP_SEND:
+ iofd->io_ops.sctp_send_cb(iofd, rc, msg);
+ break;
+ default:
OSMO_ASSERT(0);
+ break;
+ }
out_free:
msgb_free(msghdr->msg);
@@ -239,10 +255,12 @@
switch (msghdr->action) {
case IOFD_ACT_READ:
case IOFD_ACT_RECVFROM:
+ case IOFD_ACT_SCTP_RECVMSG:
iofd_uring_handle_recv(msghdr, res);
break;
case IOFD_ACT_WRITE:
case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SCTP_SEND:
iofd_uring_handle_tx(msghdr, res);
break;
default:
@@ -302,6 +320,7 @@
switch (msghdr->action) {
case IOFD_ACT_WRITE:
case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SCTP_SEND:
io_uring_prep_sendmsg(sqe, msghdr->iofd->fd, &msghdr->hdr, msghdr->flags);
break;
default:
@@ -405,6 +424,9 @@
case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
iofd_uring_submit_recv(iofd, IOFD_ACT_RECVFROM);
break;
+ case OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_SCTP_RECVMSG);
+ break;
default:
OSMO_ASSERT(0);
}
To view, visit change 35068. To unsubscribe, or for help writing mail filters, visit settings.