laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/35068?usp=email )
Change subject: WIP: osmo_io: SCTP support ......................................................................
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); }