diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/osmocom/netif/Makefile.am | 9 | ||||
-rw-r--r-- | include/osmocom/netif/amr.h | 118 | ||||
-rw-r--r-- | include/osmocom/netif/datagram.h | 13 | ||||
-rw-r--r-- | include/osmocom/netif/ipa.h | 27 | ||||
-rw-r--r-- | include/osmocom/netif/osmux.h | 45 | ||||
-rw-r--r-- | include/osmocom/netif/prim.h | 66 | ||||
-rw-r--r-- | include/osmocom/netif/rtp.h | 4 | ||||
-rw-r--r-- | include/osmocom/netif/sctp.h | 60 | ||||
-rw-r--r-- | include/osmocom/netif/stream.h | 178 | ||||
-rw-r--r-- | include/osmocom/netif/stream_private.h | 42 |
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 */ |