aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/osmocom/netif/Makefile.am9
-rw-r--r--include/osmocom/netif/amr.h118
-rw-r--r--include/osmocom/netif/datagram.h13
-rw-r--r--include/osmocom/netif/ipa.h27
-rw-r--r--include/osmocom/netif/osmux.h45
-rw-r--r--include/osmocom/netif/prim.h66
-rw-r--r--include/osmocom/netif/rtp.h4
-rw-r--r--include/osmocom/netif/sctp.h60
-rw-r--r--include/osmocom/netif/stream.h178
-rw-r--r--include/osmocom/netif/stream_private.h42
10 files changed, 514 insertions, 48 deletions
diff --git a/include/osmocom/netif/Makefile.am b/include/osmocom/netif/Makefile.am
index 39df08a..3d8a56b 100644
--- a/include/osmocom/netif/Makefile.am
+++ b/include/osmocom/netif/Makefile.am
@@ -1,11 +1,20 @@
+noinst_HEADERS = \
+ stream_private.h \
+ $(NULL)
+
osmonetif_HEADERS = amr.h \
datagram.h \
jibuf.h \
osmux.h \
ipa.h \
ipa_unit.h \
+ prim.h \
rs232.h \
rtp.h \
stream.h
+if ENABLE_LIBSCTP
+osmonetif_HEADERS += sctp.h
+endif
+
osmonetifdir = $(includedir)/osmocom/netif
diff --git a/include/osmocom/netif/amr.h b/include/osmocom/netif/amr.h
index 6e37c99..a4d3da2 100644
--- a/include/osmocom/netif/amr.h
+++ b/include/osmocom/netif/amr.h
@@ -1,6 +1,10 @@
#ifndef _OSMO_AMR_H_
#define _OSMO_AMR_H_
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
#include <osmocom/core/endian.h>
/* As defined by RFC3267: Adaptive Multi-Rate (AMR) */
@@ -12,6 +16,37 @@
*/
/*
+ * 4.3. Bandwidth-Efficient Mode:
+ *
+ * Summary from 4.3.4: Same as Octet aligned (see below) but without padding after header and ToC:
+ * 0 1
+ * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | CMR |F| FT |Q|X X X X X X|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * X means AMR payload (padding in case of FT=NO_DATA).
+ */
+struct amr_hdr_bwe {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t ft_hi:3, /* coding mode highest part */
+ f:1,
+ cmr:4; /* Codec Mode Request */
+ uint8_t data_start:6,
+ q:1, /* OK (not damaged) at origin? */
+ ft_lo:1; /* coding mode lowest bit */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t cmr:4, f:1, ft_hi:3;
+ uint8_t ft_lo:1, q:1, data_start:6;
+#endif
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* See diagram above: CMR (4) + F (1) + FT (4) + Q (1) = 10 */
+#define AMR_HDR_BWE_LEN_BITS 10
+
+/*
* 4.4. Octet-aligned Mode:
*
* 4.4.1. The Payload Header:
@@ -53,10 +88,11 @@ struct amr_hdr {
ft:4, /* coding mode */
f:1; /* followed by another speech frame? */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t cmr:4, pad1:4;
uint8_t f:1, ft:4, q:1, pad2:2;
#endif
+ uint8_t data[0];
} __attribute__((packed));
static inline void *osmo_amr_get_payload(struct amr_hdr *amrh)
@@ -67,38 +103,72 @@ static inline void *osmo_amr_get_payload(struct amr_hdr *amrh)
/* AMR voice frame type identifiers
* See also 3GPP TS 26.101, Table 1a: Interpretation of Frame Type, Mode
* Indication and Mode Request fields */
-#define AMR_FT_0 0 /* 4.75 */
-#define AMR_FT_1 1 /* 5.15 */
-#define AMR_FT_2 2 /* 5.90 */
-#define AMR_FT_3 3 /* 6.70 */
-#define AMR_FT_4 4 /* 7.40 */
-#define AMR_FT_5 5 /* 7.95 */
-#define AMR_FT_6 6 /* 10.2 */
-#define AMR_FT_7 7 /* 12.2 */
-#define AMR_FT_SID 8 /* SID */
-#define AMR_FT_MAX 9
+#define AMR_FT_0 0 /* 4.75 */
+#define AMR_FT_1 1 /* 5.15 */
+#define AMR_FT_2 2 /* 5.90 */
+#define AMR_FT_3 3 /* 6.70 */
+#define AMR_FT_4 4 /* 7.40 */
+#define AMR_FT_5 5 /* 7.95 */
+#define AMR_FT_6 6 /* 10.2 */
+#define AMR_FT_7 7 /* 12.2 */
+#define AMR_FT_SID 8 /* AMR SID */
+#define AMR_FT_GSM_EFR_SID 9 /* GSM-EFR SID */
+#define AMR_FT_TDMA_EFR_SID 10 /* TDMA-EFR SID */
+#define AMR_FT_PDC_EFR_SID 11 /* PDC-EFR SID */
+/* version 16.0.0 Release 16: 12-14 for future use */
+#define AMR_FT_NO_DATA 15 /* NO_DATA */
+#define AMR_FT_MAX 16 /* INTERNAL, NO NOT USE OUTSIDE libosmo-netif */
-/* AMR voice frame length (in bytes, rounded),
- * See also RFC 3267, chapter 3.6 */
-#define AMR_FT_0_LEN 12 /* 4.75 */
-#define AMR_FT_1_LEN 13 /* 5.15 */
-#define AMR_FT_2_LEN 15 /* 5.90 */
-#define AMR_FT_3_LEN 17 /* 6.70 */
-#define AMR_FT_4_LEN 19 /* 7.40 */
-#define AMR_FT_5_LEN 20 /* 7.95 */
-#define AMR_FT_6_LEN 26 /* 10.2 */
-#define AMR_FT_7_LEN 31 /* 12.2 */
-#define AMR_FT_SID_LEN 5 /* SID */
+/* AMR voice frame length (in bits).
+ * See also RFC 3267, chapter 3.6.
+ *
+ * NOTE: These constants refer to the length of one AMR speech frame-block,
+ * not counting CMR, TOC. */
+#define AMR_FT_0_LEN_BITS 95 /* 4.75 */
+#define AMR_FT_1_LEN_BITS 103 /* 5.15 */
+#define AMR_FT_2_LEN_BITS 118 /* 5.90 */
+#define AMR_FT_3_LEN_BITS 134 /* 6.70 */
+#define AMR_FT_4_LEN_BITS 148 /* 7.40 */
+#define AMR_FT_5_LEN_BITS 159 /* 7.95 */
+#define AMR_FT_6_LEN_BITS 204 /* 10.2 */
+#define AMR_FT_7_LEN_BITS 244 /* 12.2 */
+#define AMR_FT_SID_LEN_BITS 39 /* SID */
+#define AMR_FT_GSM_EFR_SID_LEN_BITS 43 /* GSM-EFR SID */
+#define AMR_FT_TDMA_EFR_SID_LEN_BITS 38 /* TDMA-EFR SID */
+#define AMR_FT_PDC_EFR_SID_LEN_BITS 37 /* PDC-EFR SID */
+/* version 16.0.0 Release 16: 12-14 for future use */
+#define AMR_FT_NO_DATA_LEN_BITS 0 /* NO_DATA */
-/* NOTE: the above constant refers to the length of one AMR speech frame-block,
+/* AMR voice frame length (in bytes, rounded).
+ *
+ * NOTE: These constants refer to the length of one AMR speech frame-block,
* not counting CMR, TOC. */
+#define AMR_FT_0_LEN ((AMR_FT_0_LEN_BITS+7)/8) /* 4.75 */
+#define AMR_FT_1_LEN ((AMR_FT_1_LEN_BITS+7)/8) /* 5.15 */
+#define AMR_FT_2_LEN ((AMR_FT_2_LEN_BITS+7)/8) /* 5.90 */
+#define AMR_FT_3_LEN ((AMR_FT_3_LEN_BITS+7)/8) /* 6.70 */
+#define AMR_FT_4_LEN ((AMR_FT_4_LEN_BITS+7)/8) /* 7.40 */
+#define AMR_FT_5_LEN ((AMR_FT_5_LEN_BITS+7)/8) /* 7.95 */
+#define AMR_FT_6_LEN ((AMR_FT_6_LEN_BITS+7)/8) /* 10.2 */
+#define AMR_FT_7_LEN ((AMR_FT_7_LEN_BITS+7)/8) /* 12.2 */
+#define AMR_FT_SID_LEN ((AMR_FT_SID_LEN_BITS+7)/8) /* SID */
+#define AMR_FT_GSM_EFR_SID_LEN ((AMR_FT_GSM_EFR_SID_LEN_BITS+7)/8) /* GSM-EFR SID */
+#define AMR_FT_TDMA_EFR_SID_LEN ((AMR_FT_TDMA_EFR_SID_LEN_BITS+7)/8) /* TDMA-EFR SID */
+#define AMR_FT_PDC_EFR_SID_LEN ((AMR_FT_PDC_EFR_SID_LEN_BITS+7)/8) /* PDC-EFR SID */
+/* version 16.0.0 Release 16: 12-14 for future use */
+#define AMR_FT_NO_DATA_LEN ((AMR_FT_NO_DATA_LEN_BITS+7)/8) /* NO_DATA */
int osmo_amr_ft_valid(uint8_t amr_ft);
size_t osmo_amr_bytes(uint8_t amr_cmr);
+size_t osmo_amr_bits(uint8_t amr_ft);
-bool osmo_amr_is_oa(uint8_t *payload, unsigned int payload_len);
+bool osmo_amr_is_oa(const uint8_t *payload, unsigned int payload_len);
int osmo_amr_oa_to_bwe(uint8_t *payload, unsigned int payload_len);
int osmo_amr_bwe_to_oa(uint8_t *payload, unsigned int payload_len,
unsigned int payload_maxlen);
+int osmo_amr_bwe_to_iuup(uint8_t *payload, unsigned int payload_len);
+int osmo_amr_iuup_to_bwe(uint8_t *payload, unsigned int payload_len,
+ unsigned int payload_maxlen);
+int osmo_amr_bytes_to_ft(size_t bytes);
#endif
diff --git a/include/osmocom/netif/datagram.h b/include/osmocom/netif/datagram.h
index a0ff7b4..a9bc588 100644
--- a/include/osmocom/netif/datagram.h
+++ b/include/osmocom/netif/datagram.h
@@ -1,6 +1,19 @@
#ifndef _OSMO_DGRAM_H_
#define _OSMO_DGRAM_H_
+/*! \defgroup datagram Osmocom Datagram Socket
+ * @{
+ *
+ * This code is intended to abstract any use of datagram type sockets,
+ * such as UDP. It offers both transmitter and receiver side
+ * implementations, fully integrated with the libosmocore select loop
+ * abstraction.
+ */
+
+/*! \file datagram.h
+ * \brief Osmocom datagram socket helpers
+ */
+
struct osmo_dgram_tx;
struct osmo_dgram_tx *osmo_dgram_tx_create(void *ctx);
diff --git a/include/osmocom/netif/ipa.h b/include/osmocom/netif/ipa.h
index 0d1085f..038b9ac 100644
--- a/include/osmocom/netif/ipa.h
+++ b/include/osmocom/netif/ipa.h
@@ -18,7 +18,30 @@ struct ipa_head_ext {
uint8_t data[0];
} __attribute__ ((packed));
+struct osmo_ipa_msgb_cb {
+ uint8_t proto;
+ uint8_t proto_ext;
+} __attribute__ ((packed));
+
+
+/* We don't just cast to 'struct osmo_ipa_msgb_cb *', because that would
+ * break the strict aliasing rule. Casting to a reference to a union with
+ * a compatible struct member seems to be allowed, though, see:
+ * N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x,
+ * Section 6.5, §7 */
+#define OSMO_IPA_MSGB_CB(__msg) (&((( \
+ union { \
+ unsigned long cb; \
+ struct osmo_ipa_msgb_cb _cb; \
+ } \
+ *)&((__msg)->cb[0]))->_cb))
+
+#define osmo_ipa_msgb_cb_proto(__x) OSMO_IPA_MSGB_CB(__x)->proto
+#define osmo_ipa_msgb_cb_proto_ext(__x) OSMO_IPA_MSGB_CB(__x)->proto_ext
+
struct msgb *osmo_ipa_msg_alloc(int headroom);
+struct msgb *osmo_ipa_ext_msg_alloc(size_t headroom);
+
void osmo_ipa_msg_push_header(struct msgb *msg, uint8_t proto);
int osmo_ipa_process_msg(struct msgb *msg);
@@ -40,4 +63,8 @@ struct msgb *ipa_cli_id_ack(void);
int osmo_ipa_parse_msg_id_resp(struct msgb *msg, struct ipaccess_unit *unit_data);
+int osmo_ipa_segmentation_cb(struct msgb *msg);
+
+void osmo_ipa_msg_push_headers(struct msgb *msg, enum ipaccess_proto p, enum ipaccess_proto_ext pe);
+
#endif
diff --git a/include/osmocom/netif/osmux.h b/include/osmocom/netif/osmux.h
index 6305dc8..8a8bf88 100644
--- a/include/osmocom/netif/osmux.h
+++ b/include/osmocom/netif/osmux.h
@@ -3,6 +3,7 @@
#include <osmocom/core/endian.h>
#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
/*! \addtogroup osmux
* @{
@@ -12,7 +13,10 @@
* Osmocom multiplex protocol helpers
*/
-/* OSmux header:
+#define OSMUX_DEFAULT_PORT 1984
+
+/*! \struct osmux_hdr
+ * OSmux header:
*
* rtp_m (1 bit): RTP M field (RFC3550, RFC4867)
* ft (2 bits): 0=signalling, 1=voice, 2=dummy
@@ -29,6 +33,7 @@
#define OSMUX_FT_VOICE_AMR 1
#define OSMUX_FT_DUMMY 2
+/*! Osmux protocol header */
struct osmux_hdr {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t amr_q:1,
@@ -37,7 +42,7 @@ struct osmux_hdr {
ft:2,
rtp_m:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t rtp_m:1, ft:2, ctr:3, amr_f:1, amr_q:1;
#endif
uint8_t seq;
@@ -47,13 +52,15 @@ struct osmux_hdr {
uint8_t amr_cmr:4,
amr_ft:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t amr_ft:4, amr_cmr:4;
#endif
+ uint8_t data[0];
} __attribute__((packed));
-/* one to handle all existing RTP flows */
+/*! one to handle all existing RTP flows */
struct osmux_in_handle {
+ /*! Initial Osmux seqnum for each circuit, set during osmux_xfrm_input_open_circuit() */
uint8_t osmux_seq;
uint8_t batch_factor;
uint16_t batch_size;
@@ -72,7 +79,9 @@ struct osmux_in_handle {
#define OSMUX_MAX_CONCURRENT_CALLS 8
-/* one per OSmux circuit_id, ie. one per RTP flow. */
+typedef struct msgb *(*rtp_msgb_alloc_cb_t)(void *rtp_msgb_alloc_priv_data,
+ unsigned int msg_len);
+/*! one per OSmux circuit_id, ie. one per RTP flow. */
struct osmux_out_handle {
uint16_t rtp_seq;
uint32_t rtp_timestamp;
@@ -83,8 +92,11 @@ struct osmux_out_handle {
struct llist_head list;
void (*tx_cb)(struct msgb *msg, void *data); /* Used defined rtp tx callback */
void *data; /* User defined opaque data structure */
+ rtp_msgb_alloc_cb_t rtp_msgb_alloc_cb; /* User defined msgb alloc function for generated RTP pkts */
+ void *rtp_msgb_alloc_cb_data; /* Opaque data pointer set by user and passed in rtp_msgb_alloc_cb() */
};
+/*! return pointer to osmux payload (behind osmux_hdr) */
static inline uint8_t *osmux_get_payload(struct osmux_hdr *osmuxh)
{
return (uint8_t *)osmuxh + sizeof(struct osmux_hdr);
@@ -95,8 +107,15 @@ int osmux_snprintf(char *buf, size_t size, struct msgb *msg);
/* 1500 - sizeof(iphdr) = 20 bytes - sizeof(udphdr) = 8 bytes. */
#define OSMUX_BATCH_DEFAULT_MAX 1472
-void osmux_xfrm_input_init(struct osmux_in_handle *h);
-void osmux_xfrm_input_fini(struct osmux_in_handle *h);
+struct osmux_in_handle *osmux_xfrm_input_alloc(void *ctx);
+void osmux_xfrm_input_init(struct osmux_in_handle *h) OSMO_DEPRECATED("Use osmux_xfrm_input_alloc() instead");
+void osmux_xfrm_input_fini(struct osmux_in_handle *h) OSMO_DEPRECATED("Use talloc_free() instead");
+void osmux_xfrm_input_set_name(struct osmux_in_handle *h, const char *name);
+int osmux_xfrm_input_set_batch_factor(struct osmux_in_handle *h, uint8_t batch_factor);
+void osmux_xfrm_input_set_batch_size(struct osmux_in_handle *h, uint16_t batch_size);
+void osmux_xfrm_input_set_initial_seqnum(struct osmux_in_handle *h, uint8_t osmux_seqnum);
+void osmux_xfrm_input_set_deliver_cb(struct osmux_in_handle *h, void (*deliver_cb)(struct msgb *msg, void *data), void *data);
+void *osmux_xfrm_input_get_deliver_cb_data(struct osmux_in_handle *h);
int osmux_xfrm_input_open_circuit(struct osmux_in_handle *h, int ccid, int dummy);
void osmux_xfrm_input_close_circuit(struct osmux_in_handle *h, int ccid);
@@ -104,16 +123,16 @@ void osmux_xfrm_input_close_circuit(struct osmux_in_handle *h, int ccid);
int osmux_xfrm_input(struct osmux_in_handle *h, struct msgb *msg, int ccid);
void osmux_xfrm_input_deliver(struct osmux_in_handle *h);
-void osmux_xfrm_output_init(struct osmux_out_handle *h, uint32_t rtp_ssrc) OSMO_DEPRECATED("Use osmux_xfrm_output_init2() instead");
-void osmux_xfrm_output_init2(struct osmux_out_handle *h, uint32_t rtp_ssrc, uint8_t rtp_payload_type);
+struct osmux_out_handle *osmux_xfrm_output_alloc(void *ctx);
+void osmux_xfrm_output_init(struct osmux_out_handle *h, uint32_t rtp_ssrc) OSMO_DEPRECATED("Use osmux_xfrm_output_alloc() and osmux_xfrm_output_set_rtp_*() instead");
+void osmux_xfrm_output_init2(struct osmux_out_handle *h, uint32_t rtp_ssrc, uint8_t rtp_payload_type) OSMO_DEPRECATED("Use osmux_xfrm_output_alloc() and osmux_xfrm_output_set_rtp_*() instead");
+void osmux_xfrm_output_set_rtp_ssrc(struct osmux_out_handle *h, uint32_t rtp_ssrc);
+void osmux_xfrm_output_set_rtp_pl_type(struct osmux_out_handle *h, uint32_t rtp_payload_type);
void osmux_xfrm_output_set_tx_cb(struct osmux_out_handle *h, void (*tx_cb)(struct msgb *msg, void *data), void *data);
-int osmux_xfrm_output(struct osmux_hdr *osmuxh, struct osmux_out_handle *h, struct llist_head *list) OSMO_DEPRECATED("Use osmux_xfrm_output_sched() instead");
+void osmux_xfrm_output_set_rtp_msgb_alloc_cb(struct osmux_out_handle *h, rtp_msgb_alloc_cb_t cb, void *cb_data);
int osmux_xfrm_output_sched(struct osmux_out_handle *h, struct osmux_hdr *osmuxh);
void osmux_xfrm_output_flush(struct osmux_out_handle *h);
struct osmux_hdr *osmux_xfrm_output_pull(struct msgb *msg);
-
-void osmux_tx_sched(struct llist_head *list, void (*tx_cb)(struct msgb *msg, void *data), void *data) OSMO_DEPRECATED("Use osmux_xfrm_output_set_tx_cb() instead");
-
/*! @} */
#endif
diff --git a/include/osmocom/netif/prim.h b/include/osmocom/netif/prim.h
new file mode 100644
index 0000000..d26e220
--- /dev/null
+++ b/include/osmocom/netif/prim.h
@@ -0,0 +1,66 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/prim.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+
+struct osmo_prim_srv_link;
+struct osmo_prim_srv;
+
+typedef int (*osmo_prim_srv_conn_cb)(struct osmo_prim_srv *prim_srv);
+/*! oph and related msgb is owned by srv and wll be freed after the callback returns. */
+typedef int (*osmo_prim_srv_rx_cb)(struct osmo_prim_srv *prim_srv, struct osmo_prim_hdr *oph);
+
+/*! Return value:
+ * RET=rem_version: Accept the version
+ * RET!=rem_version && RET > 0: Reject the requested version but propose another candidate version
+ * In this case, the client can decide whether to request another VER
+ * or close the connection.
+ * RET<0: Reject the proposed version and close the connection.
+ */
+typedef int (*osmo_prim_srv_rx_sapi_version)(struct osmo_prim_srv *prim_srv, uint32_t sapi, uint16_t rem_version);
+
+struct osmo_prim_hdr *osmo_prim_msgb_alloc(unsigned int sap, unsigned int primitive,
+ enum osmo_prim_operation operation, size_t alloc_len);
+
+struct osmo_prim_srv_link *osmo_prim_srv_link_alloc(void *ctx);
+void osmo_prim_srv_link_free(struct osmo_prim_srv_link *prim_link);
+void osmo_prim_srv_link_set_name(struct osmo_prim_srv_link *prim_link, const char *name);
+int osmo_prim_srv_link_set_addr(struct osmo_prim_srv_link *prim_link, const char *path);
+const char *osmo_prim_srv_link_get_addr(struct osmo_prim_srv_link *prim_link);
+void osmo_prim_srv_link_set_priv(struct osmo_prim_srv_link *prim_link, void *priv);
+void *osmo_prim_srv_link_get_priv(const struct osmo_prim_srv_link *prim_link);
+void osmo_prim_srv_link_set_log_category(struct osmo_prim_srv_link *prim_link, int log_cat);
+void osmo_prim_srv_link_set_opened_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb opened_conn_cb);
+void osmo_prim_srv_link_set_closed_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb closed_conn_cb);
+void osmo_prim_srv_link_set_rx_sapi_version_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_rx_sapi_version rx_sapi_version_cb);
+void osmo_prim_srv_link_set_rx_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_rx_cb rx_cb);
+void osmo_prim_srv_link_set_rx_msgb_alloc_len(struct osmo_prim_srv_link *prim_link, size_t alloc_len);
+int osmo_prim_srv_link_open(struct osmo_prim_srv_link *prim_link);
+
+int osmo_prim_srv_send(struct osmo_prim_srv *prim_srv, struct msgb *msg);
+struct osmo_prim_srv_link *osmo_prim_srv_get_link(struct osmo_prim_srv *prims_srv);
+void osmo_prim_srv_set_name(struct osmo_prim_srv *prim_srv, const char *name);
+void osmo_prim_srv_set_priv(struct osmo_prim_srv *prim_srv, void *priv);
+void *osmo_prim_srv_get_priv(const struct osmo_prim_srv *prim_srv);
+void osmo_prim_srv_close(struct osmo_prim_srv *prim_srv);
diff --git a/include/osmocom/netif/rtp.h b/include/osmocom/netif/rtp.h
index 4e40745..7f5ecae 100644
--- a/include/osmocom/netif/rtp.h
+++ b/include/osmocom/netif/rtp.h
@@ -13,7 +13,7 @@ struct rtp_hdr {
uint8_t payload_type:7,
marker:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t version:2, padding:1, extension:1, csrc_count:4;
uint8_t marker:1, payload_type:7;
#endif
@@ -82,4 +82,6 @@ int osmo_rtp_snprintf(char *buf, size_t size, struct msgb *msg);
#define RTP_PT_AMR 98
+#define RTP_PT_CSDATA 120 /* 3GPP TS 48.103 table 5.4.2.2.1 */
+
#endif
diff --git a/include/osmocom/netif/sctp.h b/include/osmocom/netif/sctp.h
new file mode 100644
index 0000000..9dcb6dd
--- /dev/null
+++ b/include/osmocom/netif/sctp.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <osmocom/core/utils.h>
+
+/* Relevant SCTP RFCs:
+ * rfc9260 (obsoletes rfc4960): SCTP protocol
+ * rfc5061: SCTP Dynamic Address Reconfiguration
+ * rfc6458: SCTP Sockets API Extensions
+ */
+
+enum sctp_sac_state;
+extern const struct value_string osmo_sctp_assoc_chg_strs[];
+static inline const char *osmo_sctp_assoc_chg_str(enum sctp_sac_state val)
+{ return get_value_string(osmo_sctp_assoc_chg_strs, val); }
+
+enum sctp_spc_state;
+extern const struct value_string osmo_sctp_paddr_chg_strs[];
+static inline const char *osmo_sctp_paddr_chg_str(enum sctp_spc_state val)
+{ return get_value_string(osmo_sctp_paddr_chg_strs, val); }
+
+enum sctp_sn_type;
+extern const struct value_string osmo_sctp_sn_type_strs[];
+static inline const char *osmo_sctp_sn_type_str(enum sctp_sn_type val)
+{ return get_value_string(osmo_sctp_sn_type_strs, val); }
+
+enum sctp_sn_error;
+extern const struct value_string osmo_sctp_sn_error_strs[];
+static inline const char *osmo_sctp_sn_error_str(enum sctp_sn_error val)
+{ return get_value_string(osmo_sctp_sn_error_strs, val); }
+
+enum osmo_sctp_op_error {
+ OSMO_SCTP_OP_ERR_INVALID_STREAM_ID = 1,
+ OSMO_SCTP_OP_ERR_MISS_MAND_PARAM = 2,
+ OSMO_SCTP_OP_ERR_STALE_COOKIE = 3,
+ OSMO_SCTP_OP_ERR_NO_RESOURCES = 4,
+ OSMO_SCTP_OP_ERR_UNRESOLV_ADDR = 5,
+ OSMO_SCTP_OP_ERR_UNKN_CHUNK_TYPE = 6,
+ OSMO_SCTP_OP_ERR_INVALID_MAND_PARAM = 7,
+ OSMO_SCTP_OP_ERR_UNKN_PARAM = 8,
+ OSMO_SCTP_OP_ERR_NO_USER_DATA = 9,
+ OSMO_SCTP_OP_ERR_COOKIE_RX_WHILE_SHUTDOWN = 10,
+ OSMO_SCTP_OP_ERR_RESTART_ASSC_NEW_ADDR = 11,
+ OSMO_SCTP_OP_ERR_UNER_INITED_ABORT = 12,
+ OSMO_SCTP_OP_ERR_PROTO_VERSION = 13,
+};
+
+
+extern const struct value_string osmo_sctp_op_error_strs[];
+static inline const char *osmo_sctp_op_error_str(enum osmo_sctp_op_error val)
+{ return get_value_string(osmo_sctp_op_error_strs, val); }
+
+enum sctp_spinfo_state;
+extern const struct value_string osmo_sctp_spinfo_state_strs[];
+static inline const char *osmo_sctp_spinfo_state_str(enum sctp_spinfo_state val)
+{ return get_value_string(osmo_sctp_spinfo_state_strs, val); }
+
+enum sctp_sstat_state;
+extern const struct value_string osmo_sctp_sstat_state_strs[];
+static inline const char *osmo_sctp_sstat_state_str(enum sctp_sstat_state val)
+{ return get_value_string(osmo_sctp_sstat_state_strs, val); }
diff --git a/include/osmocom/netif/stream.h b/include/osmocom/netif/stream.h
index 331dec1..3c4ec7e 100644
--- a/include/osmocom/netif/stream.h
+++ b/include/osmocom/netif/stream.h
@@ -2,61 +2,201 @@
#include <stdbool.h>
#include <stdint.h>
+#include <unistd.h>
#include <osmocom/core/msgb.h>
-/*! \addtogroup stream
- * @{
- */
+/*! \file stream.h */
+
+/*! \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]
+/*! \defgroup stream_srv Osmocom Stream Server
+ * @{
+ *
+ * This code is intended to abstract any server-side use of stream-type sockets, such as TCP and SCTP.
+ *
+ * The Osmocom stream socket helper is an abstraction layer for connected SOCK_STREAM/SOCK_SEQPACKET sockets.
+ * It encapsulates common functionality like binding, accepting client connections, etc.
+ *
+ * osmo_stream_srv can operate in two different modes:
+ * 1. The legacy mode using osmo_fd (from libosmocore)
+ * 2. The modern (2023) mode using osmo_io (from libosmocore)
+ *
+ * For any new applications, you definitely should use the modern mode, as it provides you with a higher
+ * layer of abstraction and allows you to perform efficient I/O using the io_uring backend of osmo_io.
+ *
+ * The two main objects are osmo_stream_srv_link (main server accept()ing incoming connections) and
+ * osmo_stream_srv (a single given connection from a remote client).
+ *
+ * A typical stream_srv usage would look like this:
+ *
+ * * create new osmo_stream_srv_link using osmo_stream_srv_link_create()
+ * * call osmo_stream_srv_link_set_addr() to set local bind address/port
+ * * call osmo_stream_srv_link_set_accept_cb() to register the accept call-back
+ * * optionally call further osmo_stream_srv_link_set_*() functions
+ * * call osmo_stream_srv_link_open() to create socket and start listening
+ *
+ * Whenever a client connects to your listening socket, the connection will now be automatically accept()ed
+ * and the registered accept_cb call-back called. From within that accept_cb, you then
+ * * call osmo_stream_srv_create() to create a osmo_stream_srv for that specific connection
+ * * call osmo_stream_srv_set_read_cb() to register the read call-back for incoming data
+ * * call osmo_stream_srv_set_closed_cb() to register the closed call-back
+ * * call osmo_stream_srv_set_data() to associate opaque application-layer state
+ *
+ * Whenever data from a client arrives on a connection, your registered read_cb will be called together
+ * with a message buffer containing the received data. Ownership of the message buffer is transferred
+ * into the call-back, i.e. in your application. It's your responsibility to eventually msgb_free()
+ * it after usage.
+ *
+ * Whenever your application wants to transmit something to a given connection, it uses the
+ * osmo_stream_srv_send() function.
+ */
+
/*! \brief Osmocom Stream Server Link: A server socket listening/accepting */
struct osmo_stream_srv_link;
+typedef int (*osmo_stream_srv_link_accept_cb_t)(struct osmo_stream_srv_link *link, int fd);
+
struct osmo_stream_srv_link *osmo_stream_srv_link_create(void *ctx);
void osmo_stream_srv_link_destroy(struct osmo_stream_srv_link *link);
+void osmo_stream_srv_link_set_name(struct osmo_stream_srv_link *link, const char *name);
+const char *osmo_stream_srv_link_get_name(const struct osmo_stream_srv_link *link);
void osmo_stream_srv_link_set_nodelay(struct osmo_stream_srv_link *link, bool nodelay);
void osmo_stream_srv_link_set_addr(struct osmo_stream_srv_link *link, const char *addr);
int osmo_stream_srv_link_set_addrs(struct osmo_stream_srv_link *link, const char **addr, size_t addrcnt);
void osmo_stream_srv_link_set_port(struct osmo_stream_srv_link *link, uint16_t port);
void osmo_stream_srv_link_set_proto(struct osmo_stream_srv_link *link, uint16_t proto);
-void osmo_stream_srv_link_set_accept_cb(struct osmo_stream_srv_link *link, int (*accept_cb)(struct osmo_stream_srv_link *link, int fd));
+int osmo_stream_srv_link_set_type(struct osmo_stream_srv_link *link, int type);
+int osmo_stream_srv_link_set_domain(struct osmo_stream_srv_link *link, int domain);
+void osmo_stream_srv_link_set_accept_cb(struct osmo_stream_srv_link *link, osmo_stream_srv_link_accept_cb_t accept_cb);
void osmo_stream_srv_link_set_data(struct osmo_stream_srv_link *link, void *data);
void *osmo_stream_srv_link_get_data(struct osmo_stream_srv_link *link);
char *osmo_stream_srv_link_get_sockname(const struct osmo_stream_srv_link *link);
struct osmo_fd *osmo_stream_srv_link_get_ofd(struct osmo_stream_srv_link *link);
-
+int osmo_stream_srv_link_get_fd(const struct osmo_stream_srv_link *link);
+bool osmo_stream_srv_link_is_opened(const struct osmo_stream_srv_link *link);
int osmo_stream_srv_link_open(struct osmo_stream_srv_link *link);
void osmo_stream_srv_link_close(struct osmo_stream_srv_link *link);
+enum osmo_stream_srv_link_param {
+ OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */
+ OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */
+ OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS, /* uint16_t: amount of streams */
+ OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */
+};
+
+int osmo_stream_srv_link_set_param(struct osmo_stream_srv_link *link, enum osmo_stream_srv_link_param par,
+ void *val, size_t val_len);
+
/*! \brief Osmocom Stream Server: Single connection accept()ed via \ref
* osmo_stream_srv_link */
struct osmo_stream_srv;
-struct osmo_stream_srv *osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, int fd, int (*cb)(struct osmo_stream_srv *conn), int (*closed_cb)(struct osmo_stream_srv *conn), void *data);
+typedef int (*osmo_stream_srv_read_cb_t)(struct osmo_stream_srv *conn);
+typedef int (*osmo_stream_srv_closed_cb_t)(struct osmo_stream_srv *conn);
+
+/*! Completion call-back function when something was read from from the stream client socket.
+ * \param[in] conn Stream Server that got receive event.
+ * \param[in] res return value of the read()/recvmsg()/... call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually! */
+typedef int (*osmo_stream_srv_read_cb2_t)(struct osmo_stream_srv *conn, int res, struct msgb *msg);
+
+typedef int (*osmo_stream_srv_segmentation_cb_t)(struct msgb *msg);
+
+struct osmo_stream_srv *osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, int fd,
+ osmo_stream_srv_read_cb_t read_cb,
+ osmo_stream_srv_closed_cb_t closed_cb,
+ void *data);
+struct osmo_stream_srv *osmo_stream_srv_create2(void *ctx, struct osmo_stream_srv_link *link, int fd, void *data);
+void osmo_stream_srv_set_name(struct osmo_stream_srv *conn, const char *name);
+const char *osmo_stream_srv_get_name(const struct osmo_stream_srv *conn);
+void osmo_stream_srv_set_read_cb(struct osmo_stream_srv *conn, osmo_stream_srv_read_cb2_t read_cb);
+void osmo_stream_srv_set_closed_cb(struct osmo_stream_srv *conn, osmo_stream_srv_closed_cb_t close_cb);
void *osmo_stream_srv_get_data(struct osmo_stream_srv *conn);
struct osmo_stream_srv_link *osmo_stream_srv_get_master(struct osmo_stream_srv *conn);
+const char *osmo_stream_srv_get_sockname(const struct osmo_stream_srv *conn);
struct osmo_fd *osmo_stream_srv_get_ofd(struct osmo_stream_srv *srv);
+int osmo_stream_srv_get_fd(const struct osmo_stream_srv *srv);
+struct osmo_io_fd *osmo_stream_srv_get_iofd(const struct osmo_stream_srv *srv);
void osmo_stream_srv_destroy(struct osmo_stream_srv *conn);
void osmo_stream_srv_set_flush_and_destroy(struct osmo_stream_srv *conn);
void osmo_stream_srv_set_data(struct osmo_stream_srv *conn, void *data);
+void osmo_stream_srv_set_segmentation_cb(struct osmo_stream_srv *conn, osmo_stream_srv_segmentation_cb_t segmentation_cb);
+
void osmo_stream_srv_send(struct osmo_stream_srv *conn, struct msgb *msg);
int osmo_stream_srv_recv(struct osmo_stream_srv *conn, struct msgb *msg);
+void osmo_stream_srv_clear_tx_queue(struct osmo_stream_srv *conn);
+
+/*! @} */
+
+/*! \defgroup stream_cli Osmocom Stream Client
+ * @{
+ *
+ * This code is intended to abstract any client use of stream-type sockets, such as TCP and SCTP
+ *
+ * An osmo_stream_cli represents a client implementation of a SOCK_STREAM or SOCK_SEQPACKET socket. It
+ * contains all the common logic like non-blocking outbound connect to a remote server, re-connecting after
+ * disconnect or connect failure, etc.
+ *
+ * osmo_stream_cli can operate in two different modes:
+ * 1. The legacy mode using osmo_fd (from libosmocore)
+ * 2. The modern (2023) mode using osmo_io_fd (from libosmocore)
+ *
+ * For any new applications, you definitely should use the modern mode, as it provides you with a higher
+ * layer of abstraction and allows you to perform efficient I/O using the io_uring backend of osmo_io.
+ *
+ * A typical usage of osmo_stream_cli would look as follows:
+ *
+ * * call osmo_stream_cli_create() to create a new osmo_stream_cli
+ * * call osmo_stream_cli_set_addr() / osmo_stream_cli_set_port() to specify the remote address/port to connect to
+ * * optionally call further functions of the osmo_stream_cli_set_*() family
+ * * call osmo_stream_cli_set_connect_cb() to register the call-back called on completion of outbound connect()
+ * * call osmo_stream_cli_set_read_cb2() to register the call-back called when incoming data has been read
+ * * call osmo_stream_cli_open() to open the connection (start outbound connect process)
+ *
+ * Once the connection is established, your connect_cb is called to notify you.
+ *
+ * You may send data to the connection using osmo_tream_cli_send().
+ *
+ * Any received inbound data on the connection is reported vie the read_cb.
+ */
+
/*! \brief Osmocom Stream Client: Single client connection */
struct osmo_stream_cli;
+typedef int (*osmo_stream_cli_connect_cb_t)(struct osmo_stream_cli *cli);
+typedef int (*osmo_stream_cli_disconnect_cb_t)(struct osmo_stream_cli *cli);
+typedef int (*osmo_stream_cli_read_cb_t)(struct osmo_stream_cli *cli);
+
+/*! Completion call-back function when something was read from from the stream client socket.
+ * \param[in] cli Stream Client that got receive event.
+ * \param[in] res return value of the read()/recvmsg()/... call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually! */
+typedef int (*osmo_stream_cli_read_cb2_t)(struct osmo_stream_cli *cli, int res, struct msgb *msg);
+
+typedef int (*osmo_stream_cli_segmentation_cb_t)(struct msgb *msg);
+
+void osmo_stream_cli_set_name(struct osmo_stream_cli *cli, const char *name);
+const char *osmo_stream_cli_get_name(const struct osmo_stream_cli *cli);
void osmo_stream_cli_set_nodelay(struct osmo_stream_cli *cli, bool nodelay);
void osmo_stream_cli_set_addr(struct osmo_stream_cli *cli, const char *addr);
int osmo_stream_cli_set_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt);
void osmo_stream_cli_set_port(struct osmo_stream_cli *cli, uint16_t port);
+int osmo_stream_cli_set_type(struct osmo_stream_cli *cli, int type);
+int osmo_stream_cli_set_domain(struct osmo_stream_cli *cli, int domain);
void osmo_stream_cli_set_proto(struct osmo_stream_cli *cli, uint16_t proto);
void osmo_stream_cli_set_local_addr(struct osmo_stream_cli *cli, const char *addr);
int osmo_stream_cli_set_local_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt);
@@ -66,9 +206,13 @@ void osmo_stream_cli_set_reconnect_timeout(struct osmo_stream_cli *cli, int time
void *osmo_stream_cli_get_data(struct osmo_stream_cli *cli);
char *osmo_stream_cli_get_sockname(const struct osmo_stream_cli *cli);
struct osmo_fd *osmo_stream_cli_get_ofd(struct osmo_stream_cli *cli);
-void osmo_stream_cli_set_connect_cb(struct osmo_stream_cli *cli, int (*connect_cb)(struct osmo_stream_cli *cli));
-void osmo_stream_cli_set_disconnect_cb(struct osmo_stream_cli *cli, int (*disconnect_cb)(struct osmo_stream_cli *cli));
-void osmo_stream_cli_set_read_cb(struct osmo_stream_cli *cli, int (*read_cb)(struct osmo_stream_cli *cli));
+int osmo_stream_cli_get_fd(const struct osmo_stream_cli *cli);
+struct osmo_io_fd *osmo_stream_cli_get_iofd(const struct osmo_stream_cli *cli);
+void osmo_stream_cli_set_connect_cb(struct osmo_stream_cli *cli, osmo_stream_cli_connect_cb_t connect_cb);
+void osmo_stream_cli_set_disconnect_cb(struct osmo_stream_cli *cli, osmo_stream_cli_disconnect_cb_t disconnect_cb);
+void osmo_stream_cli_set_read_cb(struct osmo_stream_cli *cli, osmo_stream_cli_read_cb_t read_cb);
+void osmo_stream_cli_set_read_cb2(struct osmo_stream_cli *cli, osmo_stream_cli_read_cb2_t read_cb);
+void osmo_stream_cli_set_segmentation_cb(struct osmo_stream_cli *cli, osmo_stream_cli_segmentation_cb_t segmentation_cb);
void osmo_stream_cli_reconnect(struct osmo_stream_cli *cli);
bool osmo_stream_cli_is_connected(struct osmo_stream_cli *cli);
@@ -81,6 +225,20 @@ int osmo_stream_cli_open2(struct osmo_stream_cli *cli, int reconnect) \
void osmo_stream_cli_close(struct osmo_stream_cli *cli);
void osmo_stream_cli_send(struct osmo_stream_cli *cli, struct msgb *msg);
-int osmo_stream_cli_recv(struct osmo_stream_cli *conn, struct msgb *msg);
+int osmo_stream_cli_recv(struct osmo_stream_cli *cli, struct msgb *msg);
+
+void osmo_stream_cli_clear_tx_queue(struct osmo_stream_cli *cli);
+
+enum osmo_stream_cli_param {
+ OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */
+ OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */
+ OSMO_STREAM_CLI_PAR_SCTP_INIT_NUM_OSTREAMS, /* uint16_t: amount of streams */
+ OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */
+ OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_ATTEMPTS, /* uint16_t: amount of attempts */
+ OSMO_STREAM_CLI_PAR_SCTP_INIT_TIMEOUT, /* uint16_t: milliseconds */
+};
+
+int osmo_stream_cli_set_param(struct osmo_stream_cli *cli, enum osmo_stream_cli_param par,
+ void *val, size_t val_len);
/*! @} */
diff --git a/include/osmocom/netif/stream_private.h b/include/osmocom/netif/stream_private.h
new file mode 100644
index 0000000..dc7506d
--- /dev/null
+++ b/include/osmocom/netif/stream_private.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/socket.h>
+
+#include "config.h"
+
+#ifdef HAVE_LIBSCTP
+#include <netinet/sctp.h>
+ #define OSMO_STREAM_MAX_ADDRS OSMO_SOCK_MAX_ADDRS
+ /*
+ * Platforms that don't have MSG_NOSIGNAL (which disables SIGPIPE)
+ * usually have SO_NOSIGPIPE (set via setsockopt).
+ */
+ #ifndef MSG_NOSIGNAL
+ #define MSG_NOSIGNAL 0
+ #endif
+#else
+ #define OSMO_STREAM_MAX_ADDRS 1
+#endif
+
+/*! \cond private */
+
+enum osmo_stream_mode {
+ OSMO_STREAM_MODE_UNKNOWN,
+ OSMO_STREAM_MODE_OSMO_FD,
+ OSMO_STREAM_MODE_OSMO_IO,
+};
+
+struct osmo_io_fd;
+struct msghdr;
+
+int stream_sctp_sock_activate_events(int fd);
+int stream_setsockopt_nodelay(int fd, int proto, int on);
+int stream_sctp_recvmsg_wrapper(int fd, struct msgb *msg, const char *log_pfx);
+
+int stream_iofd_sctp_send_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags);
+int stream_iofd_sctp_recvmsg_trailer(struct osmo_io_fd *iofd, struct msgb *msg, int ret, const struct msghdr *msgh);
+
+/*! \endcond */