diff options
Diffstat (limited to 'include/osmocom/core')
54 files changed, 2366 insertions, 233 deletions
diff --git a/include/osmocom/core/Makefile.am b/include/osmocom/core/Makefile.am new file mode 100644 index 00000000..980f8134 --- /dev/null +++ b/include/osmocom/core/Makefile.am @@ -0,0 +1,104 @@ +osmocore_HEADERS = \ + application.h \ + backtrace.h \ + base64.h \ + bit16gen.h \ + bit32gen.h \ + bit64gen.h \ + bits.h \ + bitvec.h \ + bitcomp.h \ + byteswap.h \ + conv.h \ + counter.h \ + crc16.h \ + crc16gen.h \ + crc32gen.h \ + crc64gen.h \ + crc8gen.h \ + crcgen.h \ + endian.h \ + defs.h \ + exec.h \ + fsm.h \ + gsmtap.h \ + gsmtap_util.h \ + hash.h \ + hashtable.h \ + isdnhdlc.h \ + it_q.h \ + jhash.h \ + linuxlist.h \ + linuxrbtree.h \ + log2.h \ + logging.h \ + loggingrb.h \ + stats.h \ + macaddr.h \ + msgb.h \ + netdev.h \ + netns.h \ + osmo_io.h \ + panic.h \ + prbs.h \ + prim.h \ + process.h \ + rate_ctr.h \ + stat_item.h \ + stats_tcp.h \ + select.h \ + sercomm.h \ + signal.h \ + socket.h \ + statistics.h \ + strrb.h \ + talloc.h \ + tdef.h \ + thread.h \ + timer.h \ + timer_compat.h \ + tun.h \ + utils.h \ + write_queue.h \ + sockaddr_str.h \ + soft_uart.h \ + time_cc.h \ + use_count.h \ + socket_compat.h \ + $(NULL) + +if ENABLE_PLUGIN +osmocore_HEADERS += plugin.h +endif + +if ENABLE_MSGFILE +osmocore_HEADERS += msgfile.h +endif + +if ENABLE_SERIAL +osmocore_HEADERS += serial.h +endif + +if ENABLE_LIBMNL +osmocore_HEADERS += mnl.h +endif + +osmocoredir = $(includedir)/osmocom/core + +noinst_HEADERS = \ + logging_internal.h \ + $(NULL) + +bit%gen.h: bitXXgen.h.tpl + $(AM_V_GEN)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ + +crc%gen.h: crcXXgen.h.tpl + $(AM_V_GEN)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ + +socket_compat.h: socket_compat.h.tpl + $(AM_V_GEN)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)sed -e's/XX/$(HAVE_SYS_SOCKET_H)/g' $< > $@ + +EXTRA_DIST = socket_compat.h.tpl diff --git a/include/osmocom/core/application.h b/include/osmocom/core/application.h index edf59ed4..67a59088 100644 --- a/include/osmocom/core/application.h +++ b/include/osmocom/core/application.h @@ -2,17 +2,14 @@ #include <osmocom/core/defs.h> +struct log_info; +struct log_target; + /*! * \file application.h * Routines for helping with the osmocom application setup. */ -/*! information containing the available logging subsystems */ -struct log_info; - -/*! one instance of a logging target (file, stderr, ...) */ -struct log_target; - /*! the default logging target, logging to stderr */ extern struct log_target *osmo_stderr_target; diff --git a/include/osmocom/core/base64.h b/include/osmocom/core/base64.h new file mode 100644 index 00000000..a6c97206 --- /dev/null +++ b/include/osmocom/core/base64.h @@ -0,0 +1,69 @@ +/** + * \file base64.h + * + * \brief RFC 1521 base64 encoding/decoding + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * + * This file is part of mbed TLS (https://tls.mbed.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + */ +#pragma once + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Encode a buffer into base64 format + * + * \param dst destination buffer + * \param dlen size of the destination buffer + * \param olen number of bytes written + * \param src source buffer + * \param slen amount of data to be encoded + * + * \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL. + * *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with dlen = 0 to obtain the + * required buffer size in *olen + */ +int osmo_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); + +/** + * \brief Decode a base64-formatted buffer + * + * \param dst destination buffer (can be NULL for checking size) + * \param dlen size of the destination buffer + * \param olen number of bytes written + * \param src source buffer + * \param slen amount of data to be decoded + * + * \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or + * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is + * not correct. *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dst = NULL or dlen = 0 to obtain + * the required buffer size in *olen + */ +int osmo_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); + +#ifdef __cplusplus +} +#endif diff --git a/include/osmocom/core/bitXXgen.h.tpl b/include/osmocom/core/bitXXgen.h.tpl index 6881d87d..ab54ba9c 100644 --- a/include/osmocom/core/bitXXgen.h.tpl +++ b/include/osmocom/core/bitXXgen.h.tpl @@ -14,15 +14,13 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once -/*! load unaligned n-byte integer (little-endian encoding) into uintXX_t +#include <osmocom/core/utils.h> + +/*! load unaligned n-byte integer (little-endian encoding) into uintXX_t, into the least significant octets. * \param[in] p Buffer where integer is stored * \param[in] n Number of bytes stored in p * \returns XX bit unsigned integer @@ -32,11 +30,14 @@ static inline uintXX_t osmo_loadXXle_ext(const void *p, uint8_t n) uint8_t i; uintXX_t r = 0; const uint8_t *q = (uint8_t *)p; + OSMO_ASSERT(n <= sizeof(r)); for(i = 0; i < n; r |= ((uintXX_t)q[i] << (8 * i)), i++); return r; } -/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t +/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t, into the MOST significant octets. + * WARNING: for n < sizeof(uintXX_t), the result is not returned in the least significant octets, as one might expect. + * To always return the same value as fed to osmo_storeXXbe_ext() before, use osmo_loadXXbe_ext_2(). * \param[in] p Buffer where integer is stored * \param[in] n Number of bytes stored in p * \returns XX bit unsigned integer @@ -46,10 +47,26 @@ static inline uintXX_t osmo_loadXXbe_ext(const void *p, uint8_t n) uint8_t i; uintXX_t r = 0; const uint8_t *q = (uint8_t *)p; + OSMO_ASSERT(n <= sizeof(r)); for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i))), i++); return r; } +/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t, into the least significant octets. + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns XX bit unsigned integer + */ +static inline uintXX_t osmo_loadXXbe_ext_2(const void *p, uint8_t n) +{ + uint8_t i; + uintXX_t r = 0; + const uint8_t *q = (uint8_t *)p; + OSMO_ASSERT(n <= sizeof(r)); + for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i + (sizeof(r) - n)))), i++); + return r; +} + /*! store unaligned n-byte integer (little-endian encoding) from uintXX_t * \param[in] x unsigned XX bit integer @@ -60,6 +77,7 @@ static inline void osmo_storeXXle_ext(uintXX_t x, void *p, uint8_t n) { uint8_t i; uint8_t *q = (uint8_t *)p; + OSMO_ASSERT(n <= sizeof(x)); for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++); } @@ -72,6 +90,7 @@ static inline void osmo_storeXXbe_ext(uintXX_t x, void *p, uint8_t n) { uint8_t i; uint8_t *q = (uint8_t *)p; + OSMO_ASSERT(n <= sizeof(x)); for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++); } diff --git a/include/osmocom/core/bitcomp.h b/include/osmocom/core/bitcomp.h index 5faa5ea4..21679577 100644 --- a/include/osmocom/core/bitcomp.h +++ b/include/osmocom/core/bitcomp.h @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h index bd107093..1e2fe7b4 100644 --- a/include/osmocom/core/bitvec.h +++ b/include/osmocom/core/bitvec.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once @@ -27,7 +23,6 @@ * \file bitvec.h */ #include <stdint.h> -#include <osmocom/core/talloc.h> #include <osmocom/core/defs.h> #include <stdbool.h> @@ -65,7 +60,7 @@ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count); int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count); -struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *bvctx); +struct bitvec *bitvec_alloc(unsigned int size, void *bvctx); void bitvec_free(struct bitvec *bv); int bitvec_unhex(struct bitvec *bv, const char *src); unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer); @@ -77,7 +72,7 @@ char bit_value_to_char(enum bit_value v); void bitvec_to_string_r(const struct bitvec *bv, char *str); void bitvec_zero(struct bitvec *bv); unsigned bitvec_rl(const struct bitvec *bv, bool b); -unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits); +unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, unsigned int max_bits); void bitvec_shiftl(struct bitvec *bv, unsigned int n); int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits); unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array, diff --git a/include/osmocom/core/conv.h b/include/osmocom/core/conv.h index 8b344f4d..84ef0f85 100644 --- a/include/osmocom/core/conv.h +++ b/include/osmocom/core/conv.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \defgroup conv Convolutional encoding and decoding routines @@ -128,6 +124,7 @@ int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, const sbit_t *input, int n); int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, const sbit_t *input); +int osmo_conv_decode_get_best_end_state(struct osmo_conv_decoder *decoder); int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, ubit_t *output, int has_flush, int end_state); diff --git a/include/osmocom/core/counter.h b/include/osmocom/core/counter.h index dc627918..7b677cb1 100644 --- a/include/osmocom/core/counter.h +++ b/include/osmocom/core/counter.h @@ -31,7 +31,7 @@ static inline void osmo_counter_inc(struct osmo_counter *ctr) } /*! Get current value of counter */ -OSMO_DEPRECATED("Implement as osmo_stat_item instead") +OSMO_DEPRECATED_OUTSIDE("Implement as osmo_stat_item instead") static inline unsigned long osmo_counter_get(struct osmo_counter *ctr) { return ctr->value; @@ -52,7 +52,7 @@ void osmo_counter_free(struct osmo_counter *ctr) int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data); -int osmo_counters_count(); +int osmo_counters_count(void); struct osmo_counter *osmo_counter_get_by_name(const char *name); diff --git a/include/osmocom/core/crcXXgen.h.tpl b/include/osmocom/core/crcXXgen.h.tpl index 823f21f2..13a3623e 100644 --- a/include/osmocom/core/crcXXgen.h.tpl +++ b/include/osmocom/core/crcXXgen.h.tpl @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once diff --git a/include/osmocom/core/crcgen.h b/include/osmocom/core/crcgen.h index 7cfe8699..24d29e74 100644 --- a/include/osmocom/core/crcgen.h +++ b/include/osmocom/core/crcgen.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once diff --git a/include/osmocom/core/endian.h b/include/osmocom/core/endian.h index 6107b12f..426ab680 100644 --- a/include/osmocom/core/endian.h +++ b/include/osmocom/core/endian.h @@ -1,7 +1,7 @@ /*! \file endian.h * * GNU and FreeBSD have various ways to express the - * endianess but none of them is similiar enough. This + * endianness but none of them is similar enough. This * will create two defines that allows to decide on the * endian. The following will be defined to either 0 or * 1 at the end of the file. diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h index e63ec114..a4fdcfd3 100644 --- a/include/osmocom/core/exec.h +++ b/include/osmocom/core/exec.h @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ extern const char *osmo_environment_whitelist[]; diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h index 7b262c71..a1ffd30d 100644 --- a/include/osmocom/core/fsm.h +++ b/include/osmocom/core/fsm.h @@ -200,7 +200,7 @@ void osmo_fsm_set_dealloc_ctx(void *ctx); fmt, ## args) #define OSMO_T_FMT "%c%u" -#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? T : -T) +#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? (T) : -(T)) int osmo_fsm_register(struct osmo_fsm *fsm); void osmo_fsm_unregister(struct osmo_fsm *fsm); @@ -224,9 +224,9 @@ int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id); int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...); int osmo_fsm_inst_update_id_f_sanitize(struct osmo_fsm_inst *fi, char replace_with, const char *fmt, ...); -const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event); -const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi); -const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state); +const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event); +const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi); +const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state); /*! return the name of the state the FSM instance is currently in. */ static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi) @@ -326,4 +326,15 @@ void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, void *data, const char *file, int line); +/*! dispatch an event to all children of an osmocom finite state machine instance + * + * This is a macro that calls _osmo_fsm_inst_broadcast_children() with the given + * parameters as well as the caller's source file and line number for logging + * purposes. See there for documentation. + */ +#define osmo_fsm_inst_broadcast_children(fi, cause, data) \ + _osmo_fsm_inst_broadcast_children(fi, cause, data, __FILE__, __LINE__) +void _osmo_fsm_inst_broadcast_children(struct osmo_fsm_inst *fi, uint32_t event, + void *data, const char *file, int line); + /*! @} */ diff --git a/include/osmocom/core/gsmtap.h b/include/osmocom/core/gsmtap.h index 82e95254..ebb52960 100644 --- a/include/osmocom/core/gsmtap.h +++ b/include/osmocom/core/gsmtap.h @@ -48,6 +48,8 @@ #define GSMTAP_TYPE_OSMOCORE_LOG 0x10 /* libosmocore logging */ #define GSMTAP_TYPE_QC_DIAG 0x11 /* Qualcomm DIAG frame */ #define GSMTAP_TYPE_LTE_NAS 0x12 /* LTE Non-Access Stratum */ +#define GSMTAP_TYPE_E1T1 0x13 /* E1/T1 Lines */ +#define GSMTAP_TYPE_GSM_RLP 0x14 /* GSM RLP frames as per 3GPP TS 24.022 */ /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ @@ -171,6 +173,20 @@ #define GSMTAP_LTE_CH_DTCH 0x06 #define GSMTAP_LTE_CH_MTCH 0x07 +/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ +/* sub-types for TYPE_E1T1 */ +#define GSMTAP_E1T1_LAPD 0x01 /* Q.921 LAPD */ +#define GSMTAP_E1T1_FR 0x02 /* Frame Relay */ +#define GSMTAP_E1T1_RAW 0x03 /* raw/transparent B-channel */ +#define GSMTAP_E1T1_TRAU16 0x04 /* 16k TRAU frames; sub-slot 0-3 */ +#define GSMTAP_E1T1_TRAU8 0x05 /* 8k TRAU frames; sub-slot 0-7 */ +#define GSMTAP_E1T1_V5EF 0x06 /* V5 Envelope Function */ +#define GSMTAP_E1T1_X75 0x07 /* X.75 B-channel data */ +#define GSMTAP_E1T1_V120 0x08 /* V.120 B-channel data */ +#define GSMTAP_E1T1_V110 0x09 /* V.110 B-channel data */ +#define GSMTAP_E1T1_H221 0x0a /* H.221 B-channel data */ +#define GSMTAP_E1T1_PPP 0x0b /* PPP */ + /* flags for the ARFCN */ #define GSMTAP_ARFCN_F_PCS 0x8000 #define GSMTAP_ARFCN_F_UPLINK 0x4000 diff --git a/include/osmocom/core/gsmtap_util.h b/include/osmocom/core/gsmtap_util.h index 9b215be3..d24ee95f 100644 --- a/include/osmocom/core/gsmtap_util.h +++ b/include/osmocom/core/gsmtap_util.h @@ -17,46 +17,45 @@ void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype, uint8_t struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, - uint8_t snr, const uint8_t *data, unsigned int len); + int8_t snr, const uint8_t *data, unsigned int len); struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, - uint8_t snr, const uint8_t *data, unsigned int len); + int8_t snr, const uint8_t *data, unsigned int len); /*! one gsmtap instance */ -struct gsmtap_inst { - int ofd_wq_mode; /*!< wait queue mode? */ - struct osmo_wqueue wq; /*!< the wait queue */ - struct osmo_fd sink_ofd;/*!< file descriptor */ -}; - -/*! obtain the file descriptor associated with a gsmtap instance - * \param[in] gti GSMTAP instance - * \returns file descriptor of GSMTAP instance */ -static inline int gsmtap_inst_fd(struct gsmtap_inst *gti) -{ - return gti->wq.bfd.fd; -} +struct gsmtap_inst; + +int gsmtap_inst_fd(struct gsmtap_inst *gti) + OSMO_DEPRECATED("Use gsmtap_inst_fd2() instead"); + +int gsmtap_inst_fd2(const struct gsmtap_inst *gti); int gsmtap_source_init_fd(const char *host, uint16_t port); +int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port); int gsmtap_source_add_sink_fd(int gsmtap_fd); struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, int ofd_wq_mode); +struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port, + const char *rem_host, uint16_t rem_port, int ofd_wq_mode); + +void gsmtap_source_free(struct gsmtap_inst *gti); int gsmtap_source_add_sink(struct gsmtap_inst *gti); int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg); +int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg); int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, - int8_t signal_dbm, uint8_t snr, const uint8_t *data, + int8_t signal_dbm, int8_t snr, const uint8_t *data, unsigned int len); int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, - int8_t signal_dbm, uint8_t snr, const uint8_t *data, + int8_t signal_dbm, int8_t snr, const uint8_t *data, unsigned int len); extern const struct value_string gsmtap_gsm_channel_names[]; diff --git a/include/osmocom/core/hash.h b/include/osmocom/core/hash.h new file mode 100644 index 00000000..b45c0361 --- /dev/null +++ b/include/osmocom/core/hash.h @@ -0,0 +1,101 @@ +#pragma once +#include <osmocom/core/log2.h> +/* Fast hashing routine for ints, longs and pointers. + (C) 2002 Nadia Yvette Chambers, IBM */ + +#include <limits.h> +#if ULONG_MAX == 4294967295 +#define BITS_PER_LONG 32 +#else +#define BITS_PER_LONG 64 +#endif + +/* + * The "GOLDEN_RATIO_PRIME" is used in ifs/btrfs/brtfs_inode.h and + * fs/inode.c. It's not actually prime any more (the previous primes + * were actively bad for hashing), but the name remains. + */ +#if BITS_PER_LONG == 32 +#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32 +#define hash_long(val, bits) hash_32(val, bits) +#elif BITS_PER_LONG == 64 +#define hash_long(val, bits) hash_64(val, bits) +#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_64 +#else +#error Wordsize not 32 or 64 +#endif + +/* + * This hash multiplies the input by a large odd number and takes the + * high bits. Since multiplication propagates changes to the most + * significant end only, it is essential that the high bits of the + * product be used for the hash value. + * + * Chuck Lever verified the effectiveness of this technique: + * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf + * + * Although a random odd number will do, it turns out that the golden + * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice + * properties. (See Knuth vol 3, section 6.4, exercise 9.) + * + * These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2, + * which is very slightly easier to multiply by and makes no + * difference to the hash distribution. + */ +#define GOLDEN_RATIO_32 0x61C88647 +#define GOLDEN_RATIO_64 0x61C8864680B583EBull + +/* + * The _generic versions exist only so lib/test_hash.c can compare + * the arch-optimized versions with the generic. + * + * Note that if you change these, any <asm/hash.h> that aren't updated + * to match need to have their HAVE_ARCH_* define values updated so the + * self-test will not false-positive. + */ +#ifndef HAVE_ARCH__HASH_32 +#define __hash_32 __hash_32_generic +#endif +static inline uint32_t __hash_32_generic(uint32_t val) +{ + return val * GOLDEN_RATIO_32; +} + +#ifndef HAVE_ARCH_HASH_32 +#define hash_32 hash_32_generic +#endif +static inline uint32_t hash_32_generic(uint32_t val, unsigned int bits) +{ + /* High bits are more random, so use them. */ + return __hash_32(val) >> (32 - bits); +} + +#ifndef HAVE_ARCH_HASH_64 +#define hash_64 hash_64_generic +#endif +static __always_inline uint32_t hash_64_generic(uint64_t val, unsigned int bits) +{ +#if BITS_PER_LONG == 64 + /* 64x64-bit multiply is efficient on all 64-bit processors */ + return val * GOLDEN_RATIO_64 >> (64 - bits); +#else + /* Hash 64 bits using only 32x32-bit multiply. */ + return hash_32((uint32_t)val ^ __hash_32(val >> 32), bits); +#endif +} + +static inline uint32_t hash_ptr(const void *ptr, unsigned int bits) +{ + return hash_long((unsigned long)ptr, bits); +} + +/* This really should be called fold32_ptr; it does no hashing to speak of. */ +static inline uint32_t hash32_ptr(const void *ptr) +{ + unsigned long val = (unsigned long)ptr; + +#if BITS_PER_LONG == 64 + val ^= (val >> 32); +#endif + return (uint32_t)val; +} diff --git a/include/osmocom/core/hashtable.h b/include/osmocom/core/hashtable.h new file mode 100644 index 00000000..acaf6b91 --- /dev/null +++ b/include/osmocom/core/hashtable.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Statically sized hash table implementation + * (C) 2012 Sasha Levin <levinsasha928@gmail.com> + */ + +#pragma once + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/hash.h> + +#define DEFINE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DECLARE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] + +#define HASH_SIZE(name) (ARRAY_SIZE(name)) +#define HASH_BITS(name) ilog2(HASH_SIZE(name)) + +/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ +#define hash_min(val, bits) \ + (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) + +static inline void __hash_init(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + INIT_HLIST_HEAD(&ht[i]); +} + +/** + * hash_init - initialize a hash table + * @hashtable: hashtable to be initialized + * + * Calculates the size of the hashtable from the given parameter, otherwise + * same as hash_init_size. + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_add - add an object to a hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add(hashtable, node, key) \ + hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_hashed - check whether an object is in any hashtable + * @node: the &struct hlist_node of the object to be checked + */ +static inline bool hash_hashed(struct hlist_node *node) +{ + return !hlist_unhashed(node); +} + +static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + if (!hlist_empty(&ht[i])) + return false; + + return true; +} + +/** + * hash_empty - check whether a hashtable is empty + * @hashtable: hashtable to check + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_del - remove an object from a hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del(struct hlist_node *node) +{ + hlist_del_init(node); +} + +/** + * hash_for_each - iterate over a hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry(obj, &name[bkt], member) + +/** + * hash_for_each_safe - iterate over a hashtable safe against removal of + * hash entry + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @tmp: a &struct hlist_node used for temporary storage + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_safe(name, bkt, tmp, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_safe(obj, tmp, &name[bkt], member) + +/** + * hash_for_each_possible - iterate over all possible objects hashing to the + * same bucket + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible(name, obj, member, key) \ + hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member) + +/** + * hash_for_each_possible_safe - iterate over all possible objects hashing to the + * same bucket safe against removals + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @tmp: a &struct hlist_node used for temporary storage + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ + hlist_for_each_entry_safe(obj, tmp,\ + &name[hash_min(key, HASH_BITS(name))], member) diff --git a/include/osmocom/core/isdnhdlc.h b/include/osmocom/core/isdnhdlc.h index 56369bfd..c8cfdc3a 100644 --- a/include/osmocom/core/isdnhdlc.h +++ b/include/osmocom/core/isdnhdlc.h @@ -20,14 +20,9 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef __ISDNHDLC_H__ -#define __ISDNHDLC_H__ +#pragma once #include <stdint.h> @@ -80,5 +75,3 @@ extern void osmo_isdnhdlc_out_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t fea extern int osmo_isdnhdlc_encode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src, uint16_t slen, int *count, uint8_t *dst, int dsize); - -#endif /* __ISDNHDLC_H__ */ diff --git a/include/osmocom/core/it_q.h b/include/osmocom/core/it_q.h new file mode 100644 index 00000000..a28f524e --- /dev/null +++ b/include/osmocom/core/it_q.h @@ -0,0 +1,62 @@ +#pragma once + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/select.h> +#include <pthread.h> + +/*! \defgroup osmo_it_q Inter-Thread Queue + * @{ + * \file osmo_it_q.h */ + +/*! One instance of an inter-thread queue. The user can use this to queue messages + * between different threads. The enqueue operation is non-blocking (but of course + * grabs a mutex for the actual list operations to safeguard against races). The + * receiving thread is woken up by an event_fd which can be registered in the libosmocore + * select loop handling. */ +struct osmo_it_q { + /* entry in global list of message queues */ + struct llist_head entry; + + /* the actual list of user structs. HEAD: first in queue; TAIL: last in queue */ + struct llist_head list; + /* A pthread mutex to safeguard accesses to the queue. No rwlock as we always write. */ + pthread_mutex_t mutex; + /* Current count of messages in the queue */ + unsigned int current_length; + /* osmo-fd wrapped eventfd */ + struct osmo_fd event_ofd; + + /* a user-defined name for this queue */ + const char *name; + /* maximum permitted length of queue */ + unsigned int max_length; + /* read call-back, called for each de-queued message */ + void (*read_cb)(struct osmo_it_q *q, struct llist_head *item); + /* opaque data pointer passed through to call-back function */ + void *data; +}; + +struct osmo_it_q *osmo_it_q_by_name(const char *name); + +int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item); +#define osmo_it_q_enqueue(queue, item, member) \ + _osmo_it_q_enqueue(queue, &(item)->member) + +struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue); +#define osmo_it_q_dequeue(queue, item, member) do { \ + struct llist_head *l = _osmo_it_q_dequeue(queue); \ + if (!l) \ + *item = NULL; \ + else \ + *item = llist_entry(l, typeof(**item), member); \ +} while (0) + + +struct osmo_it_q *osmo_it_q_alloc(void *ctx, const char *name, unsigned int max_length, + + void (*read_cb)(struct osmo_it_q *q, struct llist_head *item), + void *data); +void osmo_it_q_destroy(struct osmo_it_q *q); +void osmo_it_q_flush(struct osmo_it_q *q); + +/*! @} */ diff --git a/include/osmocom/core/jhash.h b/include/osmocom/core/jhash.h new file mode 100644 index 00000000..763fcd7d --- /dev/null +++ b/include/osmocom/core/jhash.h @@ -0,0 +1,171 @@ +#pragma once +#include <osmocom/core/bit32gen.h> + +/* Below is a partial copy of + * https://raw.githubusercontent.com/torvalds/linux/3eb3c33c1d87029a3832e205eebd59cfb56ba3a4/tools/include/linux/bitops.h + * with an osmo_ prefix applied to avoid any collisions. + */ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * rol32 - rotate a 32-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint32_t osmo_rol32(uint32_t word, unsigned int shift) +{ + return (word << shift) | (word >> ((-shift) & 31)); +} + +/* Below is a partial copy of + * https://raw.githubusercontent.com/torvalds/linux/22c033989c3eb9731ad0c497dfab4231b8e367d6/include/linux/unaligned/packed_struct.h + * with an osmo_ prefix applied to avoid any collisions. + */ +struct osmo_unaligned_cpu32 { + uint32_t x; +} __attribute__((__packed__)); + +static inline uint32_t osmo_get_unaligned_cpu32(const void *p) +{ + const struct osmo_unaligned_cpu32 *ptr = (const struct osmo_unaligned_cpu32 *)p; + return ptr->x; +} + +/* Below is a partial copy of + * https://raw.githubusercontent.com/torvalds/linux/79e3ea5aab48c83de9410e43b52895406847eca7/tools/include/linux/jhash.h + * with an osmo_ prefix applied to avoid any collisions. + */ +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * https://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() + * are externally useful functions. Routines to test the hash are included + * if SELF_TEST is defined. You can use this free for any purpose. It's in + * the public domain. It has no warranty. + * + * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are my fault. + * Jozsef + */ + +/* OSMO_JHASH_MIX -- mix 3 32-bit values reversibly. */ +#define OSMO_JHASH_MIX(a, b, c) \ +{ \ + a -= c; a ^= osmo_rol32(c, 4); c += b; \ + b -= a; b ^= osmo_rol32(a, 6); a += c; \ + c -= b; c ^= osmo_rol32(b, 8); b += a; \ + a -= c; a ^= osmo_rol32(c, 16); c += b; \ + b -= a; b ^= osmo_rol32(a, 19); a += c; \ + c -= b; c ^= osmo_rol32(b, 4); b += a; \ +} + +/* OSMO_JHASH_FINAL - final mixing of 3 32-bit values (a,b,c) into c */ +#define OSMO_JHASH_FINAL(a, b, c) \ +{ \ + c ^= b; c -= osmo_rol32(b, 14); \ + a ^= c; a -= osmo_rol32(c, 11); \ + b ^= a; b -= osmo_rol32(a, 25); \ + c ^= b; c -= osmo_rol32(b, 16); \ + a ^= c; a -= osmo_rol32(c, 4); \ + b ^= a; b -= osmo_rol32(a, 14); \ + c ^= b; c -= osmo_rol32(b, 24); \ +} + +/* An arbitrary initial parameter */ +#define JHASH_INITVAL 0xdeadbeef + +/* osmo_jhash - hash an arbitrary key + * @k: sequence of bytes as key + * @length: the length of the key + * @initval: the previous hash, or an arbitray value + * + * The generic version, hashes an arbitrary sequence of bytes. + * No alignment or length assumptions are made about the input key. + * + * Returns the hash value of the key. The result depends on endianness. + */ +static inline uint32_t osmo_jhash(const void *key, uint32_t length, uint32_t initval) +{ + uint32_t a, b, c; + const uint8_t *k = key; + + /* Set up the internal state */ + a = b = c = JHASH_INITVAL + length + initval; + + /* All but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) { + a += osmo_get_unaligned_cpu32(k); + b += osmo_get_unaligned_cpu32(k + 4); + c += osmo_get_unaligned_cpu32(k + 8); + OSMO_JHASH_MIX(a, b, c); + length -= 12; + k += 12; + } + /* Last block: affect all 32 bits of (c) */ + /* All the case statements fall through */ + switch (length) { + case 12: c += (uint32_t)k[11]<<24; + case 11: c += (uint32_t)k[10]<<16; + case 10: c += (uint32_t)k[9]<<8; + case 9: c += k[8]; + case 8: b += (uint32_t)k[7]<<24; + case 7: b += (uint32_t)k[6]<<16; + case 6: b += (uint32_t)k[5]<<8; + case 5: b += k[4]; + case 4: a += (uint32_t)k[3]<<24; + case 3: a += (uint32_t)k[2]<<16; + case 2: a += (uint32_t)k[1]<<8; + case 1: a += k[0]; + OSMO_JHASH_FINAL(a, b, c); + case 0: /* Nothing left to add */ + break; + } + + return c; +} + +/* osmo_jhash2 - hash an array of uint32_t's + * @k: the key which must be an array of uint32_t's + * @length: the number of uint32_t's in the key + * @initval: the previous hash, or an arbitray value + * + * Returns the hash value of the key. + */ +static inline uint32_t osmo_jhash2(const uint32_t *k, uint32_t length, uint32_t initval) +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = JHASH_INITVAL + (length<<2) + initval; + + /* Handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + OSMO_JHASH_MIX(a, b, c); + length -= 3; + k += 3; + } + + /* Handle the last 3 uint32_t's: all the case statements fall through */ + switch (length) { + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + OSMO_JHASH_FINAL(a, b, c); + case 0: /* Nothing left to add */ + break; + } + + return c; +} diff --git a/include/osmocom/core/linuxlist.h b/include/osmocom/core/linuxlist.h index 867605e5..2fc3fa75 100644 --- a/include/osmocom/core/linuxlist.h +++ b/include/osmocom/core/linuxlist.h @@ -16,6 +16,7 @@ * \file linuxlist.h */ #include <stddef.h> +#include <stdbool.h> #ifndef inline #define inline __inline__ @@ -237,6 +238,12 @@ static inline void llist_splice_init(struct llist_head *llist, #define llist_last_entry(ptr, type, member) \ llist_entry((ptr)->prev, type, member) +/*! Return the last element of the list. + * \param head the llist head of the list. + * \returns last element of the list, head if the list is empty. + */ +#define llist_last(head) (head)->prev + /*! Get the first element from a list, or NULL. * \param ptr the list head to take the element from. * \param type the type of the struct this is embedded in. @@ -321,8 +328,7 @@ static inline void llist_splice_init(struct llist_head *llist, pos = llist_entry(pos->member.next, typeof(*pos), member), \ prefetch(pos->member.next)) -/*! Iterate over llist of given type, safe against removal of - * non-consecutive(!) llist entries. +/*! Iterate over llist of given type, safe against removal of llist entry. * \param pos the 'type *' to use as a loop counter. * \param n another 'type *' to use as temporary storage. * \param head the head of the list over which to iterate. @@ -393,6 +399,252 @@ static inline unsigned int llist_count(const struct llist_head *head) return i; } + + +/*! Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +#define READ_ONCE(x) x +#define WRITE_ONCE(a, b) a = b + +/*! Has node been removed from list and reinitialized?. + * \param[in] h: Node to be checked + * \return 1 if node is unhashed; 0 if not + * + * Not that not all removal functions will leave a node in unhashed + * state. For example, hlist_nulls_del_init_rcu() does leave the + * node in unhashed state, but hlist_nulls_del() does not. + */ +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +/*! Version of hlist_unhashed for lockless use. + * \param[in] n Node to be checked + * \return 1 if node is unhashed; 0 if not + * + * This variant of hlist_unhashed() must be used in lockless contexts + * to avoid potential load-tearing. The READ_ONCE() is paired with the + * various WRITE_ONCE() in hlist helpers that are defined below. + */ +static inline int hlist_unhashed_lockless(const struct hlist_node *h) +{ + return !READ_ONCE(h->pprev); +} + +/*!Is the specified hlist_head structure an empty hlist?. + * \param[in] h Structure to check. + * \return 1 if hlist is empty; 0 if not + */ +static inline int hlist_empty(const struct hlist_head *h) +{ + return !READ_ONCE(h->first); +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + + WRITE_ONCE(*pprev, next); + if (next) + WRITE_ONCE(next->pprev, pprev); +} + +/*! Delete the specified hlist_node from its list. + * \param[in] n: Node to delete. + * + * Note that this function leaves the node in hashed state. Use + * hlist_del_init() or similar instead to unhash @n. + */ +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = (struct hlist_node *)LLIST_POISON1; + n->pprev = (struct hlist_node **)LLIST_POISON2; +} + +/*! Delete the specified hlist_node from its list and initialize. + * \param[in] n Node to delete. + * + * Note that this function leaves the node in unhashed state. + */ +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +/*! add a new entry at the beginning of the hlist. + * \param[in] n new entry to be added + * \param[in] h hlist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + WRITE_ONCE(n->next, first); + if (first) + WRITE_ONCE(first->pprev, &n->next); + WRITE_ONCE(h->first, n); + WRITE_ONCE(n->pprev, &h->first); +} + +/*! add a new entry before the one specified. + * @n: new entry to be added + * @next: hlist node to add it before, which must be non-NULL + */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + WRITE_ONCE(n->pprev, next->pprev); + WRITE_ONCE(n->next, next); + WRITE_ONCE(next->pprev, &n->next); + WRITE_ONCE(*(n->pprev), n); +} + +/*! add a new entry after the one specified + * \param[in] n new entry to be added + * \param[in] prev hlist node to add it after, which must be non-NULL + */ +static inline void hlist_add_behind(struct hlist_node *n, + struct hlist_node *prev) +{ + WRITE_ONCE(n->next, prev->next); + WRITE_ONCE(prev->next, n); + WRITE_ONCE(n->pprev, &prev->next); + + if (n->next) + WRITE_ONCE(n->next->pprev, &n->next); +} + +/*! create a fake hlist consisting of a single headless node. + * \param[in] n Node to make a fake list out of + * + * This makes @n appear to be its own predecessor on a headless hlist. + * The point of this is to allow things like hlist_del() to work correctly + * in cases where there is no list. + */ +static inline void hlist_add_fake(struct hlist_node *n) +{ + n->pprev = &n->next; +} + +/*! Is this node a fake hlist?. + * \param[in] h Node to check for being a self-referential fake hlist. + */ +static inline bool hlist_fake(struct hlist_node *h) +{ + return h->pprev == &h->next; +} + +/*!is node the only element of the specified hlist?. + * \param[in] n Node to check for singularity. + * \param[in] h Header for potentially singular list. + * + * Check whether the node is the only node of the head without + * accessing head, thus avoiding unnecessary cache misses. + */ +static inline bool +hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h) +{ + return !n->next && n->pprev == &h->first; +} + +/*! Move an hlist. + * \param[in] old hlist_head for old list. + * \param[in] new hlist_head for new list. + * + * Move a list from one list head to another. Fixup the pprev + * reference of the first entry if it exists. + */ +static inline void hlist_move_list(struct hlist_head *old, + struct hlist_head *_new) +{ + _new->first = old->first; + if (_new->first) + _new->first->pprev = &_new->first; + old->first = NULL; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos ; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +#define hlist_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ + }) + +/*! iterate over list of given type. + * \param[out] pos the type * to use as a loop cursor. + * \param[in] head the head for your list. + * \param[in] member the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(pos, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +/*! iterate over a hlist continuing after current point. + * \param[out] pos the type * to use as a loop cursor. + * \param[in] member the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(pos, member) \ + for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +/*! iterate over a hlist continuing from current point. + * \param[out] pos the type * to use as a loop cursor. + * \param[in] member the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(pos, member) \ + for (; pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +/*! hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry. + * \param[out] pos the type * to use as a loop cursor. + * \param[out] n a &struct hlist_node to use as temporary storage + * \param[in] head the head for your list. + * \param[in] member the name of the hlist_node within the struct + */ +#define hlist_for_each_entry_safe(pos, n, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\ + pos && ({ n = pos->member.next; 1; }); \ + pos = hlist_entry_safe(n, typeof(*pos), member)) + + /*! * @} */ diff --git a/include/osmocom/core/linuxrbtree.h b/include/osmocom/core/linuxrbtree.h index d3f9fd12..e15317ec 100644 --- a/include/osmocom/core/linuxrbtree.h +++ b/include/osmocom/core/linuxrbtree.h @@ -12,11 +12,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - MA 02110-1301, USA. - linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. diff --git a/include/osmocom/core/log2.h b/include/osmocom/core/log2.h new file mode 100644 index 00000000..8c65768f --- /dev/null +++ b/include/osmocom/core/log2.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Integer base 2 logarithm calculation + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#pragma once +#include <stdint.h> + +/* from linux/asm-generic/bitops/{fls,fls64}.h - could later be enhanced + * with architecture specific optimized versions */ + +/** + * fls - find last (most-significant) bit set + * @x: the word to search + * + * This is defined the same way as ffs. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ +static inline __attribute__((always_inline)) int fls(unsigned int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +/** + * fls64 - find last set bit in a 64-bit word + * @x: the word to search + * + * This is defined in a similar way as the libc and compiler builtin + * ffsll, but returns the position of the most significant set bit. + * + * fls64(value) returns 0 if value is 0 or the position of the last + * set bit if value is nonzero. The last (most significant) bit is + * at position 64. + */ +static inline __attribute__((always_inline)) int fls64(uint64_t x) +{ + uint32_t h = x >> 32; + if (h) + return fls(h) + 32; + return fls(x); +} + +/* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented + * more efficiently than using fls() and fls64() + * - the arch is not required to handle n==0 if implementing the fallback + */ +#ifndef CONFIG_ARCH_HAS_ILOG2_U32 +static inline __attribute__((const)) +int __ilog2_u32(uint32_t n) +{ + return fls(n) - 1; +} +#endif + +#ifndef CONFIG_ARCH_HAS_ILOG2_U64 +static inline __attribute__((const)) +int __ilog2_u64(uint64_t n) +{ + return fls64(n) - 1; +} +#endif + +/** + * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value + * @n: parameter + * + * Use this where sparse expects a true constant expression, e.g. for array + * indices. + */ +#define const_ilog2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n) < 2 ? 0 : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ + (n) & (1ULL << 60) ? 60 : \ + (n) & (1ULL << 59) ? 59 : \ + (n) & (1ULL << 58) ? 58 : \ + (n) & (1ULL << 57) ? 57 : \ + (n) & (1ULL << 56) ? 56 : \ + (n) & (1ULL << 55) ? 55 : \ + (n) & (1ULL << 54) ? 54 : \ + (n) & (1ULL << 53) ? 53 : \ + (n) & (1ULL << 52) ? 52 : \ + (n) & (1ULL << 51) ? 51 : \ + (n) & (1ULL << 50) ? 50 : \ + (n) & (1ULL << 49) ? 49 : \ + (n) & (1ULL << 48) ? 48 : \ + (n) & (1ULL << 47) ? 47 : \ + (n) & (1ULL << 46) ? 46 : \ + (n) & (1ULL << 45) ? 45 : \ + (n) & (1ULL << 44) ? 44 : \ + (n) & (1ULL << 43) ? 43 : \ + (n) & (1ULL << 42) ? 42 : \ + (n) & (1ULL << 41) ? 41 : \ + (n) & (1ULL << 40) ? 40 : \ + (n) & (1ULL << 39) ? 39 : \ + (n) & (1ULL << 38) ? 38 : \ + (n) & (1ULL << 37) ? 37 : \ + (n) & (1ULL << 36) ? 36 : \ + (n) & (1ULL << 35) ? 35 : \ + (n) & (1ULL << 34) ? 34 : \ + (n) & (1ULL << 33) ? 33 : \ + (n) & (1ULL << 32) ? 32 : \ + (n) & (1ULL << 31) ? 31 : \ + (n) & (1ULL << 30) ? 30 : \ + (n) & (1ULL << 29) ? 29 : \ + (n) & (1ULL << 28) ? 28 : \ + (n) & (1ULL << 27) ? 27 : \ + (n) & (1ULL << 26) ? 26 : \ + (n) & (1ULL << 25) ? 25 : \ + (n) & (1ULL << 24) ? 24 : \ + (n) & (1ULL << 23) ? 23 : \ + (n) & (1ULL << 22) ? 22 : \ + (n) & (1ULL << 21) ? 21 : \ + (n) & (1ULL << 20) ? 20 : \ + (n) & (1ULL << 19) ? 19 : \ + (n) & (1ULL << 18) ? 18 : \ + (n) & (1ULL << 17) ? 17 : \ + (n) & (1ULL << 16) ? 16 : \ + (n) & (1ULL << 15) ? 15 : \ + (n) & (1ULL << 14) ? 14 : \ + (n) & (1ULL << 13) ? 13 : \ + (n) & (1ULL << 12) ? 12 : \ + (n) & (1ULL << 11) ? 11 : \ + (n) & (1ULL << 10) ? 10 : \ + (n) & (1ULL << 9) ? 9 : \ + (n) & (1ULL << 8) ? 8 : \ + (n) & (1ULL << 7) ? 7 : \ + (n) & (1ULL << 6) ? 6 : \ + (n) & (1ULL << 5) ? 5 : \ + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ + 1) : \ + -1) + +/** + * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * @n: parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? \ + const_ilog2(n) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ + ) diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h index 79eec10d..82e686f2 100644 --- a/include/osmocom/core/logging.h +++ b/include/osmocom/core/logging.h @@ -11,15 +11,16 @@ #include <osmocom/core/defs.h> #include <osmocom/core/linuxlist.h> -/*! Maximum number of logging contexts */ -#define LOG_MAX_CTX 8 -/*! Maximum number of logging filters */ -#define LOG_MAX_FILTERS 8 +extern struct log_info *osmo_log_info; #ifndef DEBUG #define DEBUG #endif +#ifdef LIBOSMOCORE_NO_LOGGING +#undef DEBUG +#endif + #ifdef DEBUG /*! Log a debug message through the Osmocom logging framework * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL) @@ -54,11 +55,19 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, * \param[in] fmt format string * \param[in] args variable argument list */ +#ifndef LIBOSMOCORE_NO_LOGGING #define LOGPC(ss, level, fmt, args...) \ do { \ + if (!osmo_log_info) { \ + logp_stub(__FILE__, __LINE__, 1, fmt, ##args); \ + break; \ + } \ if (log_check_level(ss, level)) \ logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args); \ } while(0) +#else +#define LOGPC(ss, level, fmt, args...) +#endif /*! Log through the Osmocom logging framework with explicit source. * If caller_file is passed as NULL, __FILE__ and __LINE__ are used @@ -88,8 +97,16 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, * \param[in] fmt format string * \param[in] args variable argument list */ +#ifndef LIBOSMOCORE_NO_LOGGING #define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...) \ do { \ + if (!osmo_log_info) { \ + if (caller_file) \ + logp_stub(caller_file, caller_line, cont, fmt, ##args); \ + else \ + logp_stub(__FILE__, __LINE__, cont, fmt, ##args); \ + break; \ + } \ if (log_check_level(ss, level)) {\ if (caller_file) \ logp2(ss, level, caller_file, caller_line, cont, fmt, ##args); \ @@ -97,6 +114,9 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, logp2(ss, level, __FILE__, __LINE__, cont, fmt, ##args); \ }\ } while(0) +#else +#define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...) +#endif /*! different log levels */ #define LOGL_DEBUG 1 /*!< debugging information */ @@ -125,7 +145,17 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, #define DLMGCP -17 /*!< Osmocom MGCP */ #define DLJIBUF -18 /*!< Osmocom Jitter Buffer */ #define DLRSPRO -19 /*!< Osmocom Remote SIM Protocol */ -#define OSMO_NUM_DLIB 19 /*!< Number of logging sub-systems in libraries */ +#define DLNS -20 /*!< Osmocom NS layer */ +#define DLBSSGP -21 /*!< Osmocom BSSGP layer */ +#define DLNSDATA -22 /*!< Osmocom NS layer data pdus */ +#define DLNSSIGNAL -23 /*!< Osmocom NS layer signal pdus */ +#define DLIUUP -24 /*!< Osmocom IuUP layer */ +#define DLPFCP -25 /*!< Osmocom Packet Forwarding Control Protocol */ +#define DLCSN1 -26 /*!< CSN.1 (Concrete Syntax Notation 1) codec */ +#define DLM2PA -27 /*!< Osmocom M2PA (libosmo-sigtran) */ +#define DLM2UA -28 /*!< Reserved for future Osmocom M2UA (libosmo-sigtran) */ +#define DLIO -29 /*!< Osmocom IO sub-system */ +#define OSMO_NUM_DLIB 29 /*!< Number of logging sub-systems in libraries */ /* Colors that can be used in log_info_cat.color */ #define OSMO_LOGCOLOR_NORMAL NULL @@ -161,11 +191,6 @@ struct log_info_cat { uint8_t enabled; /*!< is this category enabled or not */ }; -/*! Log context information, passed to filter */ -struct log_context { - void *ctx[LOG_MAX_CTX+1]; -}; - /*! Indexes to indicate the object currently acted upon. * Array indexes for the global \a log_context array. */ enum log_ctx_index { @@ -174,6 +199,7 @@ enum log_ctx_index { LOG_CTX_BSC_SUBSCR, LOG_CTX_VLR_SUBSCR, LOG_CTX_L1_SAPI, + LOG_CTX_GB_NSE, _LOG_CTX_COUNT }; @@ -187,9 +213,20 @@ enum log_filter_index { LOG_FLT_BSC_SUBSCR, LOG_FLT_VLR_SUBSCR, LOG_FLT_L1_SAPI, + LOG_FLT_GB_NSE, _LOG_FLT_COUNT }; +/*! Maximum number of logging contexts */ +#define LOG_MAX_CTX _LOG_CTX_COUNT +/*! Maximum number of logging filters */ +#define LOG_MAX_FILTERS _LOG_FLT_COUNT + +/*! Log context information, passed to filter */ +struct log_context { + void *ctx[LOG_MAX_CTX+1]; +}; + /*! Compatibility with older libosmocore versions */ #define LOG_FILTER_ALL (1<<LOG_FLT_ALL) /*! Compatibility with older libosmocore versions */ @@ -243,6 +280,7 @@ enum log_target_type { LOG_TGT_TYPE_STDERR, /*!< stderr logging */ LOG_TGT_TYPE_STRRB, /*!< osmo_strrb-backed logging */ LOG_TGT_TYPE_GSMTAP, /*!< GSMTAP network logging */ + LOG_TGT_TYPE_SYSTEMD, /*!< systemd journal logging */ }; /*! Whether/how to log the source filename (and line number). */ @@ -260,7 +298,7 @@ enum log_filename_pos { /*! structure representing a logging target */ struct log_target { - struct llist_head entry; /*!< linked list */ + struct llist_head entry; /*!< linked list */ /*! Internal data for filtering */ int filter_map; @@ -276,6 +314,8 @@ struct log_target { unsigned int use_color:1; /*! should log messages be prefixed with a timestamp? */ unsigned int print_timestamp:1; + /*! should log messages be prefixed with the logger Thread ID? */ + unsigned int print_tid:1; /*! DEPRECATED: use print_filename2 instead. */ unsigned int print_filename:1; /*! should log messages be prefixed with a category name? */ @@ -288,8 +328,11 @@ struct log_target { union { struct { + /* direct, blocking output via stdio */ FILE *out; const char *fname; + /* indirect output via write_queue and osmo_select_main() */ + struct osmo_wqueue *wqueue; } tgt_file; struct { @@ -310,6 +353,10 @@ struct log_target { const char *ident; const char *hostname; } tgt_gsmtap; + + struct { + bool raw; + } sd_journal; }; /*! call-back function to be called when the logging framework @@ -351,6 +398,7 @@ struct log_target { void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7))); +void logp_stub(const char *file, int line, int cont, const char *format, ...); int log_init(const struct log_info *inf, void *talloc_ctx); void log_fini(void); int log_check_level(int subsys, unsigned int level); @@ -361,11 +409,13 @@ int log_set_context(uint8_t ctx, void *value); /* filter on the targets */ void log_set_all_filter(struct log_target *target, int); - +int log_cache_enable(void); +void log_cache_update(int mapped_subsys, uint8_t enabled, uint8_t level); void log_set_use_color(struct log_target *target, int); void log_set_print_extended_timestamp(struct log_target *target, int); void log_set_print_timestamp(struct log_target *target, int); -void log_set_print_filename(struct log_target *target, int); +void log_set_print_tid(struct log_target *target, int); +void log_set_print_filename(struct log_target *target, int) OSMO_DEPRECATED("Use log_set_print_filename2() instead"); void log_set_print_filename2(struct log_target *target, enum log_filename_type lft); void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos); void log_set_print_category(struct log_target *target, int); @@ -391,13 +441,17 @@ struct log_target *log_target_create_gsmtap(const char *host, uint16_t port, const char *ident, bool ofd_wq_mode, bool add_sink); +struct log_target *log_target_create_systemd(bool raw); +void log_target_systemd_set_raw(struct log_target *target, bool raw); int log_target_file_reopen(struct log_target *tgt); +int log_target_file_switch_to_stream(struct log_target *tgt); +int log_target_file_switch_to_wqueue(struct log_target *tgt); int log_targets_reopen(void); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); -struct log_target *log_target_find(int type, const char *fname); +struct log_target *log_target_find(enum log_target_type type, const char *fname); void log_enable_multithread(void); diff --git a/include/osmocom/core/loggingrb.h b/include/osmocom/core/loggingrb.h index a9fb4047..6d501466 100644 --- a/include/osmocom/core/loggingrb.h +++ b/include/osmocom/core/loggingrb.h @@ -11,10 +11,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once diff --git a/include/osmocom/core/mnl.h b/include/osmocom/core/mnl.h new file mode 100644 index 00000000..11c83530 --- /dev/null +++ b/include/osmocom/core/mnl.h @@ -0,0 +1,22 @@ +/*! \file select.h + * libmnl integration + */ +#pragma once + +#include <osmocom/core/select.h> +#include <libmnl/libmnl.h> + +/*! osmocom wrapper around libmnl abstraction of netlink socket */ +struct osmo_mnl { + /*! osmo-wrapped netlink file descriptor */ + struct osmo_fd ofd; + /*! libmnl socket abstraction */ + struct mnl_socket *mnls; + /*! call-back called for received netlink messages */ + mnl_cb_t mnl_cb; + /*! opaque data provided by user */ + void *priv; +}; + +struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv); +void osmo_mnl_destroy(struct osmo_mnl *omnl); diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h index cc76e3ad..b05d8b6b 100644 --- a/include/osmocom/core/msgb.h +++ b/include/osmocom/core/msgb.h @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -74,6 +70,8 @@ extern int msgb_resize_area(struct msgb *msg, uint8_t *area, int old_size, int new_size); extern struct msgb *msgb_copy(const struct msgb *msg, const char *name); extern struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name); +extern struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name); +extern struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name); static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure)); /*! Free all msgbs from a queue built with msgb_enqueue(). @@ -129,13 +127,13 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue, #endif /*! obtain L1 header of msgb */ -#define msgb_l1(m) ((void *)(m->l1h)) +#define msgb_l1(m) ((void *)((m)->l1h)) /*! obtain L2 header of msgb */ -#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l2(m) ((void *)((m)->l2h)) /*! obtain L3 header of msgb */ -#define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_l3(m) ((void *)((m)->l3h)) /*! obtain L4 header of msgb */ -#define msgb_l4(m) ((void *)(m->l4h)) +#define msgb_l4(m) ((void *)((m)->l4h)) /*! obtain SMS header of msgb */ #define msgb_sms(m) msgb_l4(m) @@ -148,6 +146,7 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue, */ static inline unsigned int msgb_l1len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l1h); return msgb->tail - (uint8_t *)msgb_l1(msgb); } @@ -160,6 +159,7 @@ static inline unsigned int msgb_l1len(const struct msgb *msgb) */ static inline unsigned int msgb_l2len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l2h); return msgb->tail - (uint8_t *)msgb_l2(msgb); } @@ -172,6 +172,7 @@ static inline unsigned int msgb_l2len(const struct msgb *msgb) */ static inline unsigned int msgb_l3len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l3h); return msgb->tail - (uint8_t *)msgb_l3(msgb); } @@ -184,7 +185,8 @@ static inline unsigned int msgb_l3len(const struct msgb *msgb) */ static inline unsigned int msgb_l4len(const struct msgb *msgb) { - return msgb->tail - (uint8_t *)msgb_sms(msgb); + OSMO_ASSERT(msgb->l4h); + return msgb->tail - (uint8_t *)msgb_l4(msgb); } /*! determine the length of the header @@ -238,7 +240,7 @@ static inline int msgb_headroom(const struct msgb *msgb) static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) { unsigned char *tmp = msgb->tail; - if (msgb_tailroom(msgb) < (int) len) + if (OSMO_UNLIKELY(msgb_tailroom(msgb) < (int) len)) MSGB_ABORT(msgb, "Not enough tailroom msgb_put" " (allocated %u, head at %u, len %u, tailroom %u < want tailroom %u)\n", msgb->data_len - sizeof(struct msgb), @@ -286,7 +288,7 @@ static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) */ static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) { - if (msgb_length(msgb) < len) + if (OSMO_UNLIKELY(msgb_length(msgb) < len)) MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n", len, msgb_length(msgb)); msgb->tail -= len; @@ -338,7 +340,7 @@ static inline uint32_t msgb_get_u32(struct msgb *msgb) */ static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) { - if (msgb_headroom(msgb) < (int) len) + if (OSMO_UNLIKELY(msgb_headroom(msgb) < (int) len)) MSGB_ABORT(msgb, "Not enough headroom msgb_push" " (allocated %u, head at %u < want headroom %u, len %u, tailroom %u)\n", msgb->data_len - sizeof(struct msgb), @@ -401,7 +403,7 @@ static inline unsigned char *msgb_push_tl(struct msgb *msgb, uint8_t tag) */ static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) { - if (msgb_length(msgb) < len) + if (OSMO_UNLIKELY(msgb_length(msgb) < len)) MSGB_ABORT(msgb, "msgb too small to pull %u (len %u)\n", len, msgb_length(msgb)); msgb->len -= len; @@ -440,7 +442,7 @@ static inline unsigned char *msgb_pull_to_l2(struct msgb *msg) /*! remove uint8 from front of message * \param[in] msgb message buffer - * \returns 8bit value taken from end of msgb + * \returns 8bit value taken from the front of msgb */ static inline uint8_t msgb_pull_u8(struct msgb *msgb) { @@ -450,7 +452,7 @@ static inline uint8_t msgb_pull_u8(struct msgb *msgb) /*! remove uint16 from front of message * \param[in] msgb message buffer - * \returns 16bit value taken from end of msgb + * \returns 16bit value taken from the front of msgb */ static inline uint16_t msgb_pull_u16(struct msgb *msgb) { @@ -460,7 +462,7 @@ static inline uint16_t msgb_pull_u16(struct msgb *msgb) /*! remove uint32 from front of message * \param[in] msgb message buffer - * \returns 32bit value taken from end of msgb + * \returns 32bit value taken from the front of msgb */ static inline uint32_t msgb_pull_u32(struct msgb *msgb) { @@ -492,9 +494,9 @@ static inline void msgb_reserve(struct msgb *msg, int len) */ static inline int msgb_trim(struct msgb *msg, int len) { - if (len < 0) + if (OSMO_UNLIKELY(len < 0)) MSGB_ABORT(msg, "Negative length is not allowed\n"); - if (len > msg->data_len) + if (OSMO_UNLIKELY(len > msg->data_len)) return -1; msg->len = len; @@ -524,13 +526,13 @@ static inline int msgb_l3trim(struct msgb *msg, int l3len) * followed by \ref msgb_reserve in order to create a new \ref msgb with * user-specified amount of headroom. */ -static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int headroom, +static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, uint16_t size, uint16_t headroom, const char *name) { - osmo_static_assert(size >= headroom, headroom_bigger); + OSMO_ASSERT(size >= headroom); struct msgb *msg = msgb_alloc_c(ctx, size, name); - if (msg) + if (OSMO_LIKELY(msg)) msgb_reserve(msg, headroom); return msg; } @@ -546,13 +548,13 @@ static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int * followed by \ref msgb_reserve in order to create a new \ref msgb with * user-specified amount of headroom. */ -static inline struct msgb *msgb_alloc_headroom(int size, int headroom, +static inline struct msgb *msgb_alloc_headroom(uint16_t size, uint16_t headroom, const char *name) { - osmo_static_assert(size >= headroom, headroom_bigger); + OSMO_ASSERT(size >= headroom); struct msgb *msg = msgb_alloc(size, name); - if (msg) + if (OSMO_LIKELY(msg)) msgb_reserve(msg, headroom); return msg; } diff --git a/include/osmocom/core/msgfile.h b/include/osmocom/core/msgfile.h index 800b4311..cfa95905 100644 --- a/include/osmocom/core/msgfile.h +++ b/include/osmocom/core/msgfile.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once diff --git a/include/osmocom/core/netdev.h b/include/osmocom/core/netdev.h new file mode 100644 index 00000000..3238ec33 --- /dev/null +++ b/include/osmocom/core/netdev.h @@ -0,0 +1,49 @@ +/*! \file netdev.h + * network device (interface) convenience functions. */ + +#pragma once +#if (!EMBEDDED) + +#include <stddef.h> +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> + +struct osmo_netdev; + +typedef int (*osmo_netdev_ifupdown_ind_cb_t)(struct osmo_netdev *netdev, bool ifupdown); +typedef int (*osmo_netdev_dev_name_chg_cb_t)(struct osmo_netdev *netdev, const char *new_dev_name); +typedef int (*osmo_netdev_mtu_chg_cb_t)(struct osmo_netdev *netdev, unsigned int new_mtu); + +struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name); +void osmo_netdev_free(struct osmo_netdev *netdev); + +int osmo_netdev_register(struct osmo_netdev *netdev); +int osmo_netdev_unregister(struct osmo_netdev *netdev); +bool osmo_netdev_is_registered(struct osmo_netdev *netdev); + +const char *osmo_netdev_get_name(const struct osmo_netdev *netdev); + +void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data); +void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev); + +int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex); +unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev); + +const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev); + +int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns); +const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev); + +void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb); +void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb); +void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb); + +int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen); +int osmo_netdev_add_route(struct osmo_netdev *netdev, const struct osmo_sockaddr *dst_addr, + uint8_t dst_prefixlen, const struct osmo_sockaddr *gw_addr); +int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown); + +#endif /* (!EMBEDDED) */ +/*! @} */ diff --git a/include/osmocom/core/netns.h b/include/osmocom/core/netns.h new file mode 100644 index 00000000..5bbf2242 --- /dev/null +++ b/include/osmocom/core/netns.h @@ -0,0 +1,24 @@ +/*! \file netns.h + * Network namespace convenience functions. */ + +#pragma once +#if (!EMBEDDED) + +#if defined(__linux__) + +#include <signal.h> + +struct osmo_netns_switch_state { + sigset_t prev_sigmask; + int prev_nsfd; +}; + +int osmo_netns_open_fd(const char *name); +int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state); +int osmo_netns_switch_exit(struct osmo_netns_switch_state *state); + + +#endif /* defined(__linux__) */ + +#endif /* (!EMBEDDED) */ +/*! @} */ diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h new file mode 100644 index 00000000..fa1f9c3c --- /dev/null +++ b/include/osmocom/core/osmo_io.h @@ -0,0 +1,231 @@ +/*! \file osmo_io.h + * io(_uring) abstraction osmo fd compatibility + */ + +#pragma once + +#include <sys/socket.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> + +/*! \defgroup osmo_io Osmocom I/O interface + * @{ + * + * osmo_io is the new (2023) interface for performing asynchronous I/O. + * osmo_io encapsulates asynchronous, non-blocking I/O to sockets or other file descriptors + * with a submission/completion model. + * + * For writes, the API user submits write requests, and receives + * completion call-backs once the write completes. + * + * For reads, the API user specifies the size (and headroom) for message buffers, and osmo_io + * internally allocates msgb's accordingly. Whenever data arrives at the socket/file descriptor, + * osmo_io reads the data into such a msgb and hands it to a read-completion call-back provided + * by the API user. + * + * A given socket/file descriptor is represented by struct osmo_io_fd. osmo_io_fd are named, + * i.e. the API user can provide a meaningful name describing the purpose (such as protocol/interface or the + * name of the remote peer). This allows osmo_io to log any related [error] messages using this name as + * context. + * + * When implementing some SOCK_STREAM / SOCK_SEQPACKET based client/server transports (such as those on top + * of TCP or SCTP), you are most likely better off using the osmo_stream_cli / osmo_stream_srv abstractions + * provided by libosmo-netif. They in turn can be used in an osmo_io mode, see the respective documentation. + * + * If you use osmo_io_fd directly, the life-cycle usually will look as follows: + * + * 1. open some socket and bind and/or connect it + * 2. Allocate an osmo_io_fd using osmo_iofd_setup(), configuring the mode and specifying the call-backs + * 3. Registering it with osmo_iofd_register(), which enables reading + * 4. Handle inbound data via {read,recvfrom,recvmsg} call-backs; write to it using + * osmo_iofd_{write,sendto_sendmsg}_msg() + * 5. Eventually un-register it using osmo_iofd_unregister(). Afterwards, you can re-cycle the iofd by + * calling osmo_iofd_register() with a new file-descriptor, or free it using osmo_iofd_free(). + * + * \file osmo_io.h */ + +/*! log macro used for logging information related to the osmo_io_fd. + * \param[in] iofd osmo_io_fd about which we're logging + * \param[in] level log-level (LOGL_DEBUG, LOGL_INFO, LOGL_NOTICE, LOGL_ERROR, LOGL_FATAL) + * \param[in] fmt printf-style format string + * \param[in] args arguments to the format string + */ +#define LOGPIO(iofd, level, fmt, args...) \ + LOGP(DLIO, level, "iofd(%s) " fmt, iofd->name, ## args) + +struct osmo_io_fd; + +/*! The _mode_ of an osmo_io_fd determines if read/write, recvfrom/sendmsg or recvmsg/sendmsg semantics are + * used. */ +enum osmo_io_fd_mode { + /*! use read() / write() semantics with read_cb/write_cb in osmo_io_ops */ + OSMO_IO_FD_MODE_READ_WRITE, + /*! use recvfrom() / sendto() semantics with recvfrom_cb/sendto_cb in osmo_io_ops */ + OSMO_IO_FD_MODE_RECVFROM_SENDTO, + /*! emulate recvmsg() / sendmsg() semantics with recvmsg_cb/sendto_cb in osmo_io_ops */ + OSMO_IO_FD_MODE_RECVMSG_SENDMSG, +}; + +/*! The back-end used by osmo_io. There can be multiple different back-ends available on a given system; + * only one of it is used for all I/O performed via osmo_io in one given process. */ +enum osmo_io_backend { + /*! classic back-end using poll(2) and direct read/write/recvfrom/sendto/recvmsg/sendmsg syscalls */ + OSMO_IO_BACKEND_POLL, + /*! back-end using io_uring to perform efficient I/O and reduce syscall overhead */ + OSMO_IO_BACKEND_IO_URING, +}; + +extern const struct value_string osmo_io_backend_names[]; +/*! return the string name of an osmo_io_backend */ +static inline const char *osmo_io_backend_name(enum osmo_io_backend val) +{ return get_value_string(osmo_io_backend_names, val); } + +extern const struct value_string osmo_iofd_mode_names[]; +/*! return the string name of an osmo_io_mode */ +static inline const char *osmo_iofd_mode_name(enum osmo_io_fd_mode val) +{ return get_value_string(osmo_iofd_mode_names, val); } + +/*! I/O operations (call-back functions) related to an osmo_io_fd */ +struct osmo_io_ops { + /* mode OSMO_IO_FD_MODE_READ_WRITE: */ + struct { + /*! completion call-back function when something was read from fd. Only valid in + * OSMO_IO_FD_MODE_READ_WRITE. + * \param[in] iofd osmo_io_fd for which read() has completed. + * \param[in] res return value of the read() 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! */ + void (*read_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg); + + /*! completion call-back function when write issued via osmo_iofd_write_msgb() has completed + * on fd. Only valid in OSMO_IO_FD_MODE_READ_WRITE. + * \param[in] iofd on which a write() has completed. + * \param[in] res return value of the write() call, or -errno in case of error. + * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the + * call-back; it is automatically freed after the call-back terminates! */ + void (*write_cb)(struct osmo_io_fd *iofd, int res, + struct msgb *msg); + + /*! optional call-back function to segment the data at message boundaries. + * \param[in] msg message buffer whose data is to be segmented + * \returns See full function description. + * + * This is useful when message boundaries are to be preserved over a SOCK_STREAM transport + * socket like TCP. Can be NULL for any application not requiring de-segmentation of + * received data. + * + * The call-back needs to return the size of the next message. If it returns + * -EAGAIN or a value larger than msgb_length() (message is incomplete) + * osmo_io will wait for more data to be read. Other negative values + * cause the msg to be discarded. + * If a full message was received (segmentation_cb() returns a value <= msgb_length()) + * the msgb will be trimmed to size by osmo_io and forwarded to the read call-back. Any + * parsing done to the msgb by segmentation_cb() will be preserved for the read_cb() + * (e.g. setting lxh or msgb->cb). + * + * Only one (or none) of both segmentation_cb and segmentation_cb2 shall be set. + * Having both set will be considered an error during iofd setup. */ + int (*segmentation_cb)(struct msgb *msg); + + /*! optional call-back function to segment the data at message boundaries. + * \param[in] iofd handling msg + * \param[in] msg message buffer whose data is to be segmented + * \returns See full function description. + * + * Same as segmentation_cb above, with an extra parameter to have access to the iofd and its + * related functionalities (eg data pointer). This is useful for users requiring to store + * global state or access external objects while segmenting. + * + * The provided iofd shall not be freed by the user during the callback. + * + * Only one (or none) of both segmentation_cb and segmentation_cb2 shall be set. + * Having both set will be considered an error during iofd setup. */ + int (*segmentation_cb2)(struct osmo_io_fd *iofd, struct msgb *msg); + }; + + /* mode OSMO_IO_FD_MODE_RECVFROM_SENDTO: */ + struct { + /*! completion call-back function when recvfrom(2) has completed. + * Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO. + * \param[in] iofd osmo_io_fd for which recvfrom() has completed. + * \param[in] res return value of the recvfrom() 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! + * \param[in] saddr socket-address of sender from which data was received. */ + void (*recvfrom_cb)(struct osmo_io_fd *iofd, int res, + struct msgb *msg, + const struct osmo_sockaddr *saddr); + /*! completion call-back function when sendto() issued via osmo_iofd_sendto_msgb() has + * completed on fd. Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO. + * \param[in] iofd on which a sendto() has completed. + * \param[in] res return value of the sendto() call, or -errno in case of error. + * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the + * call-back; it is automatically freed after the call-back terminates! + * \param[in] daddr socket-address of destination to which data was sent. */ + void (*sendto_cb)(struct osmo_io_fd *iofd, int res, + struct msgb *msg, + const struct osmo_sockaddr *daddr); + }; + + /* mode OSMO_IO_FD_MODE_RECVMSG_SENDMSG: */ + struct { + /*! completion call-back function when recvmsg(2) has completed. + * Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG. + * \param[in] iofd osmo_io_fd for which recvmsg() has completed. + * \param[in] res return value of the 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! + * \param[in] msgh msghdr containing metadata related to the recvmsg call. Only valid until + * call-back ends. */ + void (*recvmsg_cb)(struct osmo_io_fd *iofd, int res, + struct msgb *msg, const struct msghdr *msgh); + /*! completion call-back function when sendmsg() issued via osmo_iofd_sendmsg_msgb() has + * completed on fd. Only valid in Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG. + * \param[in] iofd on which a sendmsg() has completed. + * \param[in] res return value of the sendmsg() call, or -errno in case of error. + * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the + * call-back; it is automatically freed after the call-back terminates! */ + void (*sendmsg_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg); + }; +}; + +void osmo_iofd_init(void); + +struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name, + enum osmo_io_fd_mode mode, const struct osmo_io_ops *ioops, void *data); +int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size); +int osmo_iofd_register(struct osmo_io_fd *iofd, int fd); +int osmo_iofd_unregister(struct osmo_io_fd *iofd); +unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd); +void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd); +int osmo_iofd_close(struct osmo_io_fd *iofd); +void osmo_iofd_free(struct osmo_io_fd *iofd); + +void osmo_iofd_notify_connected(struct osmo_io_fd *iofd); + +int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg); +int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags, + const struct osmo_sockaddr *dest); +int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags, + const struct msghdr *msgh); + +void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom); +void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int size); +void *osmo_iofd_get_data(const struct osmo_io_fd *iofd); +void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data); + +unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd); +void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr); + +int osmo_iofd_get_fd(const struct osmo_io_fd *iofd); +const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd); +void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name); + +int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops); +void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops); + +/*! @} */ diff --git a/include/osmocom/core/prim.h b/include/osmocom/core/prim.h index 99eabff0..8e6b436d 100644 --- a/include/osmocom/core/prim.h +++ b/include/osmocom/core/prim.h @@ -30,7 +30,11 @@ enum osmo_prim_operation { PRIM_OP_CONFIRM, /*!< confirm */ }; -extern const struct value_string osmo_prim_op_names[5]; +extern const struct value_string osmo_prim_op_names[]; +static inline const char *osmo_prim_operation_name(enum osmo_prim_operation val) +{ + return get_value_string(osmo_prim_op_names, val); +} /*!< The upper 8 byte of the technology, the lower 24 bits for the SAP */ #define _SAP_GSM_SHIFT 24 diff --git a/include/osmocom/core/rate_ctr.h b/include/osmocom/core/rate_ctr.h index f7e6e225..0a3eb2ce 100644 --- a/include/osmocom/core/rate_ctr.h +++ b/include/osmocom/core/rate_ctr.h @@ -61,7 +61,9 @@ struct rate_ctr_group { const struct rate_ctr_group_desc *desc; /*! The index of this ctr_group within its class */ unsigned int idx; - /*! Actual counter structures below */ + /*! Optional string-based identifier to be used instead of index at report time */ + char *name; + /*! Actual counter structures below. Don't access it directly, use APIs below! */ struct rate_ctr ctr[0]; }; @@ -73,6 +75,9 @@ static inline void rate_ctr_group_upd_idx(struct rate_ctr_group *grp, unsigned i { grp->idx = idx; } +void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name); + +struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx); void rate_ctr_group_free(struct rate_ctr_group *grp); @@ -81,6 +86,15 @@ void rate_ctr_group_free(struct rate_ctr_group *grp); * \param inc quantity to increment \a ctr by */ void rate_ctr_add(struct rate_ctr *ctr, int inc); +/*! Increment the counter by \a inc + * \param ctrg \ref rate_ctr_group of counter + * \param idx index into \a ctrg counter group + * \param inc quantity to increment \a ctr by */ +static inline void rate_ctr_add2(struct rate_ctr_group *ctrg, unsigned int idx, int inc) +{ + rate_ctr_add(rate_ctr_group_get_ctr(ctrg, idx), inc); +} + /*! Increment the counter by 1 * \param ctr \ref rate_ctr to increment */ static inline void rate_ctr_inc(struct rate_ctr *ctr) @@ -93,7 +107,7 @@ static inline void rate_ctr_inc(struct rate_ctr *ctr) * \param idx index into \a ctrg counter group */ static inline void rate_ctr_inc2(struct rate_ctr_group *ctrg, unsigned int idx) { - rate_ctr_inc(&ctrg->ctr[idx]); + rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, idx)); } @@ -116,4 +130,7 @@ int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg, int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data); +void rate_ctr_reset(struct rate_ctr *ctr); +void rate_ctr_group_reset(struct rate_ctr_group *ctrg); + /*! @} */ diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h index bc601982..fc148512 100644 --- a/include/osmocom/core/select.h +++ b/include/osmocom/core/select.h @@ -19,6 +19,8 @@ #define OSMO_FD_WRITE 0x0002 /*! Indicate interest in exceptions from the file descriptor */ #define OSMO_FD_EXCEPT 0x0004 +/*! Used as when_mask in osmo_fd_update_when() */ +#define OSMO_FD_MASK 0xFFFF /* legacy naming dating back to early OpenBSC / bsc_hack of 2008 */ #define BSC_FD_READ OSMO_FD_READ @@ -47,6 +49,24 @@ void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when, int (*cb)(struct osmo_fd *fd, unsigned int what), void *data, unsigned int priv_nr); +void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when); + +static inline void osmo_fd_read_enable(struct osmo_fd *ofd) { + osmo_fd_update_when(ofd, OSMO_FD_MASK, OSMO_FD_READ); +} + +static inline void osmo_fd_read_disable(struct osmo_fd *ofd) { + osmo_fd_update_when(ofd, ~OSMO_FD_READ, 0); +} + +static inline void osmo_fd_write_enable(struct osmo_fd *ofd) { + osmo_fd_update_when(ofd, OSMO_FD_MASK, OSMO_FD_WRITE); +} + +static inline void osmo_fd_write_disable(struct osmo_fd *ofd) { + osmo_fd_update_when(ofd, ~OSMO_FD_WRITE, 0); +} + bool osmo_fd_is_registered(struct osmo_fd *fd); int osmo_fd_register(struct osmo_fd *fd); void osmo_fd_unregister(struct osmo_fd *fd); @@ -85,5 +105,8 @@ struct osmo_signalfd { struct osmo_signalfd * osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data); +void osmo_select_shutdown_request(void); +int osmo_select_shutdown_requested(void); +bool osmo_select_shutdown_done(void); /*! @} */ diff --git a/include/osmocom/core/sercomm.h b/include/osmocom/core/sercomm.h index 072f4d9c..38e6271c 100644 --- a/include/osmocom/core/sercomm.h +++ b/include/osmocom/core/sercomm.h @@ -2,8 +2,7 @@ * Osmocom Sercomm HDLC (de)multiplex. */ -#ifndef _SERCOMM_H -#define _SERCOMM_H +#pragma once #include <osmocom/core/msgb.h> @@ -110,5 +109,3 @@ static inline struct msgb *osmo_sercomm_alloc_msgb(unsigned int len) } /*! @} */ - -#endif /* _SERCOMM_H */ diff --git a/include/osmocom/core/serial.h b/include/osmocom/core/serial.h index 39614a47..0ac29681 100644 --- a/include/osmocom/core/serial.h +++ b/include/osmocom/core/serial.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \defgroup serial Utility functions to deal with serial ports @@ -32,5 +28,6 @@ int osmo_serial_init(const char *dev, speed_t baudrate); int osmo_serial_set_baudrate(int fd, speed_t baudrate); int osmo_serial_set_custom_baudrate(int fd, int baudrate); int osmo_serial_clear_custom_baudrate(int fd); +int osmo_serial_speed_t(unsigned int baudrate, speed_t *speed); /*! @} */ diff --git a/include/osmocom/core/sockaddr_str.h b/include/osmocom/core/sockaddr_str.h index e42216a4..8ec514c7 100644 --- a/include/osmocom/core/sockaddr_str.h +++ b/include/osmocom/core/sockaddr_str.h @@ -20,10 +20,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once @@ -38,6 +34,7 @@ struct in6_addr; struct sockaddr_storage; struct sockaddr_in; struct sockaddr_in6; +struct osmo_sockaddr; /*! \defgroup sockaddr_str IP address/port utilities. * @{ @@ -63,17 +60,23 @@ struct osmo_sockaddr_str { * printf("got " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(my_sockaddr_str)); */ #define OSMO_SOCKADDR_STR_FMT "%s%s%s:%u" +#define OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(R) \ + ((R)->af == AF_INET6) ? "[" : "", \ + (R)->ip, \ + ((R)->af == AF_INET6) ? "]" : "", \ + (R)->port #define OSMO_SOCKADDR_STR_FMT_ARGS(R) \ - ((R) && (R)->af == AF_INET6)? "[" : "", \ - (R)? (R)->ip : "NULL", \ - ((R) && (R)->af == AF_INET6)? "]" : "", \ - (R)? (R)->port : 0 + ((R) && (R)->af == AF_INET6) ? "[" : "", \ + (R) ? (R)->ip : "NULL", \ + ((R) && (R)->af == AF_INET6) ? "]" : "", \ + (R) ? (R)->port : 0 bool osmo_sockaddr_str_is_set(const struct osmo_sockaddr_str *sockaddr_str); bool osmo_sockaddr_str_is_nonzero(const struct osmo_sockaddr_str *sockaddr_str); int osmo_sockaddr_str_cmp(const struct osmo_sockaddr_str *a, const struct osmo_sockaddr_str *b); int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port); +int osmo_sockaddr_str_from_str2(struct osmo_sockaddr_str *sockaddr_str, const char *ip); int osmo_sockaddr_str_from_in_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in_addr *addr, uint16_t port); int osmo_sockaddr_str_from_in6_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in6_addr *addr, uint16_t port); @@ -82,6 +85,7 @@ int osmo_sockaddr_str_from_32h(struct osmo_sockaddr_str *sockaddr_str, uint32_t int osmo_sockaddr_str_from_sockaddr_in(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in *src); int osmo_sockaddr_str_from_sockaddr_in6(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in6 *src); int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_storage *src); +int osmo_sockaddr_str_from_osa(struct osmo_sockaddr_str *sockaddr_str, const struct osmo_sockaddr *src); int osmo_sockaddr_str_to_in_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in_addr *dst); int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in6_addr *dst); @@ -90,6 +94,7 @@ int osmo_sockaddr_str_to_32h(const struct osmo_sockaddr_str *sockaddr_str, uint3 int osmo_sockaddr_str_to_sockaddr_in(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in *dst); int osmo_sockaddr_str_to_sockaddr_in6(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in6 *dst); int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_storage *dst); +int osmo_sockaddr_str_to_osa(const struct osmo_sockaddr_str *sockaddr_str, struct osmo_sockaddr *dst); int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port) OSMO_DEPRECATED("osmo_sockaddr_str_from_32n() actually uses *host* byte order. Use osmo_sockaddr_str_from_32h() instead"); diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 129612c6..ea73cda8 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -2,6 +2,7 @@ * Osmocom socket convenience functions. */ #pragma once +#if (!EMBEDDED) /*! \defgroup socket Socket convenience functions * @{ @@ -11,16 +12,89 @@ #include <stdbool.h> #include <stddef.h> -#if (!EMBEDDED) #include <arpa/inet.h> +#include <osmocom/core/defs.h> + +/*! maximum number of local or remote addresses supported by an osmo_sock instance */ +#define OSMO_SOCK_MAX_ADDRS 32 + /*! maximum length of a socket name ("r=1.2.3.4:123<->l=5.6.7.8:987") */ #define OSMO_SOCK_NAME_MAXLEN (2 + INET6_ADDRSTRLEN + 1 + 5 + 3 + 2 + INET6_ADDRSTRLEN + 1 + 5 + 1) -#endif + +/*! maximum length of a multi-address socket peer (endpoint) name: (5.6.7.8|::9):987 + * char '(' + OSMO_STREAM_MAX_ADDRS - 1 addr separators + chars "):" + port buffer + char '\0' + */ +#define OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN (INET6_ADDRSTRLEN * OSMO_SOCK_MAX_ADDRS + INET6_ADDRSTRLEN + 2 + 6 + 1) +/*! maximum length of a multia-address socket name ("r=(::2|1.2.3.4):123<->l=(5.6.7.8|::9):987") */ +#define OSMO_SOCK_MULTIADDR_NAME_MAXLEN (OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN + 7) + struct sockaddr_in; struct sockaddr; struct osmo_fd; +struct sctp_paddrinfo; + +struct osmo_sockaddr { + union { + struct sockaddr sa; + struct sockaddr_storage sas; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } u; +}; + +int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen); +int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr); + +/*! Return the size of the variant used in the address + * NOTE: This does not return the size of the in{,6}_addr, but rather the size of the + * surrounding sockaddr_in{,6}. + * \param[in] addr the osmo_sockaddr to get the size of + * \return the size of the struct variant being used. If the value in sa_family is unsupported it will return + * the size of struct osmo_sockaddr. Returns 0 if addr is NULL. This way it can simply be a wrapper for sendto() + * which can be called with NULL/0 for dest_addr / addrlen (and then behaves like a send() call). + */ +static inline socklen_t osmo_sockaddr_size(const struct osmo_sockaddr *addr) +{ + if (!addr) + return 0; + + switch (addr->u.sa.sa_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + return sizeof(struct osmo_sockaddr); + } +} + +unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, + const struct sockaddr *sa); +size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, + const struct sockaddr_in *sin); + +const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst); +uint16_t osmo_sockaddr_port(const struct sockaddr *sa); +void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port); + +int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip, + const struct osmo_sockaddr *remote_ip); +int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, + const struct osmo_sockaddr *b); + +int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os); +int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len); +int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port); + +int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *addr); + +const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr); +char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len, + const struct osmo_sockaddr *sockaddr); +int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr); +char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr); /* flags for osmo_sock_init. */ /*! connect the socket to a remote peer */ @@ -36,8 +110,14 @@ struct osmo_fd; /*! use SO_REUSEADDR on UDP ports (required for multicast) */ #define OSMO_SOCK_F_UDP_REUSEADDR (1 << 5) -/*! maximum number of local or remote addresses supported by an osmo_sock instance */ -#define OSMO_SOCK_MAX_ADDRS 32 +/*! use OSMO_SOCK_F_DSCP(x) to set IP DSCP 'x' for packets transmitted on the socket */ +#define OSMO_SOCK_F_DSCP(x) (((x)&0x3f) << 24) +#define GET_OSMO_SOCK_F_DSCP(f) (((f) >> 24) & 0x3f) + +/*! use OSMO_SOCK_F_PRIO(x) to set priority 'x' for packets transmitted on the socket */ +#define OSMO_SOCK_F_PRIO(x) (((x)&0xff) << 16) +#define GET_OSMO_SOCK_F_PRIO(f) (((f) >> 16) & 0xff) + int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port, unsigned int flags); @@ -46,9 +126,48 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto, const char *local_host, uint16_t local_port, const char *remote_host, uint16_t remote_port, unsigned int flags); +struct osmo_sock_init2_multiaddr_pars { + union { + struct { + uint8_t version; /* set to 0 */ + struct { + bool set; + bool abort_on_failure; + uint32_t value; + } sockopt_auth_supported; + struct { + bool set; + bool abort_on_failure; + uint32_t value; + } sockopt_asconf_supported; + struct { + bool set; + bool abort_on_failure; + bool num_ostreams_present; + bool max_instreams_present; + bool max_attempts_present; + bool max_init_timeo_present; + uint16_t num_ostreams_value; + uint16_t max_instreams_value; + uint16_t max_attempts_value; + uint16_t max_init_timeo_value; + } sockopt_initmsg; + } sctp; + }; +}; int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto, const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port, - const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags); + const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags) + OSMO_DEPRECATED_OUTSIDE("Use osmo_sock_init2_multiaddr2() instead"); +int osmo_sock_init2_multiaddr2(uint16_t family, uint16_t type, uint8_t proto, + const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port, + const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, + unsigned int flags, struct osmo_sock_init2_multiaddr_pars *pars); + +int osmo_sock_init_osa(uint16_t type, uint8_t proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, + unsigned int flags); int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, const char *host, uint16_t port, unsigned int flags); @@ -57,16 +176,14 @@ int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto, const char *local_host, uint16_t local_port, const char *remote_host, uint16_t remote_port, unsigned int flags); +int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, + unsigned int flags); + int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, uint8_t proto, unsigned int flags); -int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen); - -unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, - const struct sockaddr *sa); -size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, - const struct sockaddr_in *sin); - int osmo_sock_unix_init(uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); @@ -83,6 +200,14 @@ int osmo_sock_get_local_ip_port(int fd, char *port, size_t len); int osmo_sock_get_remote_ip(int fd, char *host, size_t len); int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len); +int osmo_sock_multiaddr_get_ip_and_port(int fd, int ip_proto, char *ip, size_t *ip_cnt, size_t ip_len, + char *port, size_t port_len, bool local); +int osmo_multiaddr_ip_and_port_snprintf(char *str, size_t str_len, + const char *ip, size_t ip_cnt, size_t ip_len, + const char *portbuf); +int osmo_sock_multiaddr_get_name_buf(char *str, size_t str_len, int fd, int sk_proto); +int osmo_sock_multiaddr_add_local_addr(int sfd, const char **addrs, size_t addrs_cnt); +int osmo_sock_multiaddr_del_local_addr(int sfd, const char **addrs, size_t addrs_cnt); int osmo_sock_mcast_loop_set(int fd, bool enable); int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl); @@ -92,4 +217,10 @@ int osmo_sock_mcast_subscribe(int fd, const char *grp_addr); int osmo_sock_local_ip(char *local_ip, const char *remote_ip); +int osmo_sock_set_dscp(int fd, uint8_t dscp); +int osmo_sock_set_priority(int fd, int prio); + +int osmo_sock_sctp_get_peer_addr_info(int fd, struct sctp_paddrinfo *pinfo, size_t *pinfo_cnt); + +#endif /* (!EMBEDDED) */ /*! @} */ diff --git a/include/osmocom/core/socket_compat.h.tpl b/include/osmocom/core/socket_compat.h.tpl new file mode 100644 index 00000000..43bee9ee --- /dev/null +++ b/include/osmocom/core/socket_compat.h.tpl @@ -0,0 +1,10 @@ +#define HAVE_STRUCT_SOCKADDR_STORAGE XX + +#if HAVE_STRUCT_SOCKADDR_STORAGE + #include <sys/socket.h> +#else +struct sockaddr_storage { + unsigned short ss_family; + char __data[128 - sizeof(unsigned short)]; +}; +#endif diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h new file mode 100644 index 00000000..afc6ad6d --- /dev/null +++ b/include/osmocom/core/soft_uart.h @@ -0,0 +1,149 @@ +#pragma once + +/*! \file soft_uart.h + * Software UART implementation. */ +/* + * (C) 2022 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + */ + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/msgb.h> + +/*! Parity mode. + * https://en.wikipedia.org/wiki/Parity_bit */ +enum osmo_soft_uart_parity_mode { + OSMO_SUART_PARITY_NONE, /*!< No parity bit */ + OSMO_SUART_PARITY_EVEN, /*!< Even parity */ + OSMO_SUART_PARITY_ODD, /*!< Odd parity */ + OSMO_SUART_PARITY_MARK, /*!< Always 1 */ + OSMO_SUART_PARITY_SPACE, /*!< Always 0 */ + _OSMO_SUART_PARITY_NUM +}; + +/*! Flags passed to the application. */ +enum osmo_soft_uart_flags { + OSMO_SUART_F_FRAMING_ERROR = (1 << 0), /*!< Framing error occurred */ + OSMO_SUART_F_PARITY_ERROR = (1 << 1), /*!< Parity error occurred */ + OSMO_SUART_F_BREAK = (1 << 2), /*!< Break condition (not implemented) */ +}; + +/*! Modem status "line" flags. + * https://en.wikipedia.org/wiki/RS-232#Data_and_control_signals */ +enum osmo_soft_uart_status { + OSMO_SUART_STATUS_F_DTR = (1 << 0), /*!< Data Terminal Ready */ + OSMO_SUART_STATUS_F_DCD = (1 << 1), /*!< Data Carrier Detect */ + OSMO_SUART_STATUS_F_DSR = (1 << 2), /*!< Data Set Ready */ + OSMO_SUART_STATUS_F_RI = (1 << 3), /*!< Ring Indicator */ + OSMO_SUART_STATUS_F_RTS_RTR = (1 << 4), /*!< Request To Send or Ready To Receive */ + OSMO_SUART_STATUS_F_CTS = (1 << 5), /*!< Clear To Send */ +}; + +/*! Flow control mode. + * https://en.wikipedia.org/wiki/Flow_control_(data)#Hardware_flow_control */ +enum osmo_soft_uart_flow_ctrl_mode { + /*! No flow control */ + OSMO_SUART_FLOW_CTRL_NONE, + /*! DTR/DSR flow control: Tx if DSR is active and drop DTR if cannot Rx anymore. */ + OSMO_SUART_FLOW_CTRL_DTR_DSR, + /*! RTS/CTS flow control: Tx if CTS is active and drop RTS if cannot Rx anymore. + * The technically correct name would be RTR/CTS, because the RTS signal actually + * indicates readiness to *receive* data (Ready To Receive), and not really used + * to request a transmission (Request To Send) nowadays. Alternatively, the RTS + * signal can be interpreted as "Request To Send to me". */ + OSMO_SUART_FLOW_CTRL_RTS_CTS, +}; + +/*! Configuration for a soft-UART. */ +struct osmo_soft_uart_cfg { + /*! Number of data bits (typically 5, 6, 7 or 8). */ + uint8_t num_data_bits; + /*! Number of stop bits (typically 1 or 2). */ + uint8_t num_stop_bits; + /*! Parity mode (none, even, odd, space, mark). */ + enum osmo_soft_uart_parity_mode parity_mode; + /*! Size of the receive buffer; UART will buffer up to that number + * of characters before calling the receive call-back. */ + unsigned int rx_buf_size; + /*! Receive timeout; UART will flush the receive buffer via the receive call-back + * after indicated number of milliseconds, even if it is not full yet. */ + unsigned int rx_timeout_ms; + + /*! Opaque application-private data; passed to call-backs. */ + void *priv; + + /*! Receive call-back of the application. + * + * Called if at least one of the following conditions is met: + * a) rx_buf_size characters were received (Rx buffer is full); + * b) rx_timeout_ms expired and Rx buffer is not empty; + * c) a parity or framing error is occurred. + * + * \param[in] priv opaque application-private data. + * \param[in] rx_data msgb holding the received data. + * Must be free()ed by the application. + * \param[in] flags bit-mask of OSMO_SUART_F_*. */ + void (*rx_cb)(void *priv, struct msgb *rx_data, unsigned int flags); + + /*! Transmit call-back of the application. + * + * The implementation is expected to provide at most tx_data->data_len + * characters (the actual amount is determined by the number of requested + * bits and the effective UART configuration). + * + * \param[in] priv opaque application-private data. + * \param[inout] tx_data msgb for writing to be transmitted data. */ + void (*tx_cb)(void *priv, struct msgb *tx_data); + + /*! Modem status line change call-back. + * \param[in] priv opaque application-private data. + * \param[in] status updated status; bit-mask of OSMO_SUART_STATUS_F_*. */ + void (*status_change_cb)(void *priv, unsigned int status); + + /*! "Hardware" flow control mode. */ + enum osmo_soft_uart_flow_ctrl_mode flow_ctrl_mode; +}; + +extern const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg; + +struct osmo_soft_uart; + +struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name, + const struct osmo_soft_uart_cfg *cfg); +void osmo_soft_uart_free(struct osmo_soft_uart *suart); +int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg); + +const char *osmo_soft_uart_get_name(const struct osmo_soft_uart *suart); +void osmo_soft_uart_set_name(struct osmo_soft_uart *suart, const char *name); + +int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable); +int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable); + +int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits); +int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits); + +unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart); +int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status); +void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart, + enum osmo_soft_uart_status line, + bool active); + +void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart); diff --git a/include/osmocom/core/stat_item.h b/include/osmocom/core/stat_item.h index 806173ab..7f6857c0 100644 --- a/include/osmocom/core/stat_item.h +++ b/include/osmocom/core/stat_item.h @@ -6,6 +6,7 @@ #include <stdint.h> +#include <osmocom/core/defs.h> #include <osmocom/core/linuxlist.h> struct osmo_stat_item_desc; @@ -13,30 +14,16 @@ struct osmo_stat_item_desc; #define OSMO_STAT_ITEM_NOVALUE_ID 0 #define OSMO_STAT_ITEM_NO_UNIT NULL -/*! Individual entry in value FIFO */ -struct osmo_stat_item_value { - int32_t id; /*!< identifier of value */ - int32_t value; /*!< actual value */ -}; - -/*! data we keep for each actual item */ -struct osmo_stat_item { - /*! back-reference to the item description */ - const struct osmo_stat_item_desc *desc; - /*! the index of the freshest value */ - int32_t last_value_index; - /*! offset to the freshest value in the value FIFO */ - int16_t last_offs; - /*! value FIFO */ - struct osmo_stat_item_value values[0]; -}; +/*! data we keep for each actual item. Access via API functions only. + * (This struct was made opaque after libosmocore 1.5.1)*/ +struct osmo_stat_item; /*! Statistics item description */ struct osmo_stat_item_desc { const char *name; /*!< name of the item */ const char *description;/*!< description of the item */ const char *unit; /*!< unit of a value */ - unsigned int num_values;/*!< number of values to store in FIFO */ + unsigned int num_values;/*!< DEPRECATED, this value is ignored after libosmocore version 1.5.1 */ int32_t default_value; /*!< default value */ }; @@ -62,13 +49,15 @@ struct osmo_stat_item_group { const struct osmo_stat_item_group_desc *desc; /*! The index of this value group within its class */ unsigned int idx; + /*! Optional string-based identifier to be used instead of index at report time */ + char *name; /*! Actual counter structures below */ struct osmo_stat_item *items[0]; }; struct osmo_stat_item_group *osmo_stat_item_group_alloc( void *ctx, - const struct osmo_stat_item_group_desc *desc, + const struct osmo_stat_item_group_desc *group_desc, unsigned int idx); static inline void osmo_stat_item_group_udp_idx( @@ -76,7 +65,8 @@ static inline void osmo_stat_item_group_udp_idx( { grp->idx = idx; } - +struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx); +void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name); void osmo_stat_item_group_free(struct osmo_stat_item_group *statg); void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value); @@ -87,18 +77,14 @@ int osmo_stat_item_init(void *tall_ctx); struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx( const char *name, const unsigned int idx); +struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name); const struct osmo_stat_item *osmo_stat_item_get_by_name( const struct osmo_stat_item_group *statg, const char *name); -int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *idx, int32_t *value); - -/*! Get the last (freshest) value */ -static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item); - -int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx); +int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item); -int osmo_stat_item_discard_all(int32_t *idx); +void osmo_stat_item_flush(struct osmo_stat_item *item); typedef int (*osmo_stat_item_handler_t)( struct osmo_stat_item_group *, struct osmo_stat_item *, void *); @@ -110,8 +96,20 @@ int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg, int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data); -static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item) -{ - return item->values[item->last_offs].value; -} +void osmo_stat_item_reset(struct osmo_stat_item *item); +void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg); + +const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item); + +/* DEPRECATION: up until libosmocore 1.5.1, this API also defined + * - struct osmo_stat_item_value + * - osmo_stat_item_get_next() + * - osmo_stat_item_discard() + * - osmo_stat_item_discard_all() + * Despite our principle of never breaking API compatibility, we have decided to remove these, because there are no + * known users. These items were never practically used/usable outside of libosmocore since the generic stats reporter + * (stats.c) was introduced. + * We also decided to make struct osmo_stat_item opaque to allow future changes of the struct without API breakage. + */ + /*! @} */ diff --git a/include/osmocom/core/stats.h b/include/osmocom/core/stats.h index b9edac2a..a034a616 100644 --- a/include/osmocom/core/stats.h +++ b/include/osmocom/core/stats.h @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once @@ -108,10 +104,11 @@ struct osmo_stats_config { int interval; }; +extern struct llist_head osmo_stats_reporter_list; extern struct osmo_stats_config *osmo_stats_config; void osmo_stats_init(void *ctx); -int osmo_stats_report(); +int osmo_stats_report(void); int osmo_stats_set_interval(int interval); diff --git a/include/osmocom/core/stats_tcp.h b/include/osmocom/core/stats_tcp.h new file mode 100644 index 00000000..9bc7111a --- /dev/null +++ b/include/osmocom/core/stats_tcp.h @@ -0,0 +1,16 @@ +#pragma once + +#define TCP_STATS_DEFAULT_INTERVAL 0 /* secs */ +#define TCP_STATS_DEFAULT_BATCH_SIZE 5 /* sockets per interval */ + +struct osmo_tcp_stats_config { + /* poll interval in seconds, use osmo_stats_tcp_set_interval() to manipulate this value */ + int interval; + /* specify how many sockets are processed when the interval timer expires */ + int batch_size; +}; +extern struct osmo_tcp_stats_config *osmo_tcp_stats_config; + +int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name); +int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd); +int osmo_stats_tcp_set_interval(int interval); diff --git a/include/osmocom/core/strrb.h b/include/osmocom/core/strrb.h index b87239da..92d6a2f2 100644 --- a/include/osmocom/core/strrb.h +++ b/include/osmocom/core/strrb.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once @@ -30,8 +26,6 @@ #include <stdbool.h> #include <stdint.h> -#include <osmocom/core/talloc.h> - /*! A structure representing an osmocom string ringbuffer */ #define RB_MAX_MESSAGE_SIZE 240 @@ -42,7 +36,7 @@ struct osmo_strrb { char **buffer; /*!< storage for messages */ }; -struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size); +struct osmo_strrb *osmo_strrb_create(void *talloc_ctx, size_t rb_size); bool osmo_strrb_is_empty(const struct osmo_strrb *rb); const char *osmo_strrb_get_nth(const struct osmo_strrb *rb, unsigned int string_index); diff --git a/include/osmocom/core/talloc.h b/include/osmocom/core/talloc.h index c68a56cf..f15cd2a2 100644 --- a/include/osmocom/core/talloc.h +++ b/include/osmocom/core/talloc.h @@ -25,3 +25,5 @@ extern __thread struct osmo_talloc_contexts *osmo_ctx; * to the various _c functions like msgb_alloc_c() */ #define OTC_GLOBAL (osmo_ctx->global) #define OTC_SELECT (osmo_ctx->select) + +int osmo_ctx_init(const char *id); diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h index 54819d95..402d0102 100644 --- a/include/osmocom/core/tdef.h +++ b/include/osmocom/core/tdef.h @@ -40,6 +40,7 @@ enum osmo_tdef_unit { OSMO_TDEF_MS, /*!< milliseconds */ OSMO_TDEF_M, /*!< minutes */ OSMO_TDEF_CUSTOM, /*!< unspecified unit, explained in osmo_tdef.desc. */ + OSMO_TDEF_US, /*!< microseconds */ }; extern const struct value_string osmo_tdef_unit_names[]; @@ -120,11 +121,13 @@ struct osmo_tdef_state_timeout { const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array); -/*! Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and - * default_timeout. +/*! Call osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms](), + * depending on the timeouts_array, tdefs and default_timeout. * - * A T timer configured in sub-second precision is rounded up to the next full second. A timer in unit = - * OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense for custom units!). + * A timer defined with sub-millisecond precision (e.g OSMO_TDEF_US) is rounded up to the next full millisecond. + * A timer value defined in units higher than millisecond (e.g. OSMO_TDEF_S, OSMO_TDEF_M) is converted to milliseconds. + * A timer in unit = OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense + * for custom units!). * * See osmo_tdef_get_state_timeout() and osmo_tdef_get(). * @@ -152,16 +155,17 @@ const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state * \param[in] state State number to transition to. * \param[in] timeouts_array Array of struct osmo_tdef_state_timeout[32] to look up state in. * \param[in] tdefs Array of struct osmo_tdef (last entry zero initialized) to look up T in. - * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T, then use this - * default timeout value as fallback, or pass -1 to abort the program. - * \return Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(). + * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T, + * then use this default timeout value (in seconds) as fallback, + * or pass a negative number to abort the program. + * \return Return value from osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms](). */ #define osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout) \ _osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \ __FILE__, __LINE__) int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array, - const struct osmo_tdef *tdefs, unsigned long default_timeout, + const struct osmo_tdef *tdefs, signed long default_timeout, const char *file, int line); /*! Manage timer definitions in named groups. diff --git a/include/osmocom/core/thread.h b/include/osmocom/core/thread.h new file mode 100644 index 00000000..d857268d --- /dev/null +++ b/include/osmocom/core/thread.h @@ -0,0 +1,29 @@ +/*! \file thread.h + * Compatibility header with some thread related helpers + */ +/* + * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + */ + +/*! \defgroup thread Osmocom thread helpers + * @{ + * \file thread.h */ + +#pragma once + +#include <sys/types.h> + +pid_t osmo_gettid(void); diff --git a/include/osmocom/core/time_cc.h b/include/osmocom/core/time_cc.h new file mode 100644 index 00000000..36fdee46 --- /dev/null +++ b/include/osmocom/core/time_cc.h @@ -0,0 +1,187 @@ +/*! \file time_cc.h + * Report the cumulative counter of time for which a flag is true as rate counter. + */ +/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> + * + * 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/licenses/>. + * + */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/timer.h> + +/*! \defgroup time_cc Cumulative counter of time as rate counter. + * @{ + * \file time_cc.h + */ + +struct osmo_tdef; +struct rate_ctr; + +/*! Configuration for osmo_time_cc. + * Report the cumulative counter of time for which a flag is true as rate counter. + * For example, for each second that the flag is true, increment a rate counter. + * + * The flag to be monitored is reported by osmo_time_cc_set_flag(). + * + * The granularity defines how much time one rate counter increment represents: + * the default configuration is gran_usec = 1000000, i.e. one rate counter increment represents one second. + * + * Reporting as rate counter is configurable by round_threshold_usec and forget_sum_usec, examples: + * + * round_threshold_usec: + * - To get "ceil()" behavior, set round_threshold_usec = 1. This increments the rate counter for each gran_usec period + * where the flag was seen true, even if it was true for only a very short fraction of a gran_usec period. + * - To get "round()" behavior, set round_threshold_usec = half of gran_usec. The rate counter increments when the flag + * has been true for 0.5 of a gran_usec (and then again at 1.5 * gran_usec) of 'true' flag. round_threshold_usec = 0 + * is a special value that means to use half of gran_usec. + * - To get "floor()" behavior, set round_threshold_usec >= gran_usec. The rate counter increments when reaching full + * gran_usec periods of the flag being true. + * + * forget_sum_usec: + * This is a tradeoff between the accuracy of the reported rate counter and making sure that the events reported are not + * irrelevantly long ago. + * - To keep sub-granularity-period surplus time forever, set forget_sum_usec = 0. + * - To keep surplus time for up to a minute, set forget_sum_usec = 60000000 (60 seconds). + * - To get rid of "leftover" time (almost) immediately after the flag goes false, set forget_sum_usec = 1. + * - If gran_usec is set to one second and forget_sum_usec is set to one minute, the reported rate counter has a + * possible inaccuracy of 1/60th, but makes sure that no timings older than a minute affect the current reports. + * + * Reporting modes in detail: + * + * The rate_ctr increments when the cumulative counter passes round_threshold_usec (default: half of gran_usec). + * + * sum ^ + * | ________ + * | / + * | / + * | / + * 3*gran --+--------------------------------------+ + * | /: + * | / : + * | - - - - - - - - - - - - - - - - - / : + * | /. : + * | / . : + * 2*gran --+--------------------------------+ . : + * | /: . : + * | / : . : + * | - - - - - - - - - -_________/ : . : + * | / . : . : + * | / . : . : + * 1*gran --+-----------------+ . : . : + * | /: . : . : + * | / : . : . : + * | - - - - - - -/ : . : . : + * | /. : . : . : + * | ....-------' . : . : . : + * 0 +------------------------------------------------------------------------> elapsed time + * . : . : . : + * _ _ _______ ____________ + * flag: __| |_| |____| . : |_______|. : . : |__________ + * f t f t f t . : f t. : . : f + * round_threshold_usec : . : . : . : + * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()" + * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()" + * >= gran_usec: 0 1 2 3 = "floor()" + * + */ +struct osmo_time_cc_cfg { + /*! Granularity in microseconds: nr of microseconds that one rate_ctr increment represents. A typical value is + * gran_usec = 1000000, meaning one rate counter increment represents one second. When zero, use 1000000. */ + uint64_t gran_usec; + /*! Nr of microseconds above n * gran_usec at which to trigger a counter increment. When zero, use half a + * gran_usec. */ + uint64_t round_threshold_usec; + /*! Forget counted sub-gran time after the flag was false for this long. */ + uint64_t forget_sum_usec; + /*! Rate counter to report to, or NULL to not use it. */ + struct rate_ctr *rate_ctr; + + /*! Update gran_usec from this T timer value, or zero to not use any T timer. */ + int T_gran; + /*! Update round_threshold_usec from this T timer value, or zero to not use any T timer. */ + int T_round_threshold; + /*! Update forget_sum_usec from this T timer value, or zero to not use any T timer. */ + int T_forget_sum; + /*! Look up T_gran and T_forget_sum in this list of timers, or NULL to not use any T timers. */ + struct osmo_tdef *T_defs; +}; + +/*! Report the cumulative counter of time for which a flag is true as rate counter. + * See also osmo_time_cc_cfg for details on configuring. + * + * Usage: + * + * struct my_obj { + * struct osmo_time_cc flag_cc; + * }; + * + * void my_obj_init(struct my_obj *my_obj) + * { + * osmo_time_cc_init(&my_obj->flag_cc); + * my_obj->flag_cc.cfg = (struct osmo_time_cc_cfg){ + * .gran_usec = 1000000, + * .forget_sum_usec = 60000000, + * .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, MY_CTR_IDX), + * }; + * // optional: set initial flag state, default is 'false': + * // osmo_time_cc_set_flag(&my_obj->flag_cc, false); + * } + * + * void my_obj_event(struct my_obj *my_obj, bool flag) + * { + * osmo_time_cc_set_flag(&my_obj->flag_cc, flag); + * } + * + * void my_obj_destruct(struct my_obj *my_obj) + * { + * osmo_time_cc_cleanup(&my_obj->flag_cc); + * } + */ +struct osmo_time_cc { + struct osmo_time_cc_cfg cfg; + + bool flag_state; + + /*! Overall cumulative sum. Does not get reset for the entire lifetime of an osmo_time_cc. + * (Informational only, not used by the osmo_time_cc implementation.) */ + uint64_t total_sum; + + struct osmo_timer_list timer; + + /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc instance started counting. */ + uint64_t start_time; + /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc last evaluated the flag state and + * possibly added to the cumulated sum. */ + uint64_t last_counted_time; + + /*! Internal cumulative counter of time that flag_state was true. It may get reset to zero regularly, depending + * on cfg.forget_sum_usec. This is the basis for incrementing cfg.rate_ctr. */ + uint64_t sum; + /*! The amount of time that already reported cfg.rate_ctr increments account for. This may be ahead of or behind + * 'sum', depending on cfg.round_threshold_usec. */ + uint64_t reported_sum; +}; + +void osmo_time_cc_init(struct osmo_time_cc *tc); +void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag); +void osmo_time_cc_cleanup(struct osmo_time_cc *tc); + +/*! @} */ diff --git a/include/osmocom/core/timer.h b/include/osmocom/core/timer.h index 19797662..16338dad 100644 --- a/include/osmocom/core/timer.h +++ b/include/osmocom/core/timer.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \defgroup timer Osmocom timers @@ -75,7 +71,7 @@ void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microse void osmo_timer_del(struct osmo_timer_list *timer); -int osmo_timer_pending(struct osmo_timer_list *timer); +int osmo_timer_pending(const struct osmo_timer_list *timer); int osmo_timer_remaining(const struct osmo_timer_list *timer, const struct timeval *now, @@ -84,6 +80,7 @@ int osmo_timer_remaining(const struct osmo_timer_list *timer, * internal timer list management */ struct timeval *osmo_timers_nearest(void); +int osmo_timers_nearest_ms(void); void osmo_timers_prepare(void); int osmo_timers_update(void); int osmo_timers_check(void); diff --git a/include/osmocom/core/timer_compat.h b/include/osmocom/core/timer_compat.h index 916f5684..fd52ae36 100644 --- a/include/osmocom/core/timer_compat.h +++ b/include/osmocom/core/timer_compat.h @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \defgroup timer Osmocom timers diff --git a/include/osmocom/core/tun.h b/include/osmocom/core/tun.h new file mode 100644 index 00000000..86bd8df0 --- /dev/null +++ b/include/osmocom/core/tun.h @@ -0,0 +1,43 @@ +/*! \file tun.h + * tunnel network device convenience functions. */ + +#pragma once +#if (!EMBEDDED) + +#include <stddef.h> +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/netdev.h> + +struct osmo_tundev; + +/* callback user gets ownership of the msgb and is expected to free it. */ +typedef int (*osmo_tundev_data_ind_cb_t)(struct osmo_tundev *tun, struct msgb *msg); + +struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name); +void osmo_tundev_free(struct osmo_tundev *tundev); +int osmo_tundev_open(struct osmo_tundev *tundev); +int osmo_tundev_close(struct osmo_tundev *tundev); +bool osmo_tundev_is_open(struct osmo_tundev *tundev); + +void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data); +void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev); + +void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb); + +const char *osmo_tundev_get_name(const struct osmo_tundev *tundev); + +int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name); +const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev); + +int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns); +const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev); + +struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev); + +int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg); + +#endif /* (!EMBEDDED) */ +/*! @} */ diff --git a/include/osmocom/core/use_count.h b/include/osmocom/core/use_count.h index 6a4bf1f3..532d8b14 100644 --- a/include/osmocom/core/use_count.h +++ b/include/osmocom/core/use_count.h @@ -19,10 +19,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once @@ -130,8 +126,9 @@ typedef int (* osmo_use_count_cb_t )(struct osmo_use_count_entry *use_count_entr * int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count, const char *file, int line) * { * struct foo *foo = use_count_entry->use_count->talloc_object; - * if (osmo_use_count_total(&use_count_entry->use_count) == 0) + * if (osmo_use_count_total(use_count_entry->use_count) == 0) * talloc_free(foo); + * return 0; * } * * // The function name is a convenient use token: @@ -215,6 +212,8 @@ int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t const char *file, int line); const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc); +int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc); +char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc); int32_t osmo_use_count_total(const struct osmo_use_count *uc); int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use); diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h index 86191209..92bea59f 100644 --- a/include/osmocom/core/utils.h +++ b/include/osmocom/core/utils.h @@ -33,14 +33,23 @@ /*! Make a value_string entry from an enum value name */ #define OSMO_VALUE_STRING(x) { x, #x } /*! Number of bytes necessary to store given BITS */ -#define OSMO_BYTES_FOR_BITS(BITS) ((BITS + 8 - 1) / 8) +#define OSMO_BYTES_FOR_BITS(BITS) (((BITS) + 7) / 8) /*! Copy a C-string into a sized buffer using sizeof to detect buffer's size */ #define OSMO_STRLCPY_ARRAY(array, src) osmo_strlcpy(array, src, sizeof(array)) +/*! Branch prediction optimizations */ +#if defined(__GNUC__) +#define OSMO_LIKELY(exp) __builtin_expect(!!(exp), 1) +#define OSMO_UNLIKELY(exp) __builtin_expect(!!(exp), 0) +#else +#define OSMO_LIKELY(exp) exp +#define OSMO_UNLIKELY(exp) exp +#endif + /*! A mapping between human-readable string and numeric value */ struct value_string { - int value; /*!< numeric value */ + uint32_t value; /*!< numeric value */ const char *str; /*!< human-readable string */ }; @@ -57,7 +66,7 @@ uint8_t osmo_char2bcd(char c); int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex); int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex); -int osmo_hexparse(const char *str, uint8_t *b, int max_len); +int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len); char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len); char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); @@ -102,9 +111,11 @@ do { \ * the predicate evaluates to false (0). */ #define OSMO_ASSERT(exp) \ - if (!(exp)) { \ +do { \ + if (OSMO_UNLIKELY(!(exp))) { \ osmo_panic("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \ - } + } \ +} while (0); /* some code invokes OSMO_ASSERT() without the semicolon */ /*! duplicate a string using talloc and release its prior content (if any) * \param[in] ctx Talloc context to use for allocation @@ -117,6 +128,8 @@ static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *dst = talloc_strdup(ctx, newstr); } +void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...); + /*! Append to a string and re-/allocate if necessary. * \param[in] ctx Talloc context to use for initial allocation. * \param[in,out] dest char* to re-/allocate and append to. @@ -155,10 +168,12 @@ size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_le char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len); const char *osmo_escape_str(const char *str, int len); +int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len); char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len); const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize); char *osmo_escape_str_c(const void *ctx, const char *str, int in_len); const char *osmo_quote_str(const char *str, int in_len); +int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len); char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len); const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize); char *osmo_quote_str_c(const void *ctx, const char *str, int in_len); @@ -167,6 +182,18 @@ int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n); uint32_t osmo_isqrt32(uint32_t x); +/*! Floored Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists). + * \param[in] x dividend. + * \param[in] y divisor. + * \returns remainder of x divided by y. */ +#define OSMO_MOD_FLR(x, y) (((x) > 0 && (y) < 0) || ((x) < 0 && (y) > 0) ? (x) % (y) + (y) : (x) % (y)) + +/*! Euclidean Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists). + * \param[in] x dividend. + * \param[in] y divisor. + * \returns remainder of x divided by y. */ +#define OSMO_MOD_EUC(x, y) ((x) % (y) < 0 ? (y) > 0 ? (x) % (y) + (y) : (x) % (y) - (y) : (x) % (y)) + char osmo_luhn(const char* in, int in_len); /*! State for OSMO_STRBUF_APPEND() and OSMO_STRBUF_PRINTF(). See there for examples. */ @@ -222,9 +249,9 @@ struct osmo_strbuf { #define OSMO_STRBUF_APPEND(STRBUF, func, args...) do { \ if (!(STRBUF).pos) \ (STRBUF).pos = (STRBUF).buf; \ - size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \ + size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \ int _sb_l = func((STRBUF).pos, _sb_remain, ##args); \ - if (_sb_l < 0 || _sb_l > _sb_remain) \ + if (_sb_l < 0 || (size_t)_sb_l > _sb_remain) \ (STRBUF).pos = (STRBUF).buf + (STRBUF).len; \ else if ((STRBUF).pos) \ (STRBUF).pos += _sb_l; \ @@ -254,6 +281,38 @@ struct osmo_strbuf { #define OSMO_STRBUF_PRINTF(STRBUF, fmt, args...) \ OSMO_STRBUF_APPEND(STRBUF, snprintf, fmt, ##args) +/*! Get remaining space for characters and terminating nul in the given struct osmo_strbuf. + * \param[in] sb the string buffer to get the remaining space for. + * \returns remaining space in the given struct osmo_strbuf. */ +static inline size_t _osmo_strbuf_remain(const struct osmo_strbuf *sb) +{ + if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL)) + return 0; + if (sb->pos == NULL) + return sb->len; + return sb->len - (sb->pos - sb->buf); +} + +/*! Return remaining space for characters and terminating nul in the given struct osmo_strbuf. */ +#define OSMO_STRBUF_REMAIN(STRBUF) \ + _osmo_strbuf_remain(&(STRBUF)) + +/*! Get number of actual characters (without terminating nul) in the given struct osmo_strbuf. + * \param[in] sb the string buffer to get the number of characters for. + * \returns number of actual characters (without terminating nul). */ +static inline size_t _osmo_strbuf_char_count(const struct osmo_strbuf *sb) +{ + if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL)) + return 0; + if (sb->pos == NULL || sb->pos <= sb->buf) + return 0; + return OSMO_MIN((size_t)(sb->pos - sb->buf), sb->len - 1); +} + +/*! Return number of actual characters contained in struct osmo_strbuf (without terminating nul). */ +#define OSMO_STRBUF_CHAR_COUNT(STRBUF) \ + _osmo_strbuf_char_count(&(STRBUF)) + /*! Like OSMO_STRBUF_APPEND(), but for function signatures that return the char* buffer instead of a length. * When using this function, the final STRBUF.chars_needed may not reflect the actual number of characters needed, since * that number cannot be obtained from this kind of function signature. @@ -265,7 +324,7 @@ struct osmo_strbuf { #define OSMO_STRBUF_APPEND_NOLEN(STRBUF, func, args...) do { \ if (!(STRBUF).pos) \ (STRBUF).pos = (STRBUF).buf; \ - size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \ + size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \ if (_sb_remain) { \ func((STRBUF).pos, _sb_remain, ##args); \ } \ @@ -277,8 +336,25 @@ struct osmo_strbuf { (STRBUF).chars_needed += _sb_l; \ } while(0) +void osmo_strbuf_drop_tail(struct osmo_strbuf *sb, size_t n_chars); +/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as + * with all the other OSMO_STRBUF_* API. */ +#define OSMO_STRBUF_DROP_TAIL(STRBUF, N_CHARS) osmo_strbuf_drop_tail(&(STRBUF), N_CHARS) + +void osmo_strbuf_added_tail(struct osmo_strbuf *sb, size_t n_chars); +/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as + * with all the other OSMO_STRBUF_* API. */ +#define OSMO_STRBUF_ADDED_TAIL(STRBUF, N_CHARS) osmo_strbuf_added_tail(&(STRBUF), N_CHARS) + bool osmo_str_startswith(const char *str, const char *startswith_str); +int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision); +int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision); +char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision); + +int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val); +int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val); + /*! Translate a buffer function to a talloc context function. * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an * int foo_name_buf(buf, buflen, val...) function: @@ -314,7 +390,7 @@ bool osmo_str_startswith(const char *str, const char *startswith_str); _needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \ if (_needed < 0) \ goto OSMO_NAME_C_on_error; \ - if (_needed < _len) \ + if ((unsigned int) _needed < _len) \ return _str; \ _len = _needed + 1; \ if (_str) \ diff --git a/include/osmocom/core/write_queue.h b/include/osmocom/core/write_queue.h index 071621d1..fe762829 100644 --- a/include/osmocom/core/write_queue.h +++ b/include/osmocom/core/write_queue.h @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #pragma once @@ -53,6 +49,8 @@ struct osmo_wqueue { void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length); void osmo_wqueue_clear(struct osmo_wqueue *queue); int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data); +int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data); +size_t osmo_wqueue_set_maxlen(struct osmo_wqueue *queue, unsigned int len); int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what); /*! @} */ |