Projects
osmocom:latest
libosmocore
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 6
View file
libosmocore.spec
Changed
@@ -14,13 +14,14 @@ Name: libosmocore Requires: osmocom-latest -Version: 1.7.0 +Version: 1.8.0 Release: 0 Summary: The Open Source Mobile Communications Core Library License: GPL-2.0-only AND GPL-2.0-or-later AND LGPL-2.1-or-later AND AGPL-3.0-or-later Group: Productivity/Telephony/Utilities Url: https://osmocom.org/projects/libosmocore/wiki/Libosmocore -Source: libosmocore_1.7.0.tar.xz +Source: libosmocore_1.8.0.tar.xz +Source1: rpmlintrc BuildRequires: automake >= 1.6 BuildRequires: libtool >= 2 BuildRequires: lksctp-tools-devel @@ -118,14 +119,14 @@ This subpackage contains libraries and header files for developing applications that want to make use of libosmocoding. -%package -n libosmocore19 +%package -n libosmocore20 Requires: osmocom-latest Summary: Osmocom core library # crc16.c has GPL2-only clauses, the rest (*.c) is GPL-2.0+ License: GPL-2.0-only AND GPL-2.0-or-later Group: System/Libraries -%description -n libosmocore19 +%description -n libosmocore20 libosmocore is a library with various utility functions shared between OpenBSC and OsmocomBB. @@ -135,7 +136,7 @@ # crc16.h has GPL2-only clauses, the rest (*.h) is GPL-2.0+ License: GPL-2.0-only AND GPL-2.0-or-later Group: Development/Libraries/C and C++ -Requires: libosmocore19 = %version +Requires: libosmocore20 = %version Requires: libtalloc-devel Requires: lksctp-tools-devel @@ -225,6 +226,8 @@ Group: Development/Libraries/C and C++ Requires: libosmocore-devel = %version Requires: libosmogsm18 = %version +Requires: libosmoisdn-devel = %version +Requires: libosmoisdn0 = %version %description -n libosmogsm-devel The libosmogsm library in particular is a collection of common code @@ -236,6 +239,36 @@ This subpackage contains libraries and header files for developing applications that want to make use of libosmogsm. +%package -n libosmoisdn0 +Requires: osmocom-latest +Summary: Osmocom ISDN utility library +License: GPL-2.0-or-later +Group: System/Libraries + +%description -n libosmoisdn0 +libosmocore is a package with various utility functions that were +originally developed as part of the OpenBSC project. + +The libosmoisdn library in particular is a collection of common code used in +various ISDN related sub-projects inside the Osmocom family of projects. It +includes an I.460 sub-channel multiplex and a generic LAPD core. + +%package -n libosmoisdn-devel +Requires: osmocom-latest +Summary: Development files for the Osmocom ISDN utility library +License: GPL-2.0-or-later +Group: Development/Libraries/C and C++ +Requires: libosmocore-devel = %version +Requires: libosmoisdn0 = %version + +%description -n libosmoisdn-devel +The libosmoisdn library in particular is a collection of common code used in +various ISDN related sub-projects inside the Osmocom family of projects. It +includes an I.460 sub-channel multiplex and a generic LAPD core. + +This subpackage contains libraries and header files for developing +applications that want to make use of libosmogsm. + %package -n libosmosim2 Requires: osmocom-latest Summary: Osmocom SIM card related utility library @@ -323,7 +356,7 @@ %prep -%setup -q +%setup -n libosmocore -q %build echo "%version" >.tarball-version @@ -344,14 +377,16 @@ %postun -n libosmocodec0 -p /sbin/ldconfig %post -n libosmocoding0 -p /sbin/ldconfig %postun -n libosmocoding0 -p /sbin/ldconfig -%post -n libosmocore19 -p /sbin/ldconfig -%postun -n libosmocore19 -p /sbin/ldconfig +%post -n libosmocore20 -p /sbin/ldconfig +%postun -n libosmocore20 -p /sbin/ldconfig %post -n libosmoctrl0 -p /sbin/ldconfig %postun -n libosmoctrl0 -p /sbin/ldconfig %post -n libosmogb14 -p /sbin/ldconfig %postun -n libosmogb14 -p /sbin/ldconfig %post -n libosmogsm18 -p /sbin/ldconfig %postun -n libosmogsm18 -p /sbin/ldconfig +%post -n libosmoisdn0 -p /sbin/ldconfig +%postun -n libosmoisdn0 -p /sbin/ldconfig %post -n libosmosim2 -p /sbin/ldconfig %postun -n libosmosim2 -p /sbin/ldconfig %post -n libosmovty9 -p /sbin/ldconfig @@ -387,9 +422,9 @@ %_libdir/libosmocoding.so %_libdir/pkgconfig/libosmocoding.pc -%files -n libosmocore19 +%files -n libosmocore20 %defattr(-,root,root) -%_libdir/libosmocore.so.19* +%_libdir/libosmocore.so.20* %files -n libosmocore-devel %defattr(-,root,root) @@ -438,6 +473,18 @@ %_libdir/libosmogsm.so %_libdir/pkgconfig/libosmogsm.pc +%files -n libosmoisdn0 +%defattr(-,root,root) +%_libdir/libosmoisdn.so.0* + +%files -n libosmoisdn-devel +%defattr(-,root,root) +%dir %_includedir/%name +%dir %_includedir/%name/osmocom +%_includedir/%name/osmocom/isdn/ +%_libdir/libosmoisdn.so +%_libdir/pkgconfig/libosmoisdn.pc + %files -n libosmosim2 %defattr(-,root,root) %_libdir/libosmosim.so.2*
View file
libosmocore_1.7.0.tar.xz/src/bits.c
Deleted
@@ -1,313 +0,0 @@ -/* - * (C) 2011 by Harald Welte <laforge@gnumonks.org> - * (C) 2011 by Sylvain Munaut <tnt@246tNt.com> - * - * 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 <osmocom/core/bits.h> - -/*! \addtogroup bits - * @{ - * Osmocom bit level support code. - * - * This module implements the notion of different bit-fields, such as - * - unpacked bits (\ref ubit_t), i.e. 1 bit per byte - * - packed bits (\ref pbit_t), i.e. 8 bits per byte - * - soft bits (\ref sbit_t), 1 bit per byte from -127 to 127 - * - * \file bits.c */ - -/*! convert unpacked bits to packed bits, return length in bytes - * \paramout out output buffer of packed bits - * \paramin in input buffer of unpacked bits - * \paramin num_bits number of bits - */ -int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits) -{ - unsigned int i; - uint8_t curbyte = 0; - pbit_t *outptr = out; - - for (i = 0; i < num_bits; i++) { - uint8_t bitnum = 7 - (i % 8); - - curbyte |= (ini << bitnum); - - if(i % 8 == 7){ - *outptr++ = curbyte; - curbyte = 0; - } - } - /* we have a non-modulo-8 bitcount */ - if (i % 8) - *outptr++ = curbyte; - - return outptr - out; -} - -/*! Shift unaligned input to octet-aligned output - * \paramout out output buffer, unaligned - * \paramin in input buffer, octet-aligned - * \paramin num_nibbles number of nibbles - */ -void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in, - unsigned int num_nibbles) -{ - unsigned int i, num_whole_bytes = num_nibbles / 2; - if (!num_whole_bytes) - return; - - /* first byte: upper nibble empty, lower nibble from src */ - out0 = (in0 >> 4); - - /* bytes 1.. */ - for (i = 1; i < num_whole_bytes; i++) - outi = ((ini - 1 & 0xF) << 4) | (ini >> 4); - - /* shift the last nibble, in case there's an odd count */ - i = num_whole_bytes; - if (num_nibbles & 1) - outi = ((ini - 1 & 0xF) << 4) | (ini >> 4); - else - outi = (ini - 1 & 0xF) << 4; -} - -/*! Shift unaligned input to octet-aligned output - * \paramout out output buffer, octet-aligned - * \paramin in input buffer, unaligned - * \paramin num_nibbles number of nibbles - */ -void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in, - unsigned int num_nibbles) -{ - unsigned int i, num_whole_bytes = num_nibbles / 2; - if (!num_whole_bytes) - return; - - for (i = 0; i < num_whole_bytes; i++) - outi = ((ini & 0xF) << 4) | (ini + 1 >> 4); - - /* shift the last nibble, in case there's an odd count */ - i = num_whole_bytes; - if (num_nibbles & 1) - outi = (ini & 0xF) << 4; -} - -/*! convert unpacked bits to soft bits - * \paramout out output buffer of soft bits - * \paramin in input buffer of unpacked bits - * \paramin num_bits number of bits - */ -void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits) -{ - unsigned int i; - for (i = 0; i < num_bits; i++) - outi = ini ? -127 : 127; -} - -/*! convert soft bits to unpacked bits - * \paramout out output buffer of unpacked bits - * \paramin in input buffer of soft bits - * \paramin num_bits number of bits - */ -void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits) -{ - unsigned int i; - for (i = 0; i < num_bits; i++) - outi = ini < 0; -} - -/*! convert packed bits to unpacked bits, return length in bytes - * \paramout out output buffer of unpacked bits - * \paramin in input buffer of packed bits - * \paramin num_bits number of bits - * \return number of bytes used in \ref out - */ -int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits) -{ - unsigned int i; - ubit_t *cur = out; - ubit_t *limit = out + num_bits; - - for (i = 0; i < (num_bits/8)+1; i++) { - pbit_t byte = ini; - *cur++ = (byte >> 7) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 6) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 5) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 4) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 3) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 2) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 1) & 1; - if (cur >= limit) - break; - *cur++ = (byte >> 0) & 1; - if (cur >= limit) - break; - } - return cur - out; -} - -/*! convert unpacked bits to packed bits (extended options) - * \paramout out output buffer of packed bits - * \paramin out_ofs offset into output buffer - * \paramin in input buffer of unpacked bits - * \paramin in_ofs offset into input buffer - * \paramin num_bits number of bits - * \paramin lsb_mode Encode bits in LSB orde instead of MSB - * \returns length in bytes (max written offset of output buffer + 1) - */ -int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, - const ubit_t *in, unsigned int in_ofs, - unsigned int num_bits, int lsb_mode) -{ - unsigned int i, op, bn; - for (i=0; i<num_bits; i++) { - op = out_ofs + i; - bn = lsb_mode ? (op&7) : (7-(op&7)); - if (inin_ofs+i) - outop>>3 |= 1 << bn; - else - outop>>3 &= ~(1 << bn); - } - return ((out_ofs + num_bits - 1) >> 3) + 1; -} - -/*! convert packed bits to unpacked bits (extended options) - * \paramout out output buffer of unpacked bits - * \paramin out_ofs offset into output buffer - * \paramin in input buffer of packed bits - * \paramin in_ofs offset into input buffer - * \paramin num_bits number of bits - * \paramin lsb_mode Encode bits in LSB orde instead of MSB - * \returns length in bytes (max written offset of output buffer + 1) - */ -int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, - const pbit_t *in, unsigned int in_ofs, - unsigned int num_bits, int lsb_mode) -{ - unsigned int i, ip, bn; - for (i=0; i<num_bits; i++) { - ip = in_ofs + i; - bn = lsb_mode ? (ip&7) : (7-(ip&7)); - outout_ofs+i = !!(inip>>3 & (1<<bn)); - } - return out_ofs + num_bits; -} - -/* look-up table for bit-reversal within a byte. Generated using: - int i,k; - for (i = 0 ; i < 256 ; i++) { - uint8_t sample = 0 ; - for (k = 0; k<8; k++) { - if ( i & 1 << k ) sample |= 0x80 >> k; - } - flip_tablei = sample; - } - */ -static const uint8_t flip_table256 = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - -/*! generalized bit reversal function - * \paramin x the 32bit value to be reversed - * \paramin k the type of reversal requested - * \returns the reversed 32bit dword - * - * This function reverses the bit order within a 32bit word. Depending - * on "k", it either reverses all bits in a 32bit dword, or the bytes in - * the dword, or the bits in each byte of a dword, or simply swaps the - * two 16bit words in a dword. See Chapter 7 "Hackers Delight" - */ -uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k) -{ - if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; - if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; - if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; - if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; - if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; - - return x; -} - -/*! reverse the bit-order in each byte of a dword - * \paramin x 32bit input value - * \returns 32bit value where bits of each byte have been reversed - * - * See Chapter 7 "Hackers Delight" - */ -uint32_t osmo_revbytebits_32(uint32_t x) -{ - x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; - x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; - x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; - - return x; -} - -/*! reverse the bit order in a byte - * \paramin x 8bit input value - * \returns 8bit value where bits order has been reversed - */ -uint32_t osmo_revbytebits_8(uint8_t x) -{ - return flip_tablex; -} - -/*! reverse bit-order of each byte in a buffer - * \paramin buf buffer containing bytes to be bit-reversed - * \paramin len length of buffer in bytes - * - * This function reverses the bits in each byte of the buffer - */ -void osmo_revbytebits_buf(uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - bufi = flip_tablebufi; -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/context.c
Deleted
@@ -1,47 +0,0 @@ -/*! \file context.c - * talloc context handling. - * - * (C) 2019 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserverd. - * - * 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 <string.h> -#include <errno.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -__thread struct osmo_talloc_contexts *osmo_ctx; - -int osmo_ctx_init(const char *id) -{ - osmo_ctx = talloc_named(NULL, sizeof(*osmo_ctx), "global-%s", id); - if (!osmo_ctx) - return -ENOMEM; - memset(osmo_ctx, 0, sizeof(*osmo_ctx)); - osmo_ctx->global = osmo_ctx; - osmo_ctx->select = talloc_named_const(osmo_ctx->global, 0, "select"); - if (!osmo_ctx->select) { - talloc_free(osmo_ctx); - return -ENOMEM; - } - return 0; -} - -/* initialize osmo_ctx on main tread */ -static __attribute__((constructor)) void on_dso_load_ctx(void) -{ - OSMO_ASSERT(osmo_ctx_init("main") == 0); -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/counter.c
Deleted
@@ -1,108 +0,0 @@ -/*! \file counter.c - * utility routines for keeping some statistics. */ -/* - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <string.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/counter.h> - -static LLIST_HEAD(counters); - -/*! Global talloc context for all osmo_counter allocations. */ -void *tall_ctr_ctx; - -/*! Allocate a new counter with given name. Allocates from tall_ctr_ctx - * \paramin name Human-readable string name for the counter - * \returns Allocated counter on success; NULL on error */ -struct osmo_counter *osmo_counter_alloc(const char *name) -{ - struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter); - - if (!ctr) - return NULL; - - ctr->name = name; - llist_add_tail(&ctr->list, &counters); - - return ctr; -} - -/*! Release/Destroy a given counter - * \paramin ctr Counter to be destroyed */ -void osmo_counter_free(struct osmo_counter *ctr) -{ - llist_del(&ctr->list); - talloc_free(ctr); -} - -/*! Iterate over all counters; call \a handle_cunter call-back for each. - * \paramin handle_counter Call-back to be called for each counter; aborts if rc < 0 - * \paramin data Opaque data passed through to \a handle_counter function - * \returns 0 if all \a handle_counter calls successfull; negative on error */ -int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), - void *data) -{ - struct osmo_counter *ctr; - int rc = 0; - - llist_for_each_entry(ctr, &counters, list) { - rc = handle_counter(ctr, data); - if (rc < 0) - return rc; - } - - return rc; -} - -/*! Counts the registered counter - * \returns amount of counters */ -int osmo_counters_count() -{ - return llist_count(&counters); -} - -/*! Find a counter by its name. - * \paramin name Name used to look-up/search counter - * \returns Counter on success; NULL if not found */ -struct osmo_counter *osmo_counter_get_by_name(const char *name) -{ - struct osmo_counter *ctr; - - llist_for_each_entry(ctr, &counters, list) { - if (!strcmp(ctr->name, name)) - return ctr; - } - return NULL; -} - -/*! Compute difference between current and previous counter value. - * \paramin ctr Counter of which the difference is to be computed - * \returns Delta value between current counter and previous counter. Please - * note that the actual counter values are unsigned long, while the - * difference is computed as signed integer! */ -int osmo_counter_difference(struct osmo_counter *ctr) -{ - int delta = ctr->value - ctr->previous; - ctr->previous = ctr->value; - - return delta; -}
View file
libosmocore_1.7.0.tar.xz/src/fsm.c
Deleted
@@ -1,1043 +0,0 @@ -/*! \file fsm.c - * Osmocom generic Finite State Machine implementation. */ -/* - * (C) 2016-2019 by Harald Welte <laforge@gnumonks.org> - * - * 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 <errno.h> -#include <stdbool.h> -#include <string.h> -#include <inttypes.h> - -#include <osmocom/core/fsm.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/utils.h> - -/*! \addtogroup fsm - * @{ - * Finite State Machine abstraction - * - * This is a generic C-language abstraction for implementing finite - * state machines within the Osmocom framework. It is intended to - * replace existing hand-coded or even only implicitly existing FSMs - * all over the existing code base. - * - * An libosmocore FSM is described by its \ref osmo_fsm description, - * which in turn refers to an array of \ref osmo_fsm_state descriptor, - * each describing a single state in the FSM. - * - * The general idea is that all actions performed within one state are - * located at one position in the code (the state's action function), - * as opposed to the 'message-centric' view of e.g. the existing - * state machines of the LAPD(m) core, where there is one message for - * each possible event (primitive), and the function then needs to - * concern itself on how to handle that event over all possible states. - * - * For each state, there is a bit-mask of permitted input events for - * this state, as well as a bit-mask of permitted new output states to - * which the state can change. Furthermore, there is a function - * pointer implementing the actual handling of the input events - * occurring whilst in that state. - * - * Furthermore, each state offers a function pointer that can be - * executed just before leaving a state, and another one just after - * entering a state. - * - * When transitioning into a new state, an optional timer number and - * time-out can be passed along. The timer is started just after - * entering the new state, and will call the \ref osmo_fsm timer_cb - * function once it expires. This is intended to be used in telecom - * state machines where a given timer (identified by a certain number) - * is started to terminate the fsm or terminate the fsm once expected - * events are not happening before timeout expiration. - * - * As there can often be many concurrent FSMs of one given class, we - * introduce the concept of \ref osmo_fsm_inst, i.e. an FSM instance. - * The instance keeps the actual state, while the \ref osmo_fsm - * descriptor contains the static/const descriptor of the FSM's states - * and possible transitions. - * - * osmo_fsm are integrated with the libosmocore logging system. The - * logging sub-system is determined by the FSM descriptor, as we assume - * one FSM (let's say one related to a location update procedure) is - * inevitably always tied to a sub-system. The logging level however - * is configurable for each FSM instance, to ensure that e.g. DEBUG - * logging can be used for the LU procedure of one subscriber, while - * NOTICE level is used for all other subscribers. - * - * In order to attach private state to the \ref osmo_fsm_inst, it - * offers an opaque private pointer. - * - * \file fsm.c */ - -LLIST_HEAD(osmo_g_fsms); -static bool fsm_log_addr = true; -static bool fsm_log_timeouts = false; -/*! See osmo_fsm_term_safely(). */ -static bool fsm_term_safely_enabled = false; - -/*! Internal state for FSM instance termination cascades. */ -static __thread struct { - /*! The first FSM instance that invoked osmo_fsm_inst_term() in the current cascade. */ - struct osmo_fsm_inst *root_fi; - /*! 2 if a secondary FSM terminates, 3 if a secondary FSM causes a tertiary FSM to terminate, and so on. */ - unsigned int depth; - /*! Talloc context to collect all deferred deallocations (FSM instances, and talloc objects if any). */ - void *collect_ctx; - /*! See osmo_fsm_set_dealloc_ctx() */ - void *fsm_dealloc_ctx; -} fsm_term_safely; - -/*! Internal call to free an FSM instance, which redirects to the context set by osmo_fsm_set_dealloc_ctx() if any. - */ -static void fsm_free_or_steal(void *talloc_object) -{ - if (fsm_term_safely.fsm_dealloc_ctx) - talloc_steal(fsm_term_safely.fsm_dealloc_ctx, talloc_object); - else - talloc_free(talloc_object); -} - -/*! specify if FSM instance addresses should be logged or not - * - * By default, the FSM name includes the pointer address of the \ref - * osmo_fsm_inst. This behavior can be disabled (and re-enabled) - * using this function. - * - * \paramin log_addr Indicate if FSM instance address shall be logged - */ -void osmo_fsm_log_addr(bool log_addr) -{ - fsm_log_addr = log_addr; -} - -/*! Enable or disable logging of timeout values for FSM instance state changes. - * - * By default, state changes are logged by state name only, omitting the timeout. When passing true, each state change - * will also log the T number (or Osmocom-specific X number) and the chosen timeout in seconds. - * osmo_fsm_inst_state_chg_keep_timer() will log remaining timeout in millisecond precision. - * - * The default for this is false to reflect legacy behavior. Since various C tests that verify logging output already - * existed prior to this option, keeping timeout logging off makes sure that they continue to pass. Particularly, - * osmo_fsm_inst_state_chg_keep_timer() may cause non-deterministic logging of remaining timeout values. - * - * For any program that does not explicitly require deterministic logging output, i.e. anything besides regression tests - * involving FSM instances, it is recommended to call osmo_fsm_log_timeouts(true). - * - * \paramin log_timeouts Pass true to log timeouts on state transitions, false to omit timeouts. - */ -void osmo_fsm_log_timeouts(bool log_timeouts) -{ - fsm_log_timeouts = log_timeouts; -} - -/*! Enable safer way to deallocate cascades of terminating FSM instances. - * - * Note, using osmo_fsm_set_dealloc_ctx() is a more general solution to this same problem. - * Particularly, in a program using osmo_select_main_ctx(), the simplest solution to avoid most use-after-free problems - * from FSM instance deallocation is using osmo_fsm_set_dealloc_ctx(OTC_SELECT). - * - * When enabled, an FSM instance termination detects whether another FSM instance is already terminating, and instead of - * deallocating immediately, collects all terminating FSM instances in a talloc context, to be bulk deallocated once all - * event handling and termination cascades are done. - * - * For example, if an FSM's cleanup() sends an event to some "other" FSM, which in turn causes the FSM's parent to - * deallocate, then the parent would talloc_free() the child's memory, causing a use-after-free. There are infinite - * constellations like this, which all are trivially solved with this feature enabled. - * - * For illustration, see fsm_dealloc_test.c. - * - * When enabled, this feature changes the order of logging, which may break legacy unit test expectations, and changes - * the order of deallocation to after the parent term event is dispatched. - * - * \paramin term_safely Pass true to switch to safer FSM instance termination behavior. - */ -void osmo_fsm_term_safely(bool term_safely) -{ - fsm_term_safely_enabled = term_safely; -} - -/*! Instead of deallocating FSM instances, move them to the given talloc context. - * - * It is the caller's responsibility to clear this context to actually free the memory of terminated FSM instances. - * Make sure to not talloc_free(ctx) itself before setting a different osmo_fsm_set_dealloc_ctx(). To clear a ctx - * without the need to call osmo_fsm_set_dealloc_ctx() again, rather use talloc_free_children(ctx). - * - * For example, to defer deallocation to the next osmo_select_main_ctx() iteration, set this to OTC_SELECT. - * - * Deferring deallocation is the simplest solution to avoid most use-after-free problems from FSM instance deallocation. - * This is a simpler and more general solution than osmo_fsm_term_safely(). - * - * To disable the feature again, pass NULL as ctx. - * - * Both osmo_fsm_term_safely() and osmo_fsm_set_dealloc_ctx() can be enabled at the same time, which will result in - * first collecting deallocated FSM instances in fsm_term_safely.collect_ctx, and finally reparenting that to the ctx - * passed here. However, in practice, it does not really make sense to enable both at the same time. - * - * \param ctxin Instead of talloc_free()int, talloc_steal() all future deallocated osmo_fsm_inst instances to this - * ctx. If NULL, go back to talloc_free() as usual. - */ -void osmo_fsm_set_dealloc_ctx(void *ctx) -{ - fsm_term_safely.fsm_dealloc_ctx = ctx; -} - -/*! talloc_free() the given object immediately, or once ongoing FSM terminations are done. - * - * If an FSM deallocation cascade is ongoing, talloc_steal() the given talloc_object into the talloc context that is - * freed once the cascade is done. If no FSM deallocation cascade is ongoing, or if osmo_fsm_term_safely() is disabled, - * immediately talloc_free the object. - * - * This can be useful if some higher order talloc object, which is the talloc parent for FSM instances or their priv - * objects, is not itself tied to an FSM instance. This function allows safely freeing it without affecting ongoing FSM - * termination cascades. - * - * Once passed to this function, the talloc_object should be considered as already freed. Only FSM instance pre_term() - * and cleanup() functions as well as event handling caused by these may safely assume that it is still valid memory. - * - * The talloc_object should not have multiple parents. - * - * (This function may some day move to public API, which might be redundant if we introduce a select-loop volatile - * context mechanism to defer deallocation instead.) - * - * \paramin talloc_object Object pointer to free. - */ -static void osmo_fsm_defer_free(void *talloc_object) -{ - if (!fsm_term_safely.depth) { - fsm_free_or_steal(talloc_object); - return; - } - - if (!fsm_term_safely.collect_ctx) { - /* This is actually the first other object / FSM instance besides the root terminating inst. Create the - * ctx to collect this and possibly more objects to free. Avoid talloc parent loops: don't make this ctx - * the child of the root inst or anything like that. */ - fsm_term_safely.collect_ctx = talloc_named_const(NULL, 0, "fsm_term_safely.collect_ctx"); - OSMO_ASSERT(fsm_term_safely.collect_ctx); - } - talloc_steal(fsm_term_safely.collect_ctx, talloc_object); -} - -struct osmo_fsm *osmo_fsm_find_by_name(const char *name) -{ - struct osmo_fsm *fsm; - llist_for_each_entry(fsm, &osmo_g_fsms, list) { - if (!strcmp(name, fsm->name)) - return fsm; - } - return NULL; -} - -struct osmo_fsm_inst *osmo_fsm_inst_find_by_name(const struct osmo_fsm *fsm, - const char *name) -{ - struct osmo_fsm_inst *fi; - - if (!name) - return NULL; - - llist_for_each_entry(fi, &fsm->instances, list) { - if (!fi->name) - continue; - if (!strcmp(name, fi->name)) - return fi; - } - return NULL; -} - -struct osmo_fsm_inst *osmo_fsm_inst_find_by_id(const struct osmo_fsm *fsm, - const char *id) -{ - struct osmo_fsm_inst *fi; - - llist_for_each_entry(fi, &fsm->instances, list) { - if (!strcmp(id, fi->id)) - return fi; - } - return NULL; -} - -/*! register a FSM with the core - * - * A FSM descriptor needs to be registered with the core before any - * instances can be created for it. - * - * \paramin fsm Descriptor of Finite State Machine to be registered - * \returns 0 on success; negative on error - */ -int osmo_fsm_register(struct osmo_fsm *fsm) -{ - if (!osmo_identifier_valid(fsm->name)) { - LOGP(DLGLOBAL, LOGL_ERROR, "Attempting to register FSM with illegal identifier '%s'\n", fsm->name); - return -EINVAL; - } - if (osmo_fsm_find_by_name(fsm->name)) - return -EEXIST; - if (fsm->event_names == NULL) - LOGP(DLGLOBAL, LOGL_ERROR, "FSM '%s' has no event names! Please fix!\n", fsm->name); - llist_add_tail(&fsm->list, &osmo_g_fsms); - INIT_LLIST_HEAD(&fsm->instances); - - return 0; -} - -/*! unregister a FSM from the core - * - * Once the FSM descriptor is unregistered, active instances can still - * use it, but no new instances may be created for it. - * - * \paramin fsm Descriptor of Finite State Machine to be removed - */ -void osmo_fsm_unregister(struct osmo_fsm *fsm) -{ - llist_del(&fsm->list); -} - -/* small wrapper function around timer expiration (for logging) */ -static void fsm_tmr_cb(void *data) -{ - struct osmo_fsm_inst *fi = data; - struct osmo_fsm *fsm = fi->fsm; - int32_t T = fi->T; - - LOGPFSM(fi, "Timeout of " OSMO_T_FMT "\n", OSMO_T_FMT_ARGS(fi->T)); - - if (fsm->timer_cb) { - int rc = fsm->timer_cb(fi); - if (rc != 1) - /* We don't actually know whether fi exists anymore. - * Make sure to not access it and return right away. */ - return; - /* The timer_cb told us to terminate, so we can safely assume - * that fi still exists. */ - LOGPFSM(fi, "timer_cb requested termination\n"); - } else - LOGPFSM(fi, "No timer_cb, automatic termination\n"); - - /* if timer_cb returns 1 or there is no timer_cb */ - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_TIMEOUT, &T); -} - -/*! Change id of the FSM instance - * \paramin fi FSM instance - * \paramin id new ID - * \returns 0 if the ID was updated, otherwise -EINVAL - */ -int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id) -{ - if (!id) - return osmo_fsm_inst_update_id_f(fi, NULL); - else - return osmo_fsm_inst_update_id_f(fi, "%s", id); -} - -static void update_name(struct osmo_fsm_inst *fi) -{ - if (fi->name) - talloc_free((char*)fi->name); - - if (!fsm_log_addr) { - if (fi->id) - fi->name = talloc_asprintf(fi, "%s(%s)", fi->fsm->name, fi->id); - else - fi->name = talloc_asprintf(fi, "%s", fi->fsm->name); - } else { - if (fi->id) - fi->name = talloc_asprintf(fi, "%s(%s)%p", fi->fsm->name, fi->id, fi); - else - fi->name = talloc_asprintf(fi, "%s%p", fi->fsm->name, fi); - } -} - -/*! Change id of the FSM instance using a string format. - * \paramin fi FSM instance. - * \paramin fmt format string to compose new ID. - * \paramin ... variable argument list for format string. - * \returns 0 if the ID was updated, otherwise -EINVAL. - */ -int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...) -{ - char *id = NULL; - - if (fmt) { - va_list ap; - - va_start(ap, fmt); - id = talloc_vasprintf(fi, fmt, ap); - va_end(ap); - - if (!osmo_identifier_valid(id)) { - LOGP(DLGLOBAL, LOGL_ERROR, - "Attempting to set illegal id for FSM instance of type '%s': %s\n", - fi->fsm->name, osmo_quote_str(id, -1)); - talloc_free(id); - return -EINVAL; - } - } - - if (fi->id) - talloc_free((char*)fi->id); - fi->id = id; - - update_name(fi); - return 0; -} - -/*! Change id of the FSM instance using a string format, and ensuring a valid id. - * Replace any characters that are not permitted as FSM identifier with replace_with. - * \paramin fi FSM instance. - * \paramin replace_with Character to use instead of non-permitted FSM id characters. - * Make sure to choose a legal character, e.g. '-'. - * \paramin fmt format string to compose new ID. - * \paramin ... variable argument list for format string. - * \returns 0 if the ID was updated, otherwise -EINVAL. - */ -int osmo_fsm_inst_update_id_f_sanitize(struct osmo_fsm_inst *fi, char replace_with, const char *fmt, ...) -{ - char *id = NULL; - va_list ap; - int rc; - - if (!fmt) - return osmo_fsm_inst_update_id(fi, NULL); - - va_start(ap, fmt); - id = talloc_vasprintf(fi, fmt, ap); - va_end(ap); - - osmo_identifier_sanitize_buf(id, NULL, replace_with); - - rc = osmo_fsm_inst_update_id(fi, id); - talloc_free(id); - return rc; -} - -/*! allocate a new instance of a specified FSM - * \paramin fsm Descriptor of the FSM - * \paramin ctx talloc context from which to allocate memory - * \paramin priv private data reference store in fsm instance - * \paramin log_level The log level for events of this FSM - * \paramin id The name/ID of the FSM instance - * \returns newly-allocated, initialized and registered FSM instance - */ -struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv, - int log_level, const char *id) -{ - struct osmo_fsm_inst *fi = talloc_zero(ctx, struct osmo_fsm_inst); - - fi->fsm = fsm; - fi->priv = priv; - fi->log_level = log_level; - osmo_timer_setup(&fi->timer, fsm_tmr_cb, fi); - - if (osmo_fsm_inst_update_id(fi, id) < 0) { - fsm_free_or_steal(fi); - return NULL; - } - - INIT_LLIST_HEAD(&fi->proc.children); - INIT_LLIST_HEAD(&fi->proc.child); - llist_add(&fi->list, &fsm->instances); - - LOGPFSM(fi, "Allocated\n"); - - return fi; -} - -/*! allocate a new instance of a specified FSM as child of - * other FSM instance - * - * This is like \ref osmo_fsm_inst_alloc but using the parent FSM as - * talloc context, and inheriting the log level of the parent. - * - * \paramin fsm Descriptor of the to-be-allocated FSM - * \paramin parent Parent FSM instance - * \paramin parent_term_event Event to be sent to parent when terminating - * \returns newly-allocated, initialized and registered FSM instance - */ -struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, - struct osmo_fsm_inst *parent, - uint32_t parent_term_event) -{ - struct osmo_fsm_inst *fi; - - fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, - parent->id); - if (!fi) { - /* indicate immediate termination to caller */ - osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); - return NULL; - } - - LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); - - osmo_fsm_inst_change_parent(fi, parent, parent_term_event); - - return fi; -} - -/*! unlink child FSM from its parent FSM. - * \paramin fi Descriptor of the child FSM to unlink. - * \paramin ctx New talloc context - * - * Never call this function from the cleanup callback, because at that time - * the child FSMs will already be terminated. If unlinking should be performed - * on FSM termination, use the grace callback instead. */ -void osmo_fsm_inst_unlink_parent(struct osmo_fsm_inst *fi, void *ctx) -{ - if (fi->proc.parent) { - talloc_steal(ctx, fi); - fi->proc.parent = NULL; - fi->proc.parent_term_event = 0; - llist_del(&fi->proc.child); - } -} - -/*! change parent instance of an FSM. - * \paramin fi Descriptor of the to-be-allocated FSM. - * \paramin new_parent New parent FSM instance. - * \paramin new_parent_term_event Event to be sent to parent when terminating. - * - * Never call this function from the cleanup callback! - * (see also osmo_fsm_inst_unlink_parent()).*/ -void osmo_fsm_inst_change_parent(struct osmo_fsm_inst *fi, - struct osmo_fsm_inst *new_parent, - uint32_t new_parent_term_event) -{ - /* Make sure a possibly existing old parent is unlinked first - * (new_parent can be NULL) */ - osmo_fsm_inst_unlink_parent(fi, new_parent); - - /* Add new parent */ - if (new_parent) { - fi->proc.parent = new_parent; - fi->proc.parent_term_event = new_parent_term_event; - llist_add(&fi->proc.child, &new_parent->proc.children); - } -} - -/*! delete a given instance of a FSM - * \paramin fi FSM instance to be un-registered and deleted - */ -void osmo_fsm_inst_free(struct osmo_fsm_inst *fi) -{ - osmo_timer_del(&fi->timer); - llist_del(&fi->list); - - if (fsm_term_safely.depth) { - /* Another FSM instance has caused this one to free and is still busy with its termination. Don't free - * yet, until the other FSM instance is done. */ - osmo_fsm_defer_free(fi); - /* The root_fi can't go missing really, but to be safe... */ - if (fsm_term_safely.root_fi) - LOGPFSM(fi, "Deferring: will deallocate with %s\n", fsm_term_safely.root_fi->name); - else - LOGPFSM(fi, "Deferring deallocation\n"); - - /* Don't free anything yet. Exit. */ - return; - } - - /* fsm_term_safely.depth == 0. - * - If fsm_term_safely is enabled, this is the original FSM instance that started terminating first. Free this - * and along with it all other collected terminated FSM instances. - * - If fsm_term_safely is disabled, this is just any FSM instance deallocating. */ - - if (fsm_term_safely.collect_ctx) { - /* The fi may be a child of any other FSM instances or objects collected in the collect_ctx. Don't - * deallocate separately to avoid use-after-free errors, put it in there and deallocate all at once. */ - LOGPFSM(fi, "Deallocated, including all deferred deallocations\n"); - osmo_fsm_defer_free(fi); - fsm_free_or_steal(fsm_term_safely.collect_ctx); - fsm_term_safely.collect_ctx = NULL; - } else { - LOGPFSM(fi, "Deallocated\n"); - fsm_free_or_steal(fi); - } - fsm_term_safely.root_fi = NULL; -} - -/*! get human-readable name of FSM event - * \paramin fsm FSM descriptor of event - * \paramin event Event integer value - * \returns string rendering of the event - */ -const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event) -{ - static __thread char buf32; - if (!fsm->event_names) { - snprintf(buf, sizeof(buf), "%"PRIu32, event); - return buf; - } else - return get_value_string(fsm->event_names, event); -} - -/*! get human-readable name of FSM instance - * \paramin fi FSM instance - * \returns string rendering of the FSM identity - */ -const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi) -{ - if (!fi) - return "NULL"; - - if (fi->name) - return fi->name; - else - return fi->fsm->name; -} - -/*! get human-readable name of FSM state - * \paramin fsm FSM descriptor - * \paramin state FSM state number - * \returns string rendering of the FSM state - */ -const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state) -{ - static __thread char buf32; - if (state >= fsm->num_states) { - snprintf(buf, sizeof(buf), "unknown %"PRIu32, state); - return buf; - } else - return fsm->statesstate.name; -} - -static int state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, - bool keep_timer, unsigned long timeout_ms, int T, - const char *file, int line) -{ - struct osmo_fsm *fsm = fi->fsm; - uint32_t old_state = fi->state; - const struct osmo_fsm_state *st = &fsm->statesfi->state; - struct timeval remaining; - - if (fi->proc.terminating) { - LOGPFSMSRC(fi, file, line, - "FSM instance already terminating, not changing state to %s\n", - osmo_fsm_state_name(fsm, new_state)); - return -EINVAL; - } - - /* validate if new_state is a valid state */ - if (!(st->out_state_mask & (1 << new_state))) { - LOGPFSMLSRC(fi, LOGL_ERROR, file, line, - "transition to state %s not permitted!\n", - osmo_fsm_state_name(fsm, new_state)); - return -EPERM; - } - - if (!keep_timer) { - /* delete the old timer */ - osmo_timer_del(&fi->timer); - } - - if (st->onleave) - st->onleave(fi, new_state); - - if (fsm_log_timeouts) { - char trailer64; - trailer0 = '\0'; - if (keep_timer && fi->timer.active) { - /* This should always give us a timeout, but just in case the return value indicates error, omit - * logging the remaining time. */ - if (osmo_timer_remaining(&fi->timer, NULL, &remaining)) - snprintf(trailer, sizeof(trailer), "(keeping " OSMO_T_FMT ")", - OSMO_T_FMT_ARGS(fi->T)); - else - snprintf(trailer, sizeof(trailer), "(keeping " OSMO_T_FMT - ", %ld.%03lds remaining)", OSMO_T_FMT_ARGS(fi->T), - (long) remaining.tv_sec, remaining.tv_usec / 1000); - } else if (timeout_ms) { - if (timeout_ms % 1000 == 0) - /* keep log output legacy compatible to avoid autotest failures */ - snprintf(trailer, sizeof(trailer), "(" OSMO_T_FMT ", %lus)", - OSMO_T_FMT_ARGS(T), timeout_ms/1000); - else - snprintf(trailer, sizeof(trailer), "(" OSMO_T_FMT ", %lums)", - OSMO_T_FMT_ARGS(T), timeout_ms); - } else - snprintf(trailer, sizeof(trailer), "(no timeout)"); - - LOGPFSMSRC(fi, file, line, "State change to %s %s\n", - osmo_fsm_state_name(fsm, new_state), trailer); - } else { - LOGPFSMSRC(fi, file, line, "state_chg to %s\n", - osmo_fsm_state_name(fsm, new_state)); - } - - fi->state = new_state; - st = &fsm->statesnew_state; - - if (!keep_timer - || (keep_timer && !osmo_timer_pending(&fi->timer))) { - fi->T = T; - if (timeout_ms) - osmo_timer_schedule(&fi->timer, timeout_ms / 1000, timeout_ms % 1000); - } - - /* Call 'onenter' last, user might terminate FSM from there */ - if (st->onenter) - st->onenter(fi, old_state); - - return 0; -} - -/*! perform a state change of the given FSM instance - * - * Best invoke via the osmo_fsm_inst_state_chg() macro which logs the source - * file where the state change was effected. Alternatively, you may pass \a - * file as NULL to use the normal file/line indication instead. - * - * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* - * function. It verifies that the existing state actually permits a - * transition to new_state. - * - * If timeout_secs is 0, stay in the new state indefinitely, without a timeout - * (stop the FSM instance's timer if it was runnning). - * - * If timeout_secs > 0, start or reset the FSM instance's timer with this - * timeout. On expiry, invoke the FSM instance's timer_cb -- if no timer_cb is - * set, an expired timer immediately terminates the FSM instance with - * OSMO_FSM_TERM_TIMEOUT. - * - * The value of T is stored in fi->T and is then available for query in - * timer_cb. If passing timeout_secs == 0, it is recommended to also pass T == - * 0, so that fi->T is reset to 0 when no timeout is invoked. - * - * Positive values for T are considered to be 3GPP spec compliant and appear in - * logging and VTY as "T1234", while negative values are considered to be - * Osmocom specific timers, represented in logging and VTY as "X1234". - * - * See also osmo_tdef_fsm_inst_state_chg() from the osmo_tdef API, which - * provides a unified way to configure and apply GSM style Tnnnn timers to FSM - * state transitions. - * - * \paramin fi FSM instance whose state is to change - * \paramin new_state The new state into which we should change - * \paramin timeout_secs Timeout in seconds (if !=0), maximum-clamped to 2147483647 seconds. - * \paramin T Timer number, where positive numbers are considered to be 3GPP spec compliant timer numbers and are - * logged as "T1234", while negative numbers are considered Osmocom specific timer numbers logged as - * "X1234". - * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) - * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) - * \returns 0 on success; negative on error - */ -int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, - unsigned long timeout_secs, int T, - const char *file, int line) -{ - return state_chg(fi, new_state, false, timeout_secs*1000, T, file, line); -} -int _osmo_fsm_inst_state_chg_ms(struct osmo_fsm_inst *fi, uint32_t new_state, - unsigned long timeout_ms, int T, - const char *file, int line) -{ - return state_chg(fi, new_state, false, timeout_ms, T, file, line); -} - -/*! perform a state change while keeping the current timer running. - * - * This is useful to keep a timeout across several states (without having to round the - * remaining time to seconds). - * - * Best invoke via the osmo_fsm_inst_state_chg_keep_timer() macro which logs the source - * file where the state change was effected. Alternatively, you may pass \a - * file as NULL to use the normal file/line indication instead. - * - * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* - * function. It verifies that the existing state actually permits a - * transition to new_state. - * - * \paramin fi FSM instance whose state is to change - * \paramin new_state The new state into which we should change - * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) - * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) - * \returns 0 on success; negative on error - */ -int _osmo_fsm_inst_state_chg_keep_timer(struct osmo_fsm_inst *fi, uint32_t new_state, - const char *file, int line) -{ - return state_chg(fi, new_state, true, 0, 0, file, line); -} - -/*! perform a state change while keeping the current timer if running, or starting a timer otherwise. - * - * This is useful to keep a timeout across several states, but to make sure that some timeout is actually running. - * - * Best invoke via the osmo_fsm_inst_state_chg_keep_or_start_timer() macro which logs the source file where the state - * change was effected. Alternatively, you may pass file as NULL to use the normal file/line indication instead. - * - * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* - * function. It verifies that the existing state actually permits a - * transition to new_state. - * - * \paramin fi FSM instance whose state is to change - * \paramin new_state The new state into which we should change - * \paramin timeout_secs If no timer is running yet, set this timeout in seconds (if !=0), maximum-clamped to - * 2147483647 seconds. - * \paramin T Timer number, where positive numbers are considered to be 3GPP spec compliant timer numbers and are - * logged as "T1234", while negative numbers are considered Osmocom specific timer numbers logged as - * "X1234". - * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) - * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) - * \returns 0 on success; negative on error - */ -int _osmo_fsm_inst_state_chg_keep_or_start_timer(struct osmo_fsm_inst *fi, uint32_t new_state, - unsigned long timeout_secs, int T, - const char *file, int line) -{ - return state_chg(fi, new_state, true, timeout_secs*1000, T, file, line); -} -int _osmo_fsm_inst_state_chg_keep_or_start_timer_ms(struct osmo_fsm_inst *fi, uint32_t new_state, - unsigned long timeout_ms, int T, - const char *file, int line) -{ - return state_chg(fi, new_state, true, timeout_ms, T, file, line); -} - - -/*! dispatch an event to an osmocom finite state machine instance - * - * Best invoke via the osmo_fsm_inst_dispatch() macro which logs the source - * file where the event was effected. Alternatively, you may pass \a file as - * NULL to use the normal file/line indication instead. - * - * Any incoming events to \ref osmo_fsm instances must be dispatched to - * them via this function. It verifies, whether the event is permitted - * based on the current state of the FSM. If not, -1 is returned. - * - * \paramin fi FSM instance - * \paramin event Event to send to FSM instance - * \paramin data Data to pass along with the event - * \paramin file Calling source file (from osmo_fsm_inst_dispatch macro) - * \paramin line Calling source line (from osmo_fsm_inst_dispatch macro) - * \returns 0 in case of success; negative on error - */ -int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data, - const char *file, int line) -{ - struct osmo_fsm *fsm; - const struct osmo_fsm_state *fs; - - if (!fi) { - LOGPSRC(DLGLOBAL, LOGL_ERROR, file, line, - "Trying to dispatch event %"PRIu32" to non-existent" - " FSM instance!\n", event); - osmo_log_backtrace(DLGLOBAL, LOGL_ERROR); - return -ENODEV; - } - - fsm = fi->fsm; - - if (fi->proc.terminating) { - LOGPFSMSRC(fi, file, line, - "FSM instance already terminating, not dispatching event %s\n", - osmo_fsm_event_name(fsm, event)); - return -EINVAL; - } - - OSMO_ASSERT(fi->state < fsm->num_states); - fs = &fi->fsm->statesfi->state; - - LOGPFSMSRC(fi, file, line, - "Received Event %s\n", osmo_fsm_event_name(fsm, event)); - - if (((1 << event) & fsm->allstate_event_mask) && fsm->allstate_action) { - fsm->allstate_action(fi, event, data); - return 0; - } - - if (!((1 << event) & fs->in_event_mask)) { - LOGPFSMLSRC(fi, LOGL_ERROR, file, line, - "Event %s not permitted\n", - osmo_fsm_event_name(fsm, event)); - return -1; - } - - if (fs->action) - fs->action(fi, event, data); - - return 0; -} - -/*! Terminate FSM instance with given cause - * - * This safely terminates the given FSM instance by first iterating - * over all children and sending them a termination event. Next, it - * calls the FSM descriptors cleanup function (if any), followed by - * releasing any memory associated with the FSM instance. - * - * Finally, the parent FSM instance (if any) is notified using the - * parent termination event configured at time of FSM instance start. - * - * \paramin fi FSM instance to be terminated - * \paramin cause Cause / reason for termination - * \paramin data Opaque event data to be passed with the parent term event - * \paramin file Calling source file (from osmo_fsm_inst_term macro) - * \paramin line Calling source line (from osmo_fsm_inst_term macro) - */ -void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, - enum osmo_fsm_term_cause cause, void *data, - const char *file, int line) -{ - struct osmo_fsm_inst *parent; - uint32_t parent_term_event = fi->proc.parent_term_event; - - if (fi->proc.terminating) { - LOGPFSMSRC(fi, file, line, "Ignoring trigger to terminate: already terminating\n"); - return; - } - fi->proc.terminating = true; - - /* Start termination cascade handling only if the feature is enabled. Also check the current depth: though - * unlikely, theoretically the fsm_term_safely_enabled flag could be toggled in the middle of a cascaded - * termination, so make sure to continue if it already started. */ - if (fsm_term_safely_enabled || fsm_term_safely.depth) { - fsm_term_safely.depth++; - /* root_fi is just for logging, so no need to be extra careful about it. */ - if (!fsm_term_safely.root_fi) - fsm_term_safely.root_fi = fi; - } - - if (fsm_term_safely.depth > 1) { - /* fsm_term_safely is enabled and this is a secondary FSM instance terminated, caused by the root_fi. */ - LOGPFSMSRC(fi, file, line, "Terminating in cascade, depth %d (cause = %s, caused by: %s)\n", - fsm_term_safely.depth, osmo_fsm_term_cause_name(cause), - fsm_term_safely.root_fi ? fsm_term_safely.root_fi->name : "unknown"); - /* The root_fi can't go missing really, but to be safe, log "unknown" in that case. */ - } else { - /* fsm_term_safely is disabled, or this is the root_fi. */ - LOGPFSMSRC(fi, file, line, "Terminating (cause = %s)\n", osmo_fsm_term_cause_name(cause)); - } - - /* graceful exit (optional) */ - if (fi->fsm->pre_term) - fi->fsm->pre_term(fi, cause); - - _osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL, - file, line); - - /* delete ourselves from the parent */ - parent = fi->proc.parent; - if (parent) { - LOGPFSMSRC(fi, file, line, "Removing from parent %s\n", - osmo_fsm_inst_name(parent)); - llist_del(&fi->proc.child); - } - - /* call destructor / clean-up function */ - if (fi->fsm->cleanup) - fi->fsm->cleanup(fi, cause); - - /* Fetch parent again in case it has changed. */ - parent = fi->proc.parent; - - /* Legacy behavior if fsm_term_safely is disabled: free before dispatching parent event. (If fsm_term_safely is - * enabled, depth will *always* be > 0 here.) Pivot on depth instead of the enabled flag in case the enabled - * flag is toggled in the middle of an FSM term. */ - if (!fsm_term_safely.depth) { - LOGPFSMSRC(fi, file, line, "Freeing instance\n"); - osmo_fsm_inst_free(fi); - } - - /* indicate our termination to the parent */ - if (parent && cause != OSMO_FSM_TERM_PARENT) - _osmo_fsm_inst_dispatch(parent, parent_term_event, data, - file, line); - - /* Newer, safe deallocation: free only after the parent_term_event was dispatched, to catch all termination - * cascades, and free all FSM instances at once. (If fsm_term_safely is enabled, depth will *always* be > 0 - * here.) osmo_fsm_inst_free() will do the defer magic depending on the fsm_term_safely.depth. */ - if (fsm_term_safely.depth) { - fsm_term_safely.depth--; - osmo_fsm_inst_free(fi); - } -} - -/*! Terminate all child FSM instances of an FSM instance. - * - * Iterate over all children and send them a termination event, with the given - * cause. Pass OSMO_FSM_TERM_PARENT to avoid dispatching events from the - * terminated child FSMs. - * - * \paramin fi FSM instance that should be cleared of child FSMs - * \paramin cause Cause / reason for termination (OSMO_FSM_TERM_PARENT) - * \paramin data Opaque event data to be passed with the parent term events - * \paramin file Calling source file (from osmo_fsm_inst_term_children macro) - * \paramin line Calling source line (from osmo_fsm_inst_term_children macro) - */ -void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, - enum osmo_fsm_term_cause cause, - void *data, - const char *file, int line) -{ - struct osmo_fsm_inst *first_child, *last_seen_first_child; - - /* iterate over all children, starting from the beginning every time: - * terminating an FSM may emit events that cause other FSMs to also - * terminate and remove themselves from this list. */ - last_seen_first_child = NULL; - while (!llist_empty(&fi->proc.children)) { - first_child = llist_entry(fi->proc.children.next, - typeof(*first_child), - proc.child); - - /* paranoia: do not loop forever */ - if (first_child == last_seen_first_child) { - LOGPFSMLSRC(fi, LOGL_ERROR, file, line, - "Internal error while terminating child" - " FSMs: a child FSM is stuck\n"); - break; - } - last_seen_first_child = first_child; - - /* terminate child */ - _osmo_fsm_inst_term(first_child, cause, data, - file, line); - } -} - -/*! Broadcast an event to all the FSMs children. - * - * Iterate over all children and send them the specified event. - * - * \paramin fi FSM instance of the parent - * \paramin event Event to send to children of FSM instance - * \paramin data Data to pass along with the event - * \paramin file Calling source file (from osmo_fsm_inst_dispatch macro) - * \paramin line Calling source line (from osmo_fsm_inst_dispatch macro) - */ -void _osmo_fsm_inst_broadcast_children(struct osmo_fsm_inst *fi, - uint32_t event, void *data, - const char *file, int line) -{ - struct osmo_fsm_inst *child, *tmp; - llist_for_each_entry_safe(child, tmp, &fi->proc.children, proc.child) { - _osmo_fsm_inst_dispatch(child, event, data, file, line); - } -} - -const struct value_string osmo_fsm_term_cause_names = { - OSMO_VALUE_STRING(OSMO_FSM_TERM_PARENT), - OSMO_VALUE_STRING(OSMO_FSM_TERM_REQUEST), - OSMO_VALUE_STRING(OSMO_FSM_TERM_REGULAR), - OSMO_VALUE_STRING(OSMO_FSM_TERM_ERROR), - OSMO_VALUE_STRING(OSMO_FSM_TERM_TIMEOUT), - { 0, NULL } -}; - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/gsm/i460_mux.c
Deleted
@@ -1,388 +0,0 @@ -/*! \file i460_mux.c - * ITU-T I.460 sub-channel multiplexer + demultiplexer */ -/* - * (C) 2020 by Harald Welte <laforge@gnumonks.org> - * - * 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 <errno.h> - -#include <osmocom/core/bits.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/i460_mux.h> - -/* count the number of sub-channels in this I460 slot */ -static int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts) -{ - int i, num_used = 0; - - for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { - if (ts->schani.rate != OSMO_I460_RATE_NONE) - num_used++; - } - - return num_used; -} - -/* does this channel have no sub-streams (single 64k subchannel)? */ -static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts) -{ - if (osmo_i460_subchan_count(ts) != 1) - return false; - - if (ts->schan0.rate != OSMO_I460_RATE_64k) - return false; - - return true; -} - -/*********************************************************************** - * Demultiplexer - ***********************************************************************/ - -/* append a single bit to a sub-channel */ -static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit) -{ - struct osmo_i460_subchan_demux *demux = &schan->demux; - - OSMO_ASSERT(demux->out_bitbuf); - OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size); - - demux->out_bitbufdemux->out_idx++ = bit ? 1 : 0; - - if (demux->out_idx >= demux->out_bitbuf_size) { - if (demux->out_cb_bits) - demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx); - else { - /* pack bits into bytes */ - OSMO_ASSERT((demux->out_idx % 8) == 0); - unsigned int num_bytes = demux->out_idx / 8; - uint8_t bytesnum_bytes; - osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx); - demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes); - } - demux->out_idx = 0; - } -} - -/* extract those bits relevant to this schan of each byte in 'data' */ -static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len) -{ - int i; - - for (i = 0; i < data_len; i++) { - uint8_t inbyte = datai; - /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two - * most significant bits, hence we extract msb-first */ - uint8_t inbits = inbyte << schan->bit_offset; - - /* extract the bits relevant to the given schan */ - switch (schan->rate) { - case OSMO_I460_RATE_8k: - demux_subchan_append_bit(schan, inbits & 0x80); - break; - case OSMO_I460_RATE_16k: - demux_subchan_append_bit(schan, inbits & 0x80); - demux_subchan_append_bit(schan, inbits & 0x40); - break; - case OSMO_I460_RATE_32k: - demux_subchan_append_bit(schan, inbits & 0x80); - demux_subchan_append_bit(schan, inbits & 0x40); - demux_subchan_append_bit(schan, inbits & 0x20); - demux_subchan_append_bit(schan, inbits & 0x10); - break; - case OSMO_I460_RATE_64k: - demux_subchan_append_bit(schan, inbits & 0x80); - demux_subchan_append_bit(schan, inbits & 0x40); - demux_subchan_append_bit(schan, inbits & 0x20); - demux_subchan_append_bit(schan, inbits & 0x10); - demux_subchan_append_bit(schan, inbits & 0x08); - demux_subchan_append_bit(schan, inbits & 0x04); - demux_subchan_append_bit(schan, inbits & 0x02); - demux_subchan_append_bit(schan, inbits & 0x01); - break; - default: - OSMO_ASSERT(0); - } - } -} - -/*! Data from E1 timeslot into de-multiplexer - * \paramin ts timeslot state - * \paramin data input data bytes as received from E1/T1 - * \paramin data_len length of data in bytes */ -void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len) -{ - struct osmo_i460_subchan *schan; - struct osmo_i460_subchan_demux *demux; - int i; - - /* fast path if entire 64k slot is used */ - if (osmo_i460_has_single_64k_schan(ts)) { - schan = &ts->schan0; - demux = &schan->demux; - if (demux->out_cb_bytes) - demux->out_cb_bytes(schan, demux->user_data, data, data_len); - else { - ubit_t bitsdata_len*8; - osmo_pbit2ubit(bits, data, data_len*8); - demux->out_cb_bits(schan, demux->user_data, bits, data_len*8); - } - return; - } - - /* Slow path iterating over all lchans */ - for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { - schan = &ts->schani; - if (schan->rate == OSMO_I460_RATE_NONE) - continue; - demux_subchan_extract_bits(schan, data, data_len); - } -} - - -/*********************************************************************** - * Multiplexer - ***********************************************************************/ - -/*! enqueue a to-be-transmitted message buffer containing unpacked bits */ -void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg) -{ - OSMO_ASSERT(msgb_length(msg) > 0); - msgb_enqueue(&schan->mux.tx_queue, msg); -} - -/* mux: pull the next bit out of the given sub-channel */ -static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan) -{ - struct osmo_i460_subchan_mux *mux = &schan->mux; - struct msgb *msg; - ubit_t bit; - - /* if we don't have anything to transmit, return '1' bits */ - if (llist_empty(&mux->tx_queue)) { - /* User code now has a last chance to put something into the queue. */ - if (mux->in_cb_queue_empty) - mux->in_cb_queue_empty(schan, mux->user_data); - - /* If the queue is still empty, return idle bits */ - if (llist_empty(&mux->tx_queue)) - return 0x01; - } - msg = llist_entry(mux->tx_queue.next, struct msgb, list); - bit = msgb_pull_u8(msg); - - /* free msgb if we have pulled the last bit */ - if (msgb_length(msg) <= 0) { - llist_del(&msg->list); - talloc_free(msg); - } - - return bit; -} - -/*! provide one byte with the subchan-specific bits of given sub-channel. - * \paramin schan sub-channel that is to provide bits - * \parmaout mask bitmask of those bits filled in - * \returns bits of given sub-channel */ -static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask) -{ - uint8_t outbits = 0; - uint8_t outmask; - - /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two - * most significant bits, hence we provide msb-first */ - - switch (schan->rate) { - case OSMO_I460_RATE_8k: - outbits = mux_schan_provide_bit(schan) << 7; - outmask = 0x80; - break; - case OSMO_I460_RATE_16k: - outbits |= mux_schan_provide_bit(schan) << 7; - outbits |= mux_schan_provide_bit(schan) << 6; - outmask = 0xC0; - break; - case OSMO_I460_RATE_32k: - outbits |= mux_schan_provide_bit(schan) << 7; - outbits |= mux_schan_provide_bit(schan) << 6; - outbits |= mux_schan_provide_bit(schan) << 5; - outbits |= mux_schan_provide_bit(schan) << 4; - outmask = 0xF0; - break; - case OSMO_I460_RATE_64k: - outbits |= mux_schan_provide_bit(schan) << 7; - outbits |= mux_schan_provide_bit(schan) << 6; - outbits |= mux_schan_provide_bit(schan) << 5; - outbits |= mux_schan_provide_bit(schan) << 4; - outbits |= mux_schan_provide_bit(schan) << 3; - outbits |= mux_schan_provide_bit(schan) << 2; - outbits |= mux_schan_provide_bit(schan) << 1; - outbits |= mux_schan_provide_bit(schan) << 0; - outmask = 0xFF; - break; - default: - OSMO_ASSERT(0); - } - *mask = outmask >> schan->bit_offset; - return outbits >> schan->bit_offset; -} - -/* provide one byte of multiplexed I.460 bits */ -static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts) -{ - int i, count = 0; - uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */ - - for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { - struct osmo_i460_subchan *schan = &ts->schani; - uint8_t bits, mask; - - if (schan->rate == OSMO_I460_RATE_NONE) - continue; - count++; - bits = mux_subchan_provide_bits(schan, &mask); - ret &= ~mask; - ret |= bits; - } - - return ret; -} - - -/*! Data from E1 timeslot into de-multiplexer - * \paramin ts timeslot state - * \paramout out caller-provided buffer where to store generated output bytes - * \paramin out_len number of bytes to be stored at out - */ -int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len) -{ - int i; - - /* fast path if entire 64k slot is used */ - //if (osmo_i460_has_single_64k_schan(ts)) { } - - for (i = 0; i < out_len; i++) - outi = mux_timeslot_provide_bits(ts); - - return out_len; -} - - -/*********************************************************************** - * Initialization / Control - ***********************************************************************/ - - -static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits) -{ - struct osmo_i460_subchan_demux *demux = &schan->demux; - - talloc_free(demux->out_bitbuf); - demux->out_bitbuf = talloc_zero_size(ctx, num_bits); - if (!demux->out_bitbuf) - return -ENOMEM; - demux->out_bitbuf_size = num_bits; - - return 0; -} - - -static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { - const struct osmo_i460_subchan *schan = &ts->schani; - if (schan->rate == OSMO_I460_RATE_NONE) - return i; - } - return -1; -} - -/* reset subchannel struct into a defined state */ -static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time) -{ - /* Before we zero out the subchannel struct, we must be sure that the - * tx_queue is cleared and all dynamically allocated memory is freed. - * However, on an uninitalized subchannel struct we can not be sure - * that the pointers are valid. If the subchannel is reset the first - * time the caller must set first_time to true. */ - if (!first_time) { - if (schan->demux.out_bitbuf) - talloc_free(schan->demux.out_bitbuf); - msgb_queue_free(&schan->mux.tx_queue); - } - - /* Reset subchannel to a defined state */ - memset(schan, 0, sizeof(*schan)); - schan->rate = OSMO_I460_RATE_NONE; - INIT_LLIST_HEAD(&schan->mux.tx_queue); -} - -/*! initialize an I.460 timeslot */ -void osmo_i460_ts_init(struct osmo_i460_timeslot *ts) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { - struct osmo_i460_subchan *schan = &ts->schani; - schan->ts = ts; - subchan_reset(schan, true); - } -} - -/*! add a new sub-channel to the given timeslot - * \paramin ctx talloc context from where to allocate the internal buffer - * \paramin ts timeslot to which to add a sub-channel - * \paramin chd description of the sub-channel to be added - * \return pointer to sub-channel on success, NULL on error */ -struct osmo_i460_subchan * -osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd) -{ - struct osmo_i460_subchan *schan; - int idx, rc; - - idx = find_unused_subchan_idx(ts); - if (idx < 0) - return NULL; - - schan = &ts->schanidx; - - schan->rate = chd->rate; - schan->bit_offset = chd->bit_offset; - - schan->demux.out_cb_bits = chd->demux.out_cb_bits; - schan->demux.out_cb_bytes = chd->demux.out_cb_bytes; - schan->demux.user_data = chd->demux.user_data; - schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty; - schan->mux.user_data = chd->mux.user_data; - rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits); - if (rc < 0) { - subchan_reset(schan, false); - return NULL; - } - - /* return number of schan in use */ - return schan; -} - -/* remove a su-channel from the multiplex */ -void osmo_i460_subchan_del(struct osmo_i460_subchan *schan) -{ - subchan_reset(schan, false); -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/gsm/lapd_core.c
Deleted
@@ -1,2235 +0,0 @@ -/*! \file lapd_core.c - * LAPD core implementation */ -/* - * (C) 2010-2020 by Harald Welte <laforge@gnumonks.org> - * (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu> - * - * 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. - * - */ - -/*! \addtogroup lapd - * @{ - * - * Osmocom LAPD core, used for Q.921, LAPDm and others. - * - * Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue - * - * RX data is stored in the rcv_buffer (pointer). If the message is complete, it - * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is - * received while there is an incomplete rcv_buffer, it is appended to it. - * - * TX data is stored in the send_queue first. When transmitting a frame, - * the first message in the send_queue is moved to the send_buffer. There it - * resides until all fragments are acknowledged. Fragments to be sent by I - * frames are stored in the tx_hist buffer for resend, if required. Also the - * current fragment is copied into the tx_queue. There it resides until it is - * forwarded to layer 1. - * - * In case we have SAPI 0, we only have a window size of 1, so the unack- - * nowledged message resides always in the send_buffer. In case of a suspend, - * it can be written back to the first position of the send_queue. - * - * The layer 1 normally sends a PH-READY-TO-SEND. But because we use - * asynchronous transfer between layer 1 and layer 2 (serial link), we must - * send a frame before layer 1 reaches the right timeslot to send it. So we - * move the tx_queue to layer 1 when there is not already a pending frame, and - * wait until acknowledge after the frame has been sent. If we receive an - * acknowledge, we can send the next frame from the buffer, if any. - * - * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it - * will trigger next I frame, if possible. - * - * T203 is optional. It will be stated when entering MF EST state. It will also - * be started when I or S frame is received in that state . It will be - * restarted in the lapd_acknowledge() function, in case outstanding frames - * will not trigger T200. It will be stoped, when T200 is started in MF EST - * state. It will also be stoped when leaving MF EST state. - * - * \file lapd_core.c - */ - -/* Enable this to test content resolution on network side: - * - The first SABM is received, UA is dropped. - * - The phone repeats SABM, but it's content is wrong, so it is ignored - * - The phone repeats SABM again, content is right, so UA is sent. - */ -//#define TEST_CONTENT_RESOLUTION_NETWORK - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/lapd_core.h> -#include <osmocom/gsm/rsl.h> - -/* TS 04.06 Table 4 / Section 3.8.1 */ -#define LAPD_U_SABM 0x7 -#define LAPD_U_SABME 0xf -#define LAPD_U_DM 0x3 -#define LAPD_U_UI 0x0 -#define LAPD_U_DISC 0x8 -#define LAPD_U_UA 0xC -#define LAPD_U_FRMR 0x11 - -#define LAPD_S_RR 0x0 -#define LAPD_S_RNR 0x1 -#define LAPD_S_REJ 0x2 - -#define CR_USER2NET_CMD 0 -#define CR_USER2NET_RESP 1 -#define CR_NET2USER_CMD 1 -#define CR_NET2USER_RESP 0 - -#define LAPD_HEADROOM 56 -#define LAPD_TAILROOM 16 - -#define SBIT(a) (1 << a) -#define ALL_STATES 0xffffffff - -static void lapd_t200_cb(void *data); -static void lapd_t203_cb(void *data); -static int lapd_send_i(struct lapd_msg_ctx *lctx, int line); -static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); - -/* UTILITY FUNCTIONS */ - -struct msgb *lapd_msgb_alloc(int length, const char *name) -{ - /* adding space for padding, FIXME: add as an option */ - if (length < 21) - length = 21; - return msgb_alloc_headroom(length + LAPD_HEADROOM + LAPD_TAILROOM, LAPD_HEADROOM, name); -} - -static inline uint8_t do_mod(uint8_t x, uint8_t m) -{ - return x & (m - 1); -} - -static inline uint8_t inc_mod(uint8_t x, uint8_t m) -{ - return (x + 1) & (m - 1); -} - -static inline uint8_t add_mod(uint8_t x, uint8_t y, uint8_t m) -{ - return (x + y) & (m - 1); -} - -static inline uint8_t sub_mod(uint8_t x, uint8_t y, uint8_t m) -{ - return (x - y) & (m - 1); /* handle negative results correctly */ -} - -static void lapd_dl_flush_send(struct lapd_datalink *dl) -{ - struct msgb *msg; - - /* Flush send-queue */ - while ((msg = msgb_dequeue(&dl->send_queue))) - msgb_free(msg); - - /* Clear send-buffer */ - msgb_free(dl->send_buffer); - dl->send_buffer = NULL; -} - -static void lapd_dl_flush_hist(struct lapd_datalink *dl) -{ - unsigned int i; - - if (!dl->range_hist || !dl->tx_hist) - return; - - for (i = 0; i < dl->range_hist; i++) { - if (dl->tx_histi.msg) { - msgb_free(dl->tx_histi.msg); - dl->tx_histi.msg = NULL; - } - } -} - -static void lapd_dl_flush_tx(struct lapd_datalink *dl) -{ - struct msgb *msg; - - while ((msg = msgb_dequeue(&dl->tx_queue))) - msgb_free(msg); - lapd_dl_flush_hist(dl); -} - -/* Figure B.2/Q.921 */ -const struct value_string lapd_state_names = { - OSMO_VALUE_STRING(LAPD_STATE_NULL), - OSMO_VALUE_STRING(LAPD_STATE_TEI_UNASS), - OSMO_VALUE_STRING(LAPD_STATE_ASS_TEI_WAIT), - OSMO_VALUE_STRING(LAPD_STATE_EST_TEI_WAIT), - OSMO_VALUE_STRING(LAPD_STATE_IDLE), - OSMO_VALUE_STRING(LAPD_STATE_SABM_SENT), - OSMO_VALUE_STRING(LAPD_STATE_DISC_SENT), - OSMO_VALUE_STRING(LAPD_STATE_MF_EST), - OSMO_VALUE_STRING(LAPD_STATE_TIMER_RECOV), - { 0, NULL } -}; - -static inline const char *lapd_state_name(enum lapd_state state) -{ - return get_value_string(lapd_state_names, state); -} - -static void lapd_start_t200(struct lapd_datalink *dl) -{ - if (osmo_timer_pending(&dl->t200)) - return; - LOGDL(dl, LOGL_INFO, "start T200 (timeout=%d.%06ds)\n", - dl->t200_sec, dl->t200_usec); - osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec); -} - -static void lapd_start_t203(struct lapd_datalink *dl) -{ - if (osmo_timer_pending(&dl->t203)) - return; - LOGDL(dl, LOGL_INFO, "start T203\n"); - osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec); -} - -static void lapd_stop_t200(struct lapd_datalink *dl) -{ - if (!osmo_timer_pending(&dl->t200)) - return; - LOGDL(dl, LOGL_INFO, "stop T200\n"); - osmo_timer_del(&dl->t200); -} - -static void lapd_stop_t203(struct lapd_datalink *dl) -{ - if (!osmo_timer_pending(&dl->t203)) - return; - LOGDL(dl, LOGL_INFO, "stop T203\n"); - osmo_timer_del(&dl->t203); -} - -static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state) -{ - LOGDL(dl, LOGL_INFO, "new state %s -> %s\n", - lapd_state_name(dl->state), lapd_state_name(state)); - - if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) { - /* stop T203 on leaving MF EST state, if running */ - lapd_stop_t203(dl); - /* remove content res. (network side) on leaving MF EST state */ - msgb_free(dl->cont_res); - dl->cont_res = NULL; - } - - /* start T203 on entering MF EST state, if enabled */ - if ((dl->t203_sec || dl->t203_usec) - && state == LAPD_STATE_MF_EST && dl->state != LAPD_STATE_MF_EST) - lapd_start_t203(dl); - - dl->state = state; -} - -void *tall_lapd_ctx = NULL; - -/*! Initialize LAPD datalink instance and allocate history - * \paramin dl caller-allocated datalink structure - * \paramin k maximum number of unacknowledged frames - * \paramin v_range range of sequence numbers - * \paramin maxf maximum frame size (after defragmentation) - * \paramin name human-readable name for this LAPD datalink */ -void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, - const char *name) -{ - int m; - - memset(dl, 0, sizeof(*dl)); - INIT_LLIST_HEAD(&dl->send_queue); - INIT_LLIST_HEAD(&dl->tx_queue); - dl->reestablish = 1; - dl->n200_est_rel = 3; - dl->n200 = 3; - dl->t200_sec = 1; - dl->t200_usec = 0; - osmo_timer_setup(&dl->t200, lapd_t200_cb, dl); - dl->t203_sec = 10; - dl->t203_usec = 0; - osmo_timer_setup(&dl->t203, lapd_t203_cb, dl); - dl->maxf = maxf; - if (k > v_range - 1) - k = v_range - 1; - dl->k = k; - dl->v_range = v_range; - - /* Calculate modulo for history array: - * - The history range must be at least k+1. - * - The history range must be 2^x, where x is as low as possible. - */ - k++; - for (m = 0x80; m; m >>= 1) { - if ((m & k)) { - if (k > m) - m <<= 1; - dl->range_hist = m; - break; - } - } - - if (!tall_lapd_ctx) { - tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context"); - OSMO_ASSERT(tall_lapd_ctx); - } - - talloc_free(dl->name); - if (name) - dl->name = talloc_strdup(tall_lapd_ctx, name); - else - dl->name = talloc_asprintf(tall_lapd_ctx, "dl=%p", dl); - - LOGDL(dl, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, " - "history range = %d\n", dl->v_range, dl->k, dl->range_hist); - - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - - dl->tx_hist = talloc_zero_array(tall_lapd_ctx, - struct lapd_history, dl->range_hist); -} - -/*! Initialize LAPD datalink instance and allocate history - * \paramin dl caller-allocated datalink structure - * \paramin k maximum number of unacknowledged frames - * \paramin v_range range of sequence numbers - * \paramin maxf maximum frame size (after defragmentation) */ -void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf) -{ - lapd_dl_init2(dl, k, v_range, maxf, NULL); -} - -void lapd_dl_set_name(struct lapd_datalink *dl, const char *name) -{ - if (!name) - return; - osmo_talloc_replace_string(tall_lapd_ctx, &dl->name, name); -} - -/* reset to IDLE state */ -void lapd_dl_reset(struct lapd_datalink *dl) -{ - LOGDL(dl, LOGL_INFO, "Resetting LAPD instance\n"); - /* enter idle state (and remove eventual cont_res) */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - /* flush buffer */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - /* Discard partly received L3 message */ - msgb_free(dl->rcv_buffer); - dl->rcv_buffer = NULL; - /* stop Timers */ - lapd_stop_t200(dl); - lapd_stop_t203(dl); - if (dl->state == LAPD_STATE_IDLE) - return; - /* enter idle state (and remove eventual cont_res) */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); -} - -/* reset and de-allocate history buffer */ -void lapd_dl_exit(struct lapd_datalink *dl) -{ - /* free all ressources except history buffer */ - lapd_dl_reset(dl); - - /* enter null state */ - lapd_dl_newstate(dl, LAPD_STATE_NULL); - - /* free history buffer list */ - talloc_free(dl->tx_hist); - dl->tx_hist = NULL; - talloc_free(dl->name); - dl->name = NULL; -} - -/*! Set the \ref lapdm_mode of a LAPDm entity */ -int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode) -{ - switch (mode) { - case LAPD_MODE_USER: - dl->cr.loc2rem.cmd = CR_USER2NET_CMD; - dl->cr.loc2rem.resp = CR_USER2NET_RESP; - dl->cr.rem2loc.cmd = CR_NET2USER_CMD; - dl->cr.rem2loc.resp = CR_NET2USER_RESP; - break; - case LAPD_MODE_NETWORK: - dl->cr.loc2rem.cmd = CR_NET2USER_CMD; - dl->cr.loc2rem.resp = CR_NET2USER_RESP; - dl->cr.rem2loc.cmd = CR_USER2NET_CMD; - dl->cr.rem2loc.resp = CR_USER2NET_RESP; - break; - default: - return -EINVAL; - } - dl->mode = mode; - - return 0; -} - -/* send DL message with optional msgb */ -static int send_dl_l3(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx, - struct msgb *msg) -{ - struct lapd_datalink *dl = lctx->dl; - struct osmo_dlsap_prim dp; - - osmo_prim_init(&dp.oph, 0, prim, op, msg); - return dl->send_dlsap(&dp, lctx); -} - -/* send simple DL message */ -static inline int send_dl_simple(uint8_t prim, uint8_t op, - struct lapd_msg_ctx *lctx) -{ - return send_dl_l3(prim, op, lctx, NULL); -} - -/* send MDL-ERROR INDICATION */ -static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct osmo_dlsap_prim dp; - - LOGDL(dl, LOGL_NOTICE, - "sending MDL-ERROR-IND cause %d from state %s\n", - cause, lapd_state_name(dl->state)); - osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL); - dp.u.error_ind.cause = cause; - return dl->send_dlsap(&dp, lctx); -} - -/* send UA response */ -static int lapd_send_ua(struct lapd_msg_ctx *lctx, uint8_t len, uint8_t *data) -{ - struct msgb *msg = lapd_msgb_alloc(len, "LAPD UA"); - struct lapd_msg_ctx nctx; - struct lapd_datalink *dl = lctx->dl; - - memcpy(&nctx, lctx, sizeof(nctx)); - msg->l3h = msgb_put(msg, len); - if (len) - memcpy(msg->l3h, data, len); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.resp; - nctx.format = LAPD_FORM_U; - nctx.s_u = LAPD_U_UA; - /* keep nctx.p_f */ - nctx.length = len; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* send DM response */ -static int lapd_send_dm(struct lapd_msg_ctx *lctx) -{ - struct msgb *msg = lapd_msgb_alloc(0, "LAPD DM"); - struct lapd_msg_ctx nctx; - struct lapd_datalink *dl = lctx->dl; - - memcpy(&nctx, lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.resp; - nctx.format = LAPD_FORM_U; - nctx.s_u = LAPD_U_DM; - /* keep nctx.p_f */ - nctx.length = 0; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* send RR response / command */ -static int lapd_send_rr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) -{ - struct msgb *msg = lapd_msgb_alloc(0, "LAPD RR"); - struct lapd_msg_ctx nctx; - struct lapd_datalink *dl = lctx->dl; - - memcpy(&nctx, lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; - nctx.format = LAPD_FORM_S; - nctx.s_u = LAPD_S_RR; - nctx.p_f = f_bit; - nctx.n_recv = dl->v_recv; - nctx.length = 0; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* send RNR response / command */ -static int lapd_send_rnr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) -{ - struct msgb *msg = lapd_msgb_alloc(0, "LAPD RNR"); - struct lapd_msg_ctx nctx; - struct lapd_datalink *dl = lctx->dl; - - memcpy(&nctx, lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; - nctx.format = LAPD_FORM_S; - nctx.s_u = LAPD_S_RNR; - nctx.p_f = f_bit; - nctx.n_recv = dl->v_recv; - nctx.length = 0; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* send REJ response */ -static int lapd_send_rej(struct lapd_msg_ctx *lctx, uint8_t f_bit) -{ - struct msgb *msg = lapd_msgb_alloc(0, "LAPD REJ"); - struct lapd_msg_ctx nctx; - struct lapd_datalink *dl = lctx->dl; - - memcpy(&nctx, lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.resp; - nctx.format = LAPD_FORM_S; - nctx.s_u = LAPD_S_REJ; - nctx.p_f = f_bit; - nctx.n_recv = dl->v_recv; - nctx.length = 0; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* resend SABM or DISC message */ -static int lapd_send_resend(struct lapd_datalink *dl) -{ - struct msgb *msg; - uint8_t h = do_mod(dl->v_send, dl->range_hist); - int length = dl->tx_histh.msg->len; - struct lapd_msg_ctx nctx; - - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_U; - if (dl->state == LAPD_STATE_SABM_SENT) - nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; - else - nctx.s_u = LAPD_U_DISC; - nctx.p_f = 1; - nctx.length = length; - nctx.more = 0; - - /* Resend SABM/DISC from tx_hist */ - msg = lapd_msgb_alloc(length, "LAPD resend"); - msg->l3h = msgb_put(msg, length); - if (length) - memcpy(msg->l3h, dl->tx_histh.msg->data, length); - - return dl->send_ph_data_req(&nctx, msg); -} - -/* reestablish link */ -static int lapd_reestablish(struct lapd_datalink *dl) -{ - struct osmo_dlsap_prim dp; - struct msgb *msg; - - LOGDL(dl, LOGL_DEBUG, "LAPD reestablish\n"); - - msg = lapd_msgb_alloc(0, "DUMMY"); - osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg); - - return lapd_est_req(&dp, &dl->lctx); -} - -/* Timer callback on T200 expiry */ -static void lapd_t200_cb(void *data) -{ - struct lapd_datalink *dl = data; - - LOGDL(dl, LOGL_INFO, "Timeout T200 state=%s\n", lapd_state_name(dl->state)); - - switch (dl->state) { - case LAPD_STATE_SABM_SENT: - /* 5.4.1.3 */ - if (dl->retrans_ctr >= dl->n200_est_rel + 1) { - /* flush tx and send buffers */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - /* go back to idle state */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - /* NOTE: we must not change any other states or buffers - * and queues, since we may reconnect after handover - * failure. the buffered messages is replaced there */ - /* send MDL ERROR INIDCATION to L3 */ - mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); - /* send RELEASE INDICATION to L3 */ - send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, - &dl->lctx); - break; - } - /* retransmit SABM command */ - lapd_send_resend(dl); - /* increment re-transmission counter */ - dl->retrans_ctr++; - /* restart T200 (PH-READY-TO-SEND) */ - lapd_start_t200(dl); - break; - case LAPD_STATE_DISC_SENT: - /* 5.4.4.3 */ - if (dl->retrans_ctr >= dl->n200_est_rel + 1) { - /* send MDL ERROR INIDCATION to L3 */ - mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); - /* flush tx and send buffers */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - /* go back to idle state */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - /* NOTE: we must not change any other states or buffers - * and queues, since we may reconnect after handover - * failure. the buffered messages is replaced there */ - /* send RELEASE INDICATION to L3 */ - send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); - break; - } - /* retransmit DISC command */ - lapd_send_resend(dl); - /* increment re-transmission counter */ - dl->retrans_ctr++; - /* restart T200 (PH-READY-TO-SEND) */ - lapd_start_t200(dl); - break; - case LAPD_STATE_MF_EST: - /* 5.5.7 */ - dl->retrans_ctr = 0; - lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); - /* fall through */ - case LAPD_STATE_TIMER_RECOV: - dl->retrans_ctr++; - if (dl->retrans_ctr <= dl->n200) { - uint8_t vs = sub_mod(dl->v_send, 1, dl->v_range); - uint8_t h = do_mod(vs, dl->range_hist); - /* retransmit I frame (V_s-1) with P=1, if any */ - if (dl->tx_histh.msg) { - struct msgb *msg; - int length = dl->tx_histh.msg->len; - struct lapd_msg_ctx nctx; - - LOGDL(dl, LOGL_INFO, "retransmit last frame V(S)=%d\n", vs); - /* Create I frame (segment) from tx_hist */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_I; - nctx.p_f = 1; - nctx.n_send = vs; - nctx.n_recv = dl->v_recv; - nctx.length = length; - nctx.more = dl->tx_histh.more; - msg = lapd_msgb_alloc(length, "LAPD I resend"); - msg->l3h = msgb_put(msg, length); - memcpy(msg->l3h, dl->tx_histh.msg->data, - length); - dl->send_ph_data_req(&nctx, msg); - } else { - /* OR send appropriate supervision frame with P=1 */ - if (!dl->own_busy && !dl->seq_err_cond) { - lapd_send_rr(&dl->lctx, 1, 1); - /* NOTE: In case of sequence error - * condition, the REJ frame has been - * transmitted when entering the - * condition, so it has not be done - * here - */ - } else if (dl->own_busy) { - lapd_send_rnr(&dl->lctx, 1, 1); - } else { - LOGDL(dl, LOGL_INFO, "unhandled, pls. fix\n"); - } - } - /* restart T200 (PH-READY-TO-SEND) */ - lapd_start_t200(dl); - } else { - /* send MDL ERROR INIDCATION to L3 */ - mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); - /* reestablish */ - if (!dl->reestablish) - break; - LOGDL(dl, LOGL_NOTICE, "N200+1 reached, performingreestablishment\n"); - lapd_reestablish(dl); - } - break; - default: - LOGDL(dl, LOGL_INFO, "T200 expired in unexpected dl->state %s)\n", - lapd_state_name(dl->state)); - } -} - -/* Timer callback on T203 expiry */ -static void lapd_t203_cb(void *data) -{ - struct lapd_datalink *dl = data; - - LOGDL(dl, LOGL_INFO, "Timeout T203 state=%s\n", lapd_state_name(dl->state)); - - if (dl->state != LAPD_STATE_MF_EST) { - LOGDL(dl, LOGL_ERROR, "T203 fired outside MF EST state, please fix!\n"); - return; - } - - /* set retransmission counter to 0 */ - dl->retrans_ctr = 0; - /* enter timer recovery state */ - lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); - /* transmit a supervisory command with P bit set to 1 as follows: */ - if (!dl->own_busy) { - LOGDL(dl, LOGL_INFO, "transmit an RR poll command\n"); - /* Send RR with P=1 */ - lapd_send_rr(&dl->lctx, 1, 1); - } else { - LOGDL(dl, LOGL_INFO, "transmit an RNR poll command\n"); - /* Send RNR with P=1 */ - lapd_send_rnr(&dl->lctx, 1, 1); - } - /* start T200 */ - lapd_start_t200(dl); -} - -/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */ -static void lapd_acknowledge(struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - uint8_t nr = lctx->n_recv; - int s = 0, rej = 0, t200_reset = 0; - int i, h; - - /* supervisory frame ? */ - if (lctx->format == LAPD_FORM_S) - s = 1; - /* REJ frame ? */ - if (s && lctx->s_u == LAPD_S_REJ) - rej = 1; - - /* Flush all transmit buffers of acknowledged frames */ - for (i = dl->v_ack; i != nr; i = inc_mod(i, dl->v_range)) { - h = do_mod(i, dl->range_hist); - if (dl->tx_histh.msg) { - msgb_free(dl->tx_histh.msg); - dl->tx_histh.msg = NULL; - LOGDL(dl, LOGL_INFO, "ack frame %d\n", i); - } - } - - if (dl->state != LAPD_STATE_TIMER_RECOV) { - /* When not in the timer recovery condition, the data - * link layer entity shall reset the timer T200 on - * receipt of a valid I frame with N(R) higher than V(A), - * or an REJ with an N(R) equal to V(A). */ - if ((!rej && nr != dl->v_ack) - || (rej && nr == dl->v_ack)) { - t200_reset = 1; - lapd_stop_t200(dl); - /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */ - } - /* 5.7.4: N(R) sequence error - * N(R) is called valid, if and only if - * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8. - */ - if (sub_mod(nr, dl->v_ack, dl->v_range) - > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) { - LOGDL(dl, LOGL_NOTICE, "N(R) sequence error\n"); - mdl_error(MDL_CAUSE_SEQ_ERR, lctx); - } - } - - /* V(A) shall be set to the value of N(R) */ - dl->v_ack = nr; - - /* If T200 has been stopped by the receipt of an I, RR or RNR frame, - * and if there are outstanding I frames, restart T200 */ - if (t200_reset && !rej) { - if (dl->tx_histsub_mod(dl->v_send, 1, dl->range_hist).msg) { - LOGDL(dl, LOGL_INFO, "start T200, due to unacked I frame(s)\n"); - lapd_start_t200(dl); - } - } - - /* This also does a restart, when I or S frame is received */ - - /* Stop T203, if running */ - lapd_stop_t203(dl); - /* Start T203, if T200 is not running in MF EST state, if enabled */ - if (!osmo_timer_pending(&dl->t200) - && (dl->t203_sec || dl->t203_usec) - && (dl->state == LAPD_STATE_MF_EST)) { - lapd_start_t203(dl); - } -} - -/* L1 -> L2 */ - -/* Receive a LAPD U SABM(E) message from L1 */ -static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int length = lctx->length; - int rc = 0; - uint8_t prim, op; - - prim = PRIM_DL_EST; - op = PRIM_OP_INDICATION; - - LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state)); - /* 5.7.1 */ - dl->seq_err_cond = 0; - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.resp) { - LOGDL(dl, LOGL_ERROR, "SABM response error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - - /* G.4.5 If SABM is received with L>N201 or with M bit - * set, AN MDL-ERROR-INDICATION is sent to MM. - */ - if (lctx->more || length > lctx->n201) { - LOGDL(dl, LOGL_ERROR, "SABM too large error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); - return -EIO; - } - - switch (dl->state) { - case LAPD_STATE_IDLE: - break; - case LAPD_STATE_TIMER_RECOV: - LOGDL(dl, LOGL_INFO, "SABM command, timer recovery state\n"); - /* If link is lost on the remote side, we start over - * and send DL-ESTABLISH indication again. */ - /* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment" */ - if (length) { - /* check for contention resoultion */ - LOGDL(dl, LOGL_ERROR, "SABM L>0 not expected in timer " - "recovery state\n"); - mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx); - lapd_send_dm(lctx); - msgb_free(msg); - return 0; - } - /* re-establishment, continue below */ - lapd_stop_t200(dl); - break; - case LAPD_STATE_MF_EST: - LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n"); - /* If link is lost on the remote side, we start over - * and send DL-ESTABLISH indication again. */ - /* Additionally, continue in case of content resoltion - * (GSM network). This happens, if the mobile has not - * yet received UA or another mobile (collision) tries - * to establish connection. The mobile must receive - * UA again. */ - /* 5.4.2.1 */ - if (!length) { - /* If no content resolution, this is a - * re-establishment. */ - LOGDL(dl, LOGL_INFO, "Remote reestablish\n"); - break; - } - if (!dl->cont_res) { - LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n", - lapd_state_name(dl->state)); - mdl_error(MDL_CAUSE_SABM_MF, lctx); - msgb_free(msg); - return 0; - } - /* Ignore SABM if content differs from first SABM. */ - if (dl->mode == LAPD_MODE_NETWORK && length) { -#ifdef TEST_CONTENT_RESOLUTION_NETWORK - dl->cont_res->data0 ^= 0x01; -#endif - if (memcmp(dl->cont_res->data, msg->data, - length)) { - LOGDL(dl, LOGL_INFO, "Another SABM with different content - " - "ignoring!\n"); - msgb_free(msg); - return 0; - } - } - /* send UA again */ - lapd_send_ua(lctx, length, msg->l3h); - msgb_free(msg); - return 0; - case LAPD_STATE_DISC_SENT: - /* 5.4.6.2 send DM with F=P */ - lapd_send_dm(lctx); - /* stop Timer T200 */ - lapd_stop_t200(dl); - msgb_free(msg); - return send_dl_simple(prim, op, lctx); - default: - /* collision: Send UA, but still wait for rx UA, then - * change to MF_EST state. - */ - /* check for contention resoultion */ - if (dl->tx_hist0.msg && dl->tx_hist0.msg->len) { - LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention " - "resolution (state=%s)\n", lapd_state_name(dl->state)); - mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx); - } - lapd_send_ua(lctx, length, msg->l3h); - msgb_free(msg); - return 0; - } - /* save message context for further use */ - memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); -#ifndef TEST_CONTENT_RESOLUTION_NETWORK - /* send UA response */ - lapd_send_ua(lctx, length, msg->l3h); -#endif - /* set Vs, Vr and Va to 0 */ - dl->v_send = dl->v_recv = dl->v_ack = 0; - /* clear tx_hist */ - lapd_dl_flush_hist(dl); - /* enter multiple-frame-established state */ - lapd_dl_newstate(dl, LAPD_STATE_MF_EST); - /* store content resolution data on network side - * Note: cont_res will be removed when changing state again, - * so it must be allocated AFTER lapd_dl_newstate(). */ - if (dl->mode == LAPD_MODE_NETWORK && length) { - dl->cont_res = lapd_msgb_alloc(length, "CONT RES"); - memcpy(msgb_put(dl->cont_res, length), msg->l3h, - length); - LOGDL(dl, LOGL_INFO, "Store content res.\n"); - } - /* send notification to L3 */ - if (length == 0) { - /* 5.4.1.2 Normal establishment procedures */ - rc = send_dl_simple(prim, op, lctx); - msgb_free(msg); - } else { - /* 5.4.1.4 Contention resolution establishment */ - msgb_trim(msg, length); - rc = send_dl_l3(prim, op, lctx, msg); - } - return rc; -} - -/* Receive a LAPD U DM message from L1 */ -static int lapd_rx_u_dm(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int rc = 0; - - LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state)); - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.cmd) { - LOGDL(dl, LOGL_ERROR, "DM command error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - if (!lctx->p_f) { - /* 5.4.1.2 DM responses with the F bit set to "0" - * shall be ignored. - */ - msgb_free(msg); - return 0; - } - switch (dl->state) { - case LAPD_STATE_SABM_SENT: - break; - case LAPD_STATE_MF_EST: - if (lctx->p_f) { - LOGDL(dl, LOGL_INFO, "unsolicited DM response\n"); - mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx); - } else { - LOGDL(dl, LOGL_INFO, "unsolicited DM response, " - "multiple frame established state\n"); - mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); - /* reestablish */ - if (!dl->reestablish) { - msgb_free(msg); - return 0; - } - LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); - lapd_reestablish(dl); - } - msgb_free(msg); - return 0; - case LAPD_STATE_TIMER_RECOV: - /* FP = 0 (DM is normal in case PF = 1) */ - if (!lctx->p_f) { - LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame " - "established state\n"); - mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); - msgb_free(msg); - /* reestablish */ - if (!dl->reestablish) - return 0; - LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); - return lapd_reestablish(dl); - } - break; - case LAPD_STATE_DISC_SENT: - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* go to idle state */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); - msgb_free(msg); - return 0; - case LAPD_STATE_IDLE: - /* 5.4.5 all other frame types shall be discarded */ - default: - LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n"); - msgb_free(msg); - return 0; - } - /* stop timer T200 */ - lapd_stop_t200(dl); - /* go to idle state */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); - msgb_free(msg); - return rc; -} - -/* Receive a LAPD U UI message from L1 */ -static int lapd_rx_u_ui(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int length = lctx->length; - - LOGDL(dl, LOGL_INFO, "UI received\n"); - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.resp) { - LOGDL(dl, LOGL_ERROR, "UI indicates response error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - - /* G.4.5 If UI is received with L>N201 or with M bit - * set, AN MDL-ERROR-INDICATION is sent to MM. - */ - if (length > lctx->n201 || lctx->more) { - LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n", - length, lctx->n201, lctx->more); - msgb_free(msg); - mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); - return -EIO; - } - - /* do some length checks */ - if (length == 0) { - /* 5.3.3 UI frames received with the length indicator - * set to "0" shall be ignored - */ - LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n"); - msgb_free(msg); - return 0; - } - msgb_trim(msg, length); - return send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg); -} - -/* Receive a LAPD U DISC message from L1 */ -static int lapd_rx_u_disc(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int length = lctx->length; - int rc = 0; - uint8_t prim, op; - - prim = PRIM_DL_REL; - op = PRIM_OP_INDICATION; - - LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state)); - /* flush tx and send buffers */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - /* 5.7.1 */ - dl->seq_err_cond = 0; - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.resp) { - LOGDL(dl, LOGL_ERROR, "DISC response error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - if (length > 0 || lctx->more) { - /* G.4.4 If a DISC or DM frame is received with L>0 or - * with the M bit set to "1", an MDL-ERROR-INDICATION - * primitive with cause "U frame with incorrect - * parameters" is sent to the mobile management entity. - */ - LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); - return -EIO; - } - switch (dl->state) { - case LAPD_STATE_IDLE: - LOGDL(dl, LOGL_INFO, "DISC in idle state\n"); - /* send DM with F=P */ - msgb_free(msg); - return lapd_send_dm(lctx); - case LAPD_STATE_SABM_SENT: - LOGDL(dl, LOGL_INFO, "DISC in SABM state\n"); - /* 5.4.6.2 send DM with F=P */ - lapd_send_dm(lctx); - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* go to idle state */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - msgb_free(msg); - return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, - lctx); - case LAPD_STATE_MF_EST: - case LAPD_STATE_TIMER_RECOV: - LOGDL(dl, LOGL_INFO, "DISC in est state\n"); - break; - case LAPD_STATE_DISC_SENT: - LOGDL(dl, LOGL_INFO, "DISC in disc state\n"); - prim = PRIM_DL_REL; - op = PRIM_OP_CONFIRM; - break; - default: - lapd_send_ua(lctx, length, msg->l3h); - msgb_free(msg); - return 0; - } - /* send UA response */ - lapd_send_ua(lctx, length, msg->l3h); - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* enter idle state, keep tx-buffer with UA response */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - /* send notification to L3 */ - rc = send_dl_simple(prim, op, lctx); - msgb_free(msg); - return rc; -} - -/* Receive a LAPD U UA message from L1 */ -static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int length = lctx->length; - int rc = 0; - - LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state)); - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.cmd) { - LOGDL(dl, LOGL_ERROR, "UA indicates command error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - - /* G.4.5 If UA is received with L>N201 or with M bit - * set, AN MDL-ERROR-INDICATION is sent to MM. - */ - if (lctx->more || length > lctx->n201) { - LOGDL(dl, LOGL_ERROR, "UA too large error\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); - return -EIO; - } - - if (!lctx->p_f) { - /* 5.4.1.2 A UA response with the F bit set to "0" - * shall be ignored. - */ - LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n"); - msgb_free(msg); - return 0; - } - switch (dl->state) { - case LAPD_STATE_SABM_SENT: - break; - case LAPD_STATE_MF_EST: - case LAPD_STATE_TIMER_RECOV: - LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n"); - mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx); - msgb_free(msg); - return 0; - case LAPD_STATE_DISC_SENT: - LOGDL(dl, LOGL_INFO, "UA in disconnect state\n"); - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* go to idle state */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); - msgb_free(msg); - return 0; - case LAPD_STATE_IDLE: - /* 5.4.5 all other frame types shall be discarded */ - default: - LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n"); - msgb_free(msg); - return 0; - } - LOGDL(dl, LOGL_INFO, "UA in SABM state\n"); - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* compare UA with SABME if contention resolution is applied */ - if (dl->tx_hist0.msg->len) { - if (length != (dl->tx_hist0.msg->len) - || !!memcmp(dl->tx_hist0.msg->data, msg->l3h, - length)) { - LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n"); - /* go to idle state */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); - msgb_free(msg); - return 0; - } - } - /* set Vs, Vr and Va to 0 */ - dl->v_send = dl->v_recv = dl->v_ack = 0; - /* clear tx_hist */ - lapd_dl_flush_hist(dl); - /* enter multiple-frame-established state */ - lapd_dl_newstate(dl, LAPD_STATE_MF_EST); - /* send outstanding frames, if any (resume / reconnect) */ - lapd_send_i(lctx, __LINE__); - /* send notification to L3 */ - rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx); - msgb_free(msg); - return rc; -} - -/* Receive a LAPD U FRMR message from L1 */ -static int lapd_rx_u_frmr(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - - LOGDL(dl, LOGL_NOTICE, "Frame reject received\n"); - /* send MDL ERROR INIDCATION to L3 */ - mdl_error(MDL_CAUSE_FRMR, lctx); - msgb_free(msg); - /* reestablish */ - if (!dl->reestablish) - return 0; - LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); - return lapd_reestablish(dl); -} - -/* Receive a LAPD U (Unnumbered) message from L1 */ -static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - switch (lctx->s_u) { - case LAPD_U_SABM: - case LAPD_U_SABME: - return lapd_rx_u_sabm(msg, lctx); - case LAPD_U_DM: - return lapd_rx_u_dm(msg, lctx); - case LAPD_U_UI: - return lapd_rx_u_ui(msg, lctx); - case LAPD_U_DISC: - return lapd_rx_u_disc(msg, lctx); - case LAPD_U_UA: - return lapd_rx_u_ua(msg, lctx); - case LAPD_U_FRMR: - return lapd_rx_u_frmr(msg, lctx); - default: - /* G.3.1 */ - LOGDL(lctx->dl, LOGL_NOTICE, "Unnumbered frame not allowed\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } -} - -/* Receive a LAPD S (Supervisory) message from L1 */ -static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int length = lctx->length; - - if (length > 0 || lctx->more) { - /* G.4.3 If a supervisory frame is received with L>0 or - * with the M bit set to "1", an MDL-ERROR-INDICATION - * primitive with cause "S frame with incorrect - * parameters" is sent to the mobile management entity. */ - LOGDL(dl, LOGL_ERROR, "S frame with incorrect parameters\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx); - return -EIO; - } - - if (lctx->cr == dl->cr.rem2loc.resp - && lctx->p_f - && dl->state != LAPD_STATE_TIMER_RECOV) { - /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */ - LOGDL(dl, LOGL_NOTICE, "S frame response with F=1 error\n"); - mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); - } - - switch (dl->state) { - case LAPD_STATE_IDLE: - /* if P=1, respond DM with F=1 (5.2.2) */ - /* 5.4.5 all other frame types shall be discarded */ - if (lctx->p_f) - lapd_send_dm(lctx); /* F=P */ - /* fall though */ - case LAPD_STATE_SABM_SENT: - case LAPD_STATE_DISC_SENT: - LOGDL(dl, LOGL_NOTICE, "S frame ignored in this state\n"); - msgb_free(msg); - return 0; - } - switch (lctx->s_u) { - case LAPD_S_RR: - LOGDL(dl, LOGL_INFO, "RR received in state %s\n", lapd_state_name(dl->state)); - /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ - lapd_acknowledge(lctx); - - /* 5.5.3.2 */ - if (lctx->cr == dl->cr.rem2loc.cmd - && lctx->p_f) { - if (!dl->own_busy && !dl->seq_err_cond) { - LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and " - "we are not busy, so we reply with RR frame response\n"); - lapd_send_rr(lctx, 1, 0); - /* NOTE: In case of sequence error condition, - * the REJ frame has been transmitted when - * entering the condition, so it has not be - * done here - */ - } else if (dl->own_busy) { - LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and " - "we are busy, so we reply with RR frame response\n"); - lapd_send_rnr(lctx, 1, 0); - } - } else if (lctx->cr == dl->cr.rem2loc.resp - && lctx->p_f - && dl->state == LAPD_STATE_TIMER_RECOV) { - LOGDL(dl, LOGL_INFO, "RR response with F==1, and we are in timer recovery " - "state, so we leave that state\n"); - /* V(S) to the N(R) in the RR frame */ - dl->v_send = lctx->n_recv; - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* 5.5.7 Clear timer recovery condition */ - lapd_dl_newstate(dl, LAPD_STATE_MF_EST); - } - /* Send message, if possible due to acknowledged data */ - lapd_send_i(lctx, __LINE__); - - break; - case LAPD_S_RNR: - LOGDL(dl, LOGL_INFO, "RNR received in state %s\n", lapd_state_name(dl->state)); - /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ - lapd_acknowledge(lctx); - - /* 5.5.5 */ - /* Set peer receiver busy condition */ - dl->peer_busy = 1; - - if (lctx->p_f) { - if (lctx->cr == dl->cr.rem2loc.cmd) { - if (!dl->own_busy) { - LOGDL(dl, LOGL_INFO, "RNR poll command and we are not busy, " - "so we reply with RR final response\n"); - /* Send RR with F=1 */ - lapd_send_rr(lctx, 1, 0); - } else { - LOGDL(dl, LOGL_INFO, "RNR poll command and we are busy, so " - "we reply with RNR final response\n"); - /* Send RNR with F=1 */ - lapd_send_rnr(lctx, 1, 0); - } - } else if (dl->state == LAPD_STATE_TIMER_RECOV) { - LOGDL(dl, LOGL_INFO, "RNR poll response and we in timer recovery " - "state, so we leave that state\n"); - /* 5.5.7 Clear timer recovery condition */ - lapd_dl_newstate(dl, LAPD_STATE_MF_EST); - /* V(S) to the N(R) in the RNR frame */ - dl->v_send = lctx->n_recv; - } - } else - LOGDL(dl, LOGL_INFO, "RNR not polling/final state received\n"); - - /* Send message, if possible due to acknowledged data */ - lapd_send_i(lctx, __LINE__); - - break; - case LAPD_S_REJ: - LOGDL(dl, LOGL_INFO, "REJ received in state %s\n", lapd_state_name(dl->state)); - /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ - lapd_acknowledge(lctx); - - /* 5.5.4.1 */ - if (dl->state != LAPD_STATE_TIMER_RECOV) { - /* Clear an existing peer receiver busy condition */ - dl->peer_busy = 0; - /* V(S) and V(A) to the N(R) in the REJ frame */ - dl->v_send = dl->v_ack = lctx->n_recv; - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* 5.5.3.2 */ - if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { - if (!dl->own_busy && !dl->seq_err_cond) { - LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery " - "state and not in own busy condition received, so we " - "respond with RR final response\n"); - lapd_send_rr(lctx, 1, 0); - /* NOTE: In case of sequence error - * condition, the REJ frame has been - * transmitted when entering the - * condition, so it has not be done - * here - */ - } else if (dl->own_busy) { - LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery " - "state and in own busy condition received, so we " - "respond with RNR final response\n"); - lapd_send_rnr(lctx, 1, 0); - } - } else - LOGDL(dl, LOGL_INFO, "REJ response or not polling command not " - "in timer recovery state received\n"); - /* send MDL ERROR INIDCATION to L3 */ - if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { - LOGDL(dl, LOGL_ERROR, "unsolicited supervisory response!\n"); - mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); - } - - } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { - LOGDL(dl, LOGL_INFO, "REJ poll response in timer recovery state received\n"); - /* Clear an existing peer receiver busy condition */ - dl->peer_busy = 0; - /* V(S) and V(A) to the N(R) in the REJ frame */ - dl->v_send = dl->v_ack = lctx->n_recv; - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* 5.5.7 Clear timer recovery condition */ - lapd_dl_newstate(dl, LAPD_STATE_MF_EST); - } else { - /* Clear an existing peer receiver busy condition */ - dl->peer_busy = 0; - /* V(S) and V(A) to the N(R) in the REJ frame */ - dl->v_send = dl->v_ack = lctx->n_recv; - /* 5.5.3.2 */ - if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { - if (!dl->own_busy && !dl->seq_err_cond) { - LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery " - "state and not in own busy condition received, so we " - "respond with RR final response\n"); - lapd_send_rr(lctx, 1, 0); - /* NOTE: In case of sequence error - * condition, the REJ frame has been - * transmitted when entering the - * condition, so it has not be done - * here - */ - } else if (dl->own_busy) { - LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery " - "state and in own busy condition received, so we " - "respond with RNR final response\n"); - lapd_send_rnr(lctx, 1, 0); - } - } else - LOGDL(dl, LOGL_INFO, "REJ response or not polling command in " - "timer recovery state received\n"); - } - - /* FIXME: 5.5.4.2 2) */ - - /* Send message, if possible due to acknowledged data */ - lapd_send_i(lctx, __LINE__); - - break; - default: - /* G.3.1 */ - LOGDL(dl, LOGL_ERROR, "Supervisory frame not allowed\n"); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - msgb_free(msg); - return 0; -} - -/* Receive a LAPD I (Information) message from L1 */ -static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - //uint8_t nr = lctx->n_recv; - uint8_t ns = lctx->n_send; - int length = lctx->length; - int rc; - - LOGDL(dl, LOGL_INFO, "I received in state %s on SAPI(%u)\n", - lapd_state_name(dl->state), lctx->sapi); - - /* G.2.2 Wrong value of the C/R bit */ - if (lctx->cr == dl->cr.rem2loc.resp) { - LOGDL(dl, LOGL_ERROR, "I frame response not allowed (state %s)\n", - lapd_state_name(dl->state)); - msgb_free(msg); - mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); - return -EINVAL; - } - - if (length == 0 || length > lctx->n201) { - /* G.4.2 If the length indicator of an I frame is set - * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION - * primitive with cause "I frame with incorrect length" - * is sent to the mobile management entity. */ - LOGDL(dl, LOGL_ERROR, "I frame length not allowed (state %s)\n", - lapd_state_name(dl->state)); - msgb_free(msg); - mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx); - return -EIO; - } - - /* G.4.2 If the numerical value of L is L<N201 and the M - * bit is set to "1", then an MDL-ERROR-INDICATION primitive with - * cause "I frame with incorrect use of M bit" is sent to the - * mobile management entity. */ - if (lctx->more && length < lctx->n201) { - LOGDL(dl, LOGL_ERROR, "I frame with M bit too short (state %s)\n", - lapd_state_name(dl->state)); - msgb_free(msg); - mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx); - return -EIO; - } - - switch (dl->state) { - case LAPD_STATE_IDLE: - /* if P=1, respond DM with F=1 (5.2.2) */ - /* 5.4.5 all other frame types shall be discarded */ - if (lctx->p_f) - lapd_send_dm(lctx); /* F=P */ - /* fall though */ - case LAPD_STATE_SABM_SENT: - case LAPD_STATE_DISC_SENT: - LOGDL(dl, LOGL_NOTICE, "I frame ignored in state %s\n", lapd_state_name(dl->state)); - msgb_free(msg); - return 0; - } - - /* 5.7.1: N(s) sequence error */ - if (ns != dl->v_recv) { - LOGDL(dl, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, V(R)=%u (state %s)\n", - ns, dl->v_recv, lapd_state_name(dl->state)); - /* discard data */ - msgb_free(msg); - if (dl->seq_err_cond != 1) { - /* FIXME: help me understand what exactly todo here - */ - dl->seq_err_cond = 1; - lapd_send_rej(lctx, lctx->p_f); - } else { - /* If there are two subsequent sequence errors received, - * ignore it. (Ignore every second subsequent error.) - * This happens if our reply with the REJ is too slow, - * so the remote gets a T200 timeout and sends another - * frame with a sequence error. - * Test showed that replying with two subsequent REJ - * messages could the remote L2 process to abort. - * Replying too slow shouldn't happen, but may happen - * over serial link between BB and LAPD. - */ - dl->seq_err_cond = 2; - } - /* Even if N(s) sequence error, acknowledge to N(R)-1 */ - /* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */ - lapd_acknowledge(lctx); /* V(A) is also set here */ - - /* Send message, if possible due to acknowledged data */ - lapd_send_i(lctx, __LINE__); - - return 0; - } - dl->seq_err_cond = 0; - - /* Increment receiver state */ - dl->v_recv = inc_mod(dl->v_recv, dl->v_range); - LOGDL(dl, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv); - - /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */ - lapd_acknowledge(lctx); /* V(A) is also set here */ - - /* Only if we are not in own receiver busy condition */ - if (!dl->own_busy) { - /* if the frame carries a complete segment */ - if (!lctx->more && !dl->rcv_buffer) { - LOGDL(dl, LOGL_INFO, "message in single I frame\n"); - /* send a DATA INDICATION to L3 */ - msgb_trim(msg, length); - rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx, - msg); - } else { - /* create rcv_buffer */ - if (!dl->rcv_buffer) { - LOGDL(dl, LOGL_INFO, "message in multiple I frames (first message)\n"); - dl->rcv_buffer = lapd_msgb_alloc(dl->maxf, - "LAPD RX"); - dl->rcv_buffer->l3h = dl->rcv_buffer->data; - } - /* concat. rcv_buffer */ - if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) { - LOGDL(dl, LOGL_NOTICE, "Received frame overflow!\n"); - } else { - memcpy(msgb_put(dl->rcv_buffer, length), - msg->l3h, length); - } - /* if the last segment was received */ - if (!lctx->more) { - LOGDL(dl, LOGL_INFO, "message in multiple I frames (last message)\n"); - rc = send_dl_l3(PRIM_DL_DATA, - PRIM_OP_INDICATION, lctx, - dl->rcv_buffer); - dl->rcv_buffer = NULL; - } else - LOGDL(dl, LOGL_INFO, "message in multiple I frames (next message)\n"); - msgb_free(msg); - - } - /* the L3 or higher (called in-line above via send_dl_l3) might have destroyed the - * data link meanwhile. See OS#1761 */ - if (dl->state == LAPD_STATE_NULL) - return 0; - } else - LOGDL(dl, LOGL_INFO, "I frame ignored during own receiver busy condition\n"); - - /* Check for P bit */ - if (lctx->p_f) { - /* 5.5.2.1 */ - /* check if we are not in own receiver busy */ - if (!dl->own_busy) { - LOGDL(dl, LOGL_INFO, "we are not busy, send RR\n"); - /* Send RR with F=1 */ - rc = lapd_send_rr(lctx, 1, 0); - } else { - LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n"); - /* Send RNR with F=1 */ - rc = lapd_send_rnr(lctx, 1, 0); - } - } else { - /* 5.5.2.2 */ - /* check if we are not in own receiver busy */ - if (!dl->own_busy) { - /* NOTE: V(R) is already set above */ - rc = lapd_send_i(lctx, __LINE__); - - /* if update_pending_iframe returns 0 it updated - * the lapd header of an iframe in the tx queue */ - if (rc && dl->update_pending_frames) - rc = dl->update_pending_frames(lctx); - - if (rc) { - LOGDL(dl, LOGL_INFO, "we are not busy and have no pending data, " - "send RR\n"); - /* Send RR with F=0 */ - return lapd_send_rr(lctx, 0, 0); - } - /* all I or one RR is sent, we are done */ - return 0; - } else { - LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n"); - /* Send RNR with F=0 */ - rc = lapd_send_rnr(lctx, 0, 0); - } - } - - /* Send message, if possible due to acknowledged data */ - lapd_send_i(lctx, __LINE__); - - return rc; -} - -/* Receive a LAPD message from L1 */ -int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx) -{ - int rc; - - switch (lctx->format) { - case LAPD_FORM_U: - rc = lapd_rx_u(msg, lctx); - break; - case LAPD_FORM_S: - rc = lapd_rx_s(msg, lctx); - break; - case LAPD_FORM_I: - rc = lapd_rx_i(msg, lctx); - break; - default: - LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format\n"); - msgb_free(msg); - rc = -EINVAL; - } - return rc; -} - -/* L3 -> L2 */ - -/* send unit data */ -static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - struct lapd_msg_ctx nctx; - - memcpy(&nctx, lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_U; - nctx.s_u = LAPD_U_UI; - /* keep nctx.p_f */ - nctx.length = msg->len; - nctx.more = 0; - - return dl->send_ph_data_req(&nctx, msg); -} - -/* request link establishment */ -static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - struct lapd_msg_ctx nctx; - - if (msg->len) - LOGDL(dl, LOGL_INFO, "perform establishment with content (SABM)\n"); - else - LOGDL(dl, LOGL_INFO, "perform normal establishm. (SABM)\n"); - - /* Flush send-queue */ - /* Clear send-buffer */ - lapd_dl_flush_send(dl); - /* be sure that history is empty */ - lapd_dl_flush_hist(dl); - - /* save message context for further use */ - memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); - - /* Discard partly received L3 message */ - msgb_free(dl->rcv_buffer); - dl->rcv_buffer = NULL; - - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_U; - nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; - nctx.p_f = 1; - nctx.length = msg->len; - nctx.more = 0; - - /* Transmit-buffer carries exactly one segment */ - dl->tx_hist0.msg = lapd_msgb_alloc(msg->len, "HIST"); - msgb_put(dl->tx_hist0.msg, msg->len); - if (msg->len) - memcpy(dl->tx_hist0.msg->data, msg->l3h, msg->len); - dl->tx_hist0.more = 0; - /* set Vs to 0, because it is used as index when resending SABM */ - dl->v_send = 0; - - /* Set states */ - dl->own_busy = dl->peer_busy = 0; - dl->retrans_ctr = 0; - lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); - - /* Tramsmit and start T200 */ - dl->send_ph_data_req(&nctx, msg); - lapd_start_t200(dl); - - return 0; -} - -/* send data */ -static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - - if (msgb_l3len(msg) == 0) { - LOGDL(dl, LOGL_ERROR, "writing an empty message is not possible\n"); - msgb_free(msg); - return -1; - } - - LOGDL(dl, LOGL_INFO, "writing message to send-queue: l3len: %d\n", msgb_l3len(msg)); - - /* Write data into the send queue */ - msgb_enqueue(&dl->send_queue, msg); - - /* Send message, if possible */ - lapd_send_i(&dl->lctx, __LINE__); - - return 0; -} - -/* Send next I frame from queued/buffered data */ -static int lapd_send_i(struct lapd_msg_ctx *lctx, int line) -{ - struct lapd_datalink *dl = lctx->dl; - uint8_t k = dl->k; - uint8_t h; - struct msgb *msg; - int length, left; - int rc = - 1; /* we sent nothing */ - struct lapd_msg_ctx nctx; - - - LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line); - - next_frame: - - if (dl->peer_busy) { - LOGDL(dl, LOGL_INFO, "peer busy, not sending\n"); - return rc; - } - - if (dl->state == LAPD_STATE_TIMER_RECOV) { - LOGDL(dl, LOGL_INFO, "timer recovery, not sending\n"); - return rc; - } - - /* If the send state variable V(S) is equal to V(A) plus k - * (where k is the maximum number of outstanding I frames - see - * subclause 5.8.4), the data link layer entity shall not transmit any - * new I frames, but shall retransmit an I frame as a result - * of the error recovery procedures as described in subclauses 5.5.4 and - * 5.5.7. */ - if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) { - LOGDL(dl, LOGL_INFO, "k frames outstanding, not sending more " - "(k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send, dl->v_ack); - return rc; - } - - h = do_mod(dl->v_send, dl->range_hist); - - /* if we have no tx_hist yet, we create it */ - if (!dl->tx_histh.msg) { - /* Get next message into send-buffer, if any */ - if (!dl->send_buffer) { - next_message: - dl->send_out = 0; - dl->send_buffer = msgb_dequeue(&dl->send_queue); - /* No more data to be sent */ - if (!dl->send_buffer) - return rc; - LOGDL(dl, LOGL_INFO, "get message from send-queue\n"); - } - - /* How much is left in the send-buffer? */ - left = msgb_l3len(dl->send_buffer) - dl->send_out; - /* Segment, if data exceeds N201 */ - length = left; - if (length > lctx->n201) - length = lctx->n201; - LOGDL(dl, LOGL_INFO, "msg-len %d sent %d left %d N201 %d length %d " - "first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, - lctx->n201, length, dl->send_buffer->l3h0); - /* If message in send-buffer is completely sent */ - if (left == 0) { - msgb_free(dl->send_buffer); - dl->send_buffer = NULL; - goto next_message; - } - - LOGDL(dl, LOGL_INFO, "send I frame %sV(S)=%d\n", - (left > length) ? "segment " : "", dl->v_send); - - /* Create I frame (segment) and transmit-buffer content */ - msg = lapd_msgb_alloc(length, "LAPD I"); - msg->l3h = msgb_put(msg, length); - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_I; - nctx.p_f = 0; - nctx.n_send = dl->v_send; - nctx.n_recv = dl->v_recv; - nctx.length = length; - if (left > length) - nctx.more = 1; - else - nctx.more = 0; - if (length) - memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out, - length); - /* store in tx_hist */ - dl->tx_histh.msg = lapd_msgb_alloc(msg->len, "HIST"); - msgb_put(dl->tx_histh.msg, msg->len); - if (length) - memcpy(dl->tx_histh.msg->data, msg->l3h, msg->len); - dl->tx_histh.more = nctx.more; - /* Add length to track how much is already in the tx buffer */ - dl->send_out += length; - } else { - LOGDL(dl, LOGL_INFO, "resend I frame from tx buffer V(S)=%d\n", dl->v_send); - - /* Create I frame (segment) from tx_hist */ - length = dl->tx_histh.msg->len; - msg = lapd_msgb_alloc(length, "LAPD I resend"); - msg->l3h = msgb_put(msg, length); - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_I; - nctx.p_f = 0; - nctx.n_send = dl->v_send; - nctx.n_recv = dl->v_recv; - nctx.length = length; - nctx.more = dl->tx_histh.more; - if (length) - memcpy(msg->l3h, dl->tx_histh.msg->data, length); - } - - /* The value of the send state variable V(S) shall be incremented by 1 - * at the end of the transmission of the I frame */ - dl->v_send = inc_mod(dl->v_send, dl->v_range); - - /* If timer T200 is not running at the time right before transmitting a - * frame, when the PH-READY-TO-SEND primitive is received from the - * physical layer., it shall be set. */ - if (!osmo_timer_pending(&dl->t200)) { - /* stop Timer T203, if running */ - lapd_stop_t203(dl); - /* start Timer T200 */ - lapd_start_t200(dl); - } - - dl->send_ph_data_req(&nctx, msg); - - rc = 0; /* we sent something */ - goto next_frame; -} - -/* request link suspension */ -static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - - LOGDL(dl, LOGL_INFO, "perform suspension\n"); - - /* put back the send-buffer to the send-queue (first position) */ - if (dl->send_buffer) { - LOGDL(dl, LOGL_INFO, "put frame in sendbuffer back to queue\n"); - llist_add(&dl->send_buffer->list, &dl->send_queue); - dl->send_buffer = NULL; - } else - LOGDL(dl, LOGL_INFO, "no frame in sendbuffer\n"); - - /* Clear transmit buffer, but keep send buffer */ - lapd_dl_flush_tx(dl); - /* Stop timers (there is no state change, so we must stop all timers */ - lapd_stop_t200(dl); - lapd_stop_t203(dl); - - msgb_free(msg); - - return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx); -} - -/* request, resume or reconnect of link */ -static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - struct lapd_msg_ctx nctx; - - LOGDL(dl, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", msg->len); - - /* be sure that history is empty */ - lapd_dl_flush_hist(dl); - - /* save message context for further use */ - memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); - - /* Replace message in the send-buffer (reconnect) */ - msgb_free(dl->send_buffer); - dl->send_buffer = NULL; - - dl->send_out = 0; - if (msg->len) { - /* Write data into the send buffer, to be sent first */ - dl->send_buffer = msg; - } else { - msgb_free(msg); - msg = NULL; - dl->send_buffer = NULL; - } - - /* Discard partly received L3 message */ - msgb_free(dl->rcv_buffer); - dl->rcv_buffer = NULL; - - /* Create new msgb (old one is now free) */ - msg = lapd_msgb_alloc(0, "LAPD SABM"); - msg->l3h = msg->data; - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_U; - nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; - nctx.p_f = 1; - nctx.length = 0; - nctx.more = 0; - - dl->tx_hist0.msg = lapd_msgb_alloc(msg->len, "HIST"); - msgb_put(dl->tx_hist0.msg, msg->len); - if (msg->len) - memcpy(dl->tx_hist0.msg->data, msg->l3h, msg->len); - dl->tx_hist0.more = 0; - /* set Vs to 0, because it is used as index when resending SABM */ - dl->v_send = 0; - - /* Set states */ - dl->own_busy = dl->peer_busy = 0; - dl->retrans_ctr = 0; - lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); - - /* Tramsmit and start T200 */ - dl->send_ph_data_req(&nctx, msg); - lapd_start_t200(dl); - - return 0; -} - -/* requesst release of link */ -static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - struct lapd_msg_ctx nctx; - - /* local release */ - if (dp->u.rel_req.mode) { - LOGDL(dl, LOGL_INFO, "perform local release\n"); - msgb_free(msg); - /* stop Timer T200 */ - lapd_stop_t200(dl); - /* enter idle state, T203 is stopped here, if running */ - lapd_dl_newstate(dl, LAPD_STATE_IDLE); - /* flush buffers */ - lapd_dl_flush_tx(dl); - lapd_dl_flush_send(dl); - /* send notification to L3 */ - return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); - } - - /* in case we are already disconnecting */ - if (dl->state == LAPD_STATE_DISC_SENT) - return -EBUSY; - - /* flush tx_hist */ - lapd_dl_flush_hist(dl); - - LOGDL(dl, LOGL_INFO, "perform normal release (DISC)\n"); - - /* Push LAPD header on msgb */ - /* assemble message */ - memcpy(&nctx, &dl->lctx, sizeof(nctx)); - /* keep nctx.ldp */ - /* keep nctx.sapi */ - /* keep nctx.tei */ - nctx.cr = dl->cr.loc2rem.cmd; - nctx.format = LAPD_FORM_U; - nctx.s_u = LAPD_U_DISC; - nctx.p_f = 1; - nctx.length = 0; - nctx.more = 0; - - dl->tx_hist0.msg = lapd_msgb_alloc(msg->len, "HIST"); - msgb_put(dl->tx_hist0.msg, msg->len); - if (msg->len) - memcpy(dl->tx_hist0.msg->data, msg->l3h, msg->len); - dl->tx_hist0.more = 0; - /* set Vs to 0, because it is used as index when resending DISC */ - dl->v_send = 0; - - /* Set states */ - dl->own_busy = dl->peer_busy = 0; - dl->retrans_ctr = 0; - lapd_dl_newstate(dl, LAPD_STATE_DISC_SENT); - - /* Tramsmit and start T200 */ - dl->send_ph_data_req(&nctx, msg); - lapd_start_t200(dl); - - return 0; -} - -/* request release of link in idle state */ -static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp, - struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - struct msgb *msg = dp->oph.msg; - - msgb_free(msg); - - /* send notification to L3 */ - return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); -} - -/* statefull handling for DL SAP messages from L3 */ -static const struct l2downstate { - uint32_t states; - int prim, op; - const char *name; - int (*rout) (struct osmo_dlsap_prim *dp, - struct lapd_msg_ctx *lctx); -} l2downstatelist = { - /* create and send UI command */ - {ALL_STATES, - PRIM_DL_UNIT_DATA, PRIM_OP_REQUEST, - "DL-UNIT-DATA-REQUEST", lapd_udata_req}, - - /* create and send SABM command */ - {SBIT(LAPD_STATE_IDLE), - PRIM_DL_EST, PRIM_OP_REQUEST, - "DL-ESTABLISH-REQUEST", lapd_est_req}, - - /* create and send I command */ - {SBIT(LAPD_STATE_MF_EST) | - SBIT(LAPD_STATE_TIMER_RECOV), - PRIM_DL_DATA, PRIM_OP_REQUEST, - "DL-DATA-REQUEST", lapd_data_req}, - - /* suspend datalink */ - {SBIT(LAPD_STATE_MF_EST) | - SBIT(LAPD_STATE_TIMER_RECOV), - PRIM_DL_SUSP, PRIM_OP_REQUEST, - "DL-SUSPEND-REQUEST", lapd_susp_req}, - - /* create and send SABM command (resume) */ - {SBIT(LAPD_STATE_MF_EST) | - SBIT(LAPD_STATE_TIMER_RECOV), - PRIM_DL_RES, PRIM_OP_REQUEST, - "DL-RESUME-REQUEST", lapd_res_req}, - - /* create and send SABM command (reconnect) */ - {SBIT(LAPD_STATE_IDLE) | - SBIT(LAPD_STATE_MF_EST) | - SBIT(LAPD_STATE_TIMER_RECOV), - PRIM_DL_RECON, PRIM_OP_REQUEST, - "DL-RECONNECT-REQUEST", lapd_res_req}, - - /* create and send DISC command */ - {SBIT(LAPD_STATE_SABM_SENT) | - SBIT(LAPD_STATE_MF_EST) | - SBIT(LAPD_STATE_TIMER_RECOV) | - SBIT(LAPD_STATE_DISC_SENT), - PRIM_DL_REL, PRIM_OP_REQUEST, - "DL-RELEASE-REQUEST", lapd_rel_req}, - - /* release in idle state */ - {SBIT(LAPD_STATE_IDLE), - PRIM_DL_REL, PRIM_OP_REQUEST, - "DL-RELEASE-REQUEST", lapd_rel_req_idle}, -}; - -#define L2DOWNSLLEN \ - (sizeof(l2downstatelist) / sizeof(struct l2downstate)) - -int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) -{ - struct lapd_datalink *dl = lctx->dl; - int i, supported = 0; - struct msgb *msg = dp->oph.msg; - int rc; - - /* find function for current state and message */ - for (i = 0; i < L2DOWNSLLEN; i++) { - if (dp->oph.primitive == l2downstatelisti.prim - && dp->oph.operation == l2downstatelisti.op) { - supported = 1; - if ((SBIT(dl->state) & l2downstatelisti.states)) - break; - } - } - if (!supported) { - LOGDL(dl, LOGL_NOTICE, "Message %u/%u unsupported\n", - dp->oph.primitive, dp->oph.operation); - msgb_free(msg); - return 0; - } - if (i == L2DOWNSLLEN) { - LOGDL(dl, LOGL_NOTICE, "Message %u/%u unhandled at this state %s\n", - dp->oph.primitive, dp->oph.operation, lapd_state_name(dl->state)); - msgb_free(msg); - return 0; - } - - LOGDL(dl, LOGL_INFO, "Message %s received in state %s\n", - l2downstatelisti.name, lapd_state_name(dl->state)); - - rc = l2downstatelisti.rout(dp, lctx); - - return rc; -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/gsmtap_util.c
Deleted
@@ -1,554 +0,0 @@ -/*! \file gsmtap_util.c - * GSMTAP support code in libosmocore. */ -/* - * (C) 2010-2017 by Harald Welte <laforge@gnumonks.org> - * - * 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 "../config.h" - -#include <osmocom/core/gsmtap_util.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/gsmtap.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/byteswap.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/rsl.h> - -#include <sys/types.h> - -#include <stdio.h> -#include <unistd.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> - -/*! \addtogroup gsmtap - * @{ - * GSMTAP utility routines. Encapsulates GSM messages over UDP. - * - * \file gsmtap_util.c */ - - -/*! convert RSL channel number to GSMTAP channel type - * \paramin rsl_chantype RSL channel type - * \paramin link_id RSL link identifier - * \paramin user_plane Is this voice/csd user plane (1) or signaling (0) - * \returns GSMTAP channel type - */ -uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_plane) -{ - uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; - - switch (rsl_chantype) { - case RSL_CHAN_Bm_ACCHs: - case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs: - if (user_plane) - ret = GSMTAP_CHANNEL_VOICE_F; - else - ret = GSMTAP_CHANNEL_FACCH_F; - break; - case RSL_CHAN_Lm_ACCHs: - case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs: - if (user_plane) - ret = GSMTAP_CHANNEL_VOICE_H; - else - ret = GSMTAP_CHANNEL_FACCH_H; - break; - case RSL_CHAN_SDCCH4_ACCH: - ret = GSMTAP_CHANNEL_SDCCH4; - break; - case RSL_CHAN_SDCCH8_ACCH: - ret = GSMTAP_CHANNEL_SDCCH8; - break; - case RSL_CHAN_BCCH: - ret = GSMTAP_CHANNEL_BCCH; - break; - case RSL_CHAN_RACH: - ret = GSMTAP_CHANNEL_RACH; - break; - case RSL_CHAN_PCH_AGCH: - /* it could also be AGCH... */ - ret = GSMTAP_CHANNEL_PCH; - break; - case RSL_CHAN_OSMO_PDCH: - ret = GSMTAP_CHANNEL_PDCH; - break; - case RSL_CHAN_OSMO_CBCH4: - ret = GSMTAP_CHANNEL_CBCH51; - break; - case RSL_CHAN_OSMO_CBCH8: - ret = GSMTAP_CHANNEL_CBCH52; - break; - } - - if (link_id & 0x40) - ret |= GSMTAP_CHANNEL_ACCH; - - return ret; -} - -/*! convert RSL channel number to GSMTAP channel type - * \paramin rsl_chantype RSL channel type - * \paramin link_id RSL link identifier - * \returns GSMTAP channel type - */ -uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) -{ - return chantype_rsl2gsmtap2(rsl_chantype, link_id, false); -} - -/*! convert GSMTAP channel type to RSL channel number + Link ID - * \paramin gsmtap_chantype GSMTAP channel type - * \paramout rsl_chantype RSL channel mumber - * \paramout link_id RSL link identifier - */ -void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype, - uint8_t *link_id) -{ - switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) { - case GSMTAP_CHANNEL_FACCH_F: - case GSMTAP_CHANNEL_VOICE_F: // TCH/F - *rsl_chantype = RSL_CHAN_Bm_ACCHs; - break; - case GSMTAP_CHANNEL_FACCH_H: - case GSMTAP_CHANNEL_VOICE_H: // TCH/H - *rsl_chantype = RSL_CHAN_Lm_ACCHs; - break; - case GSMTAP_CHANNEL_SDCCH4: // SDCCH/4 - *rsl_chantype = RSL_CHAN_SDCCH4_ACCH; - break; - case GSMTAP_CHANNEL_SDCCH8: // SDCCH/8 - *rsl_chantype = RSL_CHAN_SDCCH8_ACCH; - break; - case GSMTAP_CHANNEL_BCCH: // BCCH - *rsl_chantype = RSL_CHAN_BCCH; - break; - case GSMTAP_CHANNEL_RACH: // RACH - *rsl_chantype = RSL_CHAN_RACH; - break; - case GSMTAP_CHANNEL_PCH: // PCH - case GSMTAP_CHANNEL_AGCH: // AGCH - *rsl_chantype = RSL_CHAN_PCH_AGCH; - break; - case GSMTAP_CHANNEL_PDCH: - *rsl_chantype = RSL_CHAN_OSMO_PDCH; - break; - } - - *link_id = gsmtap_chantype & GSMTAP_CHANNEL_ACCH ? 0x40 : 0x00; -} - -/*! create an arbitrary type GSMTAP message - * \paramin type The GSMTAP_TYPE_xxx constant of the message to create - * \paramin arfcn GSM ARFCN (Channel Number) - * \paramin ts GSM time slot - * \paramin chan_type Channel Type - * \paramin ss Sub-slot - * \paramin fn GSM Frame Number - * \paramin signal_dbm Signal Strength (dBm) - * \paramin snr Signal/Noise Ratio (SNR) - * \paramin data Pointer to data buffer - * \paramin len Length of \ref data - * \return dynamically allocated message buffer containing data - * - * This function will allocate a new msgb and fill it with a GSMTAP - * header containing the information - */ -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, - int8_t snr, const uint8_t *data, unsigned int len) -{ - struct msgb *msg; - struct gsmtap_hdr *gh; - uint8_t *dst; - - msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx"); - if (!msg) - return NULL; - - gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->version = GSMTAP_VERSION; - gh->hdr_len = sizeof(*gh)/4; - gh->type = type; - gh->timeslot = ts; - gh->sub_slot = ss; - gh->arfcn = osmo_htons(arfcn); - gh->snr_db = snr; - gh->signal_dbm = signal_dbm; - gh->frame_number = osmo_htonl(fn); - gh->sub_type = chan_type; - gh->antenna_nr = 0; - - dst = msgb_put(msg, len); - memcpy(dst, data, len); - - return msg; -} - -/*! create L1/L2 data and put it into GSMTAP - * \paramin arfcn GSM ARFCN (Channel Number) - * \paramin ts GSM time slot - * \paramin chan_type Channel Type - * \paramin ss Sub-slot - * \paramin fn GSM Frame Number - * \paramin signal_dbm Signal Strength (dBm) - * \paramin snr Signal/Noise Ratio (SNR) - * \paramin data Pointer to data buffer - * \paramin len Length of \ref data - * \return message buffer or NULL in case of error - * - * This function will allocate a new msgb and fill it with a GSMTAP - * header containing the information - */ -struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, - uint8_t ss, uint32_t fn, int8_t signal_dbm, - int8_t snr, const uint8_t *data, unsigned int len) -{ - return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type, - ss, fn, signal_dbm, snr, data, len); -} - -#ifdef HAVE_SYS_SOCKET_H - -#include <sys/socket.h> -#include <netinet/in.h> - -/*! Create a new (sending) GSMTAP source socket - * \paramin host host name or IP address in string format - * \paramin port UDP port number in host byte order - * \return file descriptor of the new socket - * - * Opens a GSMTAP source (sending) socket, conncet it to host/port and - * return resulting fd. If \a host is NULL, the destination address - * will be localhost. If \a port is 0, the default \ref - * GSMTAP_UDP_PORT will be used. - * */ -int gsmtap_source_init_fd(const char *host, uint16_t port) -{ - if (port == 0) - port = GSMTAP_UDP_PORT; - if (host == NULL) - host = "localhost"; - - return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, - OSMO_SOCK_F_CONNECT); -} - -/*! Add a local sink to an existing GSMTAP source and return fd - * \paramin gsmtap_fd file descriptor of the gsmtap socket - * \returns file descriptor of locally bound receive socket - * - * In case the GSMTAP socket is connected to a local destination - * IP/port, this function creates a corresponding receiving socket - * bound to that destination IP + port. - * - * In case the gsmtap socket is not connected to a local IP/port, or - * creation of the receiving socket fails, a negative error code is - * returned. - */ -int gsmtap_source_add_sink_fd(int gsmtap_fd) -{ - struct sockaddr_storage ss; - socklen_t ss_len = sizeof(ss); - int rc; - - rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len); - if (rc < 0) - return rc; - - if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { - rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, - IPPROTO_UDP, - OSMO_SOCK_F_BIND | - OSMO_SOCK_F_UDP_REUSEADDR); - if (rc >= 0) - return rc; - } - - return -ENODEV; -} - -/*! Send a \ref msgb through a GSMTAP source - * \paramin gti GSMTAP instance - * \paramin msg message buffer - * \return 0 in case of success; negative in case of error - * NOTE: in case of nonzero return value, the *caller* must free the msg! - * (This enables the caller to attempt re-sending the message.) - * If 0 is returned, the msgb was freed by this function. - */ -int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg) -{ - if (!gti) - return -ENODEV; - - if (gti->ofd_wq_mode) - return osmo_wqueue_enqueue(>i->wq, msg); - else { - /* try immediate send and return error if any */ - int rc; - - rc = write(gsmtap_inst_fd(gti), msg->data, msg->len); - if (rc < 0) { - return rc; - } else if (rc >= msg->len) { - msgb_free(msg); - return 0; - } else { - /* short write */ - return -EIO; - } - } -} - -/*! Send a \ref msgb through a GSMTAP source; free the message even if tx queue full. - * \paramin gti GSMTAP instance - * \paramin msg message buffer; always freed, caller must not reference it later. - * \return 0 in case of success; negative in case of error - */ -int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg) -{ - int rc; - rc = gsmtap_sendmsg(gti, msg); - if (rc < 0) - msgb_free(msg); - return rc; -} - -/*! send an arbitrary type through GSMTAP. - * See \ref gsmtap_makemsg_ex for arguments - */ -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, int8_t snr, const uint8_t *data, - unsigned int len) -{ - struct msgb *msg; - int rc; - - if (!gti) - return -ENODEV; - - msg = gsmtap_makemsg_ex(type, arfcn, ts, chan_type, ss, fn, signal_dbm, - snr, data, len); - if (!msg) - return -ENOMEM; - - rc = gsmtap_sendmsg(gti, msg); - if (rc) - msgb_free(msg); - return rc; -} - -/*! send a message from L1/L2 through GSMTAP. - * See \ref gsmtap_makemsg for arguments - */ -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, int8_t snr, const uint8_t *data, - unsigned int len) -{ - return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn, - signal_dbm, snr, data, len); -} - -/* Callback from select layer if we can write to the socket */ -static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - int rc; - - rc = write(ofd->fd, msg->data, msg->len); - if (rc < 0) { - return rc; - } - if (rc != msg->len) { - return -EIO; - } - - return 0; -} - -/* Callback from select layer if we can read from the sink socket */ -static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) -{ - int rc; - uint8_t buf4096; - - if (!(flags & OSMO_FD_READ)) - return 0; - - rc = read(fd->fd, buf, sizeof(buf)); - if (rc < 0) { - return rc; - } - /* simply discard any data arriving on the socket */ - - return 0; -} - -/*! Add a local sink to an existing GSMTAP source and return fd - * \paramin gti existing GSMTAP source - * \returns file descriptor of locally bound receive socket - * - * In case the GSMTAP socket is connected to a local destination - * IP/port, this function creates a corresponding receiving socket - * bound to that destination IP + port. - * - * In case the gsmtap socket is not connected to a local IP/port, or - * creation of the receiving socket fails, a negative error code is - * returned. - * - * The file descriptor of the receiving socket is automatically added - * to the libosmocore select() handling. - */ -int gsmtap_source_add_sink(struct gsmtap_inst *gti) -{ - int fd, rc; - - fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti)); - if (fd < 0) - return fd; - - if (gti->ofd_wq_mode) { - struct osmo_fd *sink_ofd; - - sink_ofd = >i->sink_ofd; - sink_ofd->fd = fd; - sink_ofd->when = OSMO_FD_READ; - sink_ofd->cb = gsmtap_sink_fd_cb; - - rc = osmo_fd_register(sink_ofd); - if (rc < 0) { - close(fd); - return rc; - } - } - - return fd; -} - - -/*! Open GSMTAP source socket, connect and register osmo_fd - * \paramin host host name or IP address in string format - * \paramin port UDP port number in host byte order - * \paramin ofd_wq_mode Register \ref osmo_wqueue (1) or not (0) - * \return callee-allocated \ref gsmtap_inst - * - * Open GSMTAP source (sending) socket, connect it to host/port, - * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue - * registration. - */ -struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, - int ofd_wq_mode) -{ - struct gsmtap_inst *gti; - int fd, rc; - - fd = gsmtap_source_init_fd(host, port); - if (fd < 0) - return NULL; - - gti = talloc_zero(NULL, struct gsmtap_inst); - gti->ofd_wq_mode = ofd_wq_mode; - gti->wq.bfd.fd = fd; - gti->sink_ofd.fd = -1; - - if (ofd_wq_mode) { - osmo_wqueue_init(>i->wq, 64); - gti->wq.write_cb = &gsmtap_wq_w_cb; - - rc = osmo_fd_register(>i->wq.bfd); - if (rc < 0) { - talloc_free(gti); - close(fd); - return NULL; - } - } - - return gti; -} - -void gsmtap_source_free(struct gsmtap_inst *gti) -{ - if (gti->ofd_wq_mode) { - osmo_fd_unregister(>i->wq.bfd); - osmo_wqueue_clear(>i->wq); - - if (gti->sink_ofd.fd != -1) { - osmo_fd_unregister(>i->sink_ofd); - close(gti->sink_ofd.fd); - } - } - - close(gti->wq.bfd.fd); - talloc_free(gti); -} - -#endif /* HAVE_SYS_SOCKET_H */ - -const struct value_string gsmtap_gsm_channel_names = { - { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" }, - { GSMTAP_CHANNEL_BCCH, "BCCH" }, - { GSMTAP_CHANNEL_CCCH, "CCCH" }, - { GSMTAP_CHANNEL_RACH, "RACH" }, - { GSMTAP_CHANNEL_AGCH, "AGCH" }, - { GSMTAP_CHANNEL_PCH, "PCH" }, - { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, - { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" }, - { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" }, - { GSMTAP_CHANNEL_FACCH_F, "FACCH/F" }, - { GSMTAP_CHANNEL_FACCH_H, "FACCH/H" }, - { GSMTAP_CHANNEL_PACCH, "PACCH" }, - { GSMTAP_CHANNEL_CBCH52, "CBCH" }, - { GSMTAP_CHANNEL_PDCH, "PDCH" } , - { GSMTAP_CHANNEL_PTCCH, "PTTCH" }, - { GSMTAP_CHANNEL_CBCH51, "CBCH" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH, "LSACCH" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH4, "SACCH/4" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH8, "SACCH/8" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_F, "SACCH/F" }, - { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_H, "SACCH/H" }, - { GSMTAP_CHANNEL_VOICE_F, "TCH/F" }, - { GSMTAP_CHANNEL_VOICE_H, "TCH/H" }, - { 0, NULL } -}; - -/* for debugging */ -const struct value_string gsmtap_type_names = { - { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" }, - { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" }, - { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" }, - { GSMTAP_TYPE_SIM, "SIM Card" }, - { GSMTAP_TYPE_TETRA_I1, "TETRA V+D" }, - { GSMTAP_TYPE_TETRA_I1_BURST, "TETRA bursts" }, - { GSMTAP_TYPE_WMX_BURST, "WiMAX burst" }, - { GSMTAP_TYPE_GMR1_UM, "GMR-1 air interfeace (MES-MS<->GTS)"}, - { GSMTAP_TYPE_UMTS_RLC_MAC, "UMTS RLC/MAC" }, - { GSMTAP_TYPE_UMTS_RRC, "UMTS RRC" }, - { GSMTAP_TYPE_LTE_RRC, "LTE RRC" }, - { GSMTAP_TYPE_LTE_MAC, "LTE MAC" }, - { GSMTAP_TYPE_LTE_MAC_FRAMED, "LTE MAC with context hdr" }, - { GSMTAP_TYPE_OSMOCORE_LOG, "libosmocore logging" }, - { GSMTAP_TYPE_QC_DIAG, "Qualcomm DIAG" }, - { 0, NULL } -}; - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/isdnhdlc.c
Deleted
@@ -1,613 +0,0 @@ -/* - * isdnhdlc.c -- General purpose ISDN HDLC decoder. - * - * Copyright (C) - * 2009 Karsten Keil <keil@b1-systems.de> - * 2002 Wolfgang Mües <wolfgang@iksw-muees.de> - * 2001 Frode Isaksen <fisaksen@bewan.com> - * 2001 Kai Germaschewski <kai.germaschewski@gmx.de> - * - * slightly adapted for use in userspace / osmocom envrionment by Harald Welte - * - * 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 <string.h> - -#include <osmocom/core/crc16.h> -#include <osmocom/core/bits.h> -#include <osmocom/core/isdnhdlc.h> - -enum { - HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7, - HDLC_GET_DATA, HDLC_FAST_FLAG -}; - -enum { - HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG, - HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG, - HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0, - HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE -}; - -#define crc_ccitt_byte osmo_crc16_ccitt_byte - -void osmo_isdnhdlc_rcv_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t features) -{ - memset(hdlc, 0, sizeof(*hdlc)); - hdlc->state = HDLC_GET_DATA; - if (features & OSMO_HDLC_F_56KBIT) - hdlc->do_adapt56 = 1; - if (features & OSMO_HDLC_F_BITREVERSE) - hdlc->do_bitreverse = 1; -} - -void osmo_isdnhdlc_out_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t features) -{ - memset(hdlc, 0, sizeof(*hdlc)); - if (features & OSMO_HDLC_F_DCHANNEL) { - hdlc->dchannel = 1; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - hdlc->dchannel = 0; - hdlc->state = HDLC_SEND_FAST_FLAG; - hdlc->ffvalue = 0x7e; - } - hdlc->cbin = 0x7e; - if (features & OSMO_HDLC_F_56KBIT) { - hdlc->do_adapt56 = 1; - hdlc->state = HDLC_SENDFLAG_B0; - } else - hdlc->data_bits = 8; - if (features & OSMO_HDLC_F_BITREVERSE) - hdlc->do_bitreverse = 1; -} - -static int -check_frame(struct osmo_isdnhdlc_vars *hdlc) -{ - int status; - - if (hdlc->dstpos < 2) /* too small - framing error */ - status = -OSMO_HDLC_FRAMING_ERROR; - else if (hdlc->crc != 0xf0b8) /* crc error */ - status = -OSMO_HDLC_CRC_ERROR; - else { - /* remove CRC */ - hdlc->dstpos -= 2; - /* good frame */ - status = hdlc->dstpos; - } - return status; -} - -/*! decodes HDLC frames from a transparent bit stream. - - The source buffer is scanned for valid HDLC frames looking for - flags (01111110) to indicate the start of a frame. If the start of - the frame is found, the bit stuffing is removed (0 after 5 1's). - When a new flag is found, the complete frame has been received - and the CRC is checked. - If a valid frame is found, the function returns the frame length - excluding the CRC with the bit HDLC_END_OF_FRAME set. - If the beginning of a valid frame is found, the function returns - the length. - If a framing error is found (too many 1s and not a flag) the function - returns the length with the bit OSMO_HDLC_FRAMING_ERROR set. - If a CRC error is found the function returns the length with the - bit OSMO_HDLC_CRC_ERROR set. - If the frame length exceeds the destination buffer size, the function - returns the length with the bit OSMO_HDLC_LENGTH_ERROR set. - - \paramin src source buffer - \paramin slen source buffer length - \paramout count number of bytes removed (decoded) from the source buffer - \paramout dst destination buffer - \paramin dsize destination buffer size - \returns number of decoded bytes in the destination buffer and status flag. -*/ -int osmo_isdnhdlc_decode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src, int slen, - int *count, uint8_t *dst, int dsize) -{ - int status = 0; - - static const unsigned char fast_flag = { - 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f - }; - - static const unsigned char fast_flag_value = { - 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f - }; - - static const unsigned char fast_abort = { - 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff - }; - -#define handle_fast_flag(h) \ - do { \ - if (h->cbin == fast_flagh->bit_shift) { \ - h->ffvalue = fast_flag_valueh->bit_shift; \ - h->state = HDLC_FAST_FLAG; \ - h->ffbit_shift = h->bit_shift; \ - h->bit_shift = 1; \ - } else { \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } \ - } while (0) - -#define handle_abort(h) \ - do { \ - h->shift_reg = fast_aborth->ffbit_shift - 1; \ - h->hdlc_bits1 = h->ffbit_shift - 2; \ - if (h->hdlc_bits1 < 0) \ - h->hdlc_bits1 = 0; \ - h->data_bits = h->ffbit_shift - 1; \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } while (0) - - *count = slen; - - while (slen > 0) { - if (hdlc->bit_shift == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - hdlc->cbin = osmo_revbytebits_8(*src++); - else - hdlc->cbin = *src++; - slen--; - hdlc->bit_shift = 8; - if (hdlc->do_adapt56) - hdlc->bit_shift--; - } - - switch (hdlc->state) { - case STOPPED: - return 0; - case HDLC_FAST_IDLE: - if (hdlc->cbin == 0xff) { - hdlc->bit_shift = 0; - break; - } - hdlc->state = HDLC_GET_FLAG_B0; - hdlc->hdlc_bits1 = 0; - hdlc->bit_shift = 8; - break; - case HDLC_GET_FLAG_B0: - if (!(hdlc->cbin & 0x80)) { - hdlc->state = HDLC_GETFLAG_B1A6; - hdlc->hdlc_bits1 = 0; - } else { - if ((!hdlc->do_adapt56) && - (++hdlc->hdlc_bits1 >= 8) && - (hdlc->bit_shift == 1)) - hdlc->state = HDLC_FAST_IDLE; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B1A6: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - if (hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_GETFLAG_B7; - } else - hdlc->hdlc_bits1 = 0; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B7: - if (hdlc->cbin & 0x80) { - hdlc->state = HDLC_GET_FLAG_B0; - } else { - hdlc->state = HDLC_GET_DATA; - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->hdlc_bits1 = 0; - hdlc->data_bits = 0; - hdlc->data_received = 0; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GET_DATA: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - switch (hdlc->hdlc_bits1) { - case 6: - break; - case 7: - if (hdlc->data_received) - /* bad frame */ - status = -OSMO_HDLC_FRAMING_ERROR; - if (!hdlc->do_adapt56) { - if (hdlc->cbin == fast_abort - hdlc->bit_shift + 1) { - hdlc->state = - HDLC_FAST_IDLE; - hdlc->bit_shift = 1; - break; - } - } else - hdlc->state = HDLC_GET_FLAG_B0; - break; - default: - hdlc->shift_reg >>= 1; - hdlc->shift_reg |= 0x80; - hdlc->data_bits++; - break; - } - } else { - switch (hdlc->hdlc_bits1) { - case 5: - break; - case 6: - if (hdlc->data_received) - status = check_frame(hdlc); - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->data_bits = 0; - if (!hdlc->do_adapt56) - handle_fast_flag(hdlc); - else { - hdlc->state = HDLC_GET_DATA; - hdlc->data_received = 0; - } - break; - default: - hdlc->shift_reg >>= 1; - hdlc->data_bits++; - break; - } - hdlc->hdlc_bits1 = 0; - } - if (status) { - hdlc->dstpos = 0; - *count -= slen; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - return status; - } - if (hdlc->data_bits == 8) { - hdlc->data_bits = 0; - hdlc->data_received = 1; - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - - /* good byte received */ - if (hdlc->dstpos < dsize) - dsthdlc->dstpos++ = hdlc->shift_reg; - else { - /* frame too long */ - status = -OSMO_HDLC_LENGTH_ERROR; - hdlc->dstpos = 0; - } - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_FAST_FLAG: - if (hdlc->cbin == hdlc->ffvalue) { - hdlc->bit_shift = 0; - break; - } else { - if (hdlc->cbin == 0xff) { - hdlc->state = HDLC_FAST_IDLE; - hdlc->bit_shift = 0; - } else if (hdlc->ffbit_shift == 8) { - hdlc->state = HDLC_GETFLAG_B7; - break; - } else - handle_abort(hdlc); - } - break; - default: - break; - } - } - *count -= slen; - return 0; -} -/*! encodes HDLC frames to a transparent bit stream. - - The bit stream starts with a beginning flag (01111110). After - that each byte is added to the bit stream with bit stuffing added - (0 after 5 1's). - When the last byte has been removed from the source buffer, the - CRC (2 bytes is added) and the frame terminates with the ending flag. - For the dchannel, the idle character (all 1's) is also added at the end. - If this function is called with empty source buffer (slen=0), flags or - idle character will be generated. - - \paramin src source buffer - \paramin slen source buffer length - \paramout count number of bytes removed (encoded) from source buffer - \paramout dst destination buffer - \paramin dsize destination buffer size - \returns - number of encoded bytes in the destination buffer -*/ -int osmo_isdnhdlc_encode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src, uint16_t slen, - int *count, uint8_t *dst, int dsize) -{ - static const unsigned char xfast_flag_value = { - 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e - }; - - int len = 0; - - *count = slen; - - /* special handling for one byte frames */ - if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG)) - hdlc->state = HDLC_SENDFLAG_ONE; - while (dsize > 0) { - if (hdlc->bit_shift == 0) { - if (slen && !hdlc->do_closing) { - hdlc->shift_reg = *src++; - slen--; - if (slen == 0) - /* closing sequence, CRC + flag(s) */ - hdlc->do_closing = 1; - hdlc->bit_shift = 8; - } else { - if (hdlc->state == HDLC_SEND_DATA) { - if (hdlc->data_received) { - hdlc->state = HDLC_SEND_CRC1; - hdlc->crc ^= 0xffff; - hdlc->bit_shift = 8; - hdlc->shift_reg = - hdlc->crc & 0xff; - } else if (!hdlc->do_adapt56) - hdlc->state = - HDLC_SEND_FAST_FLAG; - else - hdlc->state = - HDLC_SENDFLAG_B0; - } - - } - } - - switch (hdlc->state) { - case STOPPED: - while (dsize--) - *dst++ = 0xff; - return dsize; - case HDLC_SEND_FAST_FLAG: - hdlc->do_closing = 0; - if (slen == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = osmo_revbytebits_8(hdlc->ffvalue); - else - *dst++ = hdlc->ffvalue; - len++; - dsize--; - break; - } - /* fall through */ - case HDLC_SENDFLAG_ONE: - if (hdlc->bit_shift == 8) { - hdlc->cbin = hdlc->ffvalue >> - (8 - hdlc->data_bits); - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SENDFLAG_B0: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->hdlc_bits1 = 0; - hdlc->state = HDLC_SENDFLAG_B1A6; - break; - case HDLC_SENDFLAG_B1A6: - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->cbin++; - if (++hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_SENDFLAG_B7; - break; - case HDLC_SENDFLAG_B7: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (slen == 0) { - hdlc->state = HDLC_SENDFLAG_B0; - break; - } - if (hdlc->bit_shift == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SEND_FIRST_FLAG: - hdlc->data_received = 1; - if (hdlc->data_bits == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - break; - } - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - } - break; - case HDLC_SEND_DATA: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->bit_shift == 8) - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - break; - case HDLC_SEND_CRC1: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = (hdlc->crc >> 8); - hdlc->state = HDLC_SEND_CRC2; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CRC2: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = 0x7e; - hdlc->state = HDLC_SEND_CLOSING_FLAG; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CLOSING_FLAG: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->ffvalue = - xfast_flag_valuehdlc->data_bits; - if (hdlc->dchannel) { - hdlc->ffvalue = 0x7e; - hdlc->state = HDLC_SEND_IDLE1; - hdlc->bit_shift = 8-hdlc->data_bits; - if (hdlc->bit_shift == 0) - hdlc->state = - HDLC_SEND_FAST_IDLE; - } else { - if (!hdlc->do_adapt56) { - hdlc->state = - HDLC_SEND_FAST_FLAG; - hdlc->data_received = 0; - } else { - hdlc->state = HDLC_SENDFLAG_B0; - hdlc->data_received = 0; - } - /* Finished this frame, send flags */ - if (dsize > 1) - dsize = 1; - } - } - break; - case HDLC_SEND_IDLE1: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_FAST_IDLE; - hdlc->bit_shift = 0; - } - break; - case HDLC_SEND_FAST_IDLE: - hdlc->do_closing = 0; - hdlc->cbin = 0xff; - hdlc->data_bits = 8; - if (hdlc->bit_shift == 8) { - hdlc->cbin = 0x7e; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = osmo_revbytebits_8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->bit_shift = 0; - hdlc->data_bits = 0; - len++; - dsize = 0; - } - break; - default: - break; - } - if (hdlc->do_adapt56) { - if (hdlc->data_bits == 7) { - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - } - } - if (hdlc->data_bits == 8) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = osmo_revbytebits_8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->data_bits = 0; - len++; - dsize--; - } - } - *count -= slen; - - return len; -}
View file
libosmocore_1.7.0.tar.xz/src/it_q.c
Deleted
@@ -1,272 +0,0 @@ -/*! \file it_q.c - * Osmocom Inter-Thread queue implementation */ -/* (C) 2019 by Harald Welte <laforge@gnumonks.org> - * 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. - */ - -/*! \addtogroup it_q - * @{ - * Inter-Thread Message Queue. - * - * This implements a general-purpose queue between threads. It uses - * user-provided data types (containing a llist_head as initial member) - * as elements in the queue and an eventfd-based notification mechanism. - * Hence, it can be used for pretty much anything, including but not - * limited to msgbs, including msgb-wrapped osmo_prim. - * - * The idea is that the sending thread simply calls osmo_it_q_enqueue(). - * The receiving thread is woken up from its osmo_select_main() loop by eventfd, - * and a general osmo_fd callback function for the eventfd will dequeue each item - * and call a queue-specific callback function. - */ - -#include "../config.h" - -#ifdef HAVE_SYS_EVENTFD_H - -#include <pthread.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <sys/eventfd.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/it_q.h> - -/* "increment" the eventfd by specified 'inc' */ -static int eventfd_increment(int fd, uint64_t inc) -{ - int rc; - - rc = write(fd, &inc, sizeof(inc)); - if (rc != sizeof(inc)) - return -1; - - return 0; -} - -/* global (for all threads) list of message queues in a program + associated lock */ -static LLIST_HEAD(it_queues); -static pthread_rwlock_t it_queues_rwlock = PTHREAD_RWLOCK_INITIALIZER; - -/* resolve it-queue by its globally unique name; must be called with rwlock held */ -static struct osmo_it_q *_osmo_it_q_by_name(const char *name) -{ - struct osmo_it_q *q; - llist_for_each_entry(q, &it_queues, entry) { - if (!strcmp(q->name, name)) - return q; - } - return NULL; -} - -/*! resolve it-queue by its globally unique name */ -struct osmo_it_q *osmo_it_q_by_name(const char *name) -{ - struct osmo_it_q *q; - pthread_rwlock_rdlock(&it_queues_rwlock); - q = _osmo_it_q_by_name(name); - pthread_rwlock_unlock(&it_queues_rwlock); - return q; -} - -/* osmo_fd call-back when eventfd is readable */ -static int osmo_it_q_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct osmo_it_q *q = (struct osmo_it_q *) ofd->data; - uint64_t val; - int i, rc; - - if (!(what & OSMO_FD_READ)) - return 0; - - rc = read(ofd->fd, &val, sizeof(val)); - if (rc < sizeof(val)) - return rc; - - for (i = 0; i < val; i++) { - struct llist_head *item = _osmo_it_q_dequeue(q); - /* in case the user might have called osmo_it_q_flush() we may - * end up in the eventfd-dispatch but without any messages left in the queue, - * otherwise I'd have loved to OSMO_ASSERT(msg) here. */ - if (!item) - break; - q->read_cb(q, item); - } - - return 0; -} - -/*! Allocate a new inter-thread message queue. - * \paramin ctx talloc context from which to allocate the queue - * \paramin name human-readable string name of the queue; function creates a copy. - * \paramin read_cb call-back function to be called for each de-queued message; may be - * NULL in case you don't want eventfd/osmo_select integration and - * will manually take care of noticing if and when to dequeue. - * \returns a newly-allocated inter-thread message queue; NULL in case of error */ -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) -{ - struct osmo_it_q *q; - int fd; - - q = talloc_zero(ctx, struct osmo_it_q); - if (!q) - return NULL; - q->data = data; - q->name = talloc_strdup(q, name); - q->current_length = 0; - q->max_length = max_length; - q->read_cb = read_cb; - INIT_LLIST_HEAD(&q->list); - pthread_mutex_init(&q->mutex, NULL); - q->event_ofd.fd = -1; - - if (q->read_cb) { - /* create eventfd *if* the user has provided a read_cb function */ - fd = eventfd(0, 0); - if (fd < 0) { - talloc_free(q); - return NULL; - } - - /* initialize BUT NOT REGISTER the osmo_fd. The receiving thread must - * take are to select/poll/read/... on it */ - osmo_fd_setup(&q->event_ofd, fd, OSMO_FD_READ, osmo_it_q_fd_cb, q, 0); - } - - /* add to global list of queues, checking for duplicate names */ - pthread_rwlock_wrlock(&it_queues_rwlock); - if (_osmo_it_q_by_name(q->name)) { - pthread_rwlock_unlock(&it_queues_rwlock); - if (q->event_ofd.fd >= 0) - osmo_fd_close(&q->event_ofd); - talloc_free(q); - return NULL; - } - llist_add_tail(&q->entry, &it_queues); - pthread_rwlock_unlock(&it_queues_rwlock); - - return q; -} - -static void *item_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - if (lh) { - llist_del(lh); - return lh; - } else - return NULL; -} - -/*! Flush all messages currently present in queue */ -static void _osmo_it_q_flush(struct osmo_it_q *q) -{ - void *item; - while ((item = item_dequeue(&q->list))) { - talloc_free(item); - } - q->current_length = 0; -} - -/*! Flush all messages currently present in queue */ -void osmo_it_q_flush(struct osmo_it_q *q) -{ - OSMO_ASSERT(q); - - pthread_mutex_lock(&q->mutex); - _osmo_it_q_flush(q); - pthread_mutex_unlock(&q->mutex); -} - -/*! Destroy a message queue */ -void osmo_it_q_destroy(struct osmo_it_q *q) -{ - OSMO_ASSERT(q); - - /* first remove from global list of queues */ - pthread_rwlock_wrlock(&it_queues_rwlock); - llist_del(&q->entry); - pthread_rwlock_unlock(&it_queues_rwlock); - /* next, close the eventfd */ - if (q->event_ofd.fd >= 0) - osmo_fd_close(&q->event_ofd); - /* flush all messages still present */ - osmo_it_q_flush(q); - pthread_mutex_destroy(&q->mutex); - /* and finally release memory */ - talloc_free(q); -} - -/*! Thread-safe en-queue to an inter-thread message queue. - * \paramin queue Inter-thread queue on which to enqueue - * \paramin item Item to enqueue. Must have llist_head as first member! - * \returns 0 on success; negative on error */ -int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item) -{ - OSMO_ASSERT(queue); - OSMO_ASSERT(item); - - pthread_mutex_lock(&queue->mutex); - if (queue->current_length+1 > queue->max_length) { - pthread_mutex_unlock(&queue->mutex); - return -ENOSPC; - } - llist_add_tail(item, &queue->list); - queue->current_length++; - pthread_mutex_unlock(&queue->mutex); - /* increment eventfd counter by one */ - if (queue->event_ofd.fd >= 0) - eventfd_increment(queue->event_ofd.fd, 1); - return 0; -} - - -/*! Thread-safe de-queue from an inter-thread message queue. - * \paramin queue Inter-thread queue from which to dequeue - * \returns dequeued message buffer; NULL if none available - */ -struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue) -{ - struct llist_head *l; - OSMO_ASSERT(queue); - - pthread_mutex_lock(&queue->mutex); - - if (llist_empty(&queue->list)) - l = NULL; - l = queue->list.next; - OSMO_ASSERT(l); - llist_del(l); - queue->current_length--; - - pthread_mutex_unlock(&queue->mutex); - - return l; -} - - -#endif /* HAVE_SYS_EVENTFD_H */ - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/logging.c
Deleted
@@ -1,1526 +0,0 @@ -/*! \file logging.c - * Debugging/Logging support code. */ -/* - * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> - * 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. - * - */ - -/*! \addtogroup logging - * @{ - * libosmocore Logging sub-system - * - * \file logging.c */ - -#include "../config.h" - -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#ifdef HAVE_STRINGS_H -#include <strings.h> -#endif - -#ifdef HAVE_SYSLOG_H -#include <syslog.h> -#endif - -#ifdef HAVE_SYSTEMTAP -/* include the generated probes header and put markers in code */ -#include "probes.h" -#define TRACE(probe) probe -#define TRACE_ENABLED(probe) probe ## _ENABLED() -#else -/* Wrap the probe to allow it to be removed when no systemtap available */ -#define TRACE(probe) -#define TRACE_ENABLED(probe) (0) -#endif /* HAVE_SYSTEMTAP */ - -#include <time.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <pthread.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/thread.h> -#include <osmocom/core/select.h> -#include <osmocom/core/write_queue.h> -#include <osmocom/core/gsmtap_util.h> - -#include <osmocom/vty/logging.h> /* for LOGGING_STR. */ - -/* maximum length of the log string of a single log event (typically line) */ -#define MAX_LOG_SIZE 4096 - -/* maximum number of log statements we queue in file/stderr target write queue */ -#define LOG_WQUEUE_LEN 156 - -osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx), - enum_logging_ctx_items_fit_in_struct_log_context); -osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data), - enum_logging_filters_fit_in_log_target_filter_data); -osmo_static_assert(_LOG_FLT_COUNT <= 8*sizeof(((struct log_target*)NULL)->filter_map), - enum_logging_filters_fit_in_log_target_filter_map); - -struct log_info *osmo_log_info; - -static struct log_context log_context; -void *tall_log_ctx = NULL; -LLIST_HEAD(osmo_log_target_list); - -static __thread long int logging_tid; - -#if (!EMBEDDED) -/*! This mutex must be held while using osmo_log_target_list or any of its - log_targets in a multithread program. Prevents race conditions between threads - like producing unordered timestamps or VTY deleting a target while another - thread is writing to it */ -static pthread_mutex_t osmo_log_tgt_mutex; -static bool osmo_log_tgt_mutex_on = false; - -/*! Enable multithread support (mutex) in libosmocore logging system. - * Must be called by processes willing to use logging subsystem from several - * threads. Once enabled, it's not possible to disable it again. - */ -void log_enable_multithread(void) { - if (osmo_log_tgt_mutex_on) - return; - pthread_mutex_init(&osmo_log_tgt_mutex, NULL); - osmo_log_tgt_mutex_on = true; -} - -/*! Acquire the osmo_log_tgt_mutex. Don't use this function directly, always use - * macro log_tgt_mutex_lock() instead. - */ -void log_tgt_mutex_lock_impl(void) { - /* These lines are useful to debug scenarios where there's only 1 thread - and a double lock appears, for instance during startup and some - unlock() missing somewhere: - if (osmo_log_tgt_mutex_on && pthread_mutex_trylock(&osmo_log_tgt_mutex) != 0) - osmo_panic("acquiring already locked mutex!\n"); - return; - */ - - if (osmo_log_tgt_mutex_on) - pthread_mutex_lock(&osmo_log_tgt_mutex); -} - -/*! Release the osmo_log_tgt_mutex. Don't use this function directly, always use - * macro log_tgt_mutex_unlock() instead. - */ -void log_tgt_mutex_unlock_impl(void) { - if (osmo_log_tgt_mutex_on) - pthread_mutex_unlock(&osmo_log_tgt_mutex); -} - -#else /* if (!EMBEDDED) */ -#pragma message ("logging multithread support disabled in embedded build") -void log_enable_multithread(void) {} -void log_tgt_mutex_lock_impl(void) {} -void log_tgt_mutex_unlock_impl(void) {} -#endif /* if (!EMBEDDED) */ - -const struct value_string loglevel_strs = { - { LOGL_DEBUG, "DEBUG" }, - { LOGL_INFO, "INFO" }, - { LOGL_NOTICE, "NOTICE" }, - { LOGL_ERROR, "ERROR" }, - { LOGL_FATAL, "FATAL" }, - { 0, NULL }, -}; - -/* 256 color palette see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */ -#define INT2IDX(x) (-1*(x)-1) -static const struct log_info_cat internal_catOSMO_NUM_DLIB = { - INT2IDX(DLGLOBAL) = { /* -1 becomes 0 */ - .name = "DLGLOBAL", - .description = "Library-internal global log family", - .loglevel = LOGL_NOTICE, - .enabled = 1, - }, - INT2IDX(DLLAPD) = { /* -2 becomes 1 */ - .name = "DLLAPD", - .description = "LAPD in libosmogsm", - .loglevel = LOGL_NOTICE, - .enabled = 1, - .color = "\03338;5;12m", - }, - INT2IDX(DLINP) = { - .name = "DLINP", - .description = "A-bis Intput Subsystem", - .loglevel = LOGL_NOTICE, - .enabled = 1, - .color = "\03338;5;23m", - }, - INT2IDX(DLMUX) = { - .name = "DLMUX", - .description = "A-bis B-Subchannel TRAU Frame Multiplex", - .loglevel = LOGL_NOTICE, - .enabled = 1, - .color = "\03338;5;25m", - }, - INT2IDX(DLMI) = { - .name = "DLMI", - .description = "A-bis Input Driver for Signalling", - .enabled = 0, .loglevel = LOGL_NOTICE, - .color = "\03338;5;27m", - }, - INT2IDX(DLMIB) = { - .name = "DLMIB", - .description = "A-bis Input Driver for B-Channels (voice)", - .enabled = 0, .loglevel = LOGL_NOTICE, - .color = "\03338;5;29m", - }, - INT2IDX(DLSMS) = { - .name = "DLSMS", - .description = "Layer3 Short Message Service (SMS)", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;31m", - }, - INT2IDX(DLCTRL) = { - .name = "DLCTRL", - .description = "Control Interface", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;33m", - }, - INT2IDX(DLGTP) = { - .name = "DLGTP", - .description = "GPRS GTP library", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;35m", - }, - INT2IDX(DLSTATS) = { - .name = "DLSTATS", - .description = "Statistics messages and logging", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;37m", - }, - INT2IDX(DLGSUP) = { - .name = "DLGSUP", - .description = "Generic Subscriber Update Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;39m", - }, - INT2IDX(DLOAP) = { - .name = "DLOAP", - .description = "Osmocom Authentication Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;41m", - }, - INT2IDX(DLSS7) = { - .name = "DLSS7", - .description = "libosmo-sigtran Signalling System 7", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;43m", - }, - INT2IDX(DLSCCP) = { - .name = "DLSCCP", - .description = "libosmo-sigtran SCCP Implementation", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;45m", - }, - INT2IDX(DLSUA) = { - .name = "DLSUA", - .description = "libosmo-sigtran SCCP User Adaptation", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;47m", - }, - INT2IDX(DLM3UA) = { - .name = "DLM3UA", - .description = "libosmo-sigtran MTP3 User Adaptation", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;49m", - }, - INT2IDX(DLMGCP) = { - .name = "DLMGCP", - .description = "libosmo-mgcp Media Gateway Control Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;51m", - }, - INT2IDX(DLJIBUF) = { - .name = "DLJIBUF", - .description = "libosmo-netif Jitter Buffer", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;53m", - }, - INT2IDX(DLRSPRO) = { - .name = "DLRSPRO", - .description = "Remote SIM protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;55m", - }, - INT2IDX(DLNS) = { - .name = "DLNS", - .description = "GPRS NS layer", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;57m", - }, - INT2IDX(DLBSSGP) = { - .name = "DLBSSGP", - .description = "GPRS BSSGP layer", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;59m", - }, - INT2IDX(DLNSDATA) = { - .name = "DLNSDATA", - .description = "GPRS NS layer data PDU", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;61m", - }, - INT2IDX(DLNSSIGNAL) = { - .name = "DLNSSIGNAL", - .description = "GPRS NS layer signal PDU", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;63m", - }, - INT2IDX(DLIUUP) = { - .name = "DLIUUP", - .description = "Iu UP layer", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;65m", - }, - INT2IDX(DLPFCP) = { - .name = "DLPFCP", - .description = "libosmo-pfcp Packet Forwarding Control Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - .color = "\03338;5;51m", - }, -}; - -void assert_loginfo(const char *src) -{ - if (!osmo_log_info) { - fprintf(stderr, "ERROR: osmo_log_info == NULL! " - "You must call log_init() before using logging in %s()!\n", src); - OSMO_ASSERT(osmo_log_info); - } -} - -/* special magic for negative (library-internal) log subsystem numbers */ -static int subsys_lib2index(int subsys) -{ - return (subsys * -1) + (osmo_log_info->num_cat_user-1); -} - -/*! Parse a human-readable log level into a numeric value - * \paramin lvl zero-terminated string containing log level name - * \returns numeric log level - */ -int log_parse_level(const char *lvl) -{ - return get_string_value(loglevel_strs, lvl); -} - -/*! convert a numeric log level into human-readable string - * \paramin lvl numeric log level - * \returns zero-terminated string (log level name) - */ -const char *log_level_str(unsigned int lvl) -{ - return get_value_string(loglevel_strs, lvl); -} - -/*! parse a human-readable log category into numeric form - * \paramin category human-readable log category name - * \returns numeric category value, or -EINVAL otherwise - */ -int log_parse_category(const char *category) -{ - int i; - - assert_loginfo(__func__); - - for (i = 0; i < osmo_log_info->num_cat; ++i) { - if (osmo_log_info->cati.name == NULL) - continue; - if (!strcasecmp(osmo_log_info->cati.name+1, category)) - return i; - } - - return -EINVAL; -} - -/*! parse the log category mask - * \paramin target log target to be configured - * \paramin _mask log category mask string - * - * The format can be this: category1:category2:category3 - * or category1,2:category2,3:... - */ -void log_parse_category_mask(struct log_target* target, const char *_mask) -{ - int i = 0; - char *mask = strdup(_mask); - char *category_token = NULL; - - assert_loginfo(__func__); - - /* Disable everything to enable it afterwards */ - for (i = 0; i < osmo_log_info->num_cat; ++i) - target->categoriesi.enabled = 0; - - category_token = strtok(mask, ":"); - OSMO_ASSERT(category_token); - do { - for (i = 0; i < osmo_log_info->num_cat; ++i) { - size_t length, cat_length; - char* colon = strstr(category_token, ","); - - if (!osmo_log_info->cati.name) - continue; - - length = strlen(category_token); - cat_length = strlen(osmo_log_info->cati.name); - - /* Use longest length not to match subocurrences. */ - if (cat_length > length) - length = cat_length; - - if (colon) - length = colon - category_token; - - if (strncasecmp(osmo_log_info->cati.name, - category_token, length) == 0) { - int level = 0; - - if (colon) - level = atoi(colon+1); - - target->categoriesi.enabled = 1; - target->categoriesi.loglevel = level; - } - } - } while ((category_token = strtok(NULL, ":"))); - - free(mask); -} - -static const char* color(int subsys) -{ - if (subsys < osmo_log_info->num_cat) - return osmo_log_info->catsubsys.color; - - return NULL; -} - -static const struct value_string level_colors = { - { LOGL_DEBUG, OSMO_LOGCOLOR_BLUE }, - { LOGL_INFO, OSMO_LOGCOLOR_GREEN }, - { LOGL_NOTICE, OSMO_LOGCOLOR_YELLOW }, - { LOGL_ERROR, OSMO_LOGCOLOR_RED }, - { LOGL_FATAL, OSMO_LOGCOLOR_RED }, - { 0, NULL } -}; - -static const char *level_color(int level) -{ - const char *c = get_value_string_or_null(level_colors, level); - if (!c) - return get_value_string(level_colors, LOGL_FATAL); - return c; -} - -const char* log_category_name(int subsys) -{ - if (subsys < osmo_log_info->num_cat) - return osmo_log_info->catsubsys.name; - - return NULL; -} - -static const char *const_basename(const char *path) -{ - const char *bn = strrchr(path, '/'); - if (!bn || !bn1) - return path; - return bn + 1; -} - -/*! main output formatting function for log lines. - * \paramout buf caller-allocated output buffer for the generated string - * \paramin buf_len number of bytes available in buf - * \paramin target log target for which the string is to be formatted - * \paramin subsys Log sub-system number - * \paramin level Log level - * \paramin file name of source code file generating the log - * \paramin line line source code line number within 'file' generating the log - * \paramin cont is this a continuation (true) or not (false) - * \paramin format format string - * \paramin ap variable argument list for format - * \returns number of bytes written to out */ -static int _output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys, - unsigned int level, const char *file, int line, int cont, - const char *format, va_list ap) -{ - int ret, len = 0, offset = 0, rem = buf_len; - const char *c_subsys = NULL; - - /* are we using color */ - if (target->use_color) { - c_subsys = color(subsys); - if (c_subsys) { - ret = snprintf(buf + offset, rem, "%s", c_subsys); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - } - if (!cont) { - if (target->print_ext_timestamp) { -#ifdef HAVE_LOCALTIME_R - struct tm tm; - struct timeval tv; - osmo_gettimeofday(&tv, NULL); - localtime_r(&tv.tv_sec, &tm); - ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - (int)(tv.tv_usec / 1000)); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); -#endif - } else if (target->print_timestamp) { - time_t tm; - if ((tm = time(NULL)) == (time_t) -1) - goto err; - /* Get human-readable representation of time. - man ctime: we need at least 26 bytes in buf */ - if (rem < 26 || !ctime_r(&tm, buf + offset)) - goto err; - ret = strlen(buf + offset); - if (ret <= 0) - goto err; - /* Get rid of useless final '\n' added by ctime_r. We want a space instead. */ - bufoffset + ret - 1 = ' '; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - if (target->print_tid) { - if (logging_tid == 0) - logging_tid = (long int)osmo_gettid(); - ret = snprintf(buf + offset, rem, "%ld ", logging_tid); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - if (target->print_category) { - ret = snprintf(buf + offset, rem, "%s%s%s%s ", - target->use_color ? level_color(level) : "", - log_category_name(subsys), - target->use_color ? OSMO_LOGCOLOR_END : "", - c_subsys ? c_subsys : ""); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - if (target->print_level) { - ret = snprintf(buf + offset, rem, "%s%s%s%s ", - target->use_color ? level_color(level) : "", - log_level_str(level), - target->use_color ? OSMO_LOGCOLOR_END : "", - c_subsys ? c_subsys : ""); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - if (target->print_category_hex) { - ret = snprintf(buf + offset, rem, "<%4.4x> ", subsys); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - - if (target->print_filename_pos == LOG_FILENAME_POS_HEADER_END) { - switch (target->print_filename2) { - case LOG_FILENAME_NONE: - break; - case LOG_FILENAME_PATH: - ret = snprintf(buf + offset, rem, "%s:%d ", file, line); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - break; - case LOG_FILENAME_BASENAME: - ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - break; - } - } - } - ret = vsnprintf(buf + offset, rem, format, ap); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - /* For LOG_FILENAME_POS_LAST, print the source file info only when the caller ended the log - * message in '\n'. If so, nip the last '\n' away, insert the source file info and re-append an - * '\n'. All this to allow LOGP("start..."); LOGPC("...end\n") constructs. */ - if (target->print_filename_pos == LOG_FILENAME_POS_LINE_END - && offset > 0 && bufoffset - 1 == '\n') { - switch (target->print_filename2) { - case LOG_FILENAME_NONE: - break; - case LOG_FILENAME_PATH: - offset--; - len--; - ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - break; - case LOG_FILENAME_BASENAME: - offset--; - len--; - ret = snprintf(buf + offset, rem, " (%s:%d)\n", const_basename(file), line); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - break; - } - } - - if (target->use_color && c_subsys) { - ret = snprintf(buf + offset, rem, OSMO_LOGCOLOR_END); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } -err: - len = OSMO_MIN(buf_len - 1, len); - buflen = '\0'; - return len; -} - -/* Format the log line for given target; use a stack buffer and call target->output */ -static void _output(struct log_target *target, unsigned int subsys, - unsigned int level, const char *file, int line, int cont, - const char *format, va_list ap) -{ - char bufMAX_LOG_SIZE; - int rc; - - rc = _output_buf(buf, sizeof(buf), target, subsys, level, file, line, cont, format, ap); - if (rc > 0) - target->output(target, level, buf); -} - -/* Catch internal logging category indexes as well as out-of-bounds indexes. - * For internal categories, the ID is negative starting with -1; and internal - * logging categories are added behind the user categories. For out-of-bounds - * indexes, return the index of DLGLOBAL. The returned category index is - * guaranteed to exist in osmo_log_info, otherwise the program would abort, - * which should never happen unless even the DLGLOBAL category is missing. */ -static inline int map_subsys(int subsys) -{ - /* Note: comparing signed and unsigned integers */ - - if (subsys > 0 && ((unsigned int)subsys) >= osmo_log_info->num_cat_user) - subsys = DLGLOBAL; - - if (subsys < 0) - subsys = subsys_lib2index(subsys); - - if (subsys < 0 || subsys >= osmo_log_info->num_cat) - subsys = subsys_lib2index(DLGLOBAL); - - OSMO_ASSERT(!(subsys < 0 || subsys >= osmo_log_info->num_cat)); - - return subsys; -} - -static inline bool should_log_to_target(struct log_target *tar, int subsys, - int level) -{ - struct log_category *category; - - category = &tar->categoriessubsys; - - /* subsystem is not supposed to be logged */ - if (!category->enabled) - return false; - - /* Check the global log level */ - if (tar->loglevel != 0 && level < tar->loglevel) - return false; - - /* Check the category log level */ - if (tar->loglevel == 0 && category->loglevel != 0 && - level < category->loglevel) - return false; - - /* Apply filters here... if that becomes messy we will - * need to put filters in a list and each filter will - * say stop, continue, output */ - if ((tar->filter_map & (1 << LOG_FLT_ALL)) != 0) - return true; - - if (osmo_log_info->filter_fn) - return osmo_log_info->filter_fn(&log_context, tar); - - /* TODO: Check the filter/selector too? */ - return true; -} - -/*! vararg version of logging function - * \paramin subsys Logging sub-system - * \paramin level Log level - * \paramin file name of source code file - * \paramin cont continuation (1) or new line (0) - * \paramin format format string - * \paramin ap vararg-list containing format string arguments - */ -void osmo_vlogp(int subsys, int level, const char *file, int line, - int cont, const char *format, va_list ap) -{ - struct log_target *tar; - - subsys = map_subsys(subsys); - - log_tgt_mutex_lock(); - - llist_for_each_entry(tar, &osmo_log_target_list, entry) { - va_list bp; - - if (!should_log_to_target(tar, subsys, level)) - continue; - - /* According to the manpage, vsnprintf leaves the value of ap - * in undefined state. Since _output uses vsnprintf and it may - * be called several times, we have to pass a copy of ap. */ - va_copy(bp, ap); - if (tar->raw_output) - tar->raw_output(tar, subsys, level, file, line, cont, format, bp); - else - _output(tar, subsys, level, file, line, cont, format, bp); - va_end(bp); - } - - log_tgt_mutex_unlock(); -} - -/*! logging function used by DEBUGP() macro - * \paramin subsys Logging sub-system - * \paramin file name of source code file - * \paramin cont continuation (1) or new line (0) - * \paramin format format string - */ -void logp(int subsys, const char *file, int line, int cont, - const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap); - va_end(ap); -} - -/*! logging function used by LOGP() macro - * \paramin subsys Logging sub-system - * \paramin level Log level - * \paramin file name of source code file - * \paramin cont continuation (1) or new line (0) - * \paramin format format string - */ -void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...) -{ - va_list ap; - - TRACE(LIBOSMOCORE_LOG_START()); - va_start(ap, format); - osmo_vlogp(subsys, level, file, line, cont, format, ap); - va_end(ap); - TRACE(LIBOSMOCORE_LOG_DONE()); -} - -/* This logging function is used as a fallback when the logging framework is - * not is not properly initialized. */ -void logp_stub(const char *file, int line, int cont, const char *format, ...) -{ - va_list ap; - if (!cont) - fprintf(stderr, "%s:%d ", file, line); - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); -} - -/*! Register a new log target with the logging core - * \paramin target Log target to be registered - */ -void log_add_target(struct log_target *target) -{ - llist_add_tail(&target->entry, &osmo_log_target_list); -} - -/*! Unregister a log target from the logging core - * \paramin target Log target to be unregistered - */ -void log_del_target(struct log_target *target) -{ - llist_del(&target->entry); -} - -/*! Reset (clear) the logging context */ -void log_reset_context(void) -{ - memset(&log_context, 0, sizeof(log_context)); -} - -/*! Set the logging context - * \paramin ctx_nr logging context number - * \paramin value value to which the context is to be set - * \returns 0 in case of success; negative otherwise - * - * A logging context is something like the subscriber identity to which - * the currently processed message relates, or the BTS through which it - * was received. As soon as this data is known, it can be set using - * this function. The main use of context information is for logging - * filters. - */ -int log_set_context(uint8_t ctx_nr, void *value) -{ - if (ctx_nr > LOG_MAX_CTX) - return -EINVAL; - - log_context.ctxctx_nr = value; - - return 0; -} - -/*! Enable the \ref LOG_FLT_ALL log filter - * \paramin target Log target to be affected - * \paramin all enable (1) or disable (0) the ALL filter - * - * When the \ref LOG_FLT_ALL filter is enabled, all log messages will be - * printed. It acts as a wildcard. Setting it to \a 1 means there is no - * filtering. - */ -void log_set_all_filter(struct log_target *target, int all) -{ - if (all) - target->filter_map |= (1 << LOG_FLT_ALL); - else - target->filter_map &= ~(1 << LOG_FLT_ALL); -} - -/*! Enable or disable the use of colored output - * \paramin target Log target to be affected - * \paramin use_color Use color (1) or don't use color (0) - */ -void log_set_use_color(struct log_target *target, int use_color) -{ - target->use_color = use_color; -} - -/*! Enable or disable printing of timestamps while logging - * \paramin target Log target to be affected - * \paramin print_timestamp Enable (1) or disable (0) timestamps - */ -void log_set_print_timestamp(struct log_target *target, int print_timestamp) -{ - target->print_timestamp = print_timestamp; -} - -/*! Enable or disable printing of extended timestamps while logging - * \paramin target Log target to be affected - * \paramin print_timestamp Enable (1) or disable (0) timestamps - * - * When both timestamp and extended timestamp is enabled then only - * the extended timestamp will be used. The format of the timestamp - * is YYYYMMDDhhmmssnnn. - */ -void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp) -{ - target->print_ext_timestamp = print_timestamp; -} - -/*! Enable or disable printing of timestamps while logging - * \paramin target Log target to be affected - * \paramin print_tid Enable (1) or disable (0) Thread ID logging - */ -void log_set_print_tid(struct log_target *target, int print_tid) -{ - target->print_tid = print_tid; -} - -/*! Use log_set_print_filename2() instead. - * Call log_set_print_filename2() with LOG_FILENAME_PATH or LOG_FILENAME_NONE, *as well as* call - * log_set_print_category_hex() with the argument passed to this function. This is to mirror legacy - * behavior, which combined the category in hex with the filename. For example, if the category-hex - * output were no longer affected by log_set_print_filename(), many unit tests (in libosmocore as well as - * dependent projects) would fail since they expect the category to disappear along with the filename. - * \paramin target Log target to be affected - * \paramin print_filename Enable (1) or disable (0) filenames - */ -void log_set_print_filename(struct log_target *target, int print_filename) -{ - log_set_print_filename2(target, print_filename ? LOG_FILENAME_PATH : LOG_FILENAME_NONE); - log_set_print_category_hex(target, print_filename); -} - -/*! Enable or disable printing of the filename while logging. - * \paramin target Log target to be affected. - * \paramin lft An LOG_FILENAME_* enum value. - * LOG_FILENAME_NONE omits the source file and line information from logs. - * LOG_FILENAME_PATH prints the entire source file path as passed to LOGP macros. - */ -void log_set_print_filename2(struct log_target *target, enum log_filename_type lft) -{ - target->print_filename2 = lft; -} - -/*! Set the position where on a log line the source file info should be logged. - * \paramin target Log target to be affected. - * \paramin pos A LOG_FILENAME_POS_* enum value. - * LOG_FILENAME_POS_DEFAULT logs just before the caller supplied log message. - * LOG_FILENAME_POS_LAST logs only at the end of a log line, where the caller issued an '\n' to end the - */ -void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos) -{ - target->print_filename_pos = pos; -} - -/*! Enable or disable printing of the category name - * \paramin target Log target to be affected - * \paramin print_category Enable (1) or disable (0) filenames - * - * Print the category/subsys name in front of every log message. - */ -void log_set_print_category(struct log_target *target, int print_category) -{ - target->print_category = print_category; -} - -/*! Enable or disable printing of the category number in hex ('<000b>'). - * \paramin target Log target to be affected. - * \paramin print_category_hex Enable (1) or disable (0) hex category. - */ -void log_set_print_category_hex(struct log_target *target, int print_category_hex) -{ - target->print_category_hex = print_category_hex; -} - -/*! Enable or disable printing of the log level name. - * \paramin target Log target to be affected - * \paramin print_level Enable (1) or disable (0) log level name - * - * Print the log level name in front of every log message. - */ -void log_set_print_level(struct log_target *target, int print_level) -{ - target->print_level = (bool)print_level; -} - -/*! Set the global log level for a given log target - * \paramin target Log target to be affected - * \paramin log_level New global log level - */ -void log_set_log_level(struct log_target *target, int log_level) -{ - target->loglevel = log_level; -} - -/*! Set a category filter on a given log target - * \paramin target Log target to be affected - * \paramin category Log category to be affected - * \paramin enable whether to enable or disable the filter - * \paramin level Log level of the filter - */ -void log_set_category_filter(struct log_target *target, int category, - int enable, int level) -{ - if (!target) - return; - category = map_subsys(category); - target->categoriescategory.enabled = !!enable; - target->categoriescategory.loglevel = level; -} - -#if (!EMBEDDED) -/* write-queue tells us we should write another msgb (log line) to the output fd */ -static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc < 0) - return rc; - if (rc != msgb_length(msg)) { - /* pull the number of bytes we have already written */ - msgb_pull(msg, rc); - /* ask write_queue to re-insert the msgb at the head of the queue */ - return -EAGAIN; - } - return 0; -} - -/* output via buffered, blocking stdio streams */ -static void _file_output_stream(struct log_target *target, unsigned int level, - const char *log) -{ - OSMO_ASSERT(target->tgt_file.out); - fputs(log, target->tgt_file.out); - fflush(target->tgt_file.out); -} - -/* output via non-blocking write_queue, doing internal buffering */ -static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file, - int line, int cont, const char *format, va_list ap) -{ - struct msgb *msg; - int rc; - - OSMO_ASSERT(target->tgt_file.wqueue); - msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg"); - if (!msg) - return; - - /* we simply enqueue the log message to a write queue here, to avoid any blocking - * writes on the output file. The write queue will tell us once the file is writable - * and call _file_wq_write_cb() */ - rc = _output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap); - msgb_put(msg, rc); - - /* attempt a synchronous, non-blocking write, if the write queue is empty */ - if (target->tgt_file.wqueue->current_length == 0) { - rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg); - if (rc == 0) { - /* the write was complete, we can exit early */ - msgb_free(msg); - return; - } - } - /* if we reach here, either we already had elements in the write_queue, or the synchronous write - * failed: enqueue the message to the write_queue (backlog) */ - if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) { - msgb_free(msg); - /* TODO: increment some counter so we can see that messages were dropped */ - } -} -#endif - -/*! Create a new log target skeleton - * \returns dynamically-allocated log target - * This funcition allocates a \ref log_target and initializes it - * with some default values. The newly created target is not - * registered yet. - */ -struct log_target *log_target_create(void) -{ - struct log_target *target; - unsigned int i; - - assert_loginfo(__func__); - - target = talloc_zero(tall_log_ctx, struct log_target); - if (!target) - return NULL; - - target->categories = talloc_zero_array(target, - struct log_category, - osmo_log_info->num_cat); - if (!target->categories) { - talloc_free(target); - return NULL; - } - - INIT_LLIST_HEAD(&target->entry); - - /* initialize the per-category enabled/loglevel from defaults */ - for (i = 0; i < osmo_log_info->num_cat; i++) { - struct log_category *cat = &target->categoriesi; - cat->enabled = osmo_log_info->cati.enabled; - cat->loglevel = osmo_log_info->cati.loglevel; - } - - /* global settings */ - target->use_color = 1; - target->print_timestamp = 0; - target->print_tid = 0; - target->print_filename2 = LOG_FILENAME_PATH; - target->print_category_hex = true; - - /* global log level */ - target->loglevel = 0; - return target; -} - -/*! Create the STDERR log target - * \returns dynamically-allocated \ref log_target for STDERR */ -struct log_target *log_target_create_stderr(void) -{ -/* since C89/C99 says stderr is a macro, we can safely do this! */ -#if !EMBEDDED && defined(stderr) - struct log_target *target; - - target = log_target_create(); - if (!target) - return NULL; - - target->type = LOG_TGT_TYPE_STDERR; - target->tgt_file.out = stderr; - target->output = _file_output_stream; - return target; -#else - return NULL; -#endif /* stderr */ -} - -#if (!EMBEDDED) -/*! Create a new file-based log target using buffered, blocking stream output - * \paramin fname File name of the new log file - * \returns Log target in case of success, NULL otherwise - */ -struct log_target *log_target_create_file_stream(const char *fname) -{ - struct log_target *target; - - target = log_target_create(); - if (!target) - return NULL; - - target->type = LOG_TGT_TYPE_FILE; - target->tgt_file.out = fopen(fname, "a"); - if (!target->tgt_file.out) { - log_target_destroy(target); - return NULL; - } - target->output = _file_output_stream; - target->tgt_file.fname = talloc_strdup(target, fname); - - return target; -} - -/*! switch from non-blocking/write-queue to blocking + buffered stream output - * \paramin target log target which we should switch - * \return 0 on success; 1 if already switched before; negative on error - * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. - */ -int log_target_file_switch_to_stream(struct log_target *target) -{ - struct osmo_wqueue *wq; - - if (!target) - return -ENODEV; - - if (target->tgt_file.out) { - /* target has already been switched over */ - return 1; - } - - wq = target->tgt_file.wqueue; - OSMO_ASSERT(wq); - - /* re-open output as stream */ - if (target->type == LOG_TGT_TYPE_STDERR) - target->tgt_file.out = stderr; - else - target->tgt_file.out = fopen(target->tgt_file.fname, "a"); - if (!target->tgt_file.out) { - return -EIO; - } - - /* synchronously write anything left in the queue */ - while (!llist_empty(&wq->msg_queue)) { - struct msgb *msg = msgb_dequeue(&wq->msg_queue); - fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out); - msgb_free(msg); - } - - /* now that everything succeeded, we can finally close the old output fd */ - if (target->type == LOG_TGT_TYPE_FILE) { - osmo_fd_unregister(&wq->bfd); - close(wq->bfd.fd); - } - - /* release the queue itself */ - talloc_free(wq); - target->tgt_file.wqueue = NULL; - target->output = _file_output_stream; - target->raw_output = NULL; - - return 0; -} - -/*! switch from blocking + buffered file output to non-blocking write-queue based output. - * \paramin target log target which we should switch - * \return 0 on success; 1 if already switched before; negative on error - * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. - */ -int log_target_file_switch_to_wqueue(struct log_target *target) -{ - struct osmo_wqueue *wq; - int rc; - - if (!target) - return -ENODEV; - - if (!target->tgt_file.out) { - /* target has already been switched over */ - return 1; - } - - /* we create a ~640kB sized talloc pool within the write-queue to ensure individual - * log lines (stored as msgbs) will not put result in malloc() calls, and also to - * reduce the OOM probability within logging, as the pool is already allocated */ - wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, - LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); - if (!wq) - return -ENOMEM; - osmo_wqueue_init(wq, LOG_WQUEUE_LEN); - - fflush(target->tgt_file.out); - if (target->type == LOG_TGT_TYPE_FILE) { - rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); - if (rc < 0) { - talloc_free(wq); - return -errno; - } - } else { - rc = STDERR_FILENO; - } - wq->bfd.fd = rc; - wq->bfd.when = OSMO_FD_WRITE; - wq->write_cb = _file_wq_write_cb; - - rc = osmo_fd_register(&wq->bfd); - if (rc < 0) { - talloc_free(wq); - return -EIO; - } - target->tgt_file.wqueue = wq; - target->raw_output = _file_raw_output; - target->output = NULL; - - /* now that everything succeeded, we can finally close the old output stream */ - if (target->type == LOG_TGT_TYPE_FILE) - fclose(target->tgt_file.out); - target->tgt_file.out = NULL; - - return 0; -} - -/*! Create a new file-based log target using non-blocking write_queue - * \paramin fname File name of the new log file - * \returns Log target in case of success, NULL otherwise - */ -struct log_target *log_target_create_file(const char *fname) -{ - struct log_target *target; - struct osmo_wqueue *wq; - int rc; - - target = log_target_create(); - if (!target) - return NULL; - - target->type = LOG_TGT_TYPE_FILE; - /* we create a ~640kB sized talloc pool within the write-queue to ensure individual - * log lines (stored as msgbs) will not put result in malloc() calls, and also to - * reduce the OOM probability within logging, as the pool is already allocated */ - wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, - LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); - if (!wq) { - log_target_destroy(target); - return NULL; - } - osmo_wqueue_init(wq, LOG_WQUEUE_LEN); - wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); - if (wq->bfd.fd < 0) { - talloc_free(wq); - log_target_destroy(target); - return NULL; - } - wq->bfd.when = OSMO_FD_WRITE; - wq->write_cb = _file_wq_write_cb; - - rc = osmo_fd_register(&wq->bfd); - if (rc < 0) { - talloc_free(wq); - log_target_destroy(target); - return NULL; - } - - target->tgt_file.wqueue = wq; - target->raw_output = _file_raw_output; - target->tgt_file.fname = talloc_strdup(target, fname); - - return target; -} -#endif - -/*! Find a registered log target - * \paramin type Log target type - * \paramin fname File name - * \returns Log target (if found), NULL otherwise - * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. - */ -struct log_target *log_target_find(enum log_target_type type, const char *fname) -{ - struct log_target *tgt; - - llist_for_each_entry(tgt, &osmo_log_target_list, entry) { - if (tgt->type != type) - continue; - switch (tgt->type) { - case LOG_TGT_TYPE_FILE: - if (!strcmp(fname, tgt->tgt_file.fname)) - return tgt; - break; - case LOG_TGT_TYPE_GSMTAP: - if (!strcmp(fname, tgt->tgt_gsmtap.hostname)) - return tgt; - break; - default: - return tgt; - } - } - return NULL; -} - -/*! Unregister, close and delete a log target - * \paramin target log target to unregister, close and delete */ -void log_target_destroy(struct log_target *target) -{ - /* just in case, to make sure we don't have any references */ - log_del_target(target); - -#if (!EMBEDDED) - struct osmo_wqueue *wq; - switch (target->type) { - case LOG_TGT_TYPE_FILE: - case LOG_TGT_TYPE_STDERR: - if (target->tgt_file.out) { - if (target->type == LOG_TGT_TYPE_FILE) - fclose(target->tgt_file.out); - target->tgt_file.out = NULL; - } - wq = target->tgt_file.wqueue; - if (wq) { - if (wq->bfd.fd >= 0) { - if (target->type == LOG_TGT_TYPE_FILE) - close(wq->bfd.fd); - wq->bfd.fd = -1; - } - osmo_fd_unregister(&wq->bfd); - osmo_wqueue_clear(wq); - talloc_free(wq); - target->tgt_file.wqueue = NULL; - } - talloc_free((void *)target->tgt_file.fname); - target->tgt_file.fname = NULL; - break; - case LOG_TGT_TYPE_GSMTAP: - gsmtap_source_free(target->tgt_gsmtap.gsmtap_inst); - break; -#ifdef HAVE_SYSLOG_H - case LOG_TGT_TYPE_SYSLOG: - closelog(); - break; -#endif /* HAVE_SYSLOG_H */ - default: - /* make GCC happy */ - break; - } -#endif - - talloc_free(target); -} - -/*! close and re-open a log file (for log file rotation) - * \paramin target log target to re-open - * \returns 0 in case of success; negative otherwise */ -int log_target_file_reopen(struct log_target *target) -{ - struct osmo_wqueue *wq; - int rc; - - OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR); - OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue); - - if (target->tgt_file.out) { - fclose(target->tgt_file.out); - target->tgt_file.out = fopen(target->tgt_file.fname, "a"); - if (!target->tgt_file.out) - return -errno; - } else { - wq = target->tgt_file.wqueue; - osmo_fd_unregister(&wq->bfd); - if (wq->bfd.fd >= 0) { - close(wq->bfd.fd); - wq->bfd.fd = -1; - } - - rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); - if (rc < 0) - return -errno; - wq->bfd.fd = rc; - rc = osmo_fd_register(&wq->bfd); - if (rc < 0) - return rc; - } - - return 0; -} - -/*! close and re-open all log files (for log file rotation) - * \returns 0 in case of success; negative otherwise */ -int log_targets_reopen(void) -{ - struct log_target *tar; - int rc = 0; - - log_tgt_mutex_lock(); - - llist_for_each_entry(tar, &osmo_log_target_list, entry) { - switch (tar->type) { - case LOG_TGT_TYPE_FILE: - if (log_target_file_reopen(tar) < 0) - rc = -1; - break; - default: - break; - } - } - - log_tgt_mutex_unlock(); - - return rc; -} - -/*! Initialize the Osmocom logging core - * \paramin inf Information regarding logging categories, could be NULL - * \paramin ctx talloc context for logging allocations - * \returns 0 in case of success, negative in case of error - * - * If inf is NULL then only library-internal categories are initialized. - */ -int log_init(const struct log_info *inf, void *ctx) -{ - int i; - struct log_info_cat *cat_ptr; - - /* Ensure that log_init is not called multiple times */ - OSMO_ASSERT(tall_log_ctx == NULL) - - tall_log_ctx = talloc_named_const(ctx, 1, "logging"); - if (!tall_log_ctx) - return -ENOMEM; - - osmo_log_info = talloc_zero(tall_log_ctx, struct log_info); - if (!osmo_log_info) - return -ENOMEM; - - osmo_log_info->num_cat = ARRAY_SIZE(internal_cat); - - if (inf) { - osmo_log_info->filter_fn = inf->filter_fn; - osmo_log_info->num_cat_user = inf->num_cat; - osmo_log_info->num_cat += inf->num_cat; - } - - cat_ptr = talloc_zero_array(osmo_log_info, struct log_info_cat, - osmo_log_info->num_cat); - if (!cat_ptr) { - talloc_free(osmo_log_info); - osmo_log_info = NULL; - return -ENOMEM; - } - - /* copy over the user part and sanitize loglevel */ - if (inf) { - for (i = 0; i < inf->num_cat; i++) { - memcpy(&cat_ptri, &inf->cati, - sizeof(struct log_info_cat)); - - /* Make sure that the loglevel is set to NOTICE in case - * no loglevel has been preset. */ - if (!cat_ptri.loglevel) { - cat_ptri.loglevel = LOGL_NOTICE; - } - } - } - - /* copy over the library part */ - for (i = 0; i < ARRAY_SIZE(internal_cat); i++) { - unsigned int cn = osmo_log_info->num_cat_user + i; - memcpy(&cat_ptrcn, &internal_cati, sizeof(struct log_info_cat)); - } - - osmo_log_info->cat = cat_ptr; - - return 0; -} - -/* De-initialize the Osmocom logging core - * This function destroys all targets and releases associated memory */ -void log_fini(void) -{ - struct log_target *tar, *tar2; - - log_tgt_mutex_lock(); - - llist_for_each_entry_safe(tar, tar2, &osmo_log_target_list, entry) - log_target_destroy(tar); - - talloc_free(osmo_log_info); - osmo_log_info = NULL; - talloc_free(tall_log_ctx); - tall_log_ctx = NULL; - - log_tgt_mutex_unlock(); -} - -/*! Check whether a log entry will be generated. - * \returns != 0 if a log entry might get generated by at least one target */ -int log_check_level(int subsys, unsigned int level) -{ - struct log_target *tar; - - assert_loginfo(__func__); - - subsys = map_subsys(subsys); - - /* TODO: The following could/should be cached (update on config) */ - - log_tgt_mutex_lock(); - - llist_for_each_entry(tar, &osmo_log_target_list, entry) { - if (!should_log_to_target(tar, subsys, level)) - continue; - - /* This might get logged (ignoring filters) */ - log_tgt_mutex_unlock(); - return 1; - } - - /* We are sure, that this will not be logged. */ - log_tgt_mutex_unlock(); - return 0; -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/logging_gsmtap.c
Deleted
@@ -1,161 +0,0 @@ -/*! \file logging_gsmtap.c - * libosmocore log output encapsulated in GSMTAP. - * - * Encapsulating the log output inside GSMTAP frames allows us to - * observer protocol traces (of Um, Abis, A or any other interface in - * the Osmocom world) with synchronous interspersed log messages. - */ -/* - * (C) 2016 by Harald Welte <laforge@gnumonks.org> - * 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. - * - */ - -/*! \addtogroup logging - * @{ - * \file logging_gsmtap.c */ - -#include "../config.h" - -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdbool.h> -#include <unistd.h> - -#ifdef HAVE_STRINGS_H -#include <strings.h> -#endif - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/gsmtap.h> -#include <osmocom/core/gsmtap_util.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/byteswap.h> -#include <osmocom/core/thread.h> - -#define GSMTAP_LOG_MAX_SIZE 4096 - -static __thread uint32_t logging_gsmtap_tid; - -static void _gsmtap_raw_output(struct log_target *target, int subsys, - unsigned int level, const char *file, - int line, int cont, const char *format, - va_list ap) -{ - struct msgb *msg; - struct gsmtap_hdr *gh; - struct gsmtap_osmocore_log_hdr *golh; - const char *subsys_name = log_category_name(subsys); - struct timeval tv; - int rc; - const char *file_basename; - - /* get timestamp ASAP */ - osmo_gettimeofday(&tv, NULL); - - msg = msgb_alloc(sizeof(*gh)+sizeof(*golh)+GSMTAP_LOG_MAX_SIZE, - "GSMTAP logging"); - - /* GSMTAP header */ - gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); - memset(gh, 0, sizeof(*gh)); - gh->version = GSMTAP_VERSION; - gh->hdr_len = sizeof(*gh)/4; - gh->type = GSMTAP_TYPE_OSMOCORE_LOG; - - /* Logging header */ - golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh)); - OSMO_STRLCPY_ARRAY(golh->proc_name, target->tgt_gsmtap.ident); - if (logging_gsmtap_tid == 0) - osmo_store32be((uint32_t)osmo_gettid(), &logging_gsmtap_tid); - golh->pid = logging_gsmtap_tid; - if (subsys_name) - OSMO_STRLCPY_ARRAY(golh->subsys, subsys_name + 1); - else - golh->subsys0 = '\0'; - - /* strip all leading path elements from file, if any. */ - file_basename = strrchr(file, '/'); - file = (file_basename && file_basename1)? file_basename + 1 : file; - OSMO_STRLCPY_ARRAY(golh->src_file.name, file); - golh->src_file.line_nr = osmo_htonl(line); - golh->level = level; - /* we always store the timestamp in the message, irrespective - * of hat prrint_ext_timestamp say */ - golh->ts.sec = osmo_htonl(tv.tv_sec); - golh->ts.usec = osmo_htonl(tv.tv_usec); - - rc = vsnprintf((char *) msg->tail, msgb_tailroom(msg), format, ap); - if (rc < 0) { - msgb_free(msg); - return; - } else if (rc >= msgb_tailroom(msg)) { - /* If the output was truncated, vsnprintf() returns the - * number of characters which would have been written - * if enough space had been available (excluding '\0'). */ - rc = msgb_tailroom(msg); - msg->tailrc - 1 = '\0'; - } - msgb_put(msg, rc); - - rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg); - if (rc) - msgb_free(msg); -} - -/*! Create a new logging target for GSMTAP logging - * \paramin host remote host to send the logs to - * \paramin port remote port to send the logs to - * \paramin ident string identifier - * \paramin ofd_wq_mode register osmo_wqueue (1) or not (0) - * \paramin add_sink add GSMTAP sink or not - * \returns Log target in case of success, NULL in case of error - */ -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 *target; - struct gsmtap_inst *gti; - - target = log_target_create(); - if (!target) - return NULL; - - gti = gsmtap_source_init(host, port, ofd_wq_mode); - if (!gti) { - log_target_destroy(target); - return NULL; - } - - if (add_sink) - gsmtap_source_add_sink(gti); - - target->tgt_gsmtap.gsmtap_inst = gti; - target->tgt_gsmtap.ident = talloc_strdup(target, ident); - target->tgt_gsmtap.hostname = talloc_strdup(target, host); - - target->type = LOG_TGT_TYPE_GSMTAP; - target->raw_output = _gsmtap_raw_output; - - return target; -} - -/* @} */
View file
libosmocore_1.7.0.tar.xz/src/logging_syslog.c
Deleted
@@ -1,89 +0,0 @@ -/*! \file logging_syslog.c - * Syslog logging support code. */ -/* - * (C) 2011 by Harald Welte <laforge@gnumonks.org> - * 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. - * - */ - -/*! \addtogroup logging - * @{ - * \file logging_syslog.c */ - -#include "../config.h" - -#ifdef HAVE_SYSLOG_H - -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <syslog.h> - -#ifdef HAVE_STRINGS_H -#include <strings.h> -#endif - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/logging.h> - -static int logp2syslog_level(unsigned int level) -{ - if (level >= LOGL_FATAL) - return LOG_CRIT; - else if (level >= LOGL_ERROR) - return LOG_ERR; - else if (level >= LOGL_NOTICE) - return LOG_NOTICE; - else if (level >= LOGL_INFO) - return LOG_INFO; - else - return LOG_DEBUG; -} - -static void _syslog_output(struct log_target *target, - unsigned int level, const char *log) -{ - syslog(logp2syslog_level(level), "%s", log); -} - -/*! Create a new logging target for syslog logging - * \paramin ident syslog string identifier - * \paramin option syslog options - * \paramin facility syslog facility - * \returns Log target in case of success, NULL in case of error - */ -struct log_target *log_target_create_syslog(const char *ident, int option, - int facility) -{ - struct log_target *target; - - target = log_target_create(); - if (!target) - return NULL; - - target->tgt_syslog.facility = facility; - target->type = LOG_TGT_TYPE_SYSLOG; - target->output = _syslog_output; - - openlog(ident, option, facility); - - return target; -} - -#endif /* HAVE_SYSLOG_H */ - -/* @} */
View file
libosmocore_1.7.0.tar.xz/src/mnl.c
Deleted
@@ -1,111 +0,0 @@ -/*! \file mnl.c - * - * This code integrates libmnl (minimal netlink library) into the osmocom select - * loop abstraction. It allows other osmocom libraries or application code to - * create netlink sockets and subscribe to netlink events via libmnl. The completion - * handler / callbacks are dispatched via libosmocore select loop handling. - */ - -/* - * (C) 2020 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserverd. - * - * 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 <osmocom/core/select.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/mnl.h> - -#include <libmnl/libmnl.h> - -#include <errno.h> -#include <string.h> - -/* osmo_fd call-back for when RTNL socket is readable */ -static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - uint8_t bufMNL_SOCKET_BUFFER_SIZE; - struct osmo_mnl *omnl = ofd->data; - int rc; - - if (!(what & OSMO_FD_READ)) - return 0; - - rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf)); - if (rc <= 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n", - strerror(errno)); - return -EIO; - } - - return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl); -} - -/*! create an osmocom-wrapped limnl netlink socket. - * \parmain ctx talloc context from which to allocate - * \paramin bus netlink socket bus ID (see NETLINK_* constants) - * \paramin groups groups of messages to bind/subscribe to - * \paramin mnl_cb callback function called for each incoming message - * \paramin priv opaque private user data - * \returns newly-allocated osmo_mnl or NULL in case of error. */ -struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv) -{ - struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl); - - if (!olm) - return NULL; - - olm->priv = priv; - olm->mnl_cb = mnl_cb; - olm->mnls = mnl_socket_open(bus); - if (!olm->mnls) { - LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n", - bus, strerror(errno)); - goto out_free; - } - - if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n", - bus, groups, strerror(errno)); - goto out_close; - } - - osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0); - - if (osmo_fd_register(&olm->ofd)) { - LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n"); - goto out_close; - } - - return olm; - -out_close: - mnl_socket_close(olm->mnls); -out_free: - talloc_free(olm); - return NULL; -} - -/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free. - * \paramin omnl osmo_mnl socket previously returned by osmo_mnl_init() */ -void osmo_mnl_destroy(struct osmo_mnl *omnl) -{ - if (!omnl) - return; - - osmo_fd_unregister(&omnl->ofd); - mnl_socket_close(omnl->mnls); - talloc_free(omnl); -}
View file
libosmocore_1.7.0.tar.xz/src/msgb.c
Deleted
@@ -1,573 +0,0 @@ -/* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * 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. - * - */ - -/*! \addtogroup msgb - * @{ - * - * libosmocore message buffers, inspired by Linux kernel skbuff - * - * Inspired by the 'struct skbuff' of the Linux kernel, we implement a - * 'struct msgb' which we use for handling network - * packets aka messages aka PDUs. - * - * A msgb consists of - * * a header with some metadata, such as - * * a linked list header for message queues or the like - * * pointers to the headers of various protocol layers inside - * the packet - * * a data section consisting of - * * headroom, i.e. space in front of the message, to allow - * for additional headers being pushed in front of the current - * data - * * the currently occupied data for the message - * * tailroom, i.e. space at the end of the message, to - * allow more data to be added after the end of the current - * data - * - * We have plenty of utility functions around the \ref msgb: - * * allocation / release - * * enqueue / dequeue from/to message queues - * * prepending (pushing) and appending (putting) data - * * copying / resizing - * * hex-dumping to a string for debug purposes - * - * \file msgb.c - */ - -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <inttypes.h> -#include <stdarg.h> -#include <errno.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/logging.h> - -/*! Allocate a new message buffer from given talloc context - * \paramin ctx talloc context from which to allocate - * \paramin size Length in octets, including headroom - * \paramin name Human-readable name to be associated with msgb - * \returns dynamically-allocated \ref msgb - * - * This function allocates a 'struct msgb' as well as the underlying - * memory buffer for the actual message data (size specified by \a size) - * using the talloc memory context previously set by \ref msgb_set_talloc_ctx - */ -struct msgb *msgb_alloc_c(const void *ctx, uint16_t size, const char *name) -{ - struct msgb *msg; - - msg = talloc_named_const(ctx, sizeof(*msg) + size, name); - if (!msg) { - LOGP(DLGLOBAL, LOGL_FATAL, "Unable to allocate a msgb: " - "name='%s', size=%u\n", name, size); - return NULL; - } - - /* Manually zero-initialize allocated memory */ - memset(msg, 0x00, sizeof(*msg) + size); - - msg->data_len = size; - msg->len = 0; - msg->data = msg->_data; - msg->head = msg->_data; - msg->tail = msg->_data; - - return msg; -} - -/* default msgb allocation context for msgb_alloc() */ -void *tall_msgb_ctx = NULL; - -/*! Allocate a new message buffer from tall_msgb_ctx - * \paramin size Length in octets, including headroom - * \paramin name Human-readable name to be associated with msgb - * \returns dynamically-allocated \ref msgb - * - * This function allocates a 'struct msgb' as well as the underlying - * memory buffer for the actual message data (size specified by \a size) - * using the talloc memory context previously set by \ref msgb_set_talloc_ctx - */ -struct msgb *msgb_alloc(uint16_t size, const char *name) -{ - return msgb_alloc_c(tall_msgb_ctx, size, name); -} - - -/*! Release given message buffer - * \paramin m Message buffer to be freed - */ -void msgb_free(struct msgb *m) -{ - talloc_free(m); -} - -/*! Enqueue message buffer to tail of a queue - * \paramin queue linked list header of queue - * \paramin msg message buffer to be added to the queue - * - * The function will append the specified message buffer \a msg to the - * queue implemented by \ref llist_head \a queue - */ -void msgb_enqueue(struct llist_head *queue, struct msgb *msg) -{ - llist_add_tail(&msg->list, queue); -} - -/*! Dequeue message buffer from head of queue - * \paramin queue linked list header of queue - * \returns message buffer (if any) or NULL if queue empty - * - * The function will remove the first message buffer from the queue - * implemented by \ref llist_head \a queue. - */ -struct msgb *msgb_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - - if (lh) { - llist_del(lh); - return llist_entry(lh, struct msgb, list); - } else - return NULL; -} - -/*! Re-set all message buffer pointers - * \paramin msg message buffer that is to be resetted - * - * This will re-set the various internal pointers into the underlying - * message buffer, i.e. remove all headroom and treat the msgb as - * completely empty. It also initializes the control buffer to zero. - */ -void msgb_reset(struct msgb *msg) -{ - msg->len = 0; - msg->data = msg->_data; - msg->head = msg->_data; - msg->tail = msg->_data; - - msg->trx = NULL; - msg->lchan = NULL; - msg->l2h = NULL; - msg->l3h = NULL; - msg->l4h = NULL; - - memset(&msg->cb, 0, sizeof(msg->cb)); -} - -/*! get pointer to data section of message buffer - * \paramin msg message buffer - * \returns pointer to data section of message buffer - */ -uint8_t *msgb_data(const struct msgb *msg) -{ - return msg->data; -} - -/*! Compare and print: check data in msgb against given data and print errors if any - * \paramin file text prefix, usually __FILE__, ignored if print == false - * \paramin line numeric prefix, usually __LINE__, ignored if print == false - * \paramin func text prefix, usually __func__, ignored if print == false - * \paramin level while layer (L1, L2 etc) data should be compared against - * \paramin msg message buffer - * \paramin data expected data - * \paramin len length of data - * \paramin print boolean indicating whether we should print anything to stdout - * \returns boolean indicating whether msgb content is equal to a given data - * - * This function is not intended to be called directly but rather used through corresponding macro wrappers. - */ -bool _msgb_eq(const char *file, size_t line, const char *func, uint8_t level, - const struct msgb *msg, const uint8_t *data, size_t len, bool print) -{ - const char *m_dump; - unsigned int m_len, i; - uint8_t *m_data; - - if (!msg) { - if (print) - LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, "%s() NULL msg comparison\n", func); - return false; - } - - if (!data) { - if (print) - LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, "%s() NULL comparison data\n", func); - return false; - } - - switch (level) { - case 0: - m_len = msgb_length(msg); - m_data = msgb_data(msg); - m_dump = print ? msgb_hexdump(msg) : NULL; - break; - case 1: - m_len = msgb_l1len(msg); - m_data = msgb_l1(msg); - m_dump = print ? msgb_hexdump_l1(msg) : NULL; - break; - case 2: - m_len = msgb_l2len(msg); - m_data = msgb_l2(msg); - m_dump = print ? msgb_hexdump_l2(msg) : NULL; - break; - case 3: - m_len = msgb_l3len(msg); - m_data = msgb_l3(msg); - m_dump = print ? msgb_hexdump_l3(msg) : NULL; - break; - case 4: - m_len = msgb_l4len(msg); - m_data = msgb_l4(msg); - m_dump = print ? msgb_hexdump_l4(msg) : NULL; - break; - default: - LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, - "%s() FIXME: unexpected comparison level %u\n", func, level); - return false; - } - - if (m_len != len) { - if (print) - LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, - "%s() Length mismatch: %d != %zu, %s\n", func, m_len, len, m_dump); - return false; - } - - if (memcmp(m_data, data, len) == 0) - return true; - - if (!print) - return false; - - LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, - "%s() L%u data mismatch:\nexpected %s\n ", func, level, osmo_hexdump(data, len)); - - for(i = 0; i < len; i++) - if (datai != m_datai) { - LOGPC(DLGLOBAL, LOGL_FATAL, "!!\n"); - break; - } else - LOGPC(DLGLOBAL, LOGL_FATAL, ".. "); - - LOGPC(DLGLOBAL, LOGL_FATAL, " msgb %s\n", osmo_hexdump(m_data, len)); - - return false; -} - -/*! get length of message buffer - * \paramin msg message buffer - * \returns length of data section in message buffer - */ -uint16_t msgb_length(const struct msgb *msg) -{ - return msg->len; -} - -/*! Set the talloc context for \ref msgb_alloc - * Deprecated, use msgb_talloc_ctx_init() instead. - * \paramin ctx talloc context to be used as root for msgb allocations - */ -void msgb_set_talloc_ctx(void *ctx) -{ - tall_msgb_ctx = ctx; -} - -/*! Initialize a msgb talloc context for \ref msgb_alloc. - * Create a talloc context called "msgb". If \a pool_size is 0, create a named - * const as msgb talloc context. If \a pool_size is nonzero, create a talloc - * pool, possibly for faster msgb allocations (see talloc_pool()). - * \paramin root_ctx talloc context used as parent for the new "msgb" ctx. - * \paramin pool_size if nonzero, create a talloc pool of this size. - * \returns the new msgb talloc context, e.g. for reporting - */ -void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size) -{ - if (!pool_size) - tall_msgb_ctx = talloc_size(root_ctx, 0); - else - tall_msgb_ctx = talloc_pool(root_ctx, pool_size); - talloc_set_name_const(tall_msgb_ctx, "msgb"); - return tall_msgb_ctx; -} - -/*! Copy an msgb. - * - * This function allocates a new msgb, copies the data buffer of msg, - * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part - * is not copied. - * \paramin msg The old msgb object - * \paramin name Human-readable name to be associated with msgb - */ -struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name) -{ - struct msgb *new_msg; - - new_msg = msgb_alloc_c(ctx, msg->data_len, name); - if (!new_msg) - return NULL; - - /* copy data */ - memcpy(new_msg->_data, msg->_data, new_msg->data_len); - - /* copy header */ - new_msg->len = msg->len; - new_msg->data += msg->data - msg->_data; - new_msg->head += msg->head - msg->_data; - new_msg->tail += msg->tail - msg->_data; - - if (msg->l1h) - new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); - if (msg->l2h) - new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); - if (msg->l3h) - new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); - if (msg->l4h) - new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data); - - return new_msg; -} - -/*! Copy an msgb. - * - * This function allocates a new msgb, copies the data buffer of msg, - * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part - * is not copied. - * \paramin msg The old msgb object - * \paramin name Human-readable name to be associated with msgb - */ -struct msgb *msgb_copy(const struct msgb *msg, const char *name) -{ - return msgb_copy_c(tall_msgb_ctx, msg, name); -} - -/*! Resize an area within an msgb - * - * This resizes a sub area of the msgb data and adjusts the pointers (incl - * l1h-l4h) accordingly. The cb part is not updated. If the area is extended, - * the contents of the extension is undefined. The complete sub area must be a - * part of data,tail. - * - * \paraminout msg The msgb object - * \paramin area A pointer to the sub-area - * \paramin old_size The old size of the sub-area - * \paramin new_size The new size of the sub-area - * \returns 0 on success, -1 if there is not enough space to extend the area - */ -int msgb_resize_area(struct msgb *msg, uint8_t *area, - int old_size, int new_size) -{ - int rc; - uint8_t *post_start = area + old_size; - int pre_len = area - msg->data; - int post_len = msg->len - old_size - pre_len; - int delta_size = new_size - old_size; - - if (old_size < 0 || new_size < 0) - MSGB_ABORT(msg, "Negative sizes are not allowed\n"); - if (area < msg->data || post_start > msg->tail) - MSGB_ABORT(msg, "Sub area is not fully contained in the msg data\n"); - - if (delta_size == 0) - return 0; - - if (delta_size > 0) { - rc = msgb_trim(msg, msg->len + delta_size); - if (rc < 0) - return rc; - } - - memmove(area + new_size, area + old_size, post_len); - - if (msg->l1h >= post_start) - msg->l1h += delta_size; - if (msg->l2h >= post_start) - msg->l2h += delta_size; - if (msg->l3h >= post_start) - msg->l3h += delta_size; - if (msg->l4h >= post_start) - msg->l4h += delta_size; - - if (delta_size < 0) - msgb_trim(msg, msg->len + delta_size); - - return 0; -} - - -/*! fill user-provided buffer with hexdump of the msg. - * \paramout buf caller-allocated buffer for output string - * \paramin buf_len length of buf - * \paramin msg message buffer to be dumped - * \returns buf - */ -char *msgb_hexdump_buf(char *buf, size_t buf_len, const struct msgb *msg) -{ - unsigned int buf_offs = 0; - int nchars; - const unsigned char *start = msg->data; - const unsigned char *lxhs4; - unsigned int i; - - lxhs0 = msg->l1h; - lxhs1 = msg->l2h; - lxhs2 = msg->l3h; - lxhs3 = msg->l4h; - - for (i = 0; i < ARRAY_SIZE(lxhs); i++) { - if (!lxhsi) - continue; - - if (lxhsi < msg->head) - continue; - if (lxhsi > msg->head + msg->data_len) - continue; - if (lxhsi > msg->tail) - continue; - if (lxhsi < msg->data || lxhsi > msg->tail) { - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "(L%d=data%+" PRIdPTR ") ", - i+1, lxhsi - msg->data); - buf_offs += nchars; - continue; - } - if (lxhsi < start) { - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "(L%d%+" PRIdPTR ") ", i+1, - start - lxhsi); - buf_offs += nchars; - continue; - } - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "%sL%d> ", - osmo_hexdump(start, lxhsi - start), - i+1); - if (nchars < 0 || nchars + buf_offs >= buf_len) - return "ERROR"; - - buf_offs += nchars; - start = lxhsi; - } - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "%s", osmo_hexdump(start, msg->tail - start)); - if (nchars < 0 || nchars + buf_offs >= buf_len) - return "ERROR"; - - buf_offs += nchars; - - for (i = 0; i < ARRAY_SIZE(lxhs); i++) { - if (!lxhsi) - continue; - - if (lxhsi < msg->head || lxhsi > msg->head + msg->data_len) { - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "(L%d out of range) ", i+1); - } else if (lxhsi <= msg->data + msg->data_len && - lxhsi > msg->tail) { - nchars = snprintf(buf + buf_offs, buf_len - buf_offs, - "(L%d=tail%+" PRIdPTR ") ", - i+1, lxhsi - msg->tail); - } else - continue; - - if (nchars < 0 || nchars + buf_offs >= buf_len) - return "ERROR"; - buf_offs += nchars; - } - - return buf; -} - -/*! Return a (static) buffer containing a hexdump of the msg. - * \paramin msg message buffer - * \returns a pointer to a static char array - */ -const char *msgb_hexdump(const struct msgb *msg) -{ - static __thread char buf4100; - return msgb_hexdump_buf(buf, sizeof(buf), msg); -} - -/*! Return a dynamically allocated buffer containing a hexdump of the msg - * \paramin ctx talloc context from where to allocate the output string - * \paramin msg message buffer - * \returns a pointer to a static char array - */ -char *msgb_hexdump_c(const void *ctx, const struct msgb *msg) -{ - size_t buf_len = msgb_length(msg) * 3 + 100; - char *buf = talloc_size(ctx, buf_len); - if (!buf) - return NULL; - return msgb_hexdump_buf(buf, buf_len, msg); -} - -/*! Print a string to the end of message buffer. - * \paramin msgb message buffer. - * \paramin format format string. - * \returns 0 on success, -EINVAL on error. - * - * The resulting string is printed to the msgb without a trailing nul - * character. A nul following the data tail may be written as an implementation - * detail, but a trailing nul is never part of the msgb data in terms of - * msgb_length(). - * - * Note: the tailroom must always be one byte longer than the string to be - * written. The msgb is filled only up to tailroom=1. This is an implementation - * detail that allows leaving a nul character behind the valid data. - * - * In case of error, the msgb remains unchanged, though data may have been - * written to the (unused) memory after the tail pointer. - */ -int msgb_printf(struct msgb *msgb, const char *format, ...) -{ - va_list args; - int str_len; - int rc = 0; - - OSMO_ASSERT(msgb); - OSMO_ASSERT(format); - - /* Regardless of what we plan to add to the buffer, we must at least - * be able to store a string terminator (nullstring) */ - if (msgb_tailroom(msgb) < 1) - return -EINVAL; - - va_start(args, format); - - str_len = - vsnprintf((char *)msgb->tail, msgb_tailroom(msgb), format, args); - - if (str_len >= msgb_tailroom(msgb) || str_len < 0) { - rc = -EINVAL; - } else - msgb_put(msgb, str_len); - - va_end(args); - return rc; -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/panic.c
Deleted
@@ -1,103 +0,0 @@ -/*! \file panic.c - * Routines for panic handling. */ -/* - * (C) 2010 by Sylvain Munaut <tnt@246tNt.com> - * - * 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. - * - */ - -/*! \addtogroup utils - * @{ - * \file panic.c */ - -#include <unistd.h> -#include <osmocom/core/panic.h> -#include <osmocom/core/backtrace.h> - -#include "../config.h" - - -static osmo_panic_handler_t osmo_panic_handler = (void*)0; - - -#ifndef PANIC_INFLOOP - -#include <stdio.h> -#include <stdlib.h> - -static void osmo_panic_default(const char *fmt, va_list args) -{ - vfprintf(stderr, fmt, args); - osmo_generate_backtrace(); - abort(); -} - -#else - -static void osmo_panic_default(const char *fmt, va_list args) -{ - while (1); -} - -#endif - - -/*! Terminate the current program with a panic - * - * You can call this function in case some severely unexpected situation - * is detected and the program is supposed to terminate in a way that - * reports the fact that it terminates. - * - * The application can register a panic handler function using \ref - * osmo_set_panic_handler. If it doesn't, a default panic handler - * function is called automatically. - * - * The default function on most systems will generate a backtrace and - * then abort() the process. - */ -void osmo_panic(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - if (osmo_panic_handler) - osmo_panic_handler(fmt, args); - else - osmo_panic_default(fmt, args); - - va_end(args); - - /* not reached, but make compiler believe we really never return */ -#ifndef PANIC_INFLOOP - exit(2342); -#else - while (1) ; -#endif -} - -/*! Set the panic handler - * \paramin h New panic handler function - * - * This changes the panic handling function from the currently active - * function to a new call-back function supplied by the caller. - */ -void osmo_set_panic_handler(osmo_panic_handler_t h) -{ - osmo_panic_handler = h; -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/plugin.c
Deleted
@@ -1,71 +0,0 @@ -/*! \file plugin.c - * Routines for loading and managing shared library plug-ins. */ -/* - * (C) 2010 by Harald Welte <laforge@gnumonks.org> - * - * 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. - * - */ - -/*! \addtogroup utils - * @{ - * \file plugin.c */ - -#include "../config.h" - -#if HAVE_DLFCN_H - -#include <dirent.h> -#include <dlfcn.h> -#include <stdio.h> -#include <errno.h> -#include <limits.h> - -#include <osmocom/core/plugin.h> - -/*! Load all plugins available in given directory - * \paramin directory full path name of directory containing plug-ins - * \returns number of plugins loaded in case of success, negative in case of error - */ -int osmo_plugin_load_all(const char *directory) -{ - unsigned int num = 0; - char fnamePATH_MAX; - DIR *dir; - struct dirent *entry; - - dir = opendir(directory); - if (!dir) - return -errno; - - while ((entry = readdir(dir))) { - snprintf(fname, sizeof(fname), "%s/%s", directory, - entry->d_name); - if (dlopen(fname, RTLD_NOW)) - num++; - } - - closedir(dir); - - return num; -} -#else -int osmo_plugin_load_all(const char *directory) -{ - return 0; -} -#endif /* HAVE_DLFCN_H */ - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/rate_ctr.c
Deleted
@@ -1,470 +0,0 @@ -/* (C) 2009-2017 by Harald Welte <laforge@gnumonks.org> - * - * 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. - * - */ - -/*! \addtogroup rate_ctr - * @{ - * Counters about events and their event rates. - * - * As \ref osmo_counter and \ref osmo_stat_item are concerned only with - * a single given value that may be increased/decreased, or the difference - * to one given previous value, this module adds some support for keeping - * long term information about a given event rate. - * - * A \ref rate_ctr keeps information on the amount of events per second, - * per minute, per hour and per day. - * - * \ref rate_ctr come in groups: An application describes a group of counters - * with their names and identities once in a (typically const) \ref - * rate_ctr_group_desc. - * - * As objects (such as e.g. a subscriber or a PDP context) are - * allocated dynamically at runtime, the application calls \ref - * rate_ctr_group_alloc with a refernce to the \ref - * rate_ctr_group_desc, which causes the library to allocate one set of - * \ref rate_ctr: One for each in the group. - * - * The application then uses functions like \ref rate_ctr_add or \ref - * rate_ctr_inc to increment the value as certain events (e.g. location - * update) happens. - * - * The library internally keeps a timer once per second which iterates - * over all registered counters and which updates the per-second, - * per-minute, per-hour and per-day averages based on the current - * value. - * - * The counters can be reported using \ref stats or by VTY - * introspection, as well as by any application-specific code accessing - * the \ref rate_ctr.intv array directly. - * - * \file rate_ctr.c */ - -#include <stdbool.h> -#include <stdint.h> -#include <string.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/logging.h> - -static LLIST_HEAD(rate_ctr_groups); - -static void *tall_rate_ctr_ctx; - - -static bool rate_ctrl_group_desc_validate(const struct rate_ctr_group_desc *desc) -{ - unsigned int i; - const struct rate_ctr_desc *ctr_desc; - - if (!desc) { - LOGP(DLGLOBAL, LOGL_ERROR, "NULL is not a valid counter group descriptor\n"); - return false; - } - ctr_desc = desc->ctr_desc; - - DEBUGP(DLGLOBAL, "validating counter group %p(%s) with %u counters\n", desc, - desc->group_name_prefix, desc->num_ctr); - - if (!osmo_identifier_valid(desc->group_name_prefix)) { - LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter group identifier\n", - desc->group_name_prefix); - return false; - } - - for (i = 0; i < desc->num_ctr; i++) { - if (!osmo_identifier_valid(ctr_desci.name)) { - LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter identifier\n", - ctr_desci.name); - return false; - } - } - - return true; -} - -/* return 'in' if it doesn't contain any '.'; otherwise allocate a copy and - * replace all '.' with ':' */ -static char *mangle_identifier_ifneeded(const void *ctx, const char *in) -{ - char *out; - unsigned int i; - bool modified = false; - - if (!in) - return NULL; - - if (!strchr(in, '.')) - return (char *)in; - - out = talloc_strdup(ctx, in); - OSMO_ASSERT(out); - - for (i = 0; i < strlen(out); i++) { - if (outi == '.') { - outi = ':'; - modified = true; - } - } - - if (modified) - LOGP(DLGLOBAL, LOGL_NOTICE, "counter group name mangled: '%s' -> '%s'\n", - in, out); - - return out; -} - -/* "mangle" a rate counter group descriptor, i.e. replace any '.' with ':' */ -static struct rate_ctr_group_desc * -rate_ctr_group_desc_mangle(void *ctx, const struct rate_ctr_group_desc *desc) -{ - struct rate_ctr_group_desc *desc_new = talloc_zero(ctx, struct rate_ctr_group_desc); - int i; - - OSMO_ASSERT(desc_new); - - LOGP(DLGLOBAL, LOGL_INFO, "Needed to mangle counter group '%s' names: it is still using '.' as " - "separator, which is not allowed. please consider updating the application\n", - desc->group_name_prefix); - - /* mangle the name_prefix but copy/keep the rest */ - desc_new->group_name_prefix = mangle_identifier_ifneeded(desc_new, desc->group_name_prefix); - desc_new->group_description = desc->group_description; - desc_new->class_id = desc->class_id; - desc_new->num_ctr = desc->num_ctr; - desc_new->ctr_desc = talloc_array(desc_new, struct rate_ctr_desc, desc_new->num_ctr); - OSMO_ASSERT(desc_new->ctr_desc); - - for (i = 0; i < desc->num_ctr; i++) { - struct rate_ctr_desc *ctrd_new = (struct rate_ctr_desc *) desc_new->ctr_desc; - const struct rate_ctr_desc *ctrd = desc->ctr_desc; - - if (!ctrdi.name) { - LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s'%d == NULL, aborting\n", - desc->group_name_prefix, i); - goto err_free; - } - - ctrd_newi.name = mangle_identifier_ifneeded(desc_new->ctr_desc, ctrdi.name); - ctrd_newi.description = ctrdi.description; - } - - if (!rate_ctrl_group_desc_validate(desc_new)) { - /* simple mangling of identifiers ('.' -> ':') was not sufficient to render a valid - * descriptor, we have to bail out */ - LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' still invalid after mangling\n", - desc->group_name_prefix); - goto err_free; - } - - return desc_new; -err_free: - talloc_free(desc_new); - return NULL; -} - -/*! Find an unused index for this rate counter group. - * \paramin name Name of the counter group - * \returns the largest used index number + 1, or 0 if none exist yet. */ -static unsigned int rate_ctr_get_unused_name_idx(const char *name) -{ - unsigned int idx = 0; - struct rate_ctr_group *ctrg; - - llist_for_each_entry(ctrg, &rate_ctr_groups, list) { - if (!ctrg->desc) - continue; - - if (strcmp(ctrg->desc->group_name_prefix, name)) - continue; - - if (idx <= ctrg->idx) - idx = ctrg->idx + 1; - } - return idx; -} - -/*! Allocate a new group of counters according to description - * \paramin ctx parent talloc context - * \paramin desc Rate counter group description - * \paramin idx Index of new counter group - */ -struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, - const struct rate_ctr_group_desc *desc, - unsigned int idx) -{ - unsigned int size; - struct rate_ctr_group *group; - - if (rate_ctr_get_group_by_name_idx(desc->group_name_prefix, idx)) { - unsigned int new_idx = rate_ctr_get_unused_name_idx(desc->group_name_prefix); - LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' already exists for index %u," - " instead using index %u. This is a software bug that needs fixing.\n", - desc->group_name_prefix, idx, new_idx); - idx = new_idx; - } - - size = sizeof(struct rate_ctr_group) + - desc->num_ctr * sizeof(struct rate_ctr); - - if (!ctx) - ctx = tall_rate_ctr_ctx; - - group = talloc_zero_size(ctx, size); - if (!group) - return NULL; - - /* attempt to mangle all '.' in identifiers to ':' for backwards compat */ - if (!rate_ctrl_group_desc_validate(desc)) { - desc = rate_ctr_group_desc_mangle(group, desc); - if (!desc) { - talloc_free(group); - return NULL; - } - } - - group->desc = desc; - group->idx = idx; - - llist_add(&group->list, &rate_ctr_groups); - - return group; -} - -/*! Free the memory for the specified group of counters */ -void rate_ctr_group_free(struct rate_ctr_group *grp) -{ - if (!grp) - return; - - if (!llist_empty(&grp->list)) - llist_del(&grp->list); - talloc_free(grp); -} - -/*! Get rate counter from group, identified by index idx - * \paramin grp Rate counter group - * \paramin idx Index of the counter to retrieve - * \returns rate counter requested - */ -struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx) -{ - return &grp->ctridx; -} - -/*! Set a name for the group of counters be used instead of index value - at report time. - * \paramin grp Rate counter group - * \paramin name Name identifier to assign to the rate counter group - */ -void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name) -{ - osmo_talloc_replace_string(grp, &grp->name, name); -} - -/*! Add a number to the counter */ -void rate_ctr_add(struct rate_ctr *ctr, int inc) -{ - ctr->current += inc; -} - -/*! Return the counter difference since the last call to this function */ -int64_t rate_ctr_difference(struct rate_ctr *ctr) -{ - int64_t result = ctr->current - ctr->previous; - ctr->previous = ctr->current; - - return result; -} - -/* TODO: support update intervals > 1s */ -/* TODO: implement this as a special stats reporter */ - -static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) -{ - /* calculate rate over last interval */ - ctr->intvintv.rate = ctr->current - ctr->intvintv.last; - /* save current counter for next interval */ - ctr->intvintv.last = ctr->current; - - /* update the rate of the next bigger interval. This will - * be overwritten when that next larger interval expires */ - if (intv + 1 < ARRAY_SIZE(ctr->intv)) - ctr->intvintv+1.rate += ctr->intvintv.rate; -} - -static struct osmo_timer_list rate_ctr_timer; -static uint64_t timer_ticks; - -/* The one-second interval has expired */ -static void rate_ctr_group_intv(struct rate_ctr_group *grp) -{ - unsigned int i; - - for (i = 0; i < grp->desc->num_ctr; i++) { - struct rate_ctr *ctr = &grp->ctri; - - interval_expired(ctr, RATE_CTR_INTV_SEC); - if ((timer_ticks % 60) == 0) - interval_expired(ctr, RATE_CTR_INTV_MIN); - if ((timer_ticks % (60*60)) == 0) - interval_expired(ctr, RATE_CTR_INTV_HOUR); - if ((timer_ticks % (24*60*60)) == 0) - interval_expired(ctr, RATE_CTR_INTV_DAY); - } -} - -static void rate_ctr_timer_cb(void *data) -{ - struct rate_ctr_group *ctrg; - - /* Increment number of ticks before we calculate intervals, - * as a counter value of 0 would already wrap all counters */ - timer_ticks++; - - llist_for_each_entry(ctrg, &rate_ctr_groups, list) - rate_ctr_group_intv(ctrg); - - osmo_timer_schedule(&rate_ctr_timer, 1, 0); -} - -/*! Initialize the counter module. Call this once from your application. - * \paramin tall_ctx Talloc context from which rate_ctr_group will be allocated - * \returns 0 on success; negative on error */ -int rate_ctr_init(void *tall_ctx) -{ - /* ignore repeated initialization */ - if (osmo_timer_pending(&rate_ctr_timer)) - return 0; - - tall_rate_ctr_ctx = tall_ctx; - osmo_timer_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL); - osmo_timer_schedule(&rate_ctr_timer, 1, 0); - - return 0; -} - -/*! Search for counter group based on group name and index - * \paramin name Name of the counter group you're looking for - * \paramin idx Index inside the counter group - * \returns \ref rate_ctr_group or NULL in case of error */ -struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx) -{ - struct rate_ctr_group *ctrg; - - llist_for_each_entry(ctrg, &rate_ctr_groups, list) { - if (!ctrg->desc) - continue; - - if (!strcmp(ctrg->desc->group_name_prefix, name) && - ctrg->idx == idx) { - return ctrg; - } - } - return NULL; -} - -/*! Search for counter based on group + name - * \paramin ctrg pointer to \ref rate_ctr_group - * \paramin name name of counter inside group - * \returns \ref rate_ctr or NULL in case of error - */ -const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name) -{ - int i; - const struct rate_ctr_desc *ctr_desc; - - if (!ctrg->desc) - return NULL; - - for (i = 0; i < ctrg->desc->num_ctr; i++) { - ctr_desc = &ctrg->desc->ctr_desci; - - if (!strcmp(ctr_desc->name, name)) { - return &ctrg->ctri; - } - } - return NULL; -} - -/*! Iterate over each counter in group and call function - * \paramin ctrg counter group over which to iterate - * \paramin handle_counter function pointer - * \paramin data Data to hand transparently to handle_counter() - * \returns 0 on success; negative otherwise - */ -int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg, - rate_ctr_handler_t handle_counter, void *data) -{ - int rc = 0; - int i; - - for (i = 0; i < ctrg->desc->num_ctr; i++) { - struct rate_ctr *ctr = &ctrg->ctri; - rc = handle_counter(ctrg, - ctr, &ctrg->desc->ctr_desci, data); - if (rc < 0) - return rc; - } - - return rc; -} - -/*! Iterate over all counter groups - * \paramin handle_group function pointer of callback function - * \paramin data Data to hand transparently to handle_group() - * \returns 0 on success; negative otherwise - */ -int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data) -{ - struct rate_ctr_group *statg; - int rc = 0; - - llist_for_each_entry(statg, &rate_ctr_groups, list) { - rc = handle_group(statg, data); - if (rc < 0) - return rc; - } - - return rc; -} - -/*! Reset a rate counter back to zero - * \paramin ctr counter to reset - */ -void rate_ctr_reset(struct rate_ctr *ctr) -{ - memset(ctr, 0, sizeof(*ctr)); -} - -/*! Reset all counters in a group - * \paramin ctrg counter group to reset - */ -void rate_ctr_group_reset(struct rate_ctr_group *ctrg) -{ - int i; - - for (i = 0; i < ctrg->desc->num_ctr; i++) { - struct rate_ctr *ctr = &ctrg->ctri; - rate_ctr_reset(ctr); - } -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/select.c
Deleted
@@ -1,675 +0,0 @@ -/*! \file select.c - * select filedescriptor handling. - * Taken from: - * userspace logging daemon for the iptables ULOG target - * of the linux 2.4 netfilter subsystem. */ -/* - * (C) 2000-2020 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserverd. - * - * 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 <fcntl.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <stdbool.h> -#include <errno.h> - -#include <osmocom/core/select.h> -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/stat_item.h> -#include <osmocom/core/stats_tcp.h> - -#include "../config.h" - -#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_POLL_H) -#include <sys/select.h> -#include <poll.h> - -/*! \addtogroup select - * @{ - * select() loop abstraction - * - * \file select.c */ - -/* keep a set of file descriptors per-thread, so that each thread can have its own - * distinct set of file descriptors to interact with */ -static __thread int maxfd = 0; -static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */ -static __thread int unregistered_count; - -#ifndef FORCE_IO_SELECT -struct poll_state { - /* array of pollfd */ - struct pollfd *poll; - /* number of entries in pollfd allocated */ - unsigned int poll_size; - /* number of osmo_fd registered */ - unsigned int num_registered; -}; -static __thread struct poll_state g_poll; -#endif /* FORCE_IO_SELECT */ - -/*! See osmo_select_shutdown_request() */ -static int _osmo_select_shutdown_requested = 0; -/*! See osmo_select_shutdown_request() */ -static bool _osmo_select_shutdown_done = false; - -/*! Set up an osmo-fd. Will not register it. - * \paraminout ofd Osmo FD to be set-up - * \paramin fd OS-level file descriptor number - * \paramin when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT} - * \paramin cb Call-back function to be called - * \paramin data Private context pointer - * \paramin priv_nr Private number - */ -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) -{ - ofd->fd = fd; - ofd->when = when; - ofd->cb = cb; - ofd->data = data; - ofd->priv_nr = priv_nr; -} - -/*! Update the 'when' field of osmo_fd. "ofd->when = (ofd->when & when_mask) | when". - * Use this function instead of directly modifying ofd->when, as the latter will be - * removed soon. */ -void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when) -{ - ofd->when &= when_mask; - ofd->when |= when; -} - -/*! Check if a file descriptor is already registered - * \paramin fd osmocom file descriptor to be checked - * \returns true if registered; otherwise false - */ -bool osmo_fd_is_registered(struct osmo_fd *fd) -{ - struct osmo_fd *entry; - llist_for_each_entry(entry, &osmo_fds, list) { - if (entry == fd) { - return true; - } - } - - return false; -} - -/*! Register a new file descriptor with select loop abstraction - * \paramin fd osmocom file descriptor to be registered - * \returns 0 on success; negative in case of error - */ -int osmo_fd_register(struct osmo_fd *fd) -{ - int flags; - - /* make FD nonblocking */ - flags = fcntl(fd->fd, F_GETFL); - if (flags < 0) - return flags; - flags |= O_NONBLOCK; - flags = fcntl(fd->fd, F_SETFL, flags); - if (flags < 0) - return flags; - - /* set close-on-exec flag */ - flags = fcntl(fd->fd, F_GETFD); - if (flags < 0) - return flags; - flags |= FD_CLOEXEC; - flags = fcntl(fd->fd, F_SETFD, flags); - if (flags < 0) - return flags; - - /* Register FD */ - if (fd->fd > maxfd) - maxfd = fd->fd; - -#ifdef OSMO_FD_CHECK - if (osmo_fd_is_registered(fd)) { - fprintf(stderr, "Adding a osmo_fd that is already in the list.\n"); - return 0; - } -#endif -#ifndef FORCE_IO_SELECT - if (g_poll.num_registered + 1 > g_poll.poll_size) { - struct pollfd *p; - unsigned int new_size = g_poll.poll_size ? g_poll.poll_size * 2 : 1024; - p = talloc_realloc(OTC_GLOBAL, g_poll.poll, struct pollfd, new_size); - if (!p) - return -ENOMEM; - memset(p + g_poll.poll_size, 0, new_size - g_poll.poll_size); - g_poll.poll = p; - g_poll.poll_size = new_size; - } - g_poll.num_registered++; -#endif /* FORCE_IO_SELECT */ - - llist_add_tail(&fd->list, &osmo_fds); - - return 0; -} - -/*! Unregister a file descriptor from select loop abstraction - * \paramin fd osmocom file descriptor to be unregistered - */ -void osmo_fd_unregister(struct osmo_fd *fd) -{ - /* Note: when fd is inside the osmo_fds list (not registered before) - * this function will crash! If in doubt, check file descriptor with - * osmo_fd_is_registered() */ - unregistered_count++; - llist_del(&fd->list); -#ifndef FORCE_IO_SELECT - g_poll.num_registered--; -#endif /* FORCE_IO_SELECT */ - - /* If existent, free any statistical data */ - osmo_stats_tcp_osmo_fd_unregister(fd); -} - -/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction - * \paramin fd osmocom file descriptor to be unregistered + closed - * - * If \a fd is registered, we unregister it from the select() loop - * abstraction. We then close the fd and set it to -1, as well as - * unsetting any 'when' flags */ -void osmo_fd_close(struct osmo_fd *fd) -{ - if (osmo_fd_is_registered(fd)) - osmo_fd_unregister(fd); - if (fd->fd != -1) - close(fd->fd); - fd->fd = -1; - fd->when = 0; -} - -/*! Populate the fd_sets and return the highest fd number - * \paramin _rset The readfds to populate - * \paramin _wset The wrtiefds to populate - * \paramin _eset The errorfds to populate - * - * \returns The highest file descriptor seen or 0 on an empty list - */ -inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset) -{ - fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset; - struct osmo_fd *ufd; - int highfd = 0; - - llist_for_each_entry(ufd, &osmo_fds, list) { - if (ufd->when & OSMO_FD_READ) - FD_SET(ufd->fd, readset); - - if (ufd->when & OSMO_FD_WRITE) - FD_SET(ufd->fd, writeset); - - if (ufd->when & OSMO_FD_EXCEPT) - FD_SET(ufd->fd, exceptset); - - if (ufd->fd > highfd) - highfd = ufd->fd; - } - - return highfd; -} - -inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset) -{ - struct osmo_fd *ufd, *tmp; - int work = 0; - fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset; - -restart: - unregistered_count = 0; - llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) { - int flags = 0; - - if (FD_ISSET(ufd->fd, readset)) { - flags |= OSMO_FD_READ; - FD_CLR(ufd->fd, readset); - } - - if (FD_ISSET(ufd->fd, writeset)) { - flags |= OSMO_FD_WRITE; - FD_CLR(ufd->fd, writeset); - } - - if (FD_ISSET(ufd->fd, exceptset)) { - flags |= OSMO_FD_EXCEPT; - FD_CLR(ufd->fd, exceptset); - } - - if (flags) { - work = 1; - /* make sure to clear any log context before processing the next incoming message - * as part of some file descriptor callback. This effectively prevents "context - * leaking" from processing of one message into processing of the next message as part - * of one iteration through the list of file descriptors here. See OS#3813 */ - log_reset_context(); - ufd->cb(ufd, flags); - } - /* ugly, ugly hack. If more than one filedescriptor was - * unregistered, they might have been consecutive and - * llist_for_each_entry_safe() is no longer safe */ - /* this seems to happen with the last element of the list as well */ - if (unregistered_count >= 1) - goto restart; - } - - return work; -} - - -#ifndef FORCE_IO_SELECT -/* fill g_poll.poll and return the number of entries filled */ -static unsigned int poll_fill_fds(void) -{ - struct osmo_fd *ufd; - unsigned int i = 0; - - llist_for_each_entry(ufd, &osmo_fds, list) { - struct pollfd *p; - - if (!ufd->when) - continue; - - p = &g_poll.polli++; - - p->fd = ufd->fd; - p->events = 0; - p->revents = 0; - - /* use the same mapping as the Linux kernel does in fs/select.c */ - if (ufd->when & OSMO_FD_READ) - p->events |= POLLIN | POLLHUP | POLLERR; - - if (ufd->when & OSMO_FD_WRITE) - p->events |= POLLOUT | POLLERR; - - if (ufd->when & OSMO_FD_EXCEPT) - p->events |= POLLPRI; - - } - - return i; -} - -/* iterate over first n_fd entries of g_poll.poll + dispatch */ -static int poll_disp_fds(unsigned int n_fd) -{ - struct osmo_fd *ufd; - unsigned int i; - int work = 0; - int shutdown_pending_writes = 0; - - for (i = 0; i < n_fd; i++) { - struct pollfd *p = &g_poll.polli; - int flags = 0; - - if (!p->revents) - continue; - - ufd = osmo_fd_get_by_fd(p->fd); - if (!ufd) { - /* FD might have been unregistered meanwhile */ - continue; - } - /* use the same mapping as the Linux kernel does in fs/select.c */ - if (p->revents & (POLLIN | POLLHUP | POLLERR)) - flags |= OSMO_FD_READ; - if (p->revents & (POLLOUT | POLLERR)) - flags |= OSMO_FD_WRITE; - if (p->revents & POLLPRI) - flags |= OSMO_FD_EXCEPT; - - /* make sure we never report more than the user requested */ - flags &= ufd->when; - - if (_osmo_select_shutdown_requested > 0) { - if (ufd->when & OSMO_FD_WRITE) - shutdown_pending_writes++; - } - - if (flags) { - work = 1; - /* make sure to clear any log context before processing the next incoming message - * as part of some file descriptor callback. This effectively prevents "context - * leaking" from processing of one message into processing of the next message as part - * of one iteration through the list of file descriptors here. See OS#3813 */ - log_reset_context(); - ufd->cb(ufd, flags); - } - } - - if (_osmo_select_shutdown_requested > 0 && !shutdown_pending_writes) - _osmo_select_shutdown_done = true; - - return work; -} - -static int _osmo_select_main(int polling) -{ - unsigned int n_poll; - int rc; - int timeout = 0; - - /* prepare read and write fdsets */ - n_poll = poll_fill_fds(); - - if (!polling) { - osmo_timers_prepare(); - timeout = osmo_timers_nearest_ms(); - - if (_osmo_select_shutdown_requested && timeout == -1) - timeout = 0; - } - - rc = poll(g_poll.poll, n_poll, timeout); - if (rc < 0) - return 0; - - /* fire timers */ - if (!_osmo_select_shutdown_requested) - osmo_timers_update(); - - OSMO_ASSERT(osmo_ctx->select); - - /* call registered callback functions */ - return poll_disp_fds(n_poll); -} -#else /* FORCE_IO_SELECT */ -/* the old implementation based on select, used 2008-2020 */ -static int _osmo_select_main(int polling) -{ - fd_set readset, writeset, exceptset; - int rc; - struct timeval no_time = {0, 0}; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - FD_ZERO(&exceptset); - - /* prepare read and write fdsets */ - osmo_fd_fill_fds(&readset, &writeset, &exceptset); - - if (!polling) - osmo_timers_prepare(); - rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest()); - if (rc < 0) - return 0; - - /* fire timers */ - osmo_timers_update(); - - OSMO_ASSERT(osmo_ctx->select); - - /* call registered callback functions */ - return osmo_fd_disp_fds(&readset, &writeset, &exceptset); -} -#endif /* FORCE_IO_SELECT */ - -/*! select main loop integration - * \paramin polling should we pollonly (1) or block on select (0) - * \returns 0 if no fd handled; 1 if fd handled; negative in case of error - */ -int osmo_select_main(int polling) -{ - int rc = _osmo_select_main(polling); -#ifndef EMBEDDED - if (talloc_total_size(osmo_ctx->select) != 0) { - osmo_panic("You cannot use the 'select' volatile " - "context if you don't use osmo_select_main_ctx()!\n"); - } -#endif - return rc; -} - -#ifndef EMBEDDED -/*! select main loop integration with temporary select-dispatch talloc context - * \paramin polling should we pollonly (1) or block on select (0) - * \returns 0 if no fd handled; 1 if fd handled; negative in case of error - */ -int osmo_select_main_ctx(int polling) -{ - int rc = _osmo_select_main(polling); - /* free all the children of the volatile 'select' scope context */ - talloc_free_children(osmo_ctx->select); - return rc; -} -#endif - -/*! find an osmo_fd based on the integer fd - * \paramin fd file descriptor to use as search key - * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */ -struct osmo_fd *osmo_fd_get_by_fd(int fd) -{ - struct osmo_fd *ofd; - - llist_for_each_entry(ofd, &osmo_fds, list) { - if (ofd->fd == fd) - return ofd; - } - return NULL; -} - -/*! initialize the osmocom select abstraction for the current thread */ -void osmo_select_init(void) -{ - INIT_LLIST_HEAD(&osmo_fds); -} - -/* ensure main thread always has pre-initialized osmo_fds */ -static __attribute__((constructor)) void on_dso_load_select(void) -{ - osmo_select_init(); -} - -#ifdef HAVE_SYS_TIMERFD_H -#include <sys/timerfd.h> - -/*! disable the osmocom-wrapped timerfd */ -int osmo_timerfd_disable(struct osmo_fd *ofd) -{ - const struct itimerspec its_null = { - .it_value = { 0, 0 }, - .it_interval = { 0, 0 }, - }; - return timerfd_settime(ofd->fd, 0, &its_null, NULL); -} - -/*! schedule the osmocom-wrapped timerfd to occur first at \a first, then periodically at \a interval - * \paramin ofd Osmocom wrapped timerfd - * \paramin first Relative time at which the timer should first execute (NULL = \a interval) - * \paramin interval Time interval at which subsequent timer shall fire - * \returns 0 on success; negative on error */ -int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first, - const struct timespec *interval) -{ - struct itimerspec its; - - if (ofd->fd < 0) - return -EINVAL; - - /* first expiration */ - if (first) - its.it_value = *first; - else - its.it_value = *interval; - /* repeating interval */ - its.it_interval = *interval; - - return timerfd_settime(ofd->fd, 0, &its, NULL); -} - -/*! setup osmocom-wrapped timerfd - * \paraminout ofd Osmocom-wrapped timerfd on which to operate - * \paramin cb Call-back function called when timerfd becomes readable - * \paramin data Opaque data to be passed on to call-back - * \returns 0 on success; negative on error - * - * We simply initialize the data structures here, but do not yet - * schedule the timer. - */ -int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data) -{ - ofd->cb = cb; - ofd->data = data; - ofd->when = OSMO_FD_READ; - - if (ofd->fd < 0) { - int rc; - - ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); - if (ofd->fd < 0) - return ofd->fd; - - rc = osmo_fd_register(ofd); - if (rc < 0) { - osmo_fd_unregister(ofd); - close(ofd->fd); - ofd->fd = -1; - return rc; - } - } - return 0; -} - -#endif /* HAVE_SYS_TIMERFD_H */ - -#ifdef HAVE_SYS_SIGNALFD_H -#include <sys/signalfd.h> - -static int signalfd_callback(struct osmo_fd *ofd, unsigned int what) -{ - struct osmo_signalfd *osfd = ofd->data; - struct signalfd_siginfo fdsi; - int rc; - - rc = read(ofd->fd, &fdsi, sizeof(fdsi)); - if (rc < 0) { - osmo_fd_unregister(ofd); - close(ofd->fd); - ofd->fd = -1; - return rc; - } - - osfd->cb(osfd, &fdsi); - - return 0; -}; - -/*! create a signalfd and register it with osmocom select loop. - * \paramin ctx talloc context from which osmo_signalfd is to be allocated - * \paramin set of signals to be accept via this file descriptor - * \paramin cb call-back function to be called for each arriving signal - * \paramin data opaque user-provided data to pass to callback - * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */ -struct osmo_signalfd * -osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data) -{ - struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd)); - int fd, rc; - - if (!osfd) - return NULL; - - osfd->data = data; - osfd->sigset = set; - osfd->cb = cb; - - fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK); - if (fd < 0) { - talloc_free(osfd); - return NULL; - } - - osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0); - rc = osmo_fd_register(&osfd->ofd); - if (rc < 0) { - close(fd); - talloc_free(osfd); - return NULL; - } - - return osfd; -} - -#endif /* HAVE_SYS_SIGNALFD_H */ - -/*! Request osmo_select_* to only service pending OSMO_FD_WRITE requests. Once all writes are done, - * osmo_select_shutdown_done() returns true. This allows for example to send all outbound packets before terminating the - * process. - * - * Usage example: - * - * static void signal_handler(int signum) - * { - * fprintf(stdout, "signal %u received\n", signum); - * - * switch (signum) { - * case SIGINT: - * case SIGTERM: - * // If the user hits Ctrl-C the third time, just terminate immediately. - * if (osmo_select_shutdown_requested() >= 2) - * exit(-1); - * // Request write-only mode in osmo_select_main_ctx() - * osmo_select_shutdown_request(); - * break; - * ... - * } - * - * main() - * { - * signal(SIGINT, &signal_handler); - * signal(SIGTERM, &signal_handler); - * - * ... - * - * // After the signal_handler issued osmo_select_shutdown_request(), osmo_select_shutdown_done() returns true - * // as soon as all write queues are empty. - * while (!osmo_select_shutdown_done()) { - * osmo_select_main_ctx(0); - * } - * } - */ -void osmo_select_shutdown_request() -{ - _osmo_select_shutdown_requested++; -}; - -/*! Return the number of times osmo_select_shutdown_request() was called before. */ -int osmo_select_shutdown_requested() -{ - return _osmo_select_shutdown_requested; -}; - -/*! Return true after osmo_select_shutdown_requested() was called, and after an osmo_select poll loop found no more - * pending OSMO_FD_WRITE on any registered socket. */ -bool osmo_select_shutdown_done() { - return _osmo_select_shutdown_done; -}; - -/*! @} */ - -#endif /* _HAVE_SYS_SELECT_H */
View file
libosmocore_1.7.0.tar.xz/src/socket.c
Deleted
@@ -1,1985 +0,0 @@ -/* - * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org> - * - * 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 "../config.h" - -/*! \addtogroup socket - * @{ - * Osmocom socket convenience functions. - * - * \file socket.c */ - -#ifdef HAVE_SYS_SOCKET_H - -#include <osmocom/core/logging.h> -#include <osmocom/core/select.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/sockaddr_str.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <net/if.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <stdio.h> -#include <unistd.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> -#include <netdb.h> -#include <ifaddrs.h> - -#ifdef HAVE_LIBSCTP -#include <netinet/sctp.h> -#endif - -static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto, - const char *host, uint16_t port, bool passive) -{ - struct addrinfo hints, *result, *rp; - char portbuf6; - int rc; - - snprintf(portbuf, sizeof(portbuf), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = family; - if (type == SOCK_RAW) { - /* Workaround for glibc, that returns EAI_SERVICE (-8) if - * SOCK_RAW and IPPROTO_GRE is used. - * http://sourceware.org/bugzilla/show_bug.cgi?id=15015 - */ - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } else { - hints.ai_socktype = type; - hints.ai_protocol = proto; - } - - if (passive) - hints.ai_flags |= AI_PASSIVE; - - rc = getaddrinfo(host, portbuf, &hints, &result); - if (rc != 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo(%s, %u) failed: %s\n", - host, port, gai_strerror(rc)); - return NULL; - } - - for (rp = result; rp != NULL; rp = rp->ai_next) { - /* Workaround for glibc again */ - if (type == SOCK_RAW) { - rp->ai_socktype = SOCK_RAW; - rp->ai_protocol = proto; - } - } - - return result; -} - -#ifdef HAVE_LIBSCTP -/*! Retrieve an array of addrinfo with specified hints, one for each host in the hosts array. - * \paramout addrinfo array of addrinfo pointers, will be filled by the function on success. - * Its size must be at least the one of hosts. - * \paramin family Socket family like AF_INET, AF_INET6. - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM. - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP. - * \paramin hosts array of char pointers (strings) containing the addresses to query. - * \paramin host_cnt length of the hosts array (in items). - * \paramin port port number in host byte order. - * \paramin passive whether to include the AI_PASSIVE flag in getaddrinfo() hints. - * \returns 0 is returned on success together with a filled addrinfo array; negative on error - */ -static int addrinfo_helper_multi(struct addrinfo **addrinfo, uint16_t family, uint16_t type, uint8_t proto, - const char **hosts, size_t host_cnt, uint16_t port, bool passive) -{ - unsigned int i, j; - - for (i = 0; i < host_cnt; i++) { - addrinfoi = addrinfo_helper(family, type, proto, hostsi, port, passive); - if (!addrinfoi) { - for (j = 0; j < i; j++) - freeaddrinfo(addrinfoj); - return -EINVAL; - } - } - return 0; -} -#endif /* HAVE_LIBSCTP*/ - -static int socket_helper_tail(int sfd, unsigned int flags) -{ - int rc, on = 1; - uint8_t dscp = GET_OSMO_SOCK_F_DSCP(flags); - uint8_t prio = GET_OSMO_SOCK_F_PRIO(flags); - - if (flags & OSMO_SOCK_F_NONBLOCK) { - if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot set this socket unblocking: %s\n", - strerror(errno)); - close(sfd); - return -EINVAL; - } - } - - if (dscp) { - rc = osmo_sock_set_dscp(sfd, dscp); - if (rc) { - LOGP(DLGLOBAL, LOGL_ERROR, "cannot set IP DSCP of socket to %u: %s\n", - dscp, strerror(errno)); - /* we consider this a non-fatal error */ - } - } - - if (prio) { - rc = osmo_sock_set_priority(sfd, prio); - if (rc) { - LOGP(DLGLOBAL, LOGL_ERROR, "cannot set priority of socket to %u: %s\n", - prio, strerror(errno)); - /* we consider this a non-fatal error */ - } - } - - return 0; -} - -static int socket_helper(const struct addrinfo *rp, unsigned int flags) -{ - int sfd, rc; - - sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sfd == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, - "unable to create socket: %s\n", strerror(errno)); - return sfd; - } - - rc = socket_helper_tail(sfd, flags); - if (rc < 0) - return rc; - - return sfd; -} - -static int socket_helper_osa(const struct osmo_sockaddr *addr, uint16_t type, uint8_t proto, unsigned int flags) -{ - int sfd, rc; - - sfd = socket(addr->u.sa.sa_family, type, proto); - if (sfd == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, - "unable to create socket: %s\n", strerror(errno)); - return sfd; - } - - rc = socket_helper_tail(sfd, flags); - if (rc < 0) - return rc; - - return sfd; -} - -#ifdef HAVE_LIBSCTP -/* Fill buf with a string representation of the address set, in the form: - * buf_len == 0: "()" - * buf_len == 1: "hostA" - * buf_len >= 2: (hostA|hostB|...|...) - */ -static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, size_t host_cnt) -{ - int len = 0, offset = 0, rem = buf_len; - size_t i; - int ret; - char *after; - - if (buf_len < 3) - return -EINVAL; - - if (host_cnt != 1) { - ret = snprintf(buf, rem, "("); - if (ret < 0) - return ret; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - for (i = 0; i < host_cnt; i++) { - if (host_cnt == 1) - after = ""; - else - after = (i == (host_cnt - 1)) ? ")" : "|"; - ret = snprintf(buf + offset, rem, "%s%s", hostsi ? : "0.0.0.0", after); - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - - return len; -} -#endif /* HAVE_LIBSCTP */ - -static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags) -{ - int rc; - - /* Make sure to call 'listen' on a bound, connection-oriented sock */ - if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) { - switch (type) { - case SOCK_STREAM: - case SOCK_SEQPACKET: - rc = listen(fd, 10); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n", - strerror(errno)); - return rc; - } - break; - } - } - - if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) { - rc = osmo_sock_mcast_loop_set(fd, false); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n", - strerror(errno)); - return rc; - } - } - - if (flags & OSMO_SOCK_F_NO_MCAST_ALL) { - rc = osmo_sock_mcast_all_set(fd, false); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n", - strerror(errno)); - /* do not abort here, as this is just an - * optional additional optimization that only - * exists on Linux only */ - } - } - return 0; -} - -/*! Initialize a socket (including bind and/or connect) - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin local_host local host name or IP address in string form - * \paramin local_port local port number in host byte order - * \paramin remote_host remote host name or IP address in string form - * \paramin remote_port remote port number in host byte order - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket file descriptor on success; negative on error - * - * This function creates a new socket of the designated \a family, \a - * type and \a proto and optionally binds it to the \a local_host and \a - * local_port as well as optionally connects it to the \a remote_host - * and \q remote_port, depending on the value * of \a flags parameter. - * - * As opposed to \ref osmo_sock_init(), this function allows to combine - * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This - * is useful if you want to connect to a remote host/port, but still - * want to bind that socket to either a specific local alias IP and/or a - * specific local source port. - * - * You must specify either \ref OSMO_SOCK_F_BIND, or \ref - * OSMO_SOCK_F_CONNECT, or both. - * - * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to - * non-blocking mode. - */ -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 addrinfo *local = NULL, *remote = NULL, *rp; - int sfd = -1, rc, on = 1; - - bool local_ipv4 = false, local_ipv6 = false; - bool remote_ipv4 = false, remote_ipv6 = false; - - if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " - "BIND or CONNECT flags\n"); - return -EINVAL; - } - - /* figure out local address infos */ - if (flags & OSMO_SOCK_F_BIND) { - local = addrinfo_helper(family, type, proto, local_host, local_port, true); - if (!local) - return -EINVAL; - } - - /* figure out remote address infos */ - if (flags & OSMO_SOCK_F_CONNECT) { - remote = addrinfo_helper(family, type, proto, remote_host, remote_port, false); - if (!remote) { - if (local) - freeaddrinfo(local); - - return -EINVAL; - } - } - - /* It must do a full run to ensure AF_UNSPEC does not fail. - * In case first local valid entry is IPv4 and only remote valid entry - * is IPv6 or vice versa */ - if (family == AF_UNSPEC) { - for (rp = local; rp != NULL; rp = rp->ai_next) { - switch (rp->ai_family) { - case AF_INET: - local_ipv4 = true; - break; - case AF_INET6: - local_ipv6 = true; - break; - } - } - - for (rp = remote; rp != NULL; rp = rp->ai_next) { - switch (rp->ai_family) { - case AF_INET: - remote_ipv4 = true; - break; - case AF_INET6: - remote_ipv6 = true; - break; - } - } - - if ((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) { - /* prioritize ipv6 as per RFC */ - if (local_ipv6 && remote_ipv6) - family = AF_INET6; - else if (local_ipv4 && remote_ipv4) - family = AF_INET; - else { - if (local) - freeaddrinfo(local); - if (remote) - freeaddrinfo(remote); - LOGP(DLGLOBAL, LOGL_ERROR, - "Unable to find a common protocol (IPv4 or IPv6) " - "for local host: %s and remote host: %s.\n", - local_host, remote_host); - return -ENODEV; - } - } else if ((flags & OSMO_SOCK_F_BIND)) { - family = local_ipv6 ? AF_INET6 : AF_INET; - } else if ((flags & OSMO_SOCK_F_CONNECT)) { - family = remote_ipv6 ? AF_INET6 : AF_INET; - } - } - - /* figure out local side of socket */ - if (flags & OSMO_SOCK_F_BIND) { - for (rp = local; rp != NULL; rp = rp->ai_next) { - /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */ - if (rp->ai_family != family) - continue; - - sfd = socket_helper(rp, flags); - if (sfd < 0) - continue; - - if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket:" - " %s:%u: %s\n", - local_host, local_port, - strerror(errno)); - close(sfd); - continue; - } - } - - if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n", - local_host, local_port, strerror(errno)); - close(sfd); - continue; - } - break; - } - - freeaddrinfo(local); - if (rp == NULL) { - if (remote) - freeaddrinfo(remote); - LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n", - local_host, local_port); - return -ENODEV; - } - } - - /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it - was already closed and func returned. If OSMO_SOCK_F_BIND is not - set, then sfd = -1 */ - - /* figure out remote side of socket */ - if (flags & OSMO_SOCK_F_CONNECT) { - for (rp = remote; rp != NULL; rp = rp->ai_next) { - /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */ - if (rp->ai_family != family) - continue; - - if (sfd < 0) { - sfd = socket_helper(rp, flags); - if (sfd < 0) - continue; - } - - rc = connect(sfd, rp->ai_addr, rp->ai_addrlen); - if (rc != 0 && errno != EINPROGRESS) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n", - remote_host, remote_port, strerror(errno)); - /* We want to maintain the bind socket if bind was enabled */ - if (!(flags & OSMO_SOCK_F_BIND)) { - close(sfd); - sfd = -1; - } - continue; - } - break; - } - - freeaddrinfo(remote); - if (rp == NULL) { - LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n", - remote_host, remote_port); - if (sfd >= 0) - close(sfd); - return -ENODEV; - } - } - - rc = osmo_sock_init_tail(sfd, type, flags); - if (rc < 0) { - close(sfd); - sfd = -1; - } - - return sfd; -} - -#define _SOCKADDR_TO_STR(dest, sockaddr) do { \ - if (osmo_sockaddr_str_from_sockaddr(dest, &sockaddr->u.sas)) \ - osmo_strlcpy((dest)->ip, "Invalid IP", 11); \ - } while (0) - -/*! Initialize a socket (including bind and/or connect) - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin local local address - * \paramin remote remote address - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket file descriptor on success; negative on error - * - * This function creates a new socket of the - * \a type and \a proto and optionally binds it to the \a local - * as well as optionally connects it to the \a remote - * depending on the value * of \a flags parameter. - * - * As opposed to \ref osmo_sock_init(), this function allows to combine - * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This - * is useful if you want to connect to a remote host/port, but still - * want to bind that socket to either a specific local alias IP and/or a - * specific local source port. - * - * You must specify either \ref OSMO_SOCK_F_BIND, or \ref - * OSMO_SOCK_F_CONNECT, or both. - * - * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to - * non-blocking mode. - */ -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 sfd = -1, rc, on = 1; - struct osmo_sockaddr_str _sastr = {}; - struct osmo_sockaddr_str *sastr = &_sastr; - - if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " - "BIND or CONNECT flags\n"); - return -EINVAL; - } - - if ((flags & OSMO_SOCK_F_BIND) && !local) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot BIND when local is NULL\n"); - return -EINVAL; - } - - if ((flags & OSMO_SOCK_F_CONNECT) && !remote) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot CONNECT when remote is NULL\n"); - return -EINVAL; - } - - if ((flags & OSMO_SOCK_F_BIND) && - (flags & OSMO_SOCK_F_CONNECT) && - local->u.sa.sa_family != remote->u.sa.sa_family) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid: the family for " - "local and remote endpoint must be same.\n"); - return -EINVAL; - } - - /* figure out local side of socket */ - if (flags & OSMO_SOCK_F_BIND) { - sfd = socket_helper_osa(local, type, proto, flags); - if (sfd < 0) { - _SOCKADDR_TO_STR(sastr, local); - LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: " OSMO_SOCKADDR_STR_FMT "\n", - OSMO_SOCKADDR_STR_FMT_ARGS(sastr)); - return -ENODEV; - } - - if (proto != IPPROTO_UDP || (flags & OSMO_SOCK_F_UDP_REUSEADDR)) { - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - _SOCKADDR_TO_STR(sastr, local); - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", - OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); - close(sfd); - return rc; - } - } - - if (bind(sfd, &local->u.sa, sizeof(struct osmo_sockaddr)) == -1) { - _SOCKADDR_TO_STR(sastr, local); - LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", - OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); - close(sfd); - return -1; - } - } - - /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it - was already closed and func returned. If OSMO_SOCK_F_BIND is not - set, then sfd = -1 */ - - /* figure out remote side of socket */ - if (flags & OSMO_SOCK_F_CONNECT) { - if (sfd < 0) { - sfd = socket_helper_osa(remote, type, proto, flags); - if (sfd < 0) { - return sfd; - } - } - - rc = connect(sfd, &remote->u.sa, sizeof(struct osmo_sockaddr)); - if (rc != 0 && errno != EINPROGRESS) { - _SOCKADDR_TO_STR(sastr, remote); - LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", - OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); - close(sfd); - return rc; - } - } - - rc = osmo_sock_init_tail(sfd, type, flags); - if (rc < 0) { - close(sfd); - sfd = -1; - } - - return sfd; -} - -#ifdef HAVE_LIBSCTP - -/* Check whether there's an IPv6 Addr as first option of any addrinfo item in the addrinfo set */ -static void addrinfo_has_v4v6addr(const struct addrinfo **result, size_t result_count, bool *has_v4, bool *has_v6) -{ - size_t host_idx; - *has_v4 = false; - *has_v6 = false; - - for (host_idx = 0; host_idx < result_count; host_idx++) { - if (resulthost_idx->ai_family == AF_INET) - *has_v4 = true; - else if (resulthost_idx->ai_family == AF_INET6) - *has_v6 = true; - } -} - -/* Check whether there's an IPv6 with IN6ADDR_ANY_INIT ("::") */ -static bool addrinfo_has_in6addr_any(const struct addrinfo **result, size_t result_count) -{ - size_t host_idx; - struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; - - for (host_idx = 0; host_idx < result_count; host_idx++) { - if (resulthost_idx->ai_family != AF_INET6) - continue; - if (memcmp(&((struct sockaddr_in6 *)resulthost_idx->ai_addr)->sin6_addr, - &in6addr_any, sizeof(in6addr_any)) == 0) - return true; - } - return false; -} - -static int socket_helper_multiaddr(uint16_t family, uint16_t type, uint8_t proto, unsigned int flags) -{ - int sfd, rc; - - sfd = socket(family, type, proto); - if (sfd == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, - "Unable to create socket: %s\n", strerror(errno)); - return sfd; - } - - rc = socket_helper_tail(sfd, flags); - if (rc < 0) - return rc; - - return sfd; -} - -/* Build array of addresses taking first addrinfo result of the requested family - * for each host in addrs_buf. */ -static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result, - const char **hosts, unsigned int host_cont, - uint8_t *addrs_buf, size_t addrs_buf_len) { - size_t host_idx, offset = 0; - const struct addrinfo *rp; - - for (host_idx = 0; host_idx < host_cont; host_idx++) { - /* Addresses are ordered based on RFC 3484, see man getaddrinfo */ - for (rp = resulthost_idx; rp != NULL; rp = rp->ai_next) { - if (family != AF_UNSPEC && rp->ai_family != family) - continue; - if (offset + rp->ai_addrlen > addrs_buf_len) { - LOGP(DLGLOBAL, LOGL_ERROR, "Output buffer to small: %zu\n", - addrs_buf_len); - return -ENOSPC; - } - memcpy(addrs_buf + offset, rp->ai_addr, rp->ai_addrlen); - offset += rp->ai_addrlen; - break; - } - if (!rp) { /* No addr could be bound for this host! */ - LOGP(DLGLOBAL, LOGL_ERROR, "No suitable remote address found for host: %s\n", - hostshost_idx); - return -ENODEV; - } - } - return 0; -} - -/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses. - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin local_hosts array of char pointers (strings), each containing local host name or IP address in string form - * \paramin local_hosts_cnt length of local_hosts (in items) - * \paramin local_port local port number in host byte order - * \paramin remote_host array of char pointers (strings), each containing remote host name or IP address in string form - * \paramin remote_hosts_cnt length of remote_hosts (in items) - * \paramin remote_port remote port number in host byte order - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket file descriptor on success; negative on error - * - * This function is similar to \ref osmo_sock_init2(), but can be passed an - * array of local or remote addresses for protocols supporting multiple - * addresses per socket, like SCTP (currently only one supported). This function - * should not be used by protocols not supporting this kind of features, but - * rather \ref osmo_sock_init2() should be used instead. - * See \ref osmo_sock_init2() for more information on flags and general behavior. - */ -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) - -{ - struct addrinfo *res_locOSMO_SOCK_MAX_ADDRS, *res_remOSMO_SOCK_MAX_ADDRS; - int sfd = -1, rc, on = 1; - unsigned int i; - bool loc_has_v4addr, rem_has_v4addr; - bool loc_has_v6addr, rem_has_v6addr; - struct sockaddr_in6 addrs_bufOSMO_SOCK_MAX_ADDRS; - char strbuf512; - - /* updated later in case of AF_UNSPEC */ - loc_has_v4addr = rem_has_v4addr = (family == AF_INET); - loc_has_v6addr = rem_has_v6addr = (family == AF_INET6); - - /* TODO: So far this function is only aimed for SCTP, but could be - reused in the future for other protocols with multi-addr support */ - if (proto != IPPROTO_SCTP) - return -ENOTSUP; - - if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " - "BIND or CONNECT flags\n"); - return -EINVAL; - } - - if (((flags & OSMO_SOCK_F_BIND) && !local_hosts_cnt) || - ((flags & OSMO_SOCK_F_CONNECT) && !remote_hosts_cnt) || - local_hosts_cnt > OSMO_SOCK_MAX_ADDRS || - remote_hosts_cnt > OSMO_SOCK_MAX_ADDRS) - return -EINVAL; - - /* figure out local side of socket */ - if (flags & OSMO_SOCK_F_BIND) { - rc = addrinfo_helper_multi(res_loc, family, type, proto, local_hosts, - local_hosts_cnt, local_port, true); - if (rc < 0) - return -EINVAL; - /* Figure out if there's any IPV4 or IPv6 addr in the set */ - if (family == AF_UNSPEC) - addrinfo_has_v4v6addr((const struct addrinfo **)res_loc, local_hosts_cnt, - &loc_has_v4addr, &loc_has_v6addr); - } - /* figure out remote side of socket */ - if (flags & OSMO_SOCK_F_CONNECT) { - rc = addrinfo_helper_multi(res_rem, family, type, proto, remote_hosts, - remote_hosts_cnt, remote_port, false); - if (rc < 0) { - rc = -EINVAL; - goto ret_freeaddrinfo_loc; - } - /* Figure out if there's any IPv4 or IPv6 addr in the set */ - if (family == AF_UNSPEC) - addrinfo_has_v4v6addr((const struct addrinfo **)res_rem, remote_hosts_cnt, - &rem_has_v4addr, &rem_has_v6addr); - } - - if (((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) && - !addrinfo_has_in6addr_any((const struct addrinfo **)res_loc, local_hosts_cnt) && - (loc_has_v4addr != rem_has_v4addr || loc_has_v6addr != rem_has_v6addr)) { - LOGP(DLGLOBAL, LOGL_ERROR, "Invalid v4 vs v6 in local vs remote addresses\n"); - rc = -EINVAL; - goto ret_freeaddrinfo; - } - - sfd = socket_helper_multiaddr(loc_has_v6addr ? AF_INET6 : AF_INET, - type, proto, flags); - if (sfd < 0) { - rc = sfd; - goto ret_freeaddrinfo; - } - - if (flags & OSMO_SOCK_F_BIND) { - /* Since so far we only allow IPPROTO_SCTP in this function, - no need to check below for "proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR" */ - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket:" - " %s:%u: %s\n", - strbuf, local_port, - strerror(errno)); - goto ret_close; - } - - /* Build array of addresses taking first entry for each host. - TODO: Ideally we should use backtracking storing last used - indexes and trying next combination if connect() fails .*/ - /* We could alternatively use v4v6 mapped addresses and call sctp_bindx once with an array od sockaddr_in6 */ - rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_loc, - local_hosts, local_hosts_cnt, - (uint8_t*)addrs_buf, sizeof(addrs_buf)); - if (rc < 0) { - rc = -ENODEV; - goto ret_close; - } - - rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, local_hosts_cnt, SCTP_BINDX_ADD_ADDR); - if (rc == -1) { - multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); - LOGP(DLGLOBAL, LOGL_NOTICE, "unable to bind socket: %s:%u: %s\n", - strbuf, local_port, strerror(errno)); - rc = -ENODEV; - goto ret_close; - } - } - - if (flags & OSMO_SOCK_F_CONNECT) { - /* Build array of addresses taking first of same family for each host. - TODO: Ideally we should use backtracking storing last used - indexes and trying next combination if connect() fails .*/ - rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_rem, - remote_hosts, remote_hosts_cnt, - (uint8_t*)addrs_buf, sizeof(addrs_buf)); - if (rc < 0) { - rc = -ENODEV; - goto ret_close; - } - - rc = sctp_connectx(sfd, (struct sockaddr *)addrs_buf, remote_hosts_cnt, NULL); - if (rc != 0 && errno != EINPROGRESS) { - multiaddr_snprintf(strbuf, sizeof(strbuf), remote_hosts, remote_hosts_cnt); - LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n", - strbuf, remote_port, strerror(errno)); - rc = -ENODEV; - goto ret_close; - } - } - - rc = osmo_sock_init_tail(sfd, type, flags); - if (rc < 0) { - close(sfd); - sfd = -1; - } - - rc = sfd; - goto ret_freeaddrinfo; - -ret_close: - if (sfd >= 0) - close(sfd); -ret_freeaddrinfo: - if (flags & OSMO_SOCK_F_CONNECT) { - for (i = 0; i < remote_hosts_cnt; i++) - freeaddrinfo(res_remi); - } -ret_freeaddrinfo_loc: - if (flags & OSMO_SOCK_F_BIND) { - for (i = 0; i < local_hosts_cnt; i++) - freeaddrinfo(res_loci); - } - return rc; -} -#endif /* HAVE_LIBSCTP */ - -/*! Initialize a socket (including bind/connect) - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin host remote host name or IP address in string form - * \paramin port remote port number in host byte order - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket file descriptor on success; negative on error - * - * This function creates a new socket of the designated \a family, \a - * type and \a proto and optionally binds or connects it, depending on - * the value of \a flags parameter. - */ -int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, - const char *host, uint16_t port, unsigned int flags) -{ - struct addrinfo *result, *rp; - int sfd = -1; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */ - int on = 1; - int rc; - - if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == - (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) { - LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:" - " %s:%u\n", host, port); - return -EINVAL; - } - - result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND); - if (!result) - return -EINVAL; - - for (rp = result; rp != NULL; rp = rp->ai_next) { - sfd = socket_helper(rp, flags); - if (sfd == -1) - continue; - - if (flags & OSMO_SOCK_F_CONNECT) { - rc = connect(sfd, rp->ai_addr, rp->ai_addrlen); - if (rc != 0 && errno != EINPROGRESS) { - close(sfd); - continue; - } - } else { - if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket:" - " %s:%u: %s\n", - host, port, strerror(errno)); - close(sfd); - continue; - } - } - if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:" - "%s:%u: %s\n", - host, port, strerror(errno)); - close(sfd); - continue; - } - } - break; - } - freeaddrinfo(result); - - if (rp == NULL) { - LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n", - host, port); - return -ENODEV; - } - - if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { - rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (rc < 0) { - LOGP(DLGLOBAL, LOGL_ERROR, - "cannot setsockopt socket: %s:%u: %s\n", host, - port, strerror(errno)); - close(sfd); - sfd = -1; - } - } - - rc = osmo_sock_init_tail(sfd, type, flags); - if (rc < 0) { - close(sfd); - sfd = -1; - } - - return sfd; -} - -/*! fill \ref osmo_fd for a give sfd - * \paramout ofd file descriptor (will be filled in) - * \paramin sfd socket file descriptor - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function fills the \a ofd structure. - */ -static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd, unsigned int flags) -{ - int rc; - - if (sfd < 0) - return sfd; - - ofd->fd = sfd; - ofd->when = OSMO_FD_READ; - - /* if we're doing a non-blocking connect, the completion will be signaled - * by marking the fd as WRITE-able. So in this exceptional case, we're - * also interested in when the socket becomes write-able */ - if ((flags & (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) == - (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) { - ofd->when |= OSMO_FD_WRITE; - } - - rc = osmo_fd_register(ofd); - if (rc < 0) { - close(sfd); - return rc; - } - - return sfd; -} - -/*! Initialize a socket and fill \ref osmo_fd - * \paramout ofd file descriptor (will be filled in) - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin host remote host name or IP address in string form - * \paramin port remote port number in host byte order - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function creates (and optionall binds/connects) a socket using - * \ref osmo_sock_init, but also fills the \a ofd structure. - */ -int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, - const char *host, uint16_t port, unsigned int flags) -{ - return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags), flags); -} - -/*! Initialize a socket and fill \ref osmo_fd - * \paramout ofd file descriptor (will be filled in) - * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin local_host local host name or IP address in string form - * \paramin local_port local port number in host byte order - * \paramin remote_host remote host name or IP address in string form - * \paramin remote_port remote port number in host byte order - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function creates (and optionall binds/connects) a socket using - * \ref osmo_sock_init2, but also fills the \a ofd structure. - */ -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) -{ - return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host, - local_port, remote_host, remote_port, flags), 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) -{ - return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags), flags); -} - -/*! Initialize a socket and fill \ref sockaddr - * \paramout ss socket address (will be filled in) - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function creates (and optionall binds/connects) a socket using - * \ref osmo_sock_init, but also fills the \a ss structure. - */ -int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, - uint8_t proto, unsigned int flags) -{ - char hostNI_MAXHOST; - uint16_t port; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - int s, sa_len; - - /* determine port and host from ss */ - switch (ss->sa_family) { - case AF_INET: - sin = (struct sockaddr_in *) ss; - sa_len = sizeof(struct sockaddr_in); - port = ntohs(sin->sin_port); - break; - case AF_INET6: - sin6 = (struct sockaddr_in6 *) ss; - sa_len = sizeof(struct sockaddr_in6); - port = ntohs(sin6->sin6_port); - break; - default: - return -EINVAL; - } - - s = getnameinfo(ss, sa_len, host, NI_MAXHOST, - NULL, 0, NI_NUMERICHOST); - if (s != 0) { - LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:" - " %s\n", strerror(errno)); - return s; - } - - return osmo_sock_init(ss->sa_family, type, proto, host, port, flags); -} - -static int sockaddr_equal(const struct sockaddr *a, - const struct sockaddr *b, unsigned int len) -{ - struct sockaddr_in *sin_a, *sin_b; - struct sockaddr_in6 *sin6_a, *sin6_b; - - if (a->sa_family != b->sa_family) - return 0; - - switch (a->sa_family) { - case AF_INET: - sin_a = (struct sockaddr_in *)a; - sin_b = (struct sockaddr_in *)b; - if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, - sizeof(struct in_addr))) - return 1; - break; - case AF_INET6: - sin6_a = (struct sockaddr_in6 *)a; - sin6_b = (struct sockaddr_in6 *)b; - if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, - sizeof(struct in6_addr))) - return 1; - break; - } - return 0; -} - -/* linux has a default route: -local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 -*/ -static int sockaddr_is_local_routed(const struct sockaddr *a) -{ -#if __linux__ - if (a->sa_family != AF_INET) - return 0; - - uint32_t address = ((struct sockaddr_in *)a)->sin_addr.s_addr; /* already BE */ - uint32_t eightmask = htonl(0xff000000); /* /8 mask */ - uint32_t local_prefix_127 = htonl(0x7f000000); /* 127.0.0.0 */ - - if ((address & eightmask) == local_prefix_127) - return 1; -#endif - return 0; -} - -/*! Determine if the given address is a local address - * \paramin addr Socket Address - * \paramin addrlen Length of socket address in bytes - * \returns 1 if address is local, 0 otherwise. - */ -int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen) -{ - struct ifaddrs *ifaddr, *ifa; - - if (sockaddr_is_local_routed(addr)) - return 1; - - if (getifaddrs(&ifaddr) == -1) { - LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:" - " %s\n", strerror(errno)); - return -EIO; - } - - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; - if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) { - freeifaddrs(ifaddr); - return 1; - } - } - - freeifaddrs(ifaddr); - return 0; -} - -/*! Convert sockaddr_in to IP address as char string and port as uint16_t. - * \paramout addr String buffer to write IP address to, or NULL. - * \paramout addr_len Size of \a addr. - * \paramout port Pointer to uint16_t to write the port number to, or NULL. - * \paramin sin Sockaddr to convert. - * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL. - */ -size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, - const struct sockaddr_in *sin) -{ - if (port) - *port = ntohs(sin->sin_port); - - if (addr) - return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len); - - return 0; -} - -/*! Convert sockaddr to IP address as char string and port as uint16_t. - * \paramout addr String buffer to write IP address to, or NULL. - * \paramout addr_len Size of \a addr. - * \paramout port Pointer to uint16_t to write the port number to, or NULL. - * \paramin sa Sockaddr to convert. - * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL. - */ -unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, - const struct sockaddr *sa) -{ - - const struct sockaddr_in6 *sin6; - - switch (sa->sa_family) { - case AF_INET: - return osmo_sockaddr_in_to_str_and_uint(addr, addr_len, port, - (const struct sockaddr_in *)sa); - case AF_INET6: - sin6 = (const struct sockaddr_in6 *)sa; - if (port) - *port = ntohs(sin6->sin6_port); - if (addr && inet_ntop(sa->sa_family, &sin6->sin6_addr, addr, addr_len)) - return strlen(addr); - break; - } - return 0; -} - -/*! inet_ntop() wrapper for a struct sockaddr. - * \paramin sa source sockaddr to get the address from. - * \paramout dst string buffer of at least INET6_ADDRSTRLEN size. - * \returns returns a non-null pointer to dst. NULL is returned if there was an - * error, with errno set to indicate the error. - */ -const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst) -{ - const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa; - return inet_ntop(osa->u.sa.sa_family, - osa->u.sa.sa_family == AF_INET6 ? - (const void *)&osa->u.sin6.sin6_addr : - (const void *)&osa->u.sin.sin_addr, - dst, INET6_ADDRSTRLEN); -} - -/*! Get sockaddr port content (in host byte order) - * \paramin sa source sockaddr to get the port from. - * \returns returns the sockaddr port in host byte order - */ -uint16_t osmo_sockaddr_port(const struct sockaddr *sa) -{ - const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa; - switch (osa->u.sa.sa_family) { - case AF_INET6: - return ntohs(osa->u.sin6.sin6_port); - case AF_INET: - return ntohs(osa->u.sin.sin_port); - } - return 0; -} - -/*! Set sockaddr port content (to network byte order). - * \paramout sa sockaddr to set the port of. - * \paramin port port nr to set. - */ -void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port) -{ - struct osmo_sockaddr *osa = (struct osmo_sockaddr *)sa; - switch (osa->u.sa.sa_family) { - case AF_INET6: - osa->u.sin6.sin6_port = htons(port); - return; - case AF_INET: - osa->u.sin.sin_port = htons(port); - return; - } -} - -/*! Initialize a unix domain socket (including bind/connect) - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin socket_path path to identify the socket - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function creates a new unix domain socket, \a - * type and \a proto and optionally binds or connects it, depending on - * the value of \a flags parameter. - */ -#if defined(__clang__) && defined(SUN_LEN) -__attribute__((no_sanitize("undefined"))) -#endif -int osmo_sock_unix_init(uint16_t type, uint8_t proto, - const char *socket_path, unsigned int flags) -{ - struct sockaddr_un local; - int sfd, rc; - unsigned int namelen; - - if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == - (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) - return -EINVAL; - - local.sun_family = AF_UNIX; - /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */ - if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) { - LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n", - sizeof(local.sun_path), socket_path); - return -ENOSPC; - } - -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - sfd = socket(AF_UNIX, type, proto); - if (sfd < 0) - return -1; - - if (flags & OSMO_SOCK_F_CONNECT) { - rc = connect(sfd, (struct sockaddr *)&local, namelen); - if (rc < 0) - goto err; - } else { - unlink(local.sun_path); - rc = bind(sfd, (struct sockaddr *)&local, namelen); - if (rc < 0) - goto err; - } - - rc = socket_helper_tail(sfd, flags); - if (rc < 0) - return rc; - - rc = osmo_sock_init_tail(sfd, type, flags); - if (rc < 0) { - close(sfd); - sfd = -1; - } - - return sfd; -err: - close(sfd); - return -1; -} - -/*! Initialize a unix domain socket and fill \ref osmo_fd - * \paramout ofd file descriptor (will be filled in) - * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM - * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \paramin socket_path path to identify the socket - * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT - * \returns socket fd on success; negative on error - * - * This function creates (and optionally binds/connects) a socket - * using osmo_sock_unix_init, but also fills the ofd structure. - */ -int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, - const char *socket_path, unsigned int flags) -{ - return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags), flags); -} - -/*! Get the IP and/or port number on socket in separate string buffers. - * \paramin fd file descriptor of socket - * \paramout ip IP address (will be filled in when not NULL) - * \paramin ip_len length of the ip buffer - * \paramout port number (will be filled in when not NULL) - * \paramin port_len length of the port buffer - * \paramin local (true) or remote (false) name will get looked at - * \returns 0 on success; negative otherwise - */ -int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local) -{ - struct sockaddr_storage sa; - socklen_t len = sizeof(sa); - char ipbufINET6_ADDRSTRLEN, portbuf6; - int rc; - - rc = local ? getsockname(fd, (struct sockaddr*)&sa, &len) : getpeername(fd, (struct sockaddr*)&sa, &len); - if (rc < 0) - return rc; - - rc = getnameinfo((const struct sockaddr*)&sa, len, ipbuf, sizeof(ipbuf), - portbuf, sizeof(portbuf), - NI_NUMERICHOST | NI_NUMERICSERV); - if (rc < 0) - return rc; - - if (ip) - strncpy(ip, ipbuf, ip_len); - if (port) - strncpy(port, portbuf, port_len); - return 0; -} - -/*! Get local IP address on socket - * \paramin fd file descriptor of socket - * \paramout ip IP address (will be filled in) - * \paramin len length of the output buffer - * \returns 0 on success; negative otherwise - */ -int osmo_sock_get_local_ip(int fd, char *ip, size_t len) -{ - return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true); -} - -/*! Get local port on socket - * \paramin fd file descriptor of socket - * \paramout port number (will be filled in) - * \paramin len length of the output buffer - * \returns 0 on success; negative otherwise - */ -int osmo_sock_get_local_ip_port(int fd, char *port, size_t len) -{ - return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true); -} - -/*! Get remote IP address on socket - * \paramin fd file descriptor of socket - * \paramout ip IP address (will be filled in) - * \paramin len length of the output buffer - * \returns 0 on success; negative otherwise - */ -int osmo_sock_get_remote_ip(int fd, char *ip, size_t len) -{ - return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false); -} - -/*! Get remote port on socket - * \paramin fd file descriptor of socket - * \paramout port number (will be filled in) - * \paramin len length of the output buffer - * \returns 0 on success; negative otherwise - */ -int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len) -{ - return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false); -} - -/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)". - * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a - * caller provided string buffer, to avoid the dynamic talloc allocation. - * \paramin ctx talloc context from which to allocate string buffer - * \paramin fd file descriptor of socket - * \returns string identifying the connection of this socket, talloc'd from ctx. - */ -char *osmo_sock_get_name(const void *ctx, int fd) -{ - char strOSMO_SOCK_NAME_MAXLEN; - int rc; - rc = osmo_sock_get_name_buf(str, sizeof(str), fd); - if (rc <= 0) - return NULL; - return talloc_asprintf(ctx, "(%s)", str); -} - -/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10". - * This does not include braces like osmo_sock_get_name(). - * \paramout str Destination string buffer. - * \paramin str_len sizeof(str). - * \paramin fd File descriptor of socket. - * \return String length as returned by snprintf(), or negative on error. - */ -int osmo_sock_get_name_buf(char *str, size_t str_len, int fd) -{ - char hostbuf_lINET6_ADDRSTRLEN, hostbuf_rINET6_ADDRSTRLEN; - char portbuf_l6, portbuf_r6; - int rc; - - /* get local */ - if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true))) { - osmo_strlcpy(str, "<error-in-getsockname>", str_len); - return rc; - } - - /* get remote */ - if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0) - return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l); - - return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l); -} - -/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10". - * This does not include braces like osmo_sock_get_name(). - * \paramin fd File descriptor of socket. - * \return Static string buffer containing the result. - */ -const char *osmo_sock_get_name2(int fd) -{ - static __thread char strOSMO_SOCK_NAME_MAXLEN; - osmo_sock_get_name_buf(str, sizeof(str), fd); - return str; -} - -/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10". - * This does not include braces like osmo_sock_get_name(). - * \paramin fd File descriptor of socket. - * \return Static string buffer containing the result. - */ -char *osmo_sock_get_name2_c(const void *ctx, int fd) -{ - char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN); - if (!str) - return NULL; - osmo_sock_get_name_buf(str, OSMO_SOCK_NAME_MAXLEN, fd); - return str; -} - -static int sock_get_domain(int fd) -{ - int domain; -#ifdef SO_DOMAIN - socklen_t dom_len = sizeof(domain); - int rc; - - rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len); - if (rc < 0) - return rc; -#else - /* This of course sucks, but what shall we do on OSs like - * FreeBSD that don't seem to expose a method by which one can - * learn the address family of a socket? */ - domain = AF_INET; -#endif - return domain; -} - - -/*! Activate or de-activate local loop-back of transmitted multicast packets - * \paramin fd file descriptor of related socket - * \paramin enable Enable (true) or disable (false) loop-back - * \returns 0 on success; negative otherwise */ -int osmo_sock_mcast_loop_set(int fd, bool enable) -{ - int domain, loop = 0; - - if (enable) - loop = 1; - - domain = sock_get_domain(fd); - if (domain < 0) - return domain; - - switch (domain) { - case AF_INET: - return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); - case AF_INET6: - return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)); - default: - return -EINVAL; - } -} - -/*! Set the TTL of outbound multicast packets - * \paramin fd file descriptor of related socket - * \paramin ttl TTL of to-be-sent multicast packets - * \returns 0 on success; negative otherwise */ -int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl) -{ - int domain, ttli = ttl; - - domain = sock_get_domain(fd); - if (domain < 0) - return domain; - - switch (domain) { - case AF_INET: - return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli)); - case AF_INET6: - return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli)); - default: - return -EINVAL; - } -} - -/*! Set the network device to which we should bind the multicast socket - * \paramin fd file descriptor of related socket - * \paramin ifname name of network interface to user for multicast - * \returns 0 on success; negative otherwise */ -int osmo_sock_mcast_iface_set(int fd, const char *ifname) -{ - unsigned int ifindex; - struct ip_mreqn mr; - - /* first, resolve interface name to ifindex */ - ifindex = if_nametoindex(ifname); - if (ifindex == 0) - return -errno; - - /* next, configure kernel to use that ifindex for this sockets multicast traffic */ - memset(&mr, 0, sizeof(mr)); - mr.imr_ifindex = ifindex; - return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mr, sizeof(mr)); -} - - -/*! Enable/disable receiving all multicast packets, even for non-subscribed groups - * \paramin fd file descriptor of related socket - * \paramin enable Enable or Disable receiving of all packets - * \returns 0 on success; negative otherwise */ -int osmo_sock_mcast_all_set(int fd, bool enable) -{ - int domain, all = 0; - - if (enable) - all = 1; - - domain = sock_get_domain(fd); - if (domain < 0) - return domain; - - switch (domain) { - case AF_INET: -#ifdef IP_MULTICAST_ALL - return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all)); -#endif - case AF_INET6: - /* there seems no equivalent ?!? */ - default: - return -EINVAL; - } -} - -/* FreeBSD calls the socket option differently */ -#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP) -#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#endif - -/*! Subscribe to the given IP multicast group - * \paramin fd file descriptor of related scoket - * \paramin grp_addr ASCII representation of the multicast group address - * \returns 0 on success; negative otherwise */ -int osmo_sock_mcast_subscribe(int fd, const char *grp_addr) -{ - int rc, domain; - struct ip_mreq mreq; - struct ipv6_mreq mreq6; - struct in6_addr i6a; - - domain = sock_get_domain(fd); - if (domain < 0) - return domain; - - switch (domain) { - case AF_INET: - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr.s_addr = inet_addr(grp_addr); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); -#ifdef IPV6_ADD_MEMBERSHIP - case AF_INET6: - memset(&mreq6, 0, sizeof(mreq6)); - rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a); - if (rc < 0) - return -EINVAL; - mreq6.ipv6mr_multiaddr = i6a; - return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)); -#endif - default: - return -EINVAL; - } -} - -/*! Determine the matching local IP-address for a given remote IP-Address. - * \paramout local_ip caller provided memory for resulting local IP-address - * \paramin remote_ip remote IP-address - * \returns 0 on success; negative otherwise - * - * The function accepts IPv4 and IPv6 address strings. The caller must provide - * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as - * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */ -int osmo_sock_local_ip(char *local_ip, const char *remote_ip) -{ - int sfd; - int rc; - struct addrinfo addrinfo_hint; - struct addrinfo *addrinfo = NULL; - struct sockaddr_storage local_addr; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - socklen_t local_addr_len; - uint16_t family; - - /* Find out the address family (AF_INET or AF_INET6?) */ - memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint)); - addrinfo_hint.ai_family = AF_UNSPEC; - addrinfo_hint.ai_flags = AI_NUMERICHOST; - rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo); - if (rc) - return -EINVAL; - family = addrinfo->ai_family; - freeaddrinfo(addrinfo); - - /* Connect a dummy socket to trick the kernel into determining the - * ip-address of the interface that would be used if we would send - * out an actual packet */ - sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT); - if (sfd < 0) - return -EINVAL; - - /* Request the IP address of the interface that the kernel has - * actually choosen. */ - memset(&local_addr, 0, sizeof(local_addr)); - local_addr_len = sizeof(local_addr); - rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len); - close(sfd); - if (rc < 0) - return -EINVAL; - - switch (local_addr.ss_family) { - case AF_INET: - sin = (struct sockaddr_in*)&local_addr; - if (!inet_ntop(AF_INET, &sin->sin_addr, local_ip, INET_ADDRSTRLEN)) - return -EINVAL; - break; - case AF_INET6: - sin6 = (struct sockaddr_in6*)&local_addr; - if (!inet_ntop(AF_INET6, &sin6->sin6_addr, local_ip, INET6_ADDRSTRLEN)) - return -EINVAL; - break; - default: - return -EINVAL; - } - - return 0; -} - -/*! Determine the matching local address for a given remote address. - * \paramout local_ip caller provided memory for resulting local address - * \paramin remote_ip remote address - * \returns 0 on success; negative otherwise - */ -int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip, const struct osmo_sockaddr *remote_ip) -{ - int sfd; - int rc; - socklen_t local_ip_len; - - sfd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, NULL, remote_ip, OSMO_SOCK_F_CONNECT); - if (sfd < 0) - return -EINVAL; - - memset(local_ip, 0, sizeof(*local_ip)); - local_ip_len = sizeof(*local_ip); - rc = getsockname(sfd, (struct sockaddr *)local_ip, &local_ip_len); - close(sfd); - - return rc; -} - -/*! Copy the addr part, the IP address octets in network byte order, to a buffer. - * Useful for encoding network protocols. - * \paramout dst Write octets to this buffer. - * \paramin dst_maxlen Space available in buffer. - * \paramin os Sockaddr to copy IP of. - * \return nr of octets written on success, negative on error. - */ -int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os) -{ - const void *addr; - size_t len; - switch (os->u.sa.sa_family) { - case AF_INET: - addr = &os->u.sin.sin_addr; - len = sizeof(os->u.sin.sin_addr); - break; - case AF_INET6: - addr = &os->u.sin6.sin6_addr; - len = sizeof(os->u.sin6.sin6_addr); - break; - default: - return -ENOTSUP; - } - if (dst_maxlen < len) - return -ENOSPC; - memcpy(dst, addr, len); - return len; -} - -/*! Copy the addr part, the IP address octets in network byte order, from a buffer. - * Useful for decoding network protocols. - * \paramout os Write IP address to this sockaddr. - * \paramin src Source buffer to read IP address octets from. - * \paramin src_len Number of octets to copy. - * \return number of octets read on success, negative on error. - */ -int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len) -{ - void *addr; - size_t len; - *os = (struct osmo_sockaddr){0}; - switch (src_len) { - case sizeof(os->u.sin.sin_addr): - os->u.sa.sa_family = AF_INET; - addr = &os->u.sin.sin_addr; - len = sizeof(os->u.sin.sin_addr); - break; - case sizeof(os->u.sin6.sin6_addr): - os->u.sin6.sin6_family = AF_INET6; - addr = &os->u.sin6.sin6_addr; - len = sizeof(os->u.sin6.sin6_addr); - break; - default: - return -ENOTSUP; - } - memcpy(addr, src, len); - return len; -} - -/*! Compare two osmo_sockaddr. - * \paramin a - * \paramin b - * \return 0 if a and b are equal. Otherwise it follows memcmp() - */ -int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, - const struct osmo_sockaddr *b) -{ - if (a == b) - return 0; - if (!a) - return 1; - if (!b) - return -1; - - if (a->u.sa.sa_family != b->u.sa.sa_family) { - return OSMO_CMP(a->u.sa.sa_family, b->u.sa.sa_family); - } - - switch (a->u.sa.sa_family) { - case AF_INET: - return memcmp(&a->u.sin, &b->u.sin, sizeof(struct sockaddr_in)); - case AF_INET6: - return memcmp(&a->u.sin6, &b->u.sin6, sizeof(struct sockaddr_in6)); - default: - /* fallback to memcmp for remaining AF over the full osmo_sockaddr length */ - return memcmp(a, b, sizeof(struct osmo_sockaddr)); - } -} - -/*! string-format a given osmo_sockaddr address - * \paramin sockaddr the osmo_sockaddr to print - * \return pointer to the string on success; NULL on error - */ -const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr) -{ - /* INET6_ADDRSTRLEN contains already a null termination, - * adding '' '' ':' '16 bit port' */ - static __thread char bufINET6_ADDRSTRLEN + 8; - return osmo_sockaddr_to_str_buf(buf, sizeof(buf), sockaddr); -} - -/*! string-format a given osmo_sockaddr address into a user-supplied buffer. - * Same as osmo_sockaddr_to_str_buf() but returns a would-be length in snprintf() style. - * \paramin buf user-supplied output buffer - * \paramin buf_len size of the user-supplied output buffer in bytes - * \paramin sockaddr the osmo_sockaddr to print - * \return number of characters that would be written if the buffer is large enough, like snprintf(). - */ -int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr) -{ - struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; - uint16_t port = 0; - - if (!sockaddr) { - OSMO_STRBUF_PRINTF(sb, "NULL"); - return sb.chars_needed; - } - - switch (sockaddr->u.sa.sa_family) { - case AF_INET: - OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa); - if (port) - OSMO_STRBUF_PRINTF(sb, ":%u", port); - break; - case AF_INET6: - OSMO_STRBUF_PRINTF(sb, ""); - OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa); - OSMO_STRBUF_PRINTF(sb, ""); - if (port) - OSMO_STRBUF_PRINTF(sb, ":%u", port); - break; - default: - OSMO_STRBUF_PRINTF(sb, "unsupported family %d", sockaddr->u.sa.sa_family); - break; - } - - return sb.chars_needed; -} - -/*! string-format a given osmo_sockaddr address into a talloc allocated buffer. - * Like osmo_sockaddr_to_str_buf2() but returns a talloc allocated string. - * \paramin ctx talloc context to allocate from, e.g. OTC_SELECT. - * \paramin sockaddr the osmo_sockaddr to print. - * \return human readable string. - */ -char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr) -{ - OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sockaddr_to_str_buf2, sockaddr) -} - -/*! string-format a given osmo_sockaddr address into a user-supplied buffer. - * Like osmo_sockaddr_to_str_buf2() but returns buf, or NULL if too short. - * \paramin buf user-supplied output buffer - * \paramin buf_len size of the user-supplied output buffer in bytes - * \paramin sockaddr the osmo_sockaddr to print - * \return pointer to the string on success; NULL on error - */ -char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len, - const struct osmo_sockaddr *sockaddr) -{ - int chars_needed = osmo_sockaddr_to_str_buf2(buf, buf_len, sockaddr); - if (chars_needed >= buf_len) - return NULL; - return buf; -} - -/*! Set the DSCP (differentiated services code point) of a socket. - * \paramin dscp DSCP value in range 0..63 - * \returns 0 on success; negative on error. */ -int osmo_sock_set_dscp(int fd, uint8_t dscp) -{ - struct sockaddr_storage local_addr; - socklen_t local_addr_len = sizeof(local_addr); - uint8_t tos; - socklen_t tos_len = sizeof(tos); - int tclass; - socklen_t tclass_len = sizeof(tclass); - int rc; - - /* DSCP is a 6-bit value stored in the upper 6 bits of the 8-bit TOS */ - if (dscp > 63) - return -EINVAL; - - rc = getsockname(fd, (struct sockaddr *)&local_addr, &local_addr_len); - if (rc < 0) - return rc; - - switch (local_addr.ss_family) { - case AF_INET: - /* read the original value */ - rc = getsockopt(fd, IPPROTO_IP, IP_TOS, &tos, &tos_len); - if (rc < 0) - return rc; - /* mask-in the DSCP into the upper 6 bits */ - tos &= 0x03; - tos |= dscp << 2; - /* and write it back to the kernel */ - rc = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - break; - case AF_INET6: - /* read the original value */ - rc = getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, &tclass_len); - if (rc < 0) - return rc; - /* mask-in the DSCP into the upper 6 bits */ - tclass &= 0x03; - tclass |= dscp << 2; - /* and write it back to the kernel */ - rc = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); - break; - case AF_UNSPEC: - default: - LOGP(DLGLOBAL, LOGL_ERROR, "No DSCP support for socket family %u\n", - local_addr.ss_family); - rc = -1; - break; - } - - return rc; -} - -/*! Set the priority value of a socket. - * \paramin prio priority value. Values outside 0..6 require CAP_NET_ADMIN. - * \returns 0 on success; negative on error. */ -int osmo_sock_set_priority(int fd, int prio) -{ - /* and write it back to the kernel */ - return setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)); -} - -#endif /* HAVE_SYS_SOCKET_H */ - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/tdef.c
Deleted
@@ -1,369 +0,0 @@ -/*! \file tdef.c - * Implementation to define Tnnn timers globally and use for FSM state changes. - */ -/* - * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> - * - * All Rights Reserved - * - * SPDX-License-Identifier: GPL-2.0+ - * - * Author: Neels Hofmeyr <neels@hofmeyr.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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <limits.h> -#include <errno.h> - -#include <osmocom/core/fsm.h> -#include <osmocom/core/tdef.h> - -/*! \addtogroup Tdef - * - * Implementation to define Tnnn timers globally and use for FSM state changes. - * - * See also \ref Tdef_VTY - * - * osmo_tdef provides: - * - * - a list of Tnnnn (GSM) timers with description, unit and default value. - * - vty UI to allow users to configure non-default timeouts. - * - API to tie T timers to osmo_fsm states and set them on state transitions. - * - * - a few standard units (minute, second, millisecond) as well as a custom unit - * (which relies on the timer's human readable description to indicate the - * meaning of the value). - * - conversion for standard units: for example, some GSM timers are defined in - * minutes, while our FSM definitions need timeouts in seconds. Conversion is - * for convenience only and can be easily avoided via the custom unit. - * - * By keeping separate osmo_tdef arrays, several groups of timers can be kept - * separately. The VTY tests in tests/tdef/ showcase different schemes: - * - * - \ref tests/tdef/tdef_vty_config_root_test.c: - * Keep several timer definitions in separately named groups: showcase the - * osmo_tdef_vty_groups*() API. Each timer group exists exactly once. - * - * - \ref tests/tdef/tdef_vty_config_subnode_test.c: - * Keep a single list of timers without separate grouping. - * Put this list on a specific subnode below the CONFIG_NODE. - * There could be several separate subnodes with timers like this, i.e. - * continuing from this example, sets of timers could be separated by placing - * timers in specific config subnodes instead of using the global group name. - * - * - \ref tests/tdef/tdef_vty_dynamic_test.c: - * Dynamically allocate timer definitions per each new created object. - * Thus there can be an arbitrary number of independent timer definitions, one - * per allocated object. - * - * osmo_tdef was introduced because: - * - * - without osmo_tdef, each invocation of osmo_fsm_inst_state_chg() needs to be - * programmed with the right timeout value, for all code paths that invoke this - * state change. It is a likely source of errors to get one of them wrong. By - * defining a T timer exactly for an FSM state, the caller can merely invoke the - * state change and trust on the original state definition to apply the correct - * timeout. - * - * - it is helpful to have a standardized config file UI to provide user - * configurable timeouts, instead of inventing new VTY commands for each - * separate application of T timer numbers. See \ref tdef_vty.h. - * - * @{ - * \file tdef.c - */ - -/*! a = return_val * b. \return 0 if factor is below 1. */ -static unsigned long osmo_tdef_factor(enum osmo_tdef_unit a, enum osmo_tdef_unit b) -{ - if (b == a - || b == OSMO_TDEF_CUSTOM || a == OSMO_TDEF_CUSTOM) - return 1; - - switch (b) { - case OSMO_TDEF_US: - switch (a) { - case OSMO_TDEF_MS: - return 1000; - case OSMO_TDEF_S: - return 1000*1000; - case OSMO_TDEF_M: - return 60*1000*1000; - default: - return 0; - } - case OSMO_TDEF_MS: - switch (a) { - case OSMO_TDEF_S: - return 1000; - case OSMO_TDEF_M: - return 60*1000; - default: - return 0; - } - case OSMO_TDEF_S: - switch (a) { - case OSMO_TDEF_M: - return 60; - default: - return 0; - } - default: - return 0; - } -} - -/*! \return val in unit to_unit, rounded up to the next integer value and clamped to ULONG_MAX, or 0 if val == 0. */ -static unsigned long osmo_tdef_round(unsigned long val, enum osmo_tdef_unit from_unit, enum osmo_tdef_unit to_unit) -{ - unsigned long f; - if (!val) - return 0; - - f = osmo_tdef_factor(from_unit, to_unit); - if (f == 1) - return val; - if (f < 1) { - f = osmo_tdef_factor(to_unit, from_unit); - return (val / f) + (val % f? 1 : 0); - } - /* range checking */ - if (f > (ULONG_MAX / val)) - return ULONG_MAX; - return val * f; -} - -/*! Set all osmo_tdef values to the default_val. - * It is convenient to define a tdefs array by setting only the default_val, and calling osmo_tdefs_reset() once for - * program startup. (See also osmo_tdef_vty_init()). - * During call to this function, default values are verified to be inside valid range; process is aborted otherwise. - * \paramin tdefs Array of timer definitions, last entry being fully zero. - */ -void osmo_tdefs_reset(struct osmo_tdef *tdefs) -{ - struct osmo_tdef *t; - osmo_tdef_for_each(t, tdefs) { - if (!osmo_tdef_val_in_range(t, t->default_val)) { - char range_str64; - osmo_tdef_range_str_buf(range_str, sizeof(range_str), t); - osmo_panic("%s:%d Timer " OSMO_T_FMT " contains default value %lu not in range %s\n", - __FILE__, __LINE__, OSMO_T_FMT_ARGS(t->T), t->default_val, range_str); - } - t->val = t->default_val; - } -} - -/*! Return the value of a T timer from a list of osmo_tdef, in the given unit. - * If no such timer is defined, return the default value passed, or abort the program if default < 0. - * - * Round up any value match as_unit: 1100 ms as OSMO_TDEF_S becomes 2 seconds, as OSMO_TDEF_M becomes one minute. - * However, always return a value of zero as zero (0 ms as OSMO_TDEF_M still is 0 m). - * - * Range: even though the value range is unsigned long here, in practice, using ULONG_MAX as value for a timeout in - * seconds may actually wrap to negative or low timeout values (e.g. in struct timeval). It is recommended to stay below - * INT_MAX seconds. See also osmo_fsm_inst_state_chg(). - * - * Usage example: - * - * struct osmo_tdef global_T_defs = { - * { .T=7, .default_val=50, .desc="Water Boiling Timeout" }, // default is .unit=OSMO_TDEF_S == 0 - * { .T=8, .default_val=300, .desc="Tea brewing" }, - * { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" }, - * { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" }, - * {} // <-- important! last entry shall be zero - * }; - * osmo_tdefs_reset(global_T_defs); // make all values the default - * osmo_tdef_vty_init(global_T_defs, CONFIG_NODE); - * - * val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_S, -1); // -> 50 - * sleep(val); - * - * val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_M, -1); // 50 seconds becomes 1 minute -> 1 - * sleep_minutes(val); - * - * val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, 3); // not defined, returns 3 - * - * val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, -1); // not defined, program aborts! - * - * \paramin tdefs Array of timer definitions, last entry must be fully zero initialized. - * \paramin T Timer number to get the value for. - * \paramin as_unit Return timeout value in this unit. - * \paramin val_if_not_present Fallback value to return if no timeout is defined. - * \return Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present. - */ -unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, long val_if_not_present) -{ - const struct osmo_tdef *t = osmo_tdef_get_entry((struct osmo_tdef*)tdefs, T); - if (!t) { - OSMO_ASSERT(val_if_not_present >= 0); - return val_if_not_present; - } - return osmo_tdef_round(t->val, t->unit, as_unit); -} - -/*! Find tdef entry matching T. - * This is useful for manipulation, which is usually limited to the VTY configuration. To retrieve a timeout value, - * most callers probably should use osmo_tdef_get() instead. - * \paramin tdefs Array of timer definitions, last entry being fully zero. - * \paramin T Timer number to get the entry for. - * \return osmo_tdef entry matching T in given array, or NULL if no match is found. - */ -struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T) -{ - struct osmo_tdef *t; - osmo_tdef_for_each(t, tdefs) { - if (t->T == T) - return t; - } - return NULL; -} - -/*! Set value in entry matching T, converting val from val_unit to unit of T. - * The converted value is rounded up to the next integer value of T's unit and clamped to ULONG_MAX, or 0 if val == 0. - * \paramin tdefs Array of timer definitions, last entry being fully zero. - * \paramin T Timer number to set the value for. - * \paramin val The new timer value to set. - * \paramin val_unit Units of value in parameter val. - * \return 0 on success, negative on error. - */ -int osmo_tdef_set(struct osmo_tdef *tdefs, int T, unsigned long val, enum osmo_tdef_unit val_unit) -{ - unsigned long new_val; - struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T); - if (!t) - return -EEXIST; - - new_val = osmo_tdef_round(val, val_unit, t->unit); - if (!osmo_tdef_val_in_range(t, new_val)) - return -ERANGE; - - t->val = new_val; - return 0; -} - -/*! Check if value new_val is in range of valid possible values for timer entry tdef. - * \paramin tdef Timer entry from a timer definition table. - * \paramin new_val The value whose validity to check, in units as per this timer entry. - * \return true if inside range, false otherwise. - */ -bool osmo_tdef_val_in_range(struct osmo_tdef *tdef, unsigned long new_val) -{ - return new_val >= tdef->min_val && (!tdef->max_val || new_val <= tdef->max_val); -} - -/*! Write string representation of osmo_tdef range into buf. - * \paramin buf The buffer where the string representation is stored. - * \paramin buf_len Length of buffer in bytes. - * \paramin tdef Timer entry from a timer definition table. - * \return The number of characters printed on success (or number of characters - * which would have been written to the final string if enough space - * had been available), negative on error. See snprintf(). - */ -int osmo_tdef_range_str_buf(char *buf, size_t buf_len, struct osmo_tdef *t) -{ - int ret, len = 0, offset = 0, rem = buf_len; - - buf0 = '\0'; - ret = snprintf(buf + offset, rem, "%lu .. ", t->min_val); - if (ret < 0) - return ret; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - if (t->max_val) - ret = snprintf(buf + offset, rem, "%lu", t->max_val); - else - ret = snprintf(buf + offset, rem, "inf"); - if (ret < 0) - return ret; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - return len; -} - -/*! Using osmo_tdef for osmo_fsm_inst: find a given state's osmo_tdef_state_timeout entry. - * - * The timeouts_array shall contain exactly 32 elements, regardless whether only some of them are actually populated - * with nonzero values. 32 corresponds to the number of states allowed by the osmo_fsm_* API. Lookup is by array index. - * Not populated entries imply a state change invocation without timeout. - * - * For example: - * - * struct osmo_tdef_state_timeout my_fsm_timeouts32 = { - * MY_FSM_STATE_3 = { .T = 423 }, // look up timeout configured for T423 - * MY_FSM_STATE_7 = { .keep_timer = true, .T = 235 }, // keep previous timer if running, or start T235 - * MY_FSM_STATE_8 = { .keep_timer = true }, // keep previous state's T number, continue timeout. - * // any state that is omitted will remain zero == no timeout - * }; - * osmo_tdef_get_state_timeout(MY_FSM_STATE_0, &my_fsm_timeouts) -> NULL, - * osmo_tdef_get_state_timeout(MY_FSM_STATE_7, &my_fsm_timeouts) -> { .T = 235 } - * - * The intention is then to obtain the timer like osmo_tdef_get(global_T_defs, T=235); see also - * fsm_inst_state_chg_T() below. - * - * \paramin state State constant to look up. - * \paramin timeouts_array Array32 of struct osmo_tdef_state_timeout defining which timer number to use per state. - * \return A struct osmo_tdef_state_timeout entry, or NULL if that entry is zero initialized. - */ -const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array) -{ - const struct osmo_tdef_state_timeout *t; - OSMO_ASSERT(state < 32); - t = &timeouts_arraystate; - if (!t->keep_timer && !t->T) - return NULL; - return t; -} - -/*! See invocation macro osmo_tdef_fsm_inst_state_chg() instead. - * \paramin file Source file name, like __FILE__. - * \paramin line Source file line number, like __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, long default_timeout, - const char *file, int line) -{ - const struct osmo_tdef_state_timeout *t = osmo_tdef_get_state_timeout(state, timeouts_array); - unsigned long val = 0; - - /* No timeout defined for this state? */ - if (!t) - return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line); - - if (t->T) - val = osmo_tdef_get(tdefs, t->T, OSMO_TDEF_S, default_timeout); - - if (t->keep_timer) { - if (t->T) - return _osmo_fsm_inst_state_chg_keep_or_start_timer(fi, state, val, t->T, file, line); - else - return _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line); - } - - /* val is always initialized here, because if t->keep_timer is false, t->T must be != 0. - * Otherwise osmo_tdef_get_state_timeout() would have returned NULL. */ - OSMO_ASSERT(t->T); - return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line); -} - -const struct value_string osmo_tdef_unit_names = { - { OSMO_TDEF_S, "s" }, - { OSMO_TDEF_MS, "ms" }, - { OSMO_TDEF_M, "m" }, - { OSMO_TDEF_CUSTOM, "custom-unit" }, - { OSMO_TDEF_US, "us" }, - {} -}; - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/time_cc.c
Deleted
@@ -1,228 +0,0 @@ -/*! \file foo.c - * 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/>. - * - */ - -/*! \addtogroup time_cc - * - * Report the cumulative counter of time for which a flag is true as rate counter. - * - * Useful for reporting cumulative time counters as defined in 3GPP TS 52.402, for example allAvailableSDCCHAllocated, - * allAvailableTCHAllocated, availablePDCHAllocatedTime. - * - * For a usage example, see the description of struct osmo_time_cc. - * - * @{ - * \file time_cc.c - */ -#include "config.h" -#ifdef HAVE_CLOCK_GETTIME - -#include <limits.h> -#include <time.h> - -#include <osmocom/core/tdef.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/time_cc.h> - -#define GRAN_USEC(TIME_CC) ((TIME_CC)->cfg.gran_usec ? : 1000000) -#define ROUND_THRESHOLD_USEC(TIME_CC) ((TIME_CC)->cfg.round_threshold_usec ? \ - OSMO_MIN((TIME_CC)->cfg.round_threshold_usec, GRAN_USEC(TIME_CC)) \ - : (GRAN_USEC(TIME_CC) / 2)) - -static uint64_t time_now_usec() -{ - struct timespec tp; - if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp)) - return 0; - return (uint64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000; -} - -static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now); - -static void osmo_time_cc_update_from_tdef(struct osmo_time_cc *tc, uint64_t now) -{ - bool do_forget_sum = false; - if (!tc->cfg.T_defs) - return; - if (tc->cfg.T_gran) { - uint64_t was = GRAN_USEC(tc); - tc->cfg.gran_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_gran, OSMO_TDEF_US, -1); - if (was != GRAN_USEC(tc)) - do_forget_sum = true; - } - if (tc->cfg.T_round_threshold) - tc->cfg.round_threshold_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_round_threshold, - OSMO_TDEF_US, -1); - if (tc->cfg.T_forget_sum) { - uint64_t was = tc->cfg.forget_sum_usec; - tc->cfg.forget_sum_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_forget_sum, OSMO_TDEF_US, -1); - if (tc->cfg.forget_sum_usec && was != tc->cfg.forget_sum_usec) - do_forget_sum = true; - } - - if (do_forget_sum && tc->sum) - osmo_time_cc_forget_sum(tc, now); -} - -static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now); - -/*! Clear out osmo_timer and internal counting state of struct osmo_time_cc. The .cfg remains unaffected. After calling, - * the osmo_time_cc instance can be used again to accumulate state as if it had just been initialized. */ -void osmo_time_cc_cleanup(struct osmo_time_cc *tc) -{ - osmo_timer_del(&tc->timer); - *tc = (struct osmo_time_cc){ - .cfg = tc->cfg, - }; -} - -static void osmo_time_cc_start(struct osmo_time_cc *tc, uint64_t now) -{ - osmo_time_cc_cleanup(tc); - tc->start_time = now; - tc->last_counted_time = now; - osmo_time_cc_update_from_tdef(tc, now); - osmo_time_cc_schedule_timer(tc, now); -} - -static void osmo_time_cc_count_time(struct osmo_time_cc *tc, uint64_t now) -{ - uint64_t time_delta = now - tc->last_counted_time; - tc->last_counted_time = now; - if (!tc->flag_state) - return; - /* Flag is currently true, cumulate the elapsed time */ - tc->total_sum += time_delta; - tc->sum += time_delta; -} - -static void osmo_time_cc_report(struct osmo_time_cc *tc, uint64_t now) -{ - uint64_t delta; - uint64_t n; - /* We report a sum "rounded up", ahead of time. If the granularity period has not yet elapsed after the last - * reporting, do not report again yet. */ - if (tc->reported_sum > tc->sum) - return; - delta = tc->sum - tc->reported_sum; - /* elapsed full periods */ - n = delta / GRAN_USEC(tc); - /* If the delta has passed round_threshold (normally half of gran_usec), increment. */ - delta -= n * GRAN_USEC(tc); - if (delta >= ROUND_THRESHOLD_USEC(tc)) - n++; - if (!n) - return; - - /* integer sanity, since rate_ctr_add() takes an int argument. */ - if (n > INT_MAX) - n = INT_MAX; - if (tc->cfg.rate_ctr) - rate_ctr_add(tc->cfg.rate_ctr, n); - /* Store the increments of gran_usec that were counted. */ - tc->reported_sum += n * GRAN_USEC(tc); -} - -static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now) -{ - tc->reported_sum = 0; - tc->sum = 0; - - if (tc->last_counted_time < now) - tc->last_counted_time = now; -} - -/*! Initialize struct osmo_time_cc. Call this once before use, and before setting up the .cfg items. */ -void osmo_time_cc_init(struct osmo_time_cc *tc) -{ - *tc = (struct osmo_time_cc){0}; -} - -/*! Report state to be recorded by osmo_time_cc instance. Setting an unchanged state repeatedly has no effect. */ -void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag) -{ - uint64_t now = time_now_usec(); - if (!tc->start_time) - osmo_time_cc_start(tc, now); - /* No flag change == no effect */ - if (flag == tc->flag_state) - return; - /* Sum up elapsed time, report increments for that. */ - osmo_time_cc_count_time(tc, now); - osmo_time_cc_report(tc, now); - tc->flag_state = flag; - osmo_time_cc_schedule_timer(tc, now); -} - -static void osmo_time_cc_timer_cb(void *data) -{ - struct osmo_time_cc *tc = data; - uint64_t now = time_now_usec(); - - osmo_time_cc_update_from_tdef(tc, now); - - if (tc->flag_state) { - osmo_time_cc_count_time(tc, now); - osmo_time_cc_report(tc, now); - } else if (tc->cfg.forget_sum_usec && tc->sum - && (now >= tc->last_counted_time + tc->cfg.forget_sum_usec)) { - osmo_time_cc_forget_sum(tc, now); - } - osmo_time_cc_schedule_timer(tc, now); -} - -/*! Figure out the next time we should do anything, if the flag state remains unchanged. */ -static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now) -{ - uint64_t next_event = UINT64_MAX; - - osmo_time_cc_update_from_tdef(tc, now); - - /* If it is required, when will the next forget_sum happen? */ - if (tc->cfg.forget_sum_usec && !tc->flag_state && tc->sum > 0) { - uint64_t next_forget_time = tc->last_counted_time + tc->cfg.forget_sum_usec; - next_event = OSMO_MIN(next_event, next_forget_time); - } - /* Next rate_ctr increment? */ - if (tc->flag_state) { - uint64_t next_inc = now + (tc->reported_sum - tc->sum) + ROUND_THRESHOLD_USEC(tc); - next_event = OSMO_MIN(next_event, next_inc); - } - - /* No event coming up? */ - if (next_event == UINT64_MAX) - return; - - if (next_event <= now) - next_event = 0; - else - next_event -= now; - - osmo_timer_setup(&tc->timer, osmo_time_cc_timer_cb, tc); - osmo_timer_del(&tc->timer); - osmo_timer_schedule(&tc->timer, next_event / 1000000, next_event % 1000000); -} - -#endif /* HAVE_CLOCK_GETTIME */ - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/use_count.c
Deleted
@@ -1,304 +0,0 @@ -/*! \file use_count.c - * Generic object usage counter Implementation (get, put and deallocate on zero count). - */ -/* - * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * - * All Rights Reserved - * - * Author: Neels Hofmeyr <neels@hofmeyr.de> - * - * 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 <errno.h> -#include <inttypes.h> -#include <string.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/use_count.h> - -/*! \addtogroup use_count - * - * Generic object usage counter (get, put and deallocate on zero count). - * - * For an example and a detailed description, see struct osmo_use_count. - * - * @{ - * \file use_count.c - */ - -/*! Add two int32_t but make sure to min- and max-clamp at INT32_MIN and INT32_MAX, respectively. */ -static inline bool count_safe(int32_t *val_p, int32_t add) -{ - int32_t val = *val_p; - - /* A simpler implementation would just let the integer overflow and compare with previous value afterwards, but - * that causes runtime errors in the address sanitizer. So let's just do this without tricks. */ - if (add < 0 && val < 0 && val - INT32_MIN < -add) { - *val_p = INT32_MIN; - return false; - } - - if (add > 0 && val > 0 && INT32_MAX - val < add) { - *val_p = INT32_MAX; - return false; - } - - *val_p = val + add; - return true; -} - -/*! Return the sum of all use counts, min- and max-clamped at INT32_MIN and INT32_MAX. - * \paramin uc Use counts to sum up. - * \return Accumulated counts, or 0 if uc is NULL. - */ -int32_t osmo_use_count_total(const struct osmo_use_count *uc) -{ - struct osmo_use_count_entry *e; - int32_t total = 0; - - if (!uc || !uc->use_counts.next) - return 0; - - llist_for_each_entry(e, &uc->use_counts, entry) { - count_safe(&total, e->count); - } - return total; -} - -/*! Return use count by a single use token. - * \paramin uc Use counts to look up in. - * \paramin use Use token. - * \return Use count, or 0 if uc is NULL or use token is not present. - */ -int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use) -{ - const struct osmo_use_count_entry *e; - if (!uc) - return 0; - e = osmo_use_count_find(uc, use); - if (!e) - return 0; - return e->count; -} - -/*! Write a comprehensive listing of use counts to a string buffer. - * Reads like "12 (3*barring,fighting,8*kungfoo)". - * \paraminout buf Destination buffer. - * \paramin buf_len sizeof(buf). - * \paramin uc Use counts to print. - * \return buf, always nul-terminated (except when buf_len < 1). - */ -const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc) -{ - osmo_use_count_to_str_buf(buf, buf_len, uc); - return buf; -} - -/*! Write a comprehensive listing of use counts to a string buffer. - * Reads like "12 (3*barring,fighting,8*kungfoo)". - * \paraminout buf Destination buffer. - * \paramin buf_len sizeof(buf). - * \paramin uc Use counts to print. - * \return number of bytes that would be written, like snprintf(). - */ -int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc) -{ - int32_t count = osmo_use_count_total(uc); - struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; - struct osmo_use_count_entry *e; - bool first; - - OSMO_STRBUF_PRINTF(sb, "%" PRId32 " (", count); - - if (!uc->use_counts.next) - goto uninitialized; - - first = true; - llist_for_each_entry(e, &uc->use_counts, entry) { - if (!e->count) - continue; - if (!first) - OSMO_STRBUF_PRINTF(sb, ","); - first = false; - if (e->count != 1) - OSMO_STRBUF_PRINTF(sb, "%" PRId32 "*", e->count); - OSMO_STRBUF_PRINTF(sb, "%s", e->use ? : "NULL"); - } - if (first) - OSMO_STRBUF_PRINTF(sb, "-"); - -uninitialized: - OSMO_STRBUF_PRINTF(sb, ")"); - return sb.chars_needed; -} - -/*! Write a comprehensive listing of use counts to a talloc allocated string buffer. - * Reads like "12 (3*barring,fighting,8*kungfoo)". - * \paramin ctx talloc pool to allocate from. - * \paramin uc Use counts to print. - * \return buf, always nul-terminated. - */ -char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc) -{ - OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_use_count_to_str_buf, uc) -} - -/* Return a use token's use count entry -- probably you want osmo_use_count_by() instead. - * \paramin uc Use counts to look up in. - * \paramin use Use token. - * \return matching entry, or NULL if not present. - */ -struct osmo_use_count_entry *osmo_use_count_find(const struct osmo_use_count *uc, const char *use) -{ - struct osmo_use_count_entry *e; - if (!uc->use_counts.next) - return NULL; - llist_for_each_entry(e, &uc->use_counts, entry) { - if (e->use == use || (use && e->use && !strcmp(e->use, use))) - return e; - } - return NULL; -} - -/*! Find a use count entry that currently has zero count, and re-use that for this new use token. */ -static struct osmo_use_count_entry *osmo_use_count_repurpose_zero_entry(struct osmo_use_count *uc, const char *use) -{ - struct osmo_use_count_entry *e; - if (!uc->use_counts.next) - return NULL; - llist_for_each_entry(e, &uc->use_counts, entry) { - if (!e->count) { - e->use = use; - return e; - } - } - return NULL; -} - -/*! Allocate a new use count entry, happens implicitly in osmo_use_count_get_put(). */ -static struct osmo_use_count_entry *osmo_use_count_create(struct osmo_use_count *uc, const char *use) -{ - struct osmo_use_count_entry *e = talloc_zero(uc->talloc_object, struct osmo_use_count_entry); - if (!e) - return NULL; - *e = (struct osmo_use_count_entry){ - .use_count = uc, - .use = use, - }; - if (!uc->use_counts.next) - INIT_LLIST_HEAD(&uc->use_counts); - llist_add_tail(&e->entry, &uc->use_counts); - return e; -} - -/*! Deallocate a use count entry. - * Normally, this is not necessary -- it is ok and even desirable to leave use count entries around even when they reach - * a count of zero, until the use_count->talloc_object deallocates and removes all of them in one flush. This avoids - * repeated allocation and deallocation for use tokens, because use count entries that have reached zero count are - * repurposed for any other use tokens. A cleanup makes sense only if a very large number of differing use tokens surged - * at the same time, and the owning object will not be deallocated soon; if so, this should be done by the - * osmo_use_count_cb_t implementation. - * - * osmo_use_count_free() must *not* be called on use count entries that were added by - * osmo_use_count_make_static_entries(). This is the responsibility of the osmo_use_count_cb_t() implementation. - * - * \paramin use_count_entry Use count entry to unlist and free. - */ -void osmo_use_count_free(struct osmo_use_count_entry *use_count_entry) -{ - if (!use_count_entry) - return; - llist_del(&use_count_entry->entry); - talloc_free(use_count_entry); -} - -/*! Implementation for osmo_use_count_get_put(), which can also be directly invoked to pass source file information. For - * arguments besides file and line, see osmo_use_count_get_put(). - * \paramin file Source file path, as in __FILE__. - * \paramin line Source file line, as in __LINE__. - */ -int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t change, - const char *file, int line) -{ - struct osmo_use_count_entry *e; - int32_t old_use_count; - if (!change) - return 0; - - e = osmo_use_count_find(uc, use); - if (!e) - e = osmo_use_count_repurpose_zero_entry(uc, use); - if (!e) - e = osmo_use_count_create(uc, use); - if (!e) - return -ENOMEM; - - if (!e->count) { - /* move to end */ - llist_del(&e->entry); - llist_add_tail(&e->entry, &uc->use_counts); - } - - old_use_count = e->count; - if (!count_safe(&e->count, change)) { - e->count = old_use_count; - return -ERANGE; - } - - if (uc->use_cb) - return uc->use_cb(e, old_use_count, file, line); - return 0; -} - -/*! Add N static use token entries to avoid dynamic allocation of use count tokens. - * When not using this function, use count entries are talloc allocated from uc->talloc_object as talloc context. This - * means that there are small dynamic allocations for each use count token. osmo_use_count_get_put() normally leaves - * zero-count entries around and re-purposes them later, so the number of small allocations is at most the number of - * concurrent differently-named uses of the same object. If that is not enough, this function allows completely avoiding - * dynamic use count allocations, by adding N static entries with a zero count and a NULL use token. They will be used - * by osmo_use_count_get_put(), and, if the caller avoids using osmo_use_count_free(), the osmo_use_count implementation - * never deallocates them. The idea is that the entries are members of the uc->talloc_object, or that they will by other - * means be implicitly deallocated by the talloc_object. It is fine to call - * osmo_use_count_make_static_entries(buf_n_entries=N) and later have more than N concurrent uses, i.e. it is no problem - * to mix static and dynamic entries. To completely avoid dynamic use count entries, N has to >= the maximum number of - * concurrent differently-named uses that will occur in the lifetime of the talloc_object. - * - * struct my_object { - * struct osmo_use_count use_count; - * struct osmo_use_count_entry use_count_buf3; // planning for 3 concurrent users - * }; - * - * void example() { - * struct my_object *o = talloc_zero(ctx, struct my_object); - * osmo_use_count_make_static_entries(&o->use_count, o->use_count_buf, ARRAY_SIZE(o->use_count_buf)); - * } - */ -void osmo_use_count_make_static_entries(struct osmo_use_count *uc, struct osmo_use_count_entry *buf, - size_t buf_n_entries) -{ - size_t idx; - if (!uc->use_counts.next) - INIT_LLIST_HEAD(&uc->use_counts); - for (idx = 0; idx < buf_n_entries; idx++) { - struct osmo_use_count_entry *e = &bufidx; - *e = (struct osmo_use_count_entry){ - .use_count = uc, - }; - llist_add_tail(&e->entry, &uc->use_counts); - } -} - -/*! @} */
View file
libosmocore_1.7.0.tar.xz/src/utils.c
Deleted
@@ -1,1539 +0,0 @@ -/* - * (C) 2011 by Harald Welte <laforge@gnumonks.org> - * (C) 2011 by Sylvain Munaut <tnt@246tNt.com> - * (C) 2014 by Nils O. Selåsdal <noselasd@fiane.dyndns.org> - * - * 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 <stdbool.h> -#include <string.h> -#include <stdint.h> -#include <errno.h> -#include <stdio.h> -#include <inttypes.h> -#include <limits.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/bit64gen.h> - - -/*! \addtogroup utils - * @{ - * various utility routines - * - * \file utils.c */ - -static __thread char namebuf255; -/* shared by osmo_str_tolower() and osmo_str_toupper() */ -static __thread char capsbuf128; - -/*! get human-readable string for given value - * \paramin vs Array of value_string tuples - * \paramin val Value to be converted - * \returns pointer to human-readable string - * - * If val is found in vs, the array's string entry is returned. Otherwise, an - * "unknown" string containing the actual value is composed in a static buffer - * that is reused across invocations. - */ -const char *get_value_string(const struct value_string *vs, uint32_t val) -{ - const char *str = get_value_string_or_null(vs, val); - if (str) - return str; - - snprintf(namebuf, sizeof(namebuf), "unknown 0x%"PRIx32, val); - namebufsizeof(namebuf) - 1 = '\0'; - return namebuf; -} - -/*! get human-readable string or NULL for given value - * \paramin vs Array of value_string tuples - * \paramin val Value to be converted - * \returns pointer to human-readable string or NULL if val is not found - */ -const char *get_value_string_or_null(const struct value_string *vs, - uint32_t val) -{ - int i; - - if (!vs) - return NULL; - - for (i = 0;; i++) { - if (vsi.value == 0 && vsi.str == NULL) - break; - if (vsi.value == val) - return vsi.str; - } - - return NULL; -} - -/*! get numeric value for given human-readable string - * \paramin vs Array of value_string tuples - * \paramin str human-readable string - * \returns numeric value (>0) or negative numer in case of error - */ -int get_string_value(const struct value_string *vs, const char *str) -{ - int i; - - for (i = 0;; i++) { - if (vsi.value == 0 && vsi.str == NULL) - break; - if (!strcasecmp(vsi.str, str)) - return vsi.value; - } - return -EINVAL; -} - -/*! Convert BCD-encoded digit into printable character - * \paramin bcd A single BCD-encoded digit - * \returns single printable character - */ -char osmo_bcd2char(uint8_t bcd) -{ - if (bcd < 0xa) - return '0' + bcd; - else - return 'A' + (bcd - 0xa); -} - -/*! Convert number in ASCII to BCD value - * \paramin c ASCII character - * \returns BCD encoded value of character - */ -uint8_t osmo_char2bcd(char c) -{ - if (c >= '0' && c <= '9') - return c - 0x30; - else if (c >= 'A' && c <= 'F') - return 0xa + (c - 'A'); - else if (c >= 'a' && c <= 'f') - return 0xa + (c - 'a'); - else - return 0; -} - -/*! Convert BCD to string. - * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd0 & 0xf, nibble 1 is bcd0 >> 4, nibble - * 3 is bcd1 & 0xf, etc.. - * \paramout dst Output string buffer, is always nul terminated when dst_size > 0. - * \paramin dst_size sizeof() the output string buffer. - * \paramin bcd Binary coded data buffer. - * \paramin start_nibble Offset to start from, in nibbles, typically 1 to skip the first nibble. - * \paramin end_nibble Offset to stop before, in nibbles, e.g. sizeof(bcd)*2 - (bcd0 & GSM_MI_ODD? 0:1). - * \paramin allow_hex If false, return error if there are digits other than 0-9. If true, return those as A-F. - * \returns The strlen that would be written if the output buffer is large enough, excluding nul byte (like - * snprintf()), or -EINVAL if allow_hex is false and a digit > 9 is encountered. On -EINVAL, the conversion is - * still completed as if allow_hex were passed as true. Return -ENOMEM if dst is NULL or dst_size is zero. - * If end_nibble <= start_nibble, write an empty string to dst and return 0. - */ -int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex) -{ - char *dst_end = dst + dst_size - 1; - int nibble_i; - int rc = 0; - - if (!dst || dst_size < 1 || start_nibble < 0) - return -ENOMEM; - - for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) { - uint8_t nibble = bcdnibble_i >> 1; - if ((nibble_i & 1)) - nibble >>= 4; - nibble &= 0xf; - - if (!allow_hex && nibble > 9) - rc = -EINVAL; - - *dst = osmo_bcd2char(nibble); - } - *dst = '\0'; - - if (rc < 0) - return rc; - return OSMO_MAX(0, end_nibble - start_nibble); -} - -/*! Convert string to BCD. - * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd0 & 0x0f, nibble 1 is bcd0 & 0xf0, nibble - * 3 is bcd1 & 0x0f, etc.. - * \paramout dst Output BCD buffer. - * \paramin dst_size sizeof() the output string buffer. - * \paramin digits String containing decimal or hexadecimal digits in upper or lower case. - * \paramin start_nibble Offset to start from, in nibbles, typically 1 to skip the first (MI type) nibble. - * \paramin end_nibble Negative to write all digits found in str, followed by 0xf nibbles to fill any started octet. - * If >= 0, stop before this offset in nibbles, e.g. to get default behavior, pass - * start_nibble + strlen(str) + ((start_nibble + strlen(str)) & 1? 1 : 0) + 1. - * \paramin allow_hex If false, return error if there are hexadecimal digits (A-F). If true, write those to - * BCD. - * \returns The buffer size in octets that is used to place all bcd digits (including the skipped nibbles - * from 'start_nibble' and rounded up to full octets); -EINVAL on invalid digits; - * -ENOMEM if dst is NULL, if dst_size is too small to contain all nibbles, or if start_nibble is negative. - */ -int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex) -{ - const char *digit = digits; - int nibble_i; - - if (!dst || !dst_size || start_nibble < 0) - return -ENOMEM; - - if (end_nibble < 0) { - end_nibble = start_nibble + strlen(digits); - /* If the last octet is not complete, add another filler nibble */ - if (end_nibble & 1) - end_nibble++; - } - if ((unsigned int) (end_nibble / 2) > dst_size) - return -ENOMEM; - - for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) { - uint8_t nibble = 0xf; - int octet = nibble_i >> 1; - if (*digit) { - char c = *digit; - digit++; - if (c >= '0' && c <= '9') - nibble = c - '0'; - else if (allow_hex && c >= 'A' && c <= 'F') - nibble = 0xa + (c - 'A'); - else if (allow_hex && c >= 'a' && c <= 'f') - nibble = 0xa + (c - 'a'); - else - return -EINVAL; - } - nibble &= 0xf; - if ((nibble_i & 1)) - dstoctet = (nibble << 4) | (dstoctet & 0x0f); - else - dstoctet = (dstoctet & 0xf0) | nibble; - } - - /* floor(float(end_nibble) / 2) */ - return end_nibble / 2; -} - -/*! Parse a string containing hexadecimal digits - * \paramin str string containing ASCII encoded hexadecimal digits - * \paramout b output buffer - * \paramin max_len maximum space in output buffer - * \returns number of parsed octets, or -1 on error - */ -int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len) - -{ - char c; - uint8_t v; - const char *strpos; - unsigned int nibblepos = 0; - - memset(b, 0x00, max_len); - - for (strpos = str; (c = *strpos); strpos++) { - /* skip whitespace */ - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') - continue; - - /* If the buffer is too small, error out */ - if (nibblepos >= (max_len << 1)) - return -1; - - if (c >= '0' && c <= '9') - v = c - '0'; - else if (c >= 'a' && c <= 'f') - v = 10 + (c - 'a'); - else if (c >= 'A' && c <= 'F') - v = 10 + (c - 'A'); - else - return -1; - - bnibblepos >> 1 |= v << (nibblepos & 1 ? 0 : 4); - nibblepos ++; - } - - /* In case of uneven amount of digits, the last byte is not complete - * and that's an error. */ - if (nibblepos & 1) - return -1; - - return nibblepos >> 1; -} - -static __thread char hexd_buff4096; -static const char hex_chars = "0123456789abcdef"; - -/*! Convert binary sequence to hexadecimal ASCII string. - * \paramout out_buf Output buffer to write the resulting string to. - * \paramin out_buf_size sizeof(out_buf). - * \paramin buf Input buffer, pointer to sequence of bytes. - * \paramin len Length of input buf in number of bytes. - * \paramin delim String to separate each byte; NULL or "" for no delim. - * \paramin delim_after_last If true, end the string in delim (true: "1a:ef:d9:", false: "1a:ef:d9"); - * if out_buf has insufficient space, the string will always end in a delim. - * \returns out_buf, containing a zero-terminated string, or "" (empty string) if out_buf == NULL or out_buf_size < 1. - * - * This function will print a sequence of bytes as hexadecimal numbers, adding one delim between each byte (e.g. for - * delim passed as ":", return a string like "1a:ef:d9"). - * - * The delim_after_last argument exists to be able to exactly show the original osmo_hexdump() behavior, which always - * ends the string with a delimiter. - */ -const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned char *buf, int len, const char *delim, - bool delim_after_last) -{ - int i; - char *cur = out_buf; - size_t delim_len; - - if (!out_buf || !out_buf_size) - return ""; - - delim = delim ? : ""; - delim_len = strlen(delim); - - for (i = 0; i < len; i++) { - const char *delimp = delim; - int len_remain = out_buf_size - (cur - out_buf) - 1; - if (len_remain < (int) (2 + delim_len) - && !(!delim_after_last && i == (len - 1) && len_remain >= 2)) - break; - - *cur++ = hex_charsbufi >> 4; - *cur++ = hex_charsbufi & 0xf; - - if (i == (len - 1) && !delim_after_last) - break; - - while (len_remain > 1 && *delimp) { - *cur++ = *delimp++; - len_remain--; - } - } - *cur = '\0'; - return out_buf; -} - -/*! Convert a sequence of unpacked bits to ASCII string, in user-supplied buffer. - * \paramout buf caller-provided output string buffer - * \paramout buf_len size of buf in bytes - * \paramin bits A sequence of unpacked bits - * \paramin len Length of bits - * \return The output buffer (buf). - */ -char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len) -{ - unsigned int i; - - if (len > buf_len-1) - len = buf_len-1; - memset(buf, 0, buf_len); - - for (i = 0; i < len; i++) { - char outch; - switch (bitsi) { - case 0: - outch = '0'; - break; - case 0xff: - outch = '?'; - break; - case 1: - outch = '1'; - break; - default: - outch = 'E'; - break; - } - bufi = outch; - } - bufbuf_len-1 = 0; - return buf; -} - -/*! Convert a sequence of unpacked bits to ASCII string, in static buffer. - * \paramin bits A sequence of unpacked bits - * \paramin len Length of bits - * \returns string representation in static buffer. - */ -char *osmo_ubit_dump(const uint8_t *bits, unsigned int len) -{ - return osmo_ubit_dump_buf(hexd_buff, sizeof(hexd_buff), bits, len); -} - -/*! Convert binary sequence to hexadecimal ASCII string - * \paramin buf pointer to sequence of bytes - * \paramin len length of buf in number of bytes - * \returns pointer to zero-terminated string - * - * This function will print a sequence of bytes as hexadecimal numbers, - * adding one space character between each byte (e.g. "1a ef d9") - * - * The maximum size of the output buffer is 4096 bytes, i.e. the maximum - * number of input bytes that can be printed in one call is 1365! - */ -char *osmo_hexdump(const unsigned char *buf, int len) -{ - osmo_hexdump_buf(hexd_buff, sizeof(hexd_buff), buf, len, " ", true); - return hexd_buff; -} - -/*! Convert binary sequence to hexadecimal ASCII string - * \paramin ctx talloc context from where to allocate the output string - * \paramin buf pointer to sequence of bytes - * \paramin len length of buf in number of bytes - * \returns pointer to zero-terminated string - * - * This function will print a sequence of bytes as hexadecimal numbers, - * adding one space character between each byte (e.g. "1a ef d9") - */ -char *osmo_hexdump_c(const void *ctx, const unsigned char *buf, int len) -{ - size_t hexd_buff_len = len * 3 + 1; - char *hexd_buff = talloc_size(ctx, hexd_buff_len); - if (!hexd_buff) - return NULL; - osmo_hexdump_buf(hexd_buff, hexd_buff_len, buf, len, " ", true); - return hexd_buff; -} - -/*! Convert binary sequence to hexadecimal ASCII string - * \paramin buf pointer to sequence of bytes - * \paramin len length of buf in number of bytes - * \returns pointer to zero-terminated string - * - * This function will print a sequence of bytes as hexadecimal numbers, - * without any space character between each byte (e.g. "1aefd9") - * - * The maximum size of the output buffer is 4096 bytes, i.e. the maximum - * number of input bytes that can be printed in one call is 2048! - */ -char *osmo_hexdump_nospc(const unsigned char *buf, int len) -{ - osmo_hexdump_buf(hexd_buff, sizeof(hexd_buff), buf, len, "", true); - return hexd_buff; -} - -/*! Convert binary sequence to hexadecimal ASCII string - * \paramin ctx talloc context from where to allocate the output string - * \paramin buf pointer to sequence of bytes - * \paramin len length of buf in number of bytes - * \returns pointer to zero-terminated string - * - * This function will print a sequence of bytes as hexadecimal numbers, - * without any space character between each byte (e.g. "1aefd9") - */ -char *osmo_hexdump_nospc_c(const void *ctx, const unsigned char *buf, int len) -{ - size_t hexd_buff_len = len * 2 + 1; - char *hexd_buff = talloc_size(ctx, hexd_buff_len); - if (!hexd_buff) - return NULL; - osmo_hexdump_buf(hexd_buff, hexd_buff_len, buf, len, "", true); - return hexd_buff; -} - - -/* Compat with previous typo to preserve abi */ -char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) -#if defined(__MACH__) && defined(__APPLE__) - ; -#else - __attribute__((weak, alias("osmo_hexdump_nospc"))); -#endif - -#include "../config.h" -#ifdef HAVE_CTYPE_H -#include <ctype.h> -/*! Convert an entire string to lower case - * \paramout out output string, caller-allocated - * \paramin in input string - */ -void osmo_str2lower(char *out, const char *in) -{ - unsigned int i; - - for (i = 0; i < strlen(in); i++) - outi = tolower((const unsigned char)ini); - outstrlen(in) = '\0'; -} - -/*! Convert an entire string to upper case - * \paramout out output string, caller-allocated - * \paramin in input string - */ -void osmo_str2upper(char *out, const char *in) -{ - unsigned int i; - - for (i = 0; i < strlen(in); i++) - outi = toupper((const unsigned char)ini); - outstrlen(in) = '\0'; -} -#endif /* HAVE_CTYPE_H */ - -/*! Wishful thinking to generate a constant time compare - * \paramin exp Expected data - * \paramin rel Comparison value - * \paramin count Number of bytes to compare - * \returns 1 in case \a exp equals \a rel; zero otherwise - * - * Compare count bytes of exp to rel. Return 0 if they are identical, 1 - * otherwise. Do not return a mismatch on the first mismatching byte, - * but always compare all bytes, regardless. The idea is that the amount of - * matching bytes cannot be inferred from the time the comparison took. */ -int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) -{ - int x = 0, i; - - for (i = 0; i < count; ++i) - x |= expi ^ reli; - - /* if x is zero, all data was identical */ - return x? 1 : 0; -} - -/*! Generic retrieval of 1..8 bytes as big-endian uint64_t - * \paramin data Input data as byte-array - * \paramin data_len Length of \a data in octets - * \returns uint64_t of \a data interpreted as big-endian - * - * This is like osmo_load64be_ext, except that if data_len is less than - * sizeof(uint64_t), the data is interpreted as the least significant bytes - * (osmo_load64be_ext loads them as the most significant bytes into the - * returned uint64_t). In this way, any integer size up to 64 bits can be - * decoded conveniently by using sizeof(), without the need to call specific - * numbered functions (osmo_load16, 32, ...). */ -uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len) -{ - uint64_t value = 0; - - while (data_len > 0) { - value = (value << 8) + *data; - data += 1; - data_len -= 1; - } - - return value; -} - -/*! Generic big-endian encoding of big endian number up to 64bit - * \paramin value unsigned integer value to be stored - * \paramin data_len number of octets - * \returns static buffer containing big-endian stored value - * - * This is like osmo_store64be_ext, except that this returns a static buffer of - * the result (for convenience, but not threadsafe). If data_len is less than - * sizeof(uint64_t), only the least significant bytes of value are encoded. */ -uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len) -{ - static __thread uint8_t bufsizeof(uint64_t); - OSMO_ASSERT(data_len <= ARRAY_SIZE(buf)); - osmo_store64be_ext(value, buf, data_len); - return buf; -} - -/*! Copy a C-string into a sized buffer - * \paramin src source string - * \paramout dst destination string - * \paramin siz size of the \a dst buffer - * \returns length of \a src - * - * Copy at most \a siz bytes from \a src to \a dst, ensuring that the result is - * NUL terminated. The NUL character is included in \a siz, i.e. passing the - * actual sizeof(*dst) is correct. - * - * Note, a similar function that also limits the input buffer size is osmo_print_n(). - */ -size_t osmo_strlcpy(char *dst, const char *src, size_t siz) -{ - size_t ret = src ? strlen(src) : 0; - - if (siz) { - size_t len = OSMO_MIN(siz - 1, ret); - if (len) - memcpy(dst, src, len); - dstlen = '\0'; - } - return ret; -} - -/*! Find first occurence of a char in a size limited string. - * Like strchr() but with a buffer size limit. - * \paramin str String buffer to examine. - * \paramin str_size sizeof(str). - * \paramin c Character to look for. - * \return Pointer to the matched char, or NULL if not found. - */ -const char *osmo_strnchr(const char *str, size_t str_size, char c) -{ - const char *end = str + str_size; - const char *pos; - if (!str) - return NULL; - for (pos = str; pos < end; pos++) { - if (c == *pos) - return pos; - if (!*pos) - return NULL; - } - return NULL; -} - -/*! Validate that a given string is a hex string within given size limits. - * Note that each hex digit amounts to a nibble, so if checking for a hex - * string to result in N bytes, pass amount of digits as 2*N. - * \param str A nul-terminated string to validate, or NULL. - * \param min_digits least permitted amount of digits. - * \param max_digits most permitted amount of digits. - * \param require_even if true, require an even amount of digits. - * \returns true when the hex_str contains only hexadecimal digits (no - * whitespace) and matches the requested length; also true - * when min_digits <= 0 and str is NULL. - */ -bool osmo_is_hexstr(const char *str, int min_digits, int max_digits, - bool require_even) -{ - int len; - /* Use unsigned char * to avoid a compiler warning of - * "error: array subscript has type 'char' -Werror=char-subscripts" */ - const unsigned char *pos = (const unsigned char*)str; - if (!pos) - return min_digits < 1; - for (len = 0; *pos && len < max_digits; len++, pos++) - if (!isxdigit(*pos)) - return false; - if (len < min_digits) - return false; - /* With not too many digits, we should have reached *str == nul */ - if (*pos) - return false; - if (require_even && (len & 1)) - return false; - - return true; -} - -static const char osmo_identifier_illegal_chars = "., {}()<>|~\\^`'\"?=;/+*&%$#!"; - -/*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars - * \paramin str String to validate - * \paramin sep_chars Permitted separation characters between identifiers. - * \returns true in case \a str contains only valid identifiers and sep_chars, false otherwise - */ -bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars) -{ - /* characters that are illegal in names */ - unsigned int i; - size_t len; - - /* an empty string is not a valid identifier */ - if (!str || (len = strlen(str)) == 0) - return false; - - for (i = 0; i < len; i++) { - if (sep_chars && strchr(sep_chars, stri)) - continue; - /* check for 7-bit ASCII */ - if (stri & 0x80) - return false; - if (!isprint((int)stri)) - return false; - /* check for some explicit reserved control characters */ - if (strchr(osmo_identifier_illegal_chars, stri)) - return false; - } - - return true; -} - -/*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars - * \paramin str String to validate - * \returns true in case \a str contains valid identifier, false otherwise - */ -bool osmo_identifier_valid(const char *str) -{ - return osmo_separated_identifiers_valid(str, NULL); -} - -/*! Replace characters in the given string buffer so that it is guaranteed to pass osmo_separated_identifiers_valid(). - * To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in - * doubt, use '-'. - * \paraminout str Identifier to sanitize, must be nul terminated and in a writable buffer. - * \paramin sep_chars Additional characters that are to be replaced besides osmo_identifier_illegal_chars. - * \paramin replace_with Replace any illegal characters with this character. - */ -void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with) -{ - char *pos; - if (!str) - return; - for (pos = str; *pos; pos++) { - if (strchr(osmo_identifier_illegal_chars, *pos) - || (sep_chars && strchr(sep_chars, *pos))) - *pos = replace_with; - } -} - -/*! Like osmo_escape_str_buf2, but with unusual ordering of arguments, and may sometimes return string constants instead - * of writing to buf for error cases or empty input. - * Most *_buf() functions have the buffer and size as first arguments, here the arguments are last. - * In particular, this function signature doesn't work with OSMO_STRBUF_APPEND_NOLEN(). - * \paramin str A string that may contain any characters. - * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. - * \paraminout buf string buffer to write escaped characters to. - * \paramin bufsize size of \a buf. - * \returns buf containing an escaped representation, possibly truncated, - * or "(null)" if str == NULL, or "(error)" in case of errors. - */ -const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize) -{ - if (!str) - return "(null)"; - if (!buf || !bufsize) - return "(error)"; - return osmo_escape_str_buf2(buf, bufsize, str, in_len); -} - -/*! Copy N characters to a buffer with a function signature useful for OSMO_STRBUF_APPEND(). - * Similarly to snprintf(), the result is always nul terminated (except if buf is NULL or bufsize is 0). - * \paramout buf Target buffer. - * \paramin bufsize sizeof(buf). - * \paramin str String to copy. - * \paramin n Maximum number of non-nul characters to copy. - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n) -{ - size_t write_n; - - if (!str) - str = ""; - - n = strnlen(str, n); - - if (!buf || !bufsize) - return n; - write_n = n; - if (write_n >= bufsize) - write_n = bufsize - 1; - if (write_n) - strncpy(buf, str, write_n); - bufwrite_n = '\0'; - - return n; -} - -/*! Return the string with all non-printable characters escaped. - * This internal function is the implementation for all osmo_escape_str* and osmo_quote_str* API versions. - * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax, - * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). - * \paramin legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy - * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while - * the non-legacy format also escapes those as "\xNN" sequences. - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -static int _osmo_escape_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format) -{ - struct osmo_strbuf sb = { .buf = buf, .len = bufsize }; - int in_pos = 0; - int next_unprintable = 0; - - if (!str) - in_len = 0; - - if (in_len < 0) - in_len = strlen(str); - - /* Make sure of '\0' termination */ - if (!in_len) - OSMO_STRBUF_PRINTF(sb, "%s", ""); - - while (in_pos < in_len) { - for (next_unprintable = in_pos; - next_unprintable < in_len && isprint((int)strnext_unprintable) - && strnext_unprintable != '"' - && strnext_unprintable != '\\'; - next_unprintable++); - - OSMO_STRBUF_APPEND(sb, osmo_print_n, &strin_pos, next_unprintable - in_pos); - in_pos = next_unprintable; - - if (in_pos == in_len) - goto done; - - switch (strnext_unprintable) { -#define BACKSLASH_CASE(c, repr) \ - case c: \ - OSMO_STRBUF_PRINTF(sb, "\\%c", repr); \ - break - - BACKSLASH_CASE('\n', 'n'); - BACKSLASH_CASE('\r', 'r'); - BACKSLASH_CASE('\t', 't'); - BACKSLASH_CASE('\0', '0'); - BACKSLASH_CASE('\\', '\\'); - BACKSLASH_CASE('"', '"'); - - default: - if (legacy_format) { - switch (strnext_unprintable) { - BACKSLASH_CASE('\a', 'a'); - BACKSLASH_CASE('\b', 'b'); - BACKSLASH_CASE('\v', 'v'); - BACKSLASH_CASE('\f', 'f'); - default: - OSMO_STRBUF_PRINTF(sb, "\\%u", (unsigned char)strin_pos); - break; - } - break; - } - - OSMO_STRBUF_PRINTF(sb, "\\x%02x", (unsigned char)strin_pos); - break; - } - in_pos ++; -#undef BACKSLASH_CASE - } - -done: - return sb.chars_needed; -} - -/*! Return the string with all non-printable characters escaped. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len) -{ - return _osmo_escape_str_buf(buf, bufsize, str, in_len, false); -} - -/*! Return the string with all non-printable characters escaped. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). - * \return The output buffer (buf). - */ -char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len) -{ - _osmo_escape_str_buf(buf, bufsize, str, in_len, true); - return buf; -} - -/*! Return the string with all non-printable characters escaped. - * Call osmo_escape_str_buf() with a static buffer. - * \paramin str A string that may contain any characters. - * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns buf containing an escaped representation, possibly truncated, or str itself. - */ -const char *osmo_escape_str(const char *str, int in_len) -{ - return osmo_escape_str_buf(str, in_len, namebuf, sizeof(namebuf)); -} - -/*! Return the string with all non-printable characters escaped, in dynamically-allocated buffer. - * \paramin str A string that may contain any characters. - * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns dynamically-allocated output buffer, containing an escaped representation - */ -char *osmo_escape_str_c(const void *ctx, const char *str, int in_len) -{ - /* The string will be at least as long as in_len, but some characters might need escaping. - * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ - OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, true); -} - -/*! Return a quoted and escaped representation of the string. - * This internal function is the implementation for all osmo_quote_str* API versions. - * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax, - * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). - * \paramin legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy - * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while - * the non-legacy format also escapes those as "\xNN" sequences. - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -static size_t _osmo_quote_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format) -{ - struct osmo_strbuf sb = { .buf = buf, .len = bufsize }; - if (!str) - OSMO_STRBUF_PRINTF(sb, "NULL"); - else { - OSMO_STRBUF_PRINTF(sb, "\""); - OSMO_STRBUF_APPEND(sb, _osmo_escape_str_buf, str, in_len, legacy_format); - OSMO_STRBUF_PRINTF(sb, "\""); - } - return sb.chars_needed; -} - -/*! Like osmo_escape_str_buf3(), but returns double-quotes around a string, or "NULL" for a NULL string. - * This allows passing any char* value and get its C representation as string. - * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len) -{ - return _osmo_quote_str_buf(buf, bufsize, str, in_len, false); -} - -/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string. - * This allows passing any char* value and get its C representation as string. - * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \return The output buffer (buf). - */ -char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len) -{ - _osmo_quote_str_buf(buf, bufsize, str, in_len, true); - return buf; -} - -/*! Like osmo_quote_str_buf2, but with unusual ordering of arguments, and may sometimes return string constants instead - * of writing to buf for error cases or empty input. - * Most *_buf() functions have the buffer and size as first arguments, here the arguments are last. - * In particular, this function signature doesn't work with OSMO_STRBUF_APPEND_NOLEN(). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns buf containing a quoted and escaped representation, possibly truncated. - */ -const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize) -{ - if (!str) - return "NULL"; - if (!buf || !bufsize) - return "(error)"; - _osmo_quote_str_buf(buf, bufsize, str, in_len, true); - return buf; -} - -/*! Like osmo_quote_str_buf() but returns the result in a static buffer. - * The static buffer is shared with get_value_string() and osmo_escape_str(). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns static buffer containing a quoted and escaped representation, possibly truncated. - */ -const char *osmo_quote_str(const char *str, int in_len) -{ - _osmo_quote_str_buf(namebuf, sizeof(namebuf), str, in_len, true); - return namebuf; -} - -/*! Like osmo_quote_str_buf() but returns the result in a dynamically-allocated buffer. - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns dynamically-allocated buffer containing a quoted and escaped representation. - */ -char *osmo_quote_str_c(const void *ctx, const char *str, int in_len) -{ - /* The string will be at least as long as in_len, but some characters might need escaping. - * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ - OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, true); -} - -/*! Return the string with all non-printable characters escaped. - * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and - * this escapes characters in a way compatible with C string constant syntax. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -size_t osmo_escape_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len) -{ - return _osmo_escape_str_buf(buf, bufsize, str, in_len, false); -} - -/*! Return the string with all non-printable characters escaped, in dynamically-allocated buffer. - * In contrast to osmo_escape_str_c(), this escapes characters in a way compatible with C string constant syntax, and - * allocates sufficient memory in all cases. - * \paramin str A string that may contain any characters. - * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns dynamically-allocated buffer, containing an escaped representation. - */ -char *osmo_escape_cstr_c(void *ctx, const char *str, int in_len) -{ - /* The string will be at least as long as in_len, but some characters might need escaping. - * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ - OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, false); -} - -/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string. - * This allows passing any char* value and get its C representation as string. - * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). - * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and - * this escapes characters in a way compatible with C string constant syntax. - * \paramout buf string buffer to write escaped characters to. - * \paramin bufsize sizeof(buf). - * \paramin str A string that may contain any characters. - * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. - * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). - */ -size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len) -{ - return _osmo_quote_str_buf(buf, bufsize, str, in_len, false); -} - -/*! Return the string quoted and with all non-printable characters escaped, in dynamically-allocated buffer. - * In contrast to osmo_quote_str_c(), this escapes characters in a way compatible with C string constant syntax, and - * allocates sufficient memory in all cases. - * \paramin str A string that may contain any characters. - * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns dynamically-allocated buffer, containing a quoted and escaped representation. - */ -char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len) -{ - /* The string will be at least as long as in_len plus two quotes, but some characters might need escaping. - * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ - OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, false); -} - -/*! perform an integer square root operation on unsigned 32bit integer. - * This implementation is taken from "Hacker's Delight" Figure 11-1 "Integer square root, Newton's - * method", which can also be found at http://www.hackersdelight.org/hdcodetxt/isqrt.c.txt */ -uint32_t osmo_isqrt32(uint32_t x) -{ - uint32_t x1; - int s, g0, g1; - - if (x <= 1) - return x; - - s = 1; - x1 = x - 1; - if (x1 > 0xffff) { - s = s + 8; - x1 = x1 >> 16; - } - if (x1 > 0xff) { - s = s + 4; - x1 = x1 >> 8; - } - if (x1 > 0xf) { - s = s + 2; - x1 = x1 >> 4; - } - if (x1 > 0x3) { - s = s + 1; - } - - g0 = 1 << s; /* g0 = 2**s */ - g1 = (g0 + (x >> s)) >> 1; /* g1 = (g0 + x/g0)/2 */ - - /* converges after four to five divisions for arguments up to 16,785,407 */ - while (g1 < g0) { - g0 = g1; - g1 = (g0 + (x/g0)) >> 1; - } - return g0; -} - -/*! Convert a string to lowercase, while checking buffer size boundaries. - * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. - * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters - * length as well as nul terminated. - * Note: similar osmo_str2lower(), but safe to use for src strings of arbitrary length. - * \paramout dest Target buffer to write lowercase string. - * \paramin dest_len Maximum buffer size of dest (e.g. sizeof(dest)). - * \paramin src String to convert to lowercase. - * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. - */ -size_t osmo_str_tolower_buf(char *dest, size_t dest_len, const char *src) -{ - size_t rc; - if (dest == src) { - if (dest_len < 1) - return 0; - destdest_len - 1 = '\0'; - rc = strlen(dest); - } else { - if (dest_len < 1) - return strlen(src); - rc = osmo_strlcpy(dest, src, dest_len); - } - for (; *dest; dest++) - *dest = tolower(*dest); - return rc; -} - -/*! Convert a string to lowercase, using a static buffer. - * The resulting string may be truncated if the internally used static buffer is shorter than src. - * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a - * terminating nul. The static buffer returned is shared with osmo_str_toupper(). - * See also osmo_str_tolower_buf(). - * \paramin src String to convert to lowercase. - * \returns Resulting lowercase string in a static buffer, always nul terminated. - */ -const char *osmo_str_tolower(const char *src) -{ - osmo_str_tolower_buf(capsbuf, sizeof(capsbuf), src); - return capsbuf; -} - -/*! Convert a string to lowercase, dynamically allocating the output from given talloc context - * See also osmo_str_tolower_buf(). - * \paramin ctx talloc context from where to allocate the output string - * \paramin src String to convert to lowercase. - * \returns Resulting lowercase string in a dynamically allocated buffer, always nul terminated. - */ -char *osmo_str_tolower_c(const void *ctx, const char *src) -{ - size_t buf_len = strlen(src) + 1; - char *buf = talloc_size(ctx, buf_len); - if (!buf) - return NULL; - osmo_str_tolower_buf(buf, buf_len, src); - return buf; -} - -/*! Convert a string to uppercase, while checking buffer size boundaries. - * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. - * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters - * length as well as nul terminated. - * Note: similar osmo_str2upper(), but safe to use for src strings of arbitrary length. - * \paramout dest Target buffer to write uppercase string. - * \paramin dest_len Maximum buffer size of dest (e.g. sizeof(dest)). - * \paramin src String to convert to uppercase. - * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. - */ -size_t osmo_str_toupper_buf(char *dest, size_t dest_len, const char *src) -{ - size_t rc; - if (dest == src) { - if (dest_len < 1) - return 0; - destdest_len - 1 = '\0'; - rc = strlen(dest); - } else { - if (dest_len < 1) - return strlen(src); - rc = osmo_strlcpy(dest, src, dest_len); - } - for (; *dest; dest++) - *dest = toupper(*dest); - return rc; -} - -/*! Convert a string to uppercase, using a static buffer. - * The resulting string may be truncated if the internally used static buffer is shorter than src. - * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a - * terminating nul. The static buffer returned is shared with osmo_str_tolower(). - * See also osmo_str_toupper_buf(). - * \paramin src String to convert to uppercase. - * \returns Resulting uppercase string in a static buffer, always nul terminated. - */ -const char *osmo_str_toupper(const char *src) -{ - osmo_str_toupper_buf(capsbuf, sizeof(capsbuf), src); - return capsbuf; -} - -/*! Convert a string to uppercase, dynamically allocating the output from given talloc context - * See also osmo_str_tolower_buf(). - * \paramin ctx talloc context from where to allocate the output string - * \paramin src String to convert to uppercase. - * \returns Resulting uppercase string in a dynamically allocated buffer, always nul terminated. - */ -char *osmo_str_toupper_c(const void *ctx, const char *src) -{ - size_t buf_len = strlen(src) + 1; - char *buf = talloc_size(ctx, buf_len); - if (!buf) - return NULL; - osmo_str_toupper_buf(buf, buf_len, src); - return buf; -} - -/*! Calculate the Luhn checksum (as used for IMEIs). - * \paramin in Input digits in ASCII string representation. - * \paramin in_len Count of digits to use for the input (14 for IMEI). - * \returns checksum char (e.g. '3'); negative on error - */ -char osmo_luhn(const char* in, int in_len) -{ - int i, sum = 0; - - /* All input must be numbers */ - for (i = 0; i < in_len; i++) { - if (!isdigit((unsigned char)ini)) - return -EINVAL; - } - - /* Double every second digit and add it to sum */ - for (i = in_len - 1; i >= 0; i -= 2) { - int dbl = (ini - '0') * 2; - if (dbl > 9) - dbl -= 9; - sum += dbl; - } - - /* Add other digits to sum */ - for (i = in_len - 2; i >= 0; i -= 2) - sum += ini - '0'; - - /* Final checksum */ - return (sum * 9) % 10 + '0'; -} - -/*! Compare start of a string. - * This is an optimisation of 'strstr(str, startswith_str) == str' because it doesn't search through the entire string. - * \param str (Longer) string to compare. - * \param startswith_str (Shorter) string to compare with the start of str. - * \return true iff the first characters of str fully match startswith_str or startswith_str is empty. */ -bool osmo_str_startswith(const char *str, const char *startswith_str) -{ - if (!startswith_str || !*startswith_str) - return true; - if (!str) - return false; - return strncmp(str, startswith_str, strlen(startswith_str)) == 0; -} - -/*! Convert a string of a floating point number to a signed int, with a decimal factor (fixed-point precision). - * For example, with precision=3, convert "-1.23" to -1230. In other words, the float value is multiplied by - * 10 to-the-power-of precision to obtain the returned integer. - * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to - * reduce implementation complexity. See also utils_test.c. - * The advantage over using sscanf("%f") is guaranteed precision: float or double types may apply rounding in the - * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting - * back and forth between string and int. - * \paramout val Returned integer value. - * \paramin str String of a float, like '-12.345'. - * \paramin precision Fixed-point precision, or * \returns 0 on success, negative on error. - */ -int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision) -{ - const char *point; - char *endptr; - const char *p; - int64_t sign = 1; - int64_t integer = 0; - int64_t decimal = 0; - int64_t precision_factor; - int64_t integer_max; - int64_t decimal_max; - unsigned int i; - - OSMO_ASSERT(val); - *val = 0; - - if (!str) - return -EINVAL; - if (str0 == '-') { - str = str + 1; - sign = -1; - } else if (str0 == '+') { - str = str + 1; - } - if (!str0) - return -EINVAL; - - /* Validate entire string as purely digits and at most one decimal dot. If not doing this here in advance, - * parsing digits might stop early because of precision cut-off and miss validation of input data. */ - point = NULL; - for (p = str; *p; p++) { - if (*p == '.') { - if (point) - return -EINVAL; - point = p; - } else if (!isdigit((unsigned char)*p)) - return -EINVAL; - } - - /* Parse integer part if there is one. If the string starts with a point, there's nothing to parse for the - * integer part. */ - if (!point || point > str) { - errno = 0; - integer = strtoll(str, &endptr, 10); - if ((errno == ERANGE && (integer == LLONG_MAX || integer == LLONG_MIN)) - || (errno != 0 && integer == 0)) - return -ERANGE; - - if ((point && endptr != point) - || (!point && *endptr)) - return -EINVAL; - } - - /* Parse the fractional part if there is any, and if the precision is nonzero (if we even care about fractional - * digits) */ - if (precision && point && point1 != '\0') { - /* limit the number of digits parsed to 'precision'. - * If 'precision' is larger than the 19 digits representable in int64_t, skip some, to pick up lower - * magnitude digits. */ - unsigned int skip_digits = (precision < 20) ? 0 : precision - 20; - char decimal_strprecision + 1; - osmo_strlcpy(decimal_str, point+1, precision+1); - - /* fill with zeros to make exactly 'precision' digits */ - for (i = strlen(decimal_str); i < precision; i++) - decimal_stri = '0'; - decimal_strprecision = '\0'; - - for (i = 0; i < skip_digits; i++) { - /* When skipping digits because precision > nr-of-digits-in-int64_t, they must be zero; - * if there is a nonzero digit above the precision, it's -ERANGE. */ - if (decimal_stri != '0') - return -ERANGE; - } - errno = 0; - decimal = strtoll(decimal_str + skip_digits, &endptr, 10); - if ((errno == ERANGE && (decimal == LLONG_MAX || decimal == LLONG_MIN)) - || (errno != 0 && decimal == 0)) - return -ERANGE; - - if (*endptr) - return -EINVAL; - } - - if (precision > 18) { - /* Special case of returning more digits than fit in int64_t range, e.g. - * osmo_float_str_to_int("0.0000000012345678901234567", precision=25) -> 12345678901234567. */ - precision_factor = 0; - integer_max = 0; - decimal_max = INT64_MAX; - } else { - /* Do not surpass the resulting int64_t range. Depending on the amount of precision, the integer part - * and decimal part have specific ranges they must comply to. */ - precision_factor = 1; - for (i = 0; i < precision; i++) - precision_factor *= 10; - integer_max = INT64_MAX / precision_factor; - if (integer == integer_max) - decimal_max = INT64_MAX % precision_factor; - else - decimal_max = INT64_MAX; - } - - if (integer > integer_max) - return -ERANGE; - if (decimal > decimal_max) - return -ERANGE; - - *val = sign * (integer * precision_factor + decimal); - return 0; -} - -/*! Convert an integer to a floating point string using a decimal quotient (fixed-point precision). - * For example, with precision = 3, convert -1230 to "-1.23". - * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to - * reduce implementation complexity. See also utils_test.c. - * The advantage over using printf("%.6g") is guaranteed precision: float or double types may apply rounding in the - * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting - * back and forth between string and int. - * The resulting string omits trailing zeros in the fractional part (like "%g" would) but never applies rounding. - * \paramout buf Buffer to write string to. - * \paramin buflen sizeof(buf). - * \paramin val Value to convert to float. - * \returns number of chars that would be written, like snprintf(). - */ -int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision) -{ - struct osmo_strbuf sb = { .buf = buf, .len = buflen }; - unsigned int i; - unsigned int w; - int64_t precision_factor; - if (val < 0) { - OSMO_STRBUF_PRINTF(sb, "-"); - if (val == INT64_MIN) { - OSMO_STRBUF_PRINTF(sb, "ERR"); - return sb.chars_needed; - } - val = -val; - } - - if (precision > 18) { - /* Special case of returning more digits than fit in int64_t range, e.g. - * osmo_int_to_float_str(12345678901234567, precision=25) -> "0.0000000012345678901234567". */ - if (!val) { - OSMO_STRBUF_PRINTF(sb, "0"); - return sb.chars_needed; - } - OSMO_STRBUF_PRINTF(sb, "0."); - for (i = 19; i < precision; i++) - OSMO_STRBUF_PRINTF(sb, "0"); - precision = 19; - } else { - precision_factor = 1; - for (i = 0; i < precision; i++) - precision_factor *= 10; - - OSMO_STRBUF_PRINTF(sb, "%" PRId64, val / precision_factor); - val %= precision_factor; - if (!val) - return sb.chars_needed; - OSMO_STRBUF_PRINTF(sb, "."); - } - - /* print fractional part, skip trailing zeros */ - w = precision; - while (!(val % 10)) { - val /= 10; - w--; - } - OSMO_STRBUF_PRINTF(sb, "%0*" PRId64, w, val); - return sb.chars_needed; -} - -/*! Convert an integer with a factor of a million to a floating point string. - * For example, convert -1230000 to "-1.23". - * \paramin ctx Talloc ctx to allocate string buffer from. - * \paramin val Value to convert to float. - * \returns resulting string, dynamically allocated. - */ -char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision) -{ - OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_int_to_float_str_buf, val, precision) -} - -/*! Convert a string of a number to int64_t, including all common strtoll() validity checks. - * It's not so trivial to call strtoll() and properly verify that the input string was indeed a valid number string. - * \paramout result Buffer for the resulting integer number, or NULL if the caller is only interested in the - * validation result (returned rc). - * \paramin str The string to convert. - * \paramin base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll(). - * \paramin min_val The smallest valid number expected in the string. - * \paramin max_val The largest valid number expected in the string. - * \return 0 on success, -EOVERFLOW if the number in the string exceeds int64_t, -ENOTSUPP if the base is not supported, - * -ERANGE if the converted number exceeds the range min_val..max_val but is still within int64_t range, -E2BIG if - * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and - * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is - * clamped to INT64_MIN..INT64_MAX. - */ -int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val) -{ - long long int val; - char *endptr; - if (result) - *result = 0; - if (!str || !*str) - return -EINVAL; - errno = 0; - val = strtoll(str, &endptr, base); - /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or - * LLONG_MAX. Make sure of the same here with respect to int64_t. */ - if (val < INT64_MIN) { - if (result) - *result = INT64_MIN; - return -ERANGE; - } - if (val > INT64_MAX) { - if (result) - *result = INT64_MAX; - return -ERANGE; - } - if (result) - *result = (int64_t)val; - switch (errno) { - case 0: - break; - case ERANGE: - return -EOVERFLOW; - default: - case EINVAL: - return -ENOTSUP; - } - if (!endptr || *endptr) { - /* No chars were converted */ - if (endptr == str) - return -EINVAL; - /* Or there are surplus chars after the converted number */ - return -E2BIG; - } - if (val < min_val || val > max_val) - return -ERANGE; - return 0; -} - -/*! Convert a string of a number to int, including all common strtoll() validity checks. - * Same as osmo_str_to_int64() but using the plain int data type. - * \paramout result Buffer for the resulting integer number, or NULL if the caller is only interested in the - * validation result (returned rc). - * \paramin str The string to convert. - * \paramin base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll(). - * \paramin min_val The smallest valid number expected in the string. - * \paramin max_val The largest valid number expected in the string. - * \return 0 on success, -EOVERFLOW if the number in the string exceeds int range, -ENOTSUPP if the base is not supported, - * -ERANGE if the converted number exceeds the range min_val..max_val but is still within int range, -E2BIG if - * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and - * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is - * clamped to INT_MIN..INT_MAX. - */ -int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val) -{ - int64_t val; - int rc = osmo_str_to_int64(&val, str, base, min_val, max_val); - /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or - * LLONG_MAX. Make sure of the same here with respect to int. */ - if (val < INT_MIN) { - if (result) - *result = INT_MIN; - return -EOVERFLOW; - } - if (val > INT_MAX) { - if (result) - *result = INT_MAX; - return -EOVERFLOW; - } - if (result) - *result = (int)val; - return rc; -} - -/*! Replace a string using talloc and release its prior content (if any). - * This is a format string capable equivalent of osmo_talloc_replace_string(). - * \paramin ctx Talloc context to use for allocation. - * \paramout dst Pointer to string, will be updated with ptr to new string. - * \paramin fmt Format string that will be copied to newly allocated string. */ -void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...) -{ - char *name = NULL; - - if (fmt != NULL) { - va_list ap; - - va_start(ap, fmt); - name = talloc_vasprintf(ctx, fmt, ap); - va_end(ap); - } - - talloc_free(*dst); - *dst = name; -} - -/*! @} */
View file
libosmocore_1.7.0.dsc -> libosmocore_1.8.0.dsc
Changed
@@ -1,8 +1,8 @@ Format: 3.0 (native) Source: libosmocore -Binary: libosmocore, libosmocodec0, libosmocodec-doc, libosmocoding0, libosmocoding-doc, libosmocore19, libosmocore-doc, libosmogb14, libosmogb-doc, libosmogsm18, libosmogsm-doc, libosmovty9, libosmovty-doc, libosmoctrl0, libosmoctrl-doc, libosmosim2, libosmousb0, libosmocore-dev, libosmocore-utils, libosmocore-dbg +Binary: libosmocore, libosmocodec0, libosmocodec-doc, libosmocoding0, libosmocoding-doc, libosmocore20, libosmocore-doc, libosmogb14, libosmogb-doc, libosmogsm18, libosmogsm-doc, libosmoisdn0, libosmoisdn-doc, libosmovty9, libosmovty-doc, libosmoctrl0, libosmoctrl-doc, libosmosim2, libosmousb0, libosmocore-dev, libosmocore-utils, libosmocore-dbg Architecture: any all -Version: 1.7.0 +Version: 1.8.0 Maintainer: Osmocom team <openbsc@lists.osmocom.org> Homepage: https://projects.osmocom.org/projects/libosmocore Standards-Version: 3.9.8 @@ -19,20 +19,22 @@ libosmocore-dev deb libdevel optional arch=any libosmocore-doc deb doc optional arch=all libosmocore-utils deb utils optional arch=any - libosmocore19 deb libs optional arch=any + libosmocore20 deb libs optional arch=any libosmoctrl-doc deb doc optional arch=all libosmoctrl0 deb libs optional arch=any libosmogb-doc deb doc optional arch=all libosmogb14 deb libs optional arch=any libosmogsm-doc deb doc optional arch=all libosmogsm18 deb libs optional arch=any + libosmoisdn-doc deb doc optional arch=all + libosmoisdn0 deb libs optional arch=any libosmosim2 deb libs optional arch=any libosmousb0 deb libs optional arch=any libosmovty-doc deb doc optional arch=all libosmovty9 deb libs optional arch=any Checksums-Sha1: - 421f3f1a5738fb0d4c1218b126cc56a9e5302b55 998336 libosmocore_1.7.0.tar.xz + 84a5885b1fb36c49b840bf6979578cd764327ae6 1017004 libosmocore_1.8.0.tar.xz Checksums-Sha256: - 81953672e915a2786f70f0bb38e2fcfeb33aaa75d559adcf851e9d35ff501bd9 998336 libosmocore_1.7.0.tar.xz + d7c39f08cc052eef247fc88406ca3ca256b3c6a058686f8a46a9848f7d77dca9 1017004 libosmocore_1.8.0.tar.xz Files: - f719049ed7040f1d86212f7428bec91d 998336 libosmocore_1.7.0.tar.xz + 8887ed3ebfc0601dfe8726c3a2338486 1017004 libosmocore_1.8.0.tar.xz
View file
libosmocore_1.7.0.tar.xz/.tarball-version -> libosmocore_1.8.0.tar.xz/.tarball-version
Changed
@@ -1 +1 @@ -1.7.0 \ No newline at end of file +1.8.0
View file
libosmocore_1.7.0.tar.xz/Doxyfile.codec.in -> libosmocore_1.8.0.tar.xz/Doxyfile.codec.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Doxyfile.coding.in -> libosmocore_1.8.0.tar.xz/Doxyfile.coding.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Doxyfile.core.in -> libosmocore_1.8.0.tar.xz/Doxyfile.core.in
Changed
@@ -610,7 +610,7 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @srcdir@/include/osmocom/core @srcdir@/src +INPUT = @srcdir@/include/osmocom/core @srcdir@/src/core # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Doxyfile.ctrl.in -> libosmocore_1.8.0.tar.xz/Doxyfile.ctrl.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Doxyfile.gb.in -> libosmocore_1.8.0.tar.xz/Doxyfile.gb.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Doxyfile.gsm.in -> libosmocore_1.8.0.tar.xz/Doxyfile.gsm.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.8.0.tar.xz/Doxyfile.isdn.in
Added
@@ -0,0 +1,1716 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value value, ... +# For lists items can also be appended using: +# TAG += value value, ... +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libosmoisdn + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Osmocom ISDN library" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/isdn + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit command for a brief description.) + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag inline +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @srcdir@/include/osmocom/isdn @srcdir@/src/isdn + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +# IMAGE_PATH = images/ + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range 1..20) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range 0,1..20) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = doc/libosmoisdn.tag + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = /usr/bin/dot + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES
View file
libosmocore_1.7.0.tar.xz/Doxyfile.vty.in -> libosmocore_1.8.0.tar.xz/Doxyfile.vty.in
Changed
@@ -1485,7 +1485,7 @@ # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html +TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads.
View file
libosmocore_1.7.0.tar.xz/Makefile.am -> libosmocore_1.8.0.tar.xz/Makefile.am
Changed
@@ -1,18 +1,9 @@ ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include SUBDIRS = \ include \ src \ - src/vty \ - src/codec \ - src/gsm \ - src/coding \ - src/gb \ - src/ctrl \ - src/sim \ - src/pseudotalloc \ - src/usb \ utils \ tapset \ tests \ @@ -21,7 +12,7 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc \ libosmogb.pc libosmoctrl.pc libosmocoding.pc libosmosim.pc \ - libosmousb.pc + libosmousb.pc libosmoisdn.pc aclocaldir = $(datadir)/aclocal dist_aclocal_DATA = m4/osmo_ac_code_coverage.m4 \ @@ -56,6 +47,7 @@ HTML = \ $(top_builddir)/doc/core/html/index.html \ $(top_builddir)/doc/gsm/html/index.html \ + $(top_builddir)/doc/isdn/html/index.html \ $(top_builddir)/doc/vty/html/index.html \ $(top_builddir)/doc/codec/html/index.html \ $(top_builddir)/doc/coding/html/index.html \ @@ -90,8 +82,8 @@ $(top_builddir)/doc/libosmocore.tag.prep: $(top_builddir)/Doxyfile.core \ $(top_srcdir)/include/osmocom/core/*.h \ - $(top_srcdir)/src/*.hc \ - $(top_srcdir)/src/crcXXgen.c.tpl \ + $(top_srcdir)/src/core/*.hc \ + $(top_srcdir)/src/core/crcXXgen.c.tpl \ $(top_srcdir)/src/pseudotalloc/*.hc rm -rf $(top_builddir)/doc/core; mkdir -p $(top_builddir)/doc/core rm -rf $(top_builddir)/doc/libosmocore.map @@ -109,6 +101,14 @@ -$(DOXYGEN) $(top_builddir)/Doxyfile.gsm touch "$@" +$(top_builddir)/doc/libosmoisdn.tag.prep: $(top_builddir)/Doxyfile.isdn \ + $(top_srcdir)/include/osmocom/isdn/*.h \ + $(top_srcdir)/src/isdn/*.c + rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn + rm -rf $(top_builddir)/doc/libosmoisdn.map + -$(DOXYGEN) $(top_builddir)/Doxyfile.isdn + touch "$@" + # Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986) $(top_builddir)/doc/libosmovty.tag.prep: $(top_builddir)/Doxyfile.vty \ $(top_srcdir)/include/osmocom/vty/*.h \ @@ -164,6 +164,7 @@ $(top_builddir)/doc/core/html/index.html: $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ @@ -174,6 +175,7 @@ $(top_builddir)/doc/gsm/html/index.html: $(top_builddir)/doc/libosmogsm.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ @@ -182,10 +184,22 @@ rm -rf $(top_builddir)/doc/gsm; mkdir -p $(top_builddir)/doc/gsm $(DOXYGEN) Doxyfile.gsm +$(top_builddir)/doc/isdn/html/index.html: $(top_builddir)/doc/libosmoisdn.tag.prep \ + $(top_builddir)/doc/libosmocore.tag.prep \ + $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmovty.tag.prep \ + $(top_builddir)/doc/libosmocodec.tag.prep \ + $(top_builddir)/doc/libosmocoding.tag.prep \ + $(top_builddir)/doc/libosmoctrl.tag.prep \ + $(top_builddir)/doc/libosmogb.tag.prep + rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn + $(DOXYGEN) Doxyfile.isdn + # Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986) $(top_builddir)/doc/vty/html/index.html: $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ $(top_builddir)/doc/libosmoctrl.tag.prep \ @@ -196,6 +210,7 @@ $(top_builddir)/doc/codec/html/index.html: $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ $(top_builddir)/doc/libosmoctrl.tag.prep \ @@ -206,6 +221,7 @@ $(top_builddir)/doc/coding/html/index.html: $(top_builddir)/doc/libosmocoding.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmoctrl.tag.prep \ @@ -216,6 +232,7 @@ $(top_builddir)/doc/ctrl/html/index.html: $(top_builddir)/doc/libosmoctrl.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ @@ -226,6 +243,7 @@ $(top_builddir)/doc/gb/html/index.html: $(top_builddir)/doc/libosmogb.tag.prep \ $(top_builddir)/doc/libosmocore.tag.prep \ $(top_builddir)/doc/libosmogsm.tag.prep \ + $(top_builddir)/doc/libosmoisdn.tag.prep \ $(top_builddir)/doc/libosmovty.tag.prep \ $(top_builddir)/doc/libosmocodec.tag.prep \ $(top_builddir)/doc/libosmocoding.tag.prep \ @@ -238,9 +256,9 @@ cd $(DESTDIR)$(htmldir) && tar xf html.tar && rm -f html.tar uninstall-hook: - cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,vty,codec,coding,ctrl,gb} + cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,isdn,vty,codec,coding,ctrl,gb} -DX_CLEAN = doc/{core,gsm,vty,codec,coding,ctrl,gb}/html/search/* doc/{core,gsm,vty,codec,coding,ctrl,gb}/{html,latex}/* doc/html.tar doc/{core,gsm,vty,codec,coding,ctrl,gb}/doxygen_sqlite3.db doc/*.tag doc/*.tag.prep +DX_CLEAN = doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb}/html/search/* doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb}/{html,latex}/* doc/html.tar doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb}/doxygen_sqlite3.db doc/*.tag doc/*.tag.prep endif MOSTLYCLEANFILES = $(DX_CLEAN)
View file
libosmocore_1.7.0.tar.xz/configure.ac -> libosmocore_1.8.0.tar.xz/configure.ac
Changed
@@ -53,12 +53,14 @@ *) LTLDFLAGS_OSMOGB='-Wl,--version-script=$(srcdir)/libosmogb.map' LTLDFLAGS_OSMOGSM='-Wl,--version-script=$(srcdir)/libosmogsm.map' + LTLDFLAGS_OSMOISDN='-Wl,--version-script=$(srcdir)/libosmoisdn.map' LTLDFLAGS_OSMOCODING='-Wl,--version-script=$(srcdir)/libosmocoding.map' LTLDFLAGS_OSMOCTRL='-Wl,--version-script=$(srcdir)/libosmoctrl.map' ;; esac AC_SUBST(LTLDFLAGS_OSMOGB) AC_SUBST(LTLDFLAGS_OSMOGSM) +AC_SUBST(LTLDFLAGS_OSMOISDN) AC_SUBST(LTLDFLAGS_OSMOCODING) AC_SUBST(LTLDFLAGS_OSMOCTRL) @@ -231,7 +233,7 @@ ), mnl=$enableval, mnl="yes") AS_IF(test "x$mnl" = "xyes", - PKG_CHECK_MODULES(LIBMNL, libmnl) + PKG_CHECK_MODULES(LIBMNL, libmnl, AC_SUBST(LIBMNL_PC, libmnl)) AC_DEFINE(ENABLE_LIBMNL, 1, Enable netlink socket support via libmnl) ) AM_CONDITIONAL(ENABLE_LIBMNL, test "x$mnl" = "xyes") @@ -380,7 +382,6 @@ AM_CONDITIONAL(ENABLE_CTRL, false) AM_CONDITIONAL(ENABLE_UTILITIES, false) AM_CONDITIONAL(ENABLE_GB, false) - AM_CONDITIONAL(ENABLE_GNUTLS, false) AM_CONDITIONAL(ENABLE_LIBMNL, false) AM_CONDITIONAL(ENABLE_LIBSCTP, false) AM_CONDITIONAL(ENABLE_LIBUSB, false) @@ -427,6 +428,7 @@ if test x"$werror" = x"yes" then WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" CFLAGS="$CFLAGS $WERROR_FLAGS" @@ -580,18 +582,35 @@ libosmocoding.pc libosmovty.pc libosmogsm.pc + libosmoisdn.pc libosmogb.pc libosmoctrl.pc libosmosim.pc libosmousb.pc include/Makefile + include/osmocom/Makefile + include/osmocom/codec/Makefile + include/osmocom/coding/Makefile + include/osmocom/core/Makefile + include/osmocom/crypt/Makefile + include/osmocom/ctrl/Makefile + include/osmocom/gprs/Makefile + include/osmocom/gprs/protocol/Makefile + include/osmocom/gsm/Makefile + include/osmocom/gsm/protocol/Makefile + include/osmocom/isdn/Makefile + include/osmocom/sim/Makefile + include/osmocom/usb/Makefile + include/osmocom/vty/Makefile src/Makefile + src/core/Makefile src/vty/Makefile src/codec/Makefile src/coding/Makefile src/sim/Makefile src/usb/Makefile src/gsm/Makefile + src/isdn/Makefile src/gb/Makefile src/ctrl/Makefile src/pseudotalloc/Makefile @@ -599,8 +618,10 @@ tests/Makefile tests/atlocal utils/Makefile + utils/osmo-stat-dummy/Makefile Doxyfile.core Doxyfile.gsm + Doxyfile.isdn Doxyfile.vty Doxyfile.codec Doxyfile.coding
View file
libosmocore_1.7.0.tar.xz/contrib/jenkins_arm.sh -> libosmocore_1.8.0.tar.xz/contrib/jenkins_arm.sh
Changed
@@ -21,7 +21,6 @@ --disable-libsctp \ --disable-libusb \ --disable-libmnl \ - --enable-external-tests \ CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS" $MAKE $PARALLEL_MAKE
View file
libosmocore_1.7.0.tar.xz/contrib/libosmocore.spec.in -> libosmocore_1.8.0.tar.xz/contrib/libosmocore.spec.in
Changed
@@ -13,6 +13,7 @@ # published by the Open Source Initiative. Name: libosmocore +Requires: osmocom-latest Version: @VERSION@ Release: 0 Summary: The Open Source Mobile Communications Core Library @@ -43,6 +44,7 @@ between OsmocomBB and OpenBSC to avoid code duplication. %package tools +Requires: osmocom-latest Summary: GSM utilities from the osmocore project License: GPL-2.0-only AND GPL-2.0-or-later AND LGPL-3.0-or-later AND AGPL-3.0-or-later Group: Productivity/Telephony/Utilities @@ -58,6 +60,7 @@ for merging Osmocom configuration files. %package -n libosmocodec0 +Requires: osmocom-latest Summary: GSM 06.10, 06.20, 06.60, 06.90 codec library License: GPL-2.0-or-later Group: System/Libraries @@ -72,6 +75,7 @@ * GSM 06.90 Adaptive Multi-Rate (AMR) codec %package -n libosmocodec-devel +Requires: osmocom-latest Summary: Development files for the Osmocom GSM codec library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -85,6 +89,7 @@ applications that want to make use of libosmocodec. %package -n libosmocoding0 +Requires: osmocom-latest Summary: GSM/GPRS/EDGE transcoding routines library License: GPL-2.0-or-later Group: System/Libraries @@ -97,6 +102,7 @@ and MCS 1-9), TCH/FR, TCH/HR, TCH/AFS, RCH/AHS, RACH and SCH. %package -n libosmocoding-devel +Requires: osmocom-latest Summary: Development files for the Osmocom transcoding library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -112,22 +118,24 @@ This subpackage contains libraries and header files for developing applications that want to make use of libosmocoding. -%package -n libosmocore19 +%package -n libosmocore20 +Requires: osmocom-latest Summary: Osmocom core library # crc16.c has GPL2-only clauses, the rest (*.c) is GPL-2.0+ License: GPL-2.0-only AND GPL-2.0-or-later Group: System/Libraries -%description -n libosmocore19 +%description -n libosmocore20 libosmocore is a library with various utility functions shared between OpenBSC and OsmocomBB. %package -n libosmocore-devel +Requires: osmocom-latest Summary: Development files for the Osmocom core library # crc16.h has GPL2-only clauses, the rest (*.h) is GPL-2.0+ License: GPL-2.0-only AND GPL-2.0-or-later Group: Development/Libraries/C and C++ -Requires: libosmocore19 = %version +Requires: libosmocore20 = %version Requires: libtalloc-devel Requires: lksctp-tools-devel @@ -139,6 +147,7 @@ applications that want to make use of libosmocore. %package -n libosmoctrl0 +Requires: osmocom-latest Summary: Osmocom SNMP-like control interface library License: GPL-2.0-or-later Group: System/Libraries @@ -151,6 +160,7 @@ interface, the control interface is meant to be used by programs. %package -n libosmoctrl-devel +Requires: osmocom-latest Summary: Osmocom control interface library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -166,6 +176,7 @@ applications that want to make use of libosmoctrl. %package -n libosmogb14 +Requires: osmocom-latest Summary: Osmocom GPRS Gb Interface (NS/BSSGP) library License: AGPL-3.0-or-later Group: System/Libraries @@ -177,6 +188,7 @@ The libosmogb library contains a GPRS BSSGP protocol implementation. %package -n libosmogb-devel +Requires: osmocom-latest Summary: Development files for the Osmocom GPRS Gb interface library License: AGPL-3.0-or-later Group: Development/Libraries/C and C++ @@ -191,6 +203,7 @@ applications that want to make use of libosmogb. %package -n libosmogsm18 +Requires: osmocom-latest Summary: Osmocom GSM utility library License: GPL-2.0-or-later AND AGPL-3.0-or-later Group: System/Libraries @@ -206,11 +219,14 @@ protocol definitions for a series of protocols. %package -n libosmogsm-devel +Requires: osmocom-latest Summary: Development files for the Osmocom GSM utility library License: GPL-2.0-or-later AND AGPL-3.0-or-later Group: Development/Libraries/C and C++ Requires: libosmocore-devel = %version Requires: libosmogsm18 = %version +Requires: libosmoisdn-devel = %version +Requires: libosmoisdn0 = %version %description -n libosmogsm-devel The libosmogsm library in particular is a collection of common code @@ -222,7 +238,38 @@ This subpackage contains libraries and header files for developing applications that want to make use of libosmogsm. +%package -n libosmoisdn0 +Requires: osmocom-latest +Summary: Osmocom ISDN utility library +License: GPL-2.0-or-later +Group: System/Libraries + +%description -n libosmoisdn0 +libosmocore is a package with various utility functions that were +originally developed as part of the OpenBSC project. + +The libosmoisdn library in particular is a collection of common code used in +various ISDN related sub-projects inside the Osmocom family of projects. It +includes an I.460 sub-channel multiplex and a generic LAPD core. + +%package -n libosmoisdn-devel +Requires: osmocom-latest +Summary: Development files for the Osmocom ISDN utility library +License: GPL-2.0-or-later +Group: Development/Libraries/C and C++ +Requires: libosmocore-devel = %version +Requires: libosmoisdn0 = %version + +%description -n libosmoisdn-devel +The libosmoisdn library in particular is a collection of common code used in +various ISDN related sub-projects inside the Osmocom family of projects. It +includes an I.460 sub-channel multiplex and a generic LAPD core. + +This subpackage contains libraries and header files for developing +applications that want to make use of libosmogsm. + %package -n libosmosim2 +Requires: osmocom-latest Summary: Osmocom SIM card related utility library License: GPL-2.0-or-later Group: System/Libraries @@ -235,6 +282,7 @@ access. %package -n libosmosim-devel +Requires: osmocom-latest Summary: Development files for the Osmocom SIM card utility library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -249,6 +297,7 @@ applications that want to make use of libosmosim. %package -n libosmovty9 +Requires: osmocom-latest Summary: Osmocom VTY interface library License: GPL-2.0-or-later Group: System/Libraries @@ -261,6 +310,7 @@ VTY (Virtual TTY), as well as configuration file parsing. %package -n libosmovty-devel +Requires: osmocom-latest Summary: Development files for the Osmocom VTY interface library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -275,6 +325,7 @@ applications that want to make use of libosmovty. %package -n libosmousb0 +Requires: osmocom-latest Summary: Osmocom USB library License: GPL-2.0-or-later Group: System/Libraries @@ -287,6 +338,7 @@ access via libusb-1.0, integrated into the libosmocore select event loop. %package -n libosmousb-devel +Requires: osmocom-latest Summary: Development files for the Osmocom USB library License: GPL-2.0-or-later Group: Development/Libraries/C and C++ @@ -324,14 +376,16 @@ %postun -n libosmocodec0 -p /sbin/ldconfig %post -n libosmocoding0 -p /sbin/ldconfig %postun -n libosmocoding0 -p /sbin/ldconfig -%post -n libosmocore19 -p /sbin/ldconfig -%postun -n libosmocore19 -p /sbin/ldconfig +%post -n libosmocore20 -p /sbin/ldconfig +%postun -n libosmocore20 -p /sbin/ldconfig %post -n libosmoctrl0 -p /sbin/ldconfig %postun -n libosmoctrl0 -p /sbin/ldconfig %post -n libosmogb14 -p /sbin/ldconfig %postun -n libosmogb14 -p /sbin/ldconfig %post -n libosmogsm18 -p /sbin/ldconfig %postun -n libosmogsm18 -p /sbin/ldconfig +%post -n libosmoisdn0 -p /sbin/ldconfig +%postun -n libosmoisdn0 -p /sbin/ldconfig %post -n libosmosim2 -p /sbin/ldconfig %postun -n libosmosim2 -p /sbin/ldconfig %post -n libosmovty9 -p /sbin/ldconfig @@ -367,9 +421,9 @@ %_libdir/libosmocoding.so %_libdir/pkgconfig/libosmocoding.pc -%files -n libosmocore19 +%files -n libosmocore20 %defattr(-,root,root) -%_libdir/libosmocore.so.19* +%_libdir/libosmocore.so.20* %files -n libosmocore-devel %defattr(-,root,root) @@ -418,6 +472,18 @@ %_libdir/libosmogsm.so %_libdir/pkgconfig/libosmogsm.pc +%files -n libosmoisdn0 +%defattr(-,root,root) +%_libdir/libosmoisdn.so.0* + +%files -n libosmoisdn-devel +%defattr(-,root,root) +%dir %_includedir/%name +%dir %_includedir/%name/osmocom +%_includedir/%name/osmocom/isdn/ +%_libdir/libosmoisdn.so +%_libdir/pkgconfig/libosmoisdn.pc + %files -n libosmosim2 %defattr(-,root,root) %_libdir/libosmosim.so.2*
View file
libosmocore_1.7.0.tar.xz/debian/changelog -> libosmocore_1.8.0.tar.xz/debian/changelog
Changed
@@ -1,3 +1,155 @@ +libosmocore (1.8.0) unstable; urgency=medium + + Vadim Yanitskiy + * fix uninitialized err pointer passed to osmo_bssap_le_dec() + * gsm0408_test: do not return early in test_bearer_cap() + * gsm0408_test: add a testcase for gsm48_decode_bearer_cap() + * gsm48_ie: fix coding style: while is not a function + * gb: fix uninitialized ptr access in bssgp_encode_rim_pdu() + * fsm: add unit tests verifying state timeout s/ms accuracy + * fsm: fix state_chg(): pass microseconds to osmo_timer_schedule() + * tests/tdef: assert pointer returned by osmo_tdef_get_entry() + * gb/gprs_ns: call osmo_timer_del() unconditionally + * fsm: osmo_fsm_{event,inst,state}_name(): make *fi pointer const + * logging: add a new category DLCSN1 for libosmo-csn1 + * {gb,sim,usb}: ensure -no-undefined is present in *_la_LDFLAGS + * include: use '#pragma once' everywhere + * gsm0502: use parentheses in GSM_TDMA_FN_{SUM,SUB} macros + * configure.ac: fix 'AM_CONDITIONAL(ENABLE_GNUTLS, false)' listed twice + * {gsm,gb}/Makefile.am: drop undefined $GCC_FVISIBILITY_HIDDEN + * gsm0502: gsm0502_fn_remap(): use GSM_TDMA_FN_SUB() macro + * */Makefile.am: do not mix up AM_CFLAGS with AM_CPPFLAGS + * gsm0808: cosmetic: switch is not a function + * gsm0808: remove unneeded assignment in enc_speech_codec() + * gsm0808: remove redundant assert() in enc_speech_codec() + * gsm0808: remove over-defensive assert()s for function parameters + * gsm0808: add gsm0808_enc_speech_codec_list2() + * gsm0808: use new gsm0808_enc_speech_codec_list2() API + * gsm48_ie: gsm48_decode_freq_list(): make 'cd' argument const + + Pau Espin Pedrol + * iuup: Explicitly mark default case as unexpected with assert + * cbsp: avoid potential msgb write overflow in osmo_cbsp_recv_buffered + * gsm_23_041.h: Define CBS ETWS Warning Type values + * cbsp: Return error if decoding any of the cell id lists fail + * tests: Run smscb/gsm0341_test during make check + * cbsp: Guard against malformed msgb without l1h,l2h being passed + * cbsp: Fix decoding of Fail List + * cosmetic: tlv.h: Fix trailing whistespace + * tlv.h: Fix TLVP_PRESENT returning a pointer instead of a boolean + * gsm: Add BTS feature for Osmux + * gsm: rsl: Define new osmocom extension TLV IE to pass Osmux CID + * gsm: bts_features: Add missing entries to osmo_bts_features_names + * utils.h: protect param with parenthesis in OSMO_BYTES_FOR_BITS() + * vty: Allow using hex representations in cmd numeric ranges + * socket.h: Reorder sockaddr APIs to have them all together + * socket: Introduce API osmo_sockaddr_is_any + * gsm: constify several readonly params + * ctrl: error if program forgot to initialize the ctr handler before installing cmds + * socket.h: Introduce API osmo_sockaddr_netmask_to_prefixlen() + * Move src/*.{c,h} to src/core/ + * src/core/Makefile.am: reformat SOURCES list + * Split include/Makefile.am content into subdirs + * Makefile.am: Remove unexsiting all_includes variable + * Fix all references to config.h + * Introduce netns API + * Introduce netdev API + * Introduce tundev API + * configure --enable-libmnl: Add libmnl to libosmocore.pc.in Requires + * netdev: Fix compilation building with --disable-libmnl + * tun: Fix potential unpaired call to osmo_netns_switch_exit() + * gprs_ns2_fr: use osmo_netdev to monitor and operate network device + * debian/rules: Fix moved path crc*gen.c + + Mychaela Falconia + * gsm48_ie: fix parsing of Bearer capability IE without octet 3a + + Harald Welte + * sim/class_tables: Add GET IDENTITY, SUSPEND UICC, EXCHANGE CAPABILITIES + * allocate VTY port number 4270 for osmo-isdntap + * logging.h: Allocate DLM2PA and DLM2UA for libosmo-sigtran + * Support building with -Werror=strict-prototypes / -Werror=old-style-definition + * Disable -Wstrict-prototypes for logging_vty_add_cmds() + * vty/logging.h: Avoid -Werror=pragmas error in C++ code + * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition + * Fix typos in copyright statements. + * gsmtap.h: Add definitions for various ISDN sub-types + * create libosmoisdn sub-library + * isdndlc: Fix documentation + + Oliver Smith + * gsm0808_enc_aoip_trasp_addr: add length check + * utils/osmo-stat-dummy: check for ENABLE_UTILITIES + * d/control: libosmocore-dev: depend on libmnl-dev + * gsm_08_08.h: fix typo in GSM0808_DATA_FULL_PREF + + Alexander Couzens + * gprs_ns2: add vty `nse <0-65535> restart sns` + * gb: add bssgp2_enc_flush_ll encode FLUSH-LL + + Neels Hofmeyr + * enrich API doc for gsm0808_speech_codec + * gsm0408_test: do not print errno in expected output + * comments: gsm_08_08.h: AMR cfg: explain in much more detail + * osmo_tdef_get(): clarify API doc on val_if_not_present + + Max + * Ignore osmo-ns-dummy + * Add function to guess AF_UNSPEC address + * Add osmo_sockaddr_strs_to_str() + * cosmetic: remove trailing space + * cosmetic: make linter happy with LAPD code + * LAPD: log unknown format value + * LAPD: use bool for T200 reset flags + * msgb: expand copy test + * doc: correct typo in ticket reference + * msgb: introduce extended copy functions + * Add define for unset Frame Number + * LAPD: move tx_hist code into static functions + * osmo-ns-dummy: add ctrl interface + * jenkins_arm.sh: disable external tests + * vty: fix doc typo + * telnet_init_dynif: propagate error + * telnet_init_dynif: don't allow negative port + * rate_ctr: convert to timerfd + * rate_ctr: drop rate estimation code + * osmo-stat-dummy: add rate counters and statsd tester + * ctrl: add optional port to bind command + * ASCI: add VBS/VGCS support to BTS features list + * SI: add RR short PD message types + * Fixup .gitignore + * SI: add missing header + * Add SI10 support + + neels + * Revert "Add osmo_sockaddr_strs_to_str()" + * Revert "Add function to guess AF_UNSPEC address" + + Daniel Willmann + * use_count: Return if uc is NULL + + Keith Whyte + * Fix LCLS-CONNECT-CONTROL generation + * Fix Typo in gsm0808_msgt_names + + Philipp Maier + * msgb: assert msgb->lXh to be not NULL + * msgb: do not use msgb_l4 instead of msgb_sms + * bits: fix typo + * uitils: add floored and euclidian modulo functions + * gsm0408_test: add unittest for gsm_gsmtime2fn() + * gsm_utils: improve gsm_gsmtime2fn() + + arehbein + * gb/vty: Show if NSVC is blocked locally by O&M/vty or by remote + * libosmocore: Deprecate APIs telnet_init(_dynip)() + * libosmocore: Transition to use of 'telnet_init_default' + + Eric + * bitgen test: fix concat macro + + -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 11:20:41 +0100 + libosmocore (1.7.0) unstable; urgency=medium Vadim Yanitskiy
View file
libosmocore_1.7.0.tar.xz/debian/control -> libosmocore_1.8.0.tar.xz/debian/control
Changed
@@ -31,9 +31,10 @@ Multi-Arch: foreign Depends: osmocom-latest, libosmocodec0 (= ${binary:Version}), libosmocoding0 (= ${binary:Version}), - libosmocore19 (= ${binary:Version}), + libosmocore20 (= ${binary:Version}), libosmogb14 (= ${binary:Version}), libosmogsm18 (= ${binary:Version}), + libosmoisdn0 (= ${binary:Version}), libosmovty9 (= ${binary:Version}), libosmoctrl0 (= ${binary:Version}), libosmosim2 (= ${binary:Version}), @@ -115,7 +116,7 @@ . This package contains the documentation for the libosmocoding library. -Package: libosmocore19 +Package: libosmocore20 Section: libs Architecture: any Multi-Arch: same @@ -136,11 +137,12 @@ Architecture: all Section: doc Depends: osmocom-latest, ${misc:Depends}, - libosmocore19, + libosmocore20, libjs-jquery, libosmocodec-doc, libosmocoding-doc, libosmogsm-doc, + libosmoisdn-doc, libosmovty-doc Description: Documentation for the Osmo Core library This is part of the libosmocore "meta"-library. The libosmocore library @@ -218,6 +220,39 @@ . This package contains the documentation for the libosmogsm library. +Package: libosmoisdn0 +Section: libs +Architecture: any +Multi-Arch: same +Depends: osmocom-latest, ${shlibs:Depends}, + ${misc:Depends} +Pre-Depends: ${misc:Pre-Depends} +Description: Osmo ISDN utility library + This is part of the libosmocore "meta"-library. The libosmocore library + contains various utility functions that were originally developed as part of + the OpenBSC project, but which are of a more generic nature and thus useful to + (at least) other programs that are developed in the sphere of Free Software / + Open Source mobile communication. + . + The libosmoisdn library in particular is a collection of common code used in + various ISDN related sub-projects inside the Osmocom family of projects. It + includes an I.460 sub-channel multiplex and a generic LAPD core. + +Package: libosmoisdn-doc +Architecture: all +Section: doc +Depends: osmocom-latest, ${misc:Depends}, + libosmoisdn0, + libjs-jquery +Description: Documentation for the Osmo ISDN utility library + This is part of the libosmocore "meta"-library. The libosmocore library + contains various utility functions that were originally developed as part of + the OpenBSC project, but which are of a more generic nature and thus useful to + (at least) other programs that are developed in the sphere of Free Software / + Open Source mobile communication. + . + This package contains the documentation for the libosmoisdnm library. + Package: libosmovty9 Section: libs Architecture: any @@ -322,6 +357,7 @@ libtalloc-dev (>= 2.1.0), libsctp-dev, libusb-1.0-0-dev, + libmnl-dev, ${misc:Depends} Description: Development headers for Open Source MObile COMmunications CORE library The header files provided by this package may be used to develop
View file
libosmocore_1.7.0.tar.xz/debian/copyright -> libosmocore_1.8.0.tar.xz/debian/copyright
Changed
@@ -13,30 +13,30 @@ Files: include/osmocom/core/loggingrb.h include/osmocom/core/strrb.h - src/strrb.c - src/loggingrb.c + src/core/strrb.c + src/core/loggingrb.c Copyright: 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com> License: GPL-2+ Files: include/osmocom/core/linuxrbtree.h - src/rbtree.c + src/core/rbtree.c Copyright: 1999 Andrea Arcangeli <andrea@suse.de> 2002 David Woodhouse <dwmw2@infradead.org> License: GPL-2+ Files: include/osmocom/core/crc16.h - src/crc16.c + src/core/crc16.c Copyright: 2005 Ben Gardner <bgardner@wabtec.com> License: GPL-2 -Files: src/utils.c +Files: src/core/utils.c Copyright: 2011 Harald Welte <laforge@gnumonks.org> 2011 Sylvain Munaut <tnt@246tNt.com> 2014 Nils O. Selåsdal <noselasd@fiane.dyndns.org> License: GPL-2+ Files: src/gsm/gsm48_ie.c - src/gsm/lapd_core.c + src/isdn/lapd_core.c src/gsm/lapdm.c Copyright: 2008,2010-2011 Harald Welte <laforge@gnumonks.org> 2009-2011 Andreas Eversberg <jolly@eversberg.eu> @@ -100,8 +100,8 @@ License: GPL-2+ Files: include/osmocom/core/stats.h - src/stat_item.c - src/stats.c + src/core/stat_item.c + src/core/stats.c src/vty/stats_vty.c tests/stats/stats_test.c Copyright: 2009-2010 by Harald Welte <laforge@gnumonks.org>
View file
libosmocore_1.7.0.tar.xz/debian/libosmocore.dirs -> libosmocore_1.8.0.tar.xz/debian/libosmocore.dirs
Changed
@@ -5,4 +5,5 @@ usr/include/osmocom/core usr/include/osmocom/crypt usr/include/osmocom/gsm +usr/include/osmocom/isdn usr/include/osmocom/vty
View file
libosmocore_1.8.0.tar.xz/debian/libosmocore20.install
Changed
(renamed from debian/libosmocore19.install)
View file
libosmocore_1.8.0.tar.xz/debian/libosmoisdn-doc.doc-base
Added
@@ -0,0 +1,7 @@ +Document: libosmoisdn-doc +Title: Documentation for the libosmoisdn library +Section: Programming + +Format: HTML +Index: /usr/share/doc/libosmocore/isdn/html/index.html +Files: /usr/share/doc/libosmocore/isdn/html/*.html
View file
libosmocore_1.8.0.tar.xz/debian/libosmoisdn-doc.install
Added
@@ -0,0 +1 @@ +usr/share/doc/libosmocore/isdn/
View file
libosmocore_1.8.0.tar.xz/debian/libosmoisdn0.install
Added
@@ -0,0 +1 @@ +usr/lib/*/libosmoisdn*.so.*
View file
libosmocore_1.7.0.tar.xz/debian/rules -> libosmocore_1.8.0.tar.xz/debian/rules
Changed
@@ -40,16 +40,17 @@ $(RM) include/osmocom/core/crc32gen.h $(RM) include/osmocom/core/crc64gen.h $(RM) include/osmocom/core/crc8gen.h - $(RM) src/crc16gen.c - $(RM) src/crc32gen.c - $(RM) src/crc64gen.c - $(RM) src/crc8gen.c + $(RM) src/core/crc16gen.c + $(RM) src/core/crc32gen.c + $(RM) src/core/crc64gen.c + $(RM) src/core/crc8gen.c $(RM) tests/package.m4 $(RM) tests/testsuite $(RM) -r doc/codec/ $(RM) -r doc/core/ $(RM) -r doc/ctrl/ $(RM) -r doc/gsm/ + $(RM) -r doc/isdn/ $(RM) -r doc/gb/ $(RM) -r doc/vty/html/ $(RM) -r doc/vty/latex/
View file
libosmocore_1.7.0.tar.xz/include/Makefile.am -> libosmocore_1.8.0.tar.xz/include/Makefile.am
Changed
@@ -1,225 +1 @@ -BUILT_SOURCES = osmocom/gsm/gsm0503.h - -nobase_include_HEADERS = \ - osmocom/codec/ecu.h \ - osmocom/codec/codec.h \ - osmocom/codec/gsm610_bits.h \ - osmocom/core/application.h \ - osmocom/core/backtrace.h \ - osmocom/core/base64.h \ - osmocom/core/bit16gen.h \ - osmocom/core/bit32gen.h \ - osmocom/core/bit64gen.h \ - osmocom/core/bits.h \ - osmocom/core/bitvec.h \ - osmocom/core/bitcomp.h \ - osmocom/core/byteswap.h \ - osmocom/core/conv.h \ - osmocom/core/counter.h \ - osmocom/core/crc16.h \ - osmocom/core/crc16gen.h \ - osmocom/core/crc32gen.h \ - osmocom/core/crc64gen.h \ - osmocom/core/crc8gen.h \ - osmocom/core/crcgen.h \ - osmocom/core/endian.h \ - osmocom/core/defs.h \ - osmocom/core/exec.h \ - osmocom/core/fsm.h \ - osmocom/core/gsmtap.h \ - osmocom/core/gsmtap_util.h \ - osmocom/core/hash.h \ - osmocom/core/hashtable.h \ - osmocom/core/isdnhdlc.h \ - osmocom/core/it_q.h \ - osmocom/core/linuxlist.h \ - osmocom/core/linuxrbtree.h \ - osmocom/core/log2.h \ - osmocom/core/logging.h \ - osmocom/core/loggingrb.h \ - osmocom/core/stats.h \ - osmocom/core/macaddr.h \ - osmocom/core/msgb.h \ - osmocom/core/panic.h \ - osmocom/core/prbs.h \ - osmocom/core/prim.h \ - osmocom/core/process.h \ - osmocom/core/rate_ctr.h \ - osmocom/core/stat_item.h \ - osmocom/core/stats_tcp.h \ - osmocom/core/select.h \ - osmocom/core/sercomm.h \ - osmocom/core/signal.h \ - osmocom/core/socket.h \ - osmocom/core/statistics.h \ - osmocom/core/strrb.h \ - osmocom/core/talloc.h \ - osmocom/core/tdef.h \ - osmocom/core/thread.h \ - osmocom/core/timer.h \ - osmocom/core/timer_compat.h \ - osmocom/core/utils.h \ - osmocom/core/write_queue.h \ - osmocom/core/sockaddr_str.h \ - osmocom/core/time_cc.h \ - osmocom/core/use_count.h \ - osmocom/crypt/auth.h \ - osmocom/crypt/gprs_cipher.h \ - osmocom/crypt/kdf.h \ - osmocom/crypt/utran_cipher.h \ - osmocom/ctrl/control_cmd.h \ - osmocom/ctrl/control_if.h \ - osmocom/ctrl/ports.h \ - osmocom/gprs/frame_relay.h \ - osmocom/gprs/bssgp_bvc_fsm.h \ - osmocom/gprs/gprs_bssgp.h \ - osmocom/gprs/gprs_bssgp2.h \ - osmocom/gprs/gprs_bssgp_bss.h \ - osmocom/gprs/gprs_bssgp_rim.h \ - osmocom/gprs/gprs_msgb.h \ - osmocom/gprs/gprs_ns.h \ - osmocom/gprs/gprs_ns_frgre.h \ - osmocom/gprs/gprs_ns2.h \ - osmocom/gprs/gprs_rlc.h \ - osmocom/gprs/protocol/gsm_04_60.h \ - osmocom/gprs/protocol/gsm_08_16.h \ - osmocom/gprs/protocol/gsm_08_18.h \ - osmocom/gprs/protocol/gsm_24_301.h \ - osmocom/gsm/a5.h \ - osmocom/gsm/abis_nm.h \ - osmocom/gsm/apn.h \ - osmocom/gsm/bts_features.h \ - osmocom/gsm/cbsp.h \ - osmocom/gsm/comp128.h \ - osmocom/gsm/comp128v23.h \ - osmocom/gsm/bitvec_gsm.h \ - osmocom/gsm/gan.h \ - osmocom/gsm/gsm0341.h \ - osmocom/gsm/gsm0411_smc.h \ - osmocom/gsm/gsm0411_smr.h \ - osmocom/gsm/gsm0411_utils.h \ - osmocom/gsm/gsm0480.h \ - osmocom/gsm/gsm0502.h \ - osmocom/gsm/gsm0503.h \ - osmocom/coding/gsm0503_tables.h \ - osmocom/coding/gsm0503_parity.h \ - osmocom/coding/gsm0503_mapping.h \ - osmocom/coding/gsm0503_interleaving.h \ - osmocom/coding/gsm0503_coding.h \ - osmocom/coding/gsm0503_amr_dtx.h \ - osmocom/gsm/bsslap.h \ - osmocom/gsm/bssmap_le.h \ - osmocom/gsm/gad.h \ - osmocom/gsm/gsm0808.h \ - osmocom/gsm/gsm0808_lcs.h \ - osmocom/gsm/gsm29205.h \ - osmocom/gsm/gsm0808_utils.h \ - osmocom/gsm/gsm23003.h \ - osmocom/gsm/gsm23236.h \ - osmocom/gsm/gsm29118.h \ - osmocom/gsm/gsm48.h \ - osmocom/gsm/gsm48_arfcn_range_encode.h \ - osmocom/gsm/gsm48_ie.h \ - osmocom/gsm/gsm48_rest_octets.h \ - osmocom/gsm/gsm_utils.h \ - osmocom/gsm/gsup.h \ - osmocom/gsm/gsup_sms.h \ - osmocom/gsm/i460_mux.h \ - osmocom/gsm/ipa.h \ - osmocom/gsm/iuup.h \ - osmocom/gsm/lapd_core.h \ - osmocom/gsm/lapdm.h \ - osmocom/gsm/meas_rep.h \ - osmocom/gsm/mncc.h \ - osmocom/gsm/prim.h \ - osmocom/gsm/l1sap.h \ - osmocom/gsm/oap.h \ - osmocom/gsm/oap_client.h \ - osmocom/gsm/protocol/gsm_23_032.h \ - osmocom/gsm/protocol/gsm_03_40.h \ - osmocom/gsm/protocol/gsm_03_41.h \ - osmocom/gsm/protocol/gsm_04_08.h \ - osmocom/gsm/protocol/gsm_04_08_gprs.h \ - osmocom/gsm/protocol/gsm_04_11.h \ - osmocom/gsm/protocol/gsm_04_12.h \ - osmocom/gsm/protocol/gsm_04_14.h \ - osmocom/gsm/protocol/gsm_04_80.h \ - osmocom/gsm/protocol/gsm_08_08.h \ - osmocom/gsm/protocol/gsm_08_58.h \ - osmocom/gsm/protocol/gsm_09_02.h \ - osmocom/gsm/protocol/gsm_12_21.h \ - osmocom/gsm/protocol/gsm_23_003.h \ - osmocom/gsm/protocol/gsm_23_041.h \ - osmocom/gsm/protocol/gsm_25_415.h \ - osmocom/gsm/protocol/gsm_29_118.h \ - osmocom/gsm/protocol/gsm_44_004.h \ - osmocom/gsm/protocol/gsm_44_318.h \ - osmocom/gsm/protocol/gsm_48_049.h \ - osmocom/gsm/protocol/gsm_48_071.h \ - osmocom/gsm/protocol/gsm_49_031.h \ - osmocom/gsm/protocol/ipaccess.h \ - osmocom/gsm/protocol/smpp34_osmocom.h \ - osmocom/gsm/rsl.h \ - osmocom/gsm/rxlev_stat.h \ - osmocom/gsm/sysinfo.h \ - osmocom/gsm/tlv.h \ - osmocom/sim/class_tables.h \ - osmocom/sim/sim.h - -if ENABLE_PLUGIN -nobase_include_HEADERS += osmocom/core/plugin.h -endif - -if ENABLE_MSGFILE -nobase_include_HEADERS += osmocom/core/msgfile.h -endif - -if ENABLE_SERIAL -nobase_include_HEADERS += osmocom/core/serial.h -endif - - -if ENABLE_VTY -nobase_include_HEADERS += \ - osmocom/vty/buffer.h \ - osmocom/vty/command.h \ - osmocom/vty/logging.h \ - osmocom/vty/stats.h \ - osmocom/vty/misc.h \ - osmocom/vty/telnet_interface.h \ - osmocom/vty/vector.h \ - osmocom/vty/vty.h \ - osmocom/vty/ports.h \ - osmocom/vty/cpu_sched_vty.h \ - osmocom/vty/tdef_vty.h \ - osmocom/ctrl/control_vty.h -endif - -if ENABLE_LIBUSB -nobase_include_HEADERS += \ - osmocom/usb/libusb.h -endif - -if ENABLE_LIBMNL -nobase_include_HEADERS += osmocom/core/mnl.h -endif - -noinst_HEADERS = \ - osmocom/gsm/kasumi.h \ - osmocom/gsm/gea.h \ - osmocom/core/logging_internal.h \ - $(NULL) - -osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl - $(AM_V_GEN)$(MKDIR_P) $(dir $@) - $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ - -osmocom/core/crc%gen.h: osmocom/core/crcXXgen.h.tpl - $(AM_V_GEN)$(MKDIR_P) $(dir $@) - $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ - -osmocom/gsm/gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py - $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_header gsm \ - --target-path $(builddir)/osmocom/gsm - -CLEANFILES = osmocom/gsm/gsm0503.h +SUBDIRS = osmocom
View file
libosmocore_1.8.0.tar.xz/include/osmocom/Makefile.am
Added
@@ -0,0 +1,13 @@ +SUBDIRS = \ + core \ + vty \ + codec \ + gsm \ + isdn \ + crypt \ + coding \ + gprs \ + ctrl \ + sim \ + usb \ + $(NULL)
View file
libosmocore_1.8.0.tar.xz/include/osmocom/codec/Makefile.am
Added
@@ -0,0 +1,7 @@ +osmocodec_HEADERS = \ + ecu.h \ + codec.h \ + gsm610_bits.h \ + $(NULL) + +osmocodecdir = $(includedir)/osmocom/codec
View file
libosmocore_1.8.0.tar.xz/include/osmocom/coding/Makefile.am
Added
@@ -0,0 +1,10 @@ +osmocoding_HEADERS = \ + gsm0503_tables.h \ + gsm0503_parity.h \ + gsm0503_mapping.h \ + gsm0503_interleaving.h \ + gsm0503_coding.h \ + gsm0503_amr_dtx.h \ + $(NULL) + +osmocodingdir = $(includedir)/osmocom/coding
View file
libosmocore_1.8.0.tar.xz/include/osmocom/core/Makefile.am
Added
@@ -0,0 +1,94 @@ +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 \ + linuxlist.h \ + linuxrbtree.h \ + log2.h \ + logging.h \ + loggingrb.h \ + stats.h \ + macaddr.h \ + msgb.h \ + netdev.h \ + netns.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 \ + time_cc.h \ + use_count.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' $< > $@
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/counter.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/counter.h
Changed
@@ -52,7 +52,7 @@ 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);
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/fsm.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/fsm.h
Changed
@@ -224,9 +224,9 @@ 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)
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/gsmtap.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/gsmtap.h
Changed
@@ -179,6 +179,11 @@ #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 */ /* flags for the ARFCN */ #define GSMTAP_ARFCN_F_PCS 0x8000
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/isdnhdlc.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/isdnhdlc.h
Changed
@@ -22,8 +22,7 @@ * GNU General Public License for more details. */ -#ifndef __ISDNHDLC_H__ -#define __ISDNHDLC_H__ +#pragma once #include <stdint.h> @@ -76,5 +75,3 @@ 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__ */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/logging.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/logging.h
Changed
@@ -151,7 +151,10 @@ #define DLNSSIGNAL -23 /*!< Osmocom NS layer signal pdus */ #define DLIUUP -24 /*!< Osmocom IuUP layer */ #define DLPFCP -25 /*!< Osmocom Packet Forwarding Control Protocol */ -#define OSMO_NUM_DLIB 25 /*!< Number of logging sub-systems in libraries */ +#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 OSMO_NUM_DLIB 28 /*!< Number of logging sub-systems in libraries */ /* Colors that can be used in log_info_cat.color */ #define OSMO_LOGCOLOR_NORMAL NULL
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/msgb.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/msgb.h
Changed
@@ -70,6 +70,8 @@ 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(). @@ -144,6 +146,7 @@ */ static inline unsigned int msgb_l1len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l1h); return msgb->tail - (uint8_t *)msgb_l1(msgb); } @@ -156,6 +159,7 @@ */ static inline unsigned int msgb_l2len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l2h); return msgb->tail - (uint8_t *)msgb_l2(msgb); } @@ -168,6 +172,7 @@ */ static inline unsigned int msgb_l3len(const struct msgb *msgb) { + OSMO_ASSERT(msgb->l3h); return msgb->tail - (uint8_t *)msgb_l3(msgb); } @@ -180,7 +185,8 @@ */ 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
View file
libosmocore_1.8.0.tar.xz/include/osmocom/core/netdev.h
Added
@@ -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) */ +/*! @} */
View file
libosmocore_1.8.0.tar.xz/include/osmocom/core/netns.h
Added
@@ -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) */ +/*! @} */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/select.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/select.h
Changed
@@ -105,8 +105,8 @@ struct osmo_signalfd * osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data); -void osmo_select_shutdown_request(); -int osmo_select_shutdown_requested(); -bool osmo_select_shutdown_done(); +void osmo_select_shutdown_request(void); +int osmo_select_shutdown_requested(void); +bool osmo_select_shutdown_done(void); /*! @} */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/sercomm.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/sercomm.h
Changed
@@ -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 @@ } /*! @} */ - -#endif /* _SERCOMM_H */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/socket.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/socket.h
Changed
@@ -30,6 +30,34 @@ } u; }; +int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen); +int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr); + +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_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 */ #define OSMO_SOCK_F_CONNECT (1 << 0) @@ -87,17 +115,6 @@ 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); - -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_sock_unix_init(uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); @@ -123,20 +140,6 @@ int osmo_sock_local_ip(char *local_ip, const char *remote_ip); -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); - -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); - int osmo_sock_set_dscp(int fd, uint8_t dscp); int osmo_sock_set_priority(int fd, int prio);
View file
libosmocore_1.8.0.tar.xz/include/osmocom/core/tun.h
Added
@@ -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) */ +/*! @} */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/core/utils.h -> libosmocore_1.8.0.tar.xz/include/osmocom/core/utils.h
Changed
@@ -33,7 +33,7 @@ /*! 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)) @@ -182,6 +182,18 @@ uint32_t osmo_isqrt32(uint32_t x); +/*! Floored Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists). + * \paramin x dividend. + * \paramin 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). + * \paramin x dividend. + * \paramin 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. */
View file
libosmocore_1.8.0.tar.xz/include/osmocom/crypt/Makefile.am
Added
@@ -0,0 +1,8 @@ +osmocrypt_HEADERS = \ + auth.h \ + gprs_cipher.h \ + kdf.h \ + utran_cipher.h \ + $(NULL) + +osmocryptdir = $(includedir)/osmocom/crypt
View file
libosmocore_1.8.0.tar.xz/include/osmocom/ctrl/Makefile.am
Added
@@ -0,0 +1,13 @@ +osmoctrl_HEADERS = \ + control_cmd.h \ + control_if.h \ + ports.h \ + $(NULL) + +if ENABLE_VTY +osmoctrl_HEADERS += \ + control_vty.h \ + $(NULL) +endif + +osmoctrldir = $(includedir)/osmocom/ctrl
View file
libosmocore_1.7.0.tar.xz/include/osmocom/ctrl/control_if.h -> libosmocore_1.8.0.tar.xz/include/osmocom/ctrl/control_if.h
Changed
@@ -36,19 +36,20 @@ unsigned int node_count); struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port, ctrl_cmd_lookup lookup); +struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup, + unsigned int node_count); struct ctrl_handle *ctrl_interface_setup_dynip(void *data, const char *bind_addr, uint16_t port, - ctrl_cmd_lookup lookup); + ctrl_cmd_lookup lookup) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE; struct ctrl_handle *ctrl_interface_setup_dynip2(void *data, const char *bind_addr, uint16_t port, ctrl_cmd_lookup lookup, - unsigned int node_count); + unsigned int node_count) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE; struct ctrl_connection *osmo_ctrl_conn_alloc(void *ctx, void *data); int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data); struct ctrl_cmd *ctrl_cmd_exec_from_string(struct ctrl_handle *ch, const char *cmdstr); int ctrl_lookup_register(ctrl_cmd_lookup lookup); - int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, struct msgb *msg);
View file
libosmocore_1.7.0.tar.xz/include/osmocom/ctrl/control_vty.h -> libosmocore_1.8.0.tar.xz/include/osmocom/ctrl/control_vty.h
Changed
@@ -2,6 +2,8 @@ #pragma once +#include <stdint.h> + /* Add the 'ctrl' section to VTY, containing the 'bind' command. */ int ctrl_vty_init(void *ctx); @@ -9,3 +11,6 @@ * This should be fed to ctrl_interface_setup() once the configuration has been * read. */ const char *ctrl_vty_get_bind_addr(void); + +/* Returns configured port passed to the 'line ctrl'/'bind' command or default_port. */ +uint16_t ctrl_vty_get_bind_port(uint16_t default_port);
View file
libosmocore_1.8.0.tar.xz/include/osmocom/gprs/Makefile.am
Added
@@ -0,0 +1,17 @@ +SUBDIRS = protocol + +osmogprs_HEADERS = \ + frame_relay.h \ + bssgp_bvc_fsm.h \ + gprs_bssgp.h \ + gprs_bssgp2.h \ + gprs_bssgp_bss.h \ + gprs_bssgp_rim.h \ + gprs_msgb.h \ + gprs_ns.h \ + gprs_ns_frgre.h \ + gprs_ns2.h \ + gprs_rlc.h \ + $(NULL) + +osmogprsdir = $(includedir)/osmocom/gprs
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gprs/gprs_bssgp.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gprs/gprs_bssgp.h
Changed
@@ -235,7 +235,7 @@ int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci, uint16_t nsei, uint32_t max_queue_depth); -void bssgp_flush_all_queues(); +void bssgp_flush_all_queues(void); void bssgp_fc_flush_queue(struct bssgp_flow_control *fc); /* gprs_bssgp_vty.c */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gprs/gprs_bssgp2.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gprs/gprs_bssgp2.h
Changed
@@ -59,6 +59,8 @@ struct msgb *bssgp2_enc_bvc_reset_ack(uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id, const uint8_t *feat_bm, const uint8_t *ext_feat_bm); +struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci, + const uint16_t *new_bvci, const uint16_t *nsei); struct msgb *bssgp2_enc_status(uint8_t cause, const uint16_t *bvci, const struct msgb *orig_msg, uint16_t max_pdu_len);
View file
libosmocore_1.8.0.tar.xz/include/osmocom/gprs/protocol/Makefile.am
Added
@@ -0,0 +1,8 @@ +osmogprsproto_HEADERS = \ + gsm_04_60.h \ + gsm_08_16.h \ + gsm_08_18.h \ + gsm_24_301.h \ + $(NULL) + +osmogprsprotodir = $(includedir)/osmocom/gprs/protocol
View file
libosmocore_1.8.0.tar.xz/include/osmocom/gsm/Makefile.am
Added
@@ -0,0 +1,67 @@ +SUBDIRS = protocol + +BUILT_SOURCES = gsm0503.h + +osmogsm_HEADERS = \ + a5.h \ + abis_nm.h \ + apn.h \ + bts_features.h \ + cbsp.h \ + comp128.h \ + comp128v23.h \ + bitvec_gsm.h \ + gan.h \ + gsm0341.h \ + gsm0411_smc.h \ + gsm0411_smr.h \ + gsm0411_utils.h \ + gsm0480.h \ + gsm0502.h \ + gsm0503.h \ + bsslap.h \ + bssmap_le.h \ + gad.h \ + gsm0808.h \ + gsm0808_lcs.h \ + gsm29205.h \ + gsm0808_utils.h \ + gsm23003.h \ + gsm23236.h \ + gsm29118.h \ + gsm48.h \ + gsm48_arfcn_range_encode.h \ + gsm48_ie.h \ + gsm48_rest_octets.h \ + gsm_utils.h \ + gsup.h \ + gsup_sms.h \ + i460_mux.h \ + ipa.h \ + iuup.h \ + lapd_core.h \ + lapdm.h \ + meas_rep.h \ + mncc.h \ + prim.h \ + l1sap.h \ + oap.h \ + oap_client.h \ + rsl.h \ + rxlev_stat.h \ + sysinfo.h \ + tlv.h \ + $(NULL) + +osmogsmdir = $(includedir)/osmocom/gsm + +noinst_HEADERS = \ + kasumi.h \ + gea.h \ + $(NULL) + +gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py + $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_header gsm \ + --target-path $(builddir)/ + +CLEANFILES = gsm0503.h
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/bts_features.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/bts_features.h
Changed
@@ -33,6 +33,9 @@ BTS_FEAT_BCCH_POWER_RED, BTS_FEAT_DYN_TS_SDCCH8, /* Osmo Dynamic TS supports configured as SDCCH8 */ BTS_FEAT_ACCH_TEMP_OVP, /* FACCH/SACCH Temporary overpower */ + BTS_FEAT_OSMUX, /* Osmux (Osmocom RTP muxing) support */ + BTS_FEAT_VBS, /* Voice Broadcast Service support, 3GPP TS 43.069 */ + BTS_FEAT_VGCS, /* Voice Group Call Service support, 3GPP TS 44.068 */ _NUM_BTS_FEAT };
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm0502.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm0502.h
Changed
@@ -16,10 +16,10 @@ /*! Return the sum of two specified TDMA frame numbers (summation) */ #define GSM_TDMA_FN_SUM(a, b) \ - ((a + b) % GSM_TDMA_HYPERFRAME) + (((a) + (b)) % GSM_TDMA_HYPERFRAME) /*! Return the difference of two specified TDMA frame numbers (subtraction) */ #define GSM_TDMA_FN_SUB(a, b) \ - ((a + GSM_TDMA_HYPERFRAME - b) % GSM_TDMA_HYPERFRAME) + (((a) + GSM_TDMA_HYPERFRAME - (b)) % GSM_TDMA_HYPERFRAME) /*! Return the *minimum* difference of two specified TDMA frame numbers (distance) */ #define GSM_TDMA_FN_DIFF(a, b) \ OSMO_MIN(GSM_TDMA_FN_SUB(a, b), GSM_TDMA_FN_SUB(b, a)) @@ -33,7 +33,7 @@ /* Table 5 Clause 7 TS 05.02 */ static inline unsigned int -gsm0502_get_n_pag_blocks(struct gsm48_control_channel_descr *chan_desc) +gsm0502_get_n_pag_blocks(const struct gsm48_control_channel_descr *chan_desc) { if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C) return 3 - chan_desc->bs_ag_blks_res; @@ -58,7 +58,7 @@ } unsigned int -gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi); +gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi); enum gsm0502_fn_remap_channel { FN_REMAP_TCH_F,
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm0808.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm0808.h
Changed
@@ -73,7 +73,7 @@ struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); struct msgb *gsm0808_create_cipher_reject(enum gsm0808_cause cause); struct msgb *gsm0808_create_cipher_reject_ext(enum gsm0808_cause_class class, uint8_t ext); -struct msgb *gsm0808_create_classmark_request(); +struct msgb *gsm0808_create_classmark_request(void); struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len); struct msgb *gsm0808_create_sapi_reject_cause(uint8_t link_id, uint16_t cause); @@ -269,8 +269,8 @@ }; struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_command *params); -struct msgb *gsm0808_create_handover_detect(); -struct msgb *gsm0808_create_handover_succeeded(); +struct msgb *gsm0808_create_handover_detect(void); +struct msgb *gsm0808_create_handover_succeeded(void); struct gsm0808_handover_complete { bool rr_cause_present;
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm0808_utils.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm0808_utils.h
Changed
@@ -111,12 +111,17 @@ int gsm0808_dec_lcls(struct osmo_lcls *lcls, const struct tlv_parsed *tp); uint8_t gsm0808_enc_speech_codec(struct msgb *msg, - const struct gsm0808_speech_codec *sc); + const struct gsm0808_speech_codec *sc) + OSMO_DEPRECATED("use gsm0808_enc_speech_codec2() instead"); +int gsm0808_enc_speech_codec2(struct msgb *msg, + const struct gsm0808_speech_codec *sc); int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc, const uint8_t *elem, uint8_t len); uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg, - const struct gsm0808_speech_codec_list - *scl); + const struct gsm0808_speech_codec_list *scl) + OSMO_DEPRECATED("use gsm0808_enc_speech_codec_list2() instead"); +int gsm0808_enc_speech_codec_list2(struct msgb *msg, + const struct gsm0808_speech_codec_list *scl); int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl, const uint8_t *elem, uint8_t len); uint8_t gsm0808_enc_channel_type(struct msgb *msg,
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm48.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm48.h
Changed
@@ -35,6 +35,7 @@ const char *gsm48_cc_state_name(uint8_t state); const char *gsm48_cc_msg_name(uint8_t msgtype); const char *gsm48_rr_msg_name(uint8_t msgtype); +const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype); const char *rr_cause_name(uint8_t cause); const char *osmo_rai_name(const struct gprs_ra_id *rai); char *osmo_rai_name_buf(char *buf, size_t buf_len, const struct gprs_ra_id *rai); @@ -106,7 +107,7 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) OSMO_DEPRECATED("Use gsm48_encode_ra() instead"); bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2); -int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc); +int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc); void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc) OSMO_DEPRECATED("Use osmo_plmn_to_bcd() instead, to not lose leading zeros in the MNC");
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm48_ie.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm48_ie.h
Changed
@@ -117,8 +117,9 @@ }; /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ -int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, - uint8_t len, uint8_t mask, uint8_t frqt); +int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, + const uint8_t *cd, uint8_t len, + uint8_t mask, uint8_t frqt); /* decode "CSN.1 encoded Classmark 3" (10.5.1.7) */ int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/gsm_utils.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/gsm_utils.h
Changed
@@ -33,6 +33,7 @@ } while (0) #define GSM_MAX_FN (26*51*2048) +#define GSM_FN_UNSET 0xFFFFFFFF /* Max length of random identifier which can be requested via osmo_get_rand_id() */ #define OSMO_MAX_RAND_ID_LEN 16
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/i460_mux.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/i460_mux.h
Changed
@@ -1,116 +1,2 @@ -/*! \file i460_mux.h - * ITU-T I.460 sub-channel multiplexer + demultiplexer */ -/* - * (C) 2020 by Harald Welte <laforge@gnumonks.org> - * - * 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. - */ - #pragma once -#include <stdint.h> -#include <osmocom/core/bits.h> -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/msgb.h> - -/* I.460 sub-slot rate */ -enum osmo_i460_rate { - OSMO_I460_RATE_NONE, /* disabled */ - OSMO_I460_RATE_64k, - OSMO_I460_RATE_32k, - OSMO_I460_RATE_16k, - OSMO_I460_RATE_8k, -}; - -struct osmo_i460_subchan; - -typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data, - const ubit_t *bits, unsigned int num_bits); -typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data, - const uint8_t *bytes, unsigned int num_bytes); - -struct osmo_i460_subchan_demux { - /*! bit-buffer for output bits */ - uint8_t *out_bitbuf; - /*! size of out_bitbuf in bytes */ - unsigned int out_bitbuf_size; - /*! offset of next bit to be written in out_bitbuf */ - unsigned int out_idx; - /*! callback to be called once we have received out_bitbuf_size bits */ - out_cb_bits_t out_cb_bits; - out_cb_bytes_t out_cb_bytes; - void *user_data; -}; - -typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data); - -struct osmo_i460_subchan_mux { - /*! list of to-be-transmitted message buffers */ - struct llist_head tx_queue; - in_cb_queue_empty_t in_cb_queue_empty; - void *user_data; -}; - -struct osmo_i460_subchan { - struct osmo_i460_timeslot *ts; /* back-pointer */ - enum osmo_i460_rate rate; /* 8/16/32/64k */ - uint8_t bit_offset; /* bit offset inside each byte of the B-channel */ - struct osmo_i460_subchan_demux demux; - struct osmo_i460_subchan_mux mux; -}; - -struct osmo_i460_timeslot { - struct osmo_i460_subchan schan8; -}; - -/*! description of a sub-channel; passed by caller */ -struct osmo_i460_schan_desc { - enum osmo_i460_rate rate; - uint8_t bit_offset; - struct { - /* size (in bits) of the internal buffer; determines granularity */ - size_t num_bits; - /*! call-back function called whenever we received num_bits */ - out_cb_bits_t out_cb_bits; - /*! out_cb_bytes call-back function called whenever we received num_bits. - * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only - * out_cb_bits is provided, output data will always be provided as unpacked bits; if only - * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If - * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two, - * depending on what can be provided without extra conversion. */ - out_cb_bytes_t out_cb_bytes; - /* opaque user data pointer to pass to out_cb */ - void *user_data; - } demux; - - struct { - /* call-back function whenever the muxer requires more input data from the sub-channels, - * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */ - in_cb_queue_empty_t in_cb_queue_empty; - /* opaque user data pointer to pass to in_cb */ - void *user_data; - } mux; -}; - -void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len); - -void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg); -int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len); - -void osmo_i460_ts_init(struct osmo_i460_timeslot *ts); - -struct osmo_i460_subchan * -osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd); - -void osmo_i460_subchan_del(struct osmo_i460_subchan *schan); - -/*! @} */ +#include <osmocom/isdn/i460_mux.h>
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/lapd_core.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/lapd_core.h
Changed
@@ -1,177 +1,2 @@ -/*! \file lapd_core.h - * primitive related stuff - */ #pragma once - -#include <stdint.h> - -#include <osmocom/core/timer.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/prim.h> - -/*! \defgroup lapd LAPD implementation common part - * @{ - * \file lapd_core.h - */ - -#define LOGDL(dl, level, fmt, args...) \ - LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args) - -/*! LAPD related primitives (L2<->L3 SAP)*/ -enum osmo_dl_prim { - PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */ - PRIM_DL_DATA, /*!< DL-DATA */ - PRIM_DL_EST, /*!< DL-ESTABLISH */ - PRIM_DL_REL, /*!< DL-RLEEASE */ - PRIM_DL_SUSP, /*!< DL-SUSPEND */ - PRIM_DL_RES, /*!< DL-RESUME */ - PRIM_DL_RECON, /*!< DL-RECONNECT */ - PRIM_MDL_ERROR, /*!< MDL-ERROR */ -}; - -/* Uses the same values as RLL, so no conversion for GSM is required. */ -#define MDL_CAUSE_T200_EXPIRED 0x01 -#define MDL_CAUSE_REEST_REQ 0x02 -#define MDL_CAUSE_UNSOL_UA_RESP 0x03 -#define MDL_CAUSE_UNSOL_DM_RESP 0x04 -#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05 -#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06 -#define MDL_CAUSE_SEQ_ERR 0x07 -#define MDL_CAUSE_UFRM_INC_PARAM 0x08 -#define MDL_CAUSE_SFRM_INC_PARAM 0x09 -#define MDL_CAUSE_IFRM_INC_MBITS 0x0a -#define MDL_CAUSE_IFRM_INC_LEN 0x0b -#define MDL_CAUSE_FRM_UNIMPL 0x0c -#define MDL_CAUSE_SABM_MF 0x0d -#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e -#define MDL_CAUSE_FRMR 0x0f - -/*! for MDL-ERROR.ind */ -struct mdl_error_ind_param { - uint8_t cause; /*!< generic cause value */ -}; - -/*! for DL-REL.req */ -struct dl_rel_req_param { - uint8_t mode; /*!< release mode */ -}; - -/*! primitive header for LAPD DL-SAP primitives */ -struct osmo_dlsap_prim { - struct osmo_prim_hdr oph; /*!< generic primitive header */ - union { - struct mdl_error_ind_param error_ind; - struct dl_rel_req_param rel_req; - } u; /*!< request-specific data */ -}; - -/*! LAPD mode/role */ -enum lapd_mode { - LAPD_MODE_USER, /*!< behave like user */ - LAPD_MODE_NETWORK, /*!< behave like network */ -}; - -/*! LAPD state (Figure B.2/Q.921)*/ -enum lapd_state { - LAPD_STATE_NULL = 0, - LAPD_STATE_TEI_UNASS, - LAPD_STATE_ASS_TEI_WAIT, - LAPD_STATE_EST_TEI_WAIT, - LAPD_STATE_IDLE, - LAPD_STATE_SABM_SENT, - LAPD_STATE_DISC_SENT, - LAPD_STATE_MF_EST, - LAPD_STATE_TIMER_RECOV, -}; - -/*! LAPD message format (I / S / U) */ -enum lapd_format { - LAPD_FORM_UKN = 0, - LAPD_FORM_I, - LAPD_FORM_S, - LAPD_FORM_U, -}; - -/*! LAPD message context */ -struct lapd_msg_ctx { - struct lapd_datalink *dl; - int n201; - /* address */ - uint8_t cr; - uint8_t sapi; - uint8_t tei; - uint8_t lpd; - /* control */ - uint8_t format; - uint8_t p_f; /* poll / final bit */ - uint8_t n_send; - uint8_t n_recv; - uint8_t s_u; /* S or repectivly U function bits */ - /* length */ - int length; - uint8_t more; -}; - -struct lapd_cr_ent { - uint8_t cmd; - uint8_t resp; -}; - -struct lapd_history { - struct msgb *msg; /* message to be sent / NULL, if histoy is empty */ - int more; /* if message is fragmented */ -}; - -/*! LAPD datalink */ -struct lapd_datalink { - int (*send_dlsap)(struct osmo_dlsap_prim *dp, - struct lapd_msg_ctx *lctx); - int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg); - int (*update_pending_frames)(struct lapd_msg_ctx *lctx); - struct { - /*! filled-in once we set the lapd_mode above */ - struct lapd_cr_ent loc2rem; - struct lapd_cr_ent rem2loc; - } cr; - enum lapd_mode mode; /*!< current mode of link */ - int use_sabme; /*!< use SABME instead of SABM */ - int reestablish; /*!< enable reestablish support */ - int n200, n200_est_rel; /*!< number of retranmissions */ - struct lapd_msg_ctx lctx; /*!< LAPD context */ - int maxf; /*!< maximum frame size (after defragmentation) */ - uint8_t k; /*!< maximum number of unacknowledged frames */ - uint8_t v_range; /*!< range of sequence numbers */ - uint8_t v_send; /*!< seq nr of next I frame to be transmitted */ - uint8_t v_ack; /*!< last frame ACKed by peer */ - uint8_t v_recv; /*!< seq nr of next I frame expected to be received */ - uint32_t state; /*!< LAPD state (\ref lapd_state) */ - int seq_err_cond; /*!< condition of sequence error */ - uint8_t own_busy; /*!< receiver busy on our side */ - uint8_t peer_busy; /*!< receiver busy on remote side */ - int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */ - int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */ - struct osmo_timer_list t200; /*!< T200 timer */ - struct osmo_timer_list t203; /*!< T203 timer */ - uint8_t retrans_ctr; /*!< re-transmission counter */ - struct llist_head tx_queue; /*!< frames to L1 */ - struct llist_head send_queue; /*!< frames from L3 */ - struct msgb *send_buffer; /*!< current frame transmitting */ - int send_out; /*!< how much was sent from send_buffer */ - struct lapd_history *tx_hist; /*!< tx history structure array */ - uint8_t range_hist; /*!< range of history buffer 2..2^n */ - struct msgb *rcv_buffer; /*!< buffer to assemble the received message */ - struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */ - char *name; /*!< user-provided name */ -}; - -void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf) - OSMO_DEPRECATED("Use lapd_dl_init2() instead"); -void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name); -void lapd_dl_set_name(struct lapd_datalink *dl, const char *name); -void lapd_dl_exit(struct lapd_datalink *dl); -void lapd_dl_reset(struct lapd_datalink *dl); -int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode); -int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx); -int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); - -/*! @} */ +#include <osmocom/isdn/lapd_core.h>
View file
libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/Makefile.am
Added
@@ -0,0 +1,28 @@ +osmogsmproto_HEADERS = \ + gsm_23_032.h \ + gsm_03_40.h \ + gsm_03_41.h \ + gsm_04_08.h \ + gsm_04_08_gprs.h \ + gsm_04_11.h \ + gsm_04_12.h \ + gsm_04_14.h \ + gsm_04_80.h \ + gsm_08_08.h \ + gsm_08_58.h \ + gsm_09_02.h \ + gsm_12_21.h \ + gsm_23_003.h \ + gsm_23_041.h \ + gsm_25_415.h \ + gsm_29_118.h \ + gsm_44_004.h \ + gsm_44_318.h \ + gsm_48_049.h \ + gsm_48_071.h \ + gsm_49_031.h \ + ipaccess.h \ + smpp34_osmocom.h \ + $(NULL) + +osmogsmprotodir = $(includedir)/osmocom/gsm/protocol
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_04_08.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_04_08.h
Changed
@@ -1134,6 +1134,20 @@ #endif } __attribute__ ((packed)); +/* Section 9.1.50 System Information type 10 (ASCI) */ +struct gsm48_system_information_type_10 { +#if OSMO_IS_LITTLE_ENDIAN + uint8_t rr_short_pd:1, /* < RR short PD : bit > See 3GPP TS 24.007 §11.3.2 */ + msg_type:5, /* < message type : bit(5) > See 3GPP TS 44.018 Table 10.4.2 */ + l2_header:2; /* < short layer 2 header : bit(2) > See 3GPP TS 44.006 §6.4a */ + uint8_t rest_octets0; /* < SI10 Rest Octets : bit(160) > See 3GPP TS 44.018 §10.5.2.44 */ +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ + uint8_t l2_header:2, msg_type:5, rr_short_pd:1; + uint8_t rest_octets0; +#endif +} __attribute__ ((packed)); + /* Section 9.1.43a System Information type 13 */ struct gsm48_system_information_type_13 { struct gsm48_system_information_type_header header; @@ -1540,6 +1554,21 @@ #define GSM48_MT_RR_APP_INFO 0x38 +/* 3GPP TS 44.018 Table 10.4.2 */ +#define GSM48_MT_RR_SH_SI10 0x0 +#define GSM48_MT_RR_SH_FACCH 0x1 +#define GSM48_MT_RR_SH_UL_FREE 0x2 +#define GSM48_MT_RR_SH_MEAS_REP 0x4 +#define GSM48_MT_RR_SH_MEAS_INFO 0x5 +#define GSM48_MT_RR_SH_VGCS_RECON 0x6 +#define GSM48_MT_RR_SH_VGCS_RECON2 0x7 +#define GSM48_MT_RR_SH_VGCS_INFO 0x8 +#define GSM48_MT_RR_SH_VGCS_SMS 0x9 +#define GSM48_MT_RR_SH_SI10bis 0xA +#define GSM48_MT_RR_SH_SI10ter 0xB +#define GSM48_MT_RR_SH_VGCS_NEIGH 0xC +#define GSM48_MT_RR_SH_APP_DATA 0xD + /* Table 10.2/3GPP TS 04.08 */ #define GSM48_MT_MM_IMSI_DETACH_IND 0x01 #define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_04_08_gprs.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
Changed
@@ -1,7 +1,6 @@ /*! \file gsm_04_08_gprs.h */ -#ifndef _GSM48_GPRS_H -#define _GSM48_GPRS_H +#pragma once #include <stdint.h> #include <stdbool.h> @@ -465,6 +464,3 @@ /* octet 16 */ uint8_t guar_bitrate_down_ext; }; - - -#endif /* _GSM48_GPRS_H */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_08_08.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_08_08.h
Changed
@@ -446,10 +446,11 @@ }; /* GSM 08.08 3.2.2.11 Channel Type */ +#define GSM0808_DATA_FULL_RPREF GSM0808_DATA_FULL_PREF enum gsm0808_chan_rate_type_data { GSM0808_DATA_FULL_BM = 0x8, GSM0808_DATA_HALF_LM = 0x9, - GSM0808_DATA_FULL_RPREF = 0xa, + GSM0808_DATA_FULL_PREF = 0xa, GSM0808_DATA_HALF_PREF = 0xb, GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a, GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b, @@ -540,11 +541,16 @@ GSM0808_PAGINF_FOR_USSD = 0x02, }; -/*! 3GPP TS 48.008 3.2.2.104 Speech Codec */ +/*! 3GPP TS 48.008 3.2.2.104 Speech Codec. + * Valid if (fi || pi || pt) == true, otherwise ignore. */ struct gsm0808_speech_codec { + /*! Full IP: AoIP with compressed speech via RTP/UDP/IP. */ bool fi; + /*! PCMoIP: PCM over A-Interface via RTP/UPD/IP. */ bool pi; + /*! PCMoTDM: PCM over A-Interface with TDM as transport. */ bool pt; + /*! TFO (Inband Tandem Free Operation). Only valid if (pi || pt) == true. */ bool tf; /*! See enum gsm0808_speech_codec_type. */ uint8_t type; @@ -563,7 +569,44 @@ * * Default values for FR_AMR_WB, OFR_AMR_WB and OHR_AMR_WB: * See also: 3GPP TS 26.103, Table 5.7-1: Allowed Configurations - * for the Adaptive Multi-Rate - Wideband Codec Types */ + * for the Adaptive Multi-Rate - Wideband Codec Types + * + * This is a copy of 3GPP TS 28.062, Table 7.11.3.1.3-2: + * + * S0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * 12,20 (x) x x x + * 10,20 x x x + * 7,95 x x x + * 7,40 x x x x + * 6,70 x x x x x x + * 5,90 x x x x x x x x x x + * 5,15 + * 4,75 x x x x x x x x x x + * + * OM F F F F F F F F F F F A F A F A + * + * HR Y Y Y Y Y Y Y Y Y + * FR Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y + * + * Each bit allows one Codec Configuration. + * E.g. when bit S3 is set, look at column labeled "3", and see that only 6,7k is active in this configuration; it is + * "F" forbidden to change in Optimisation Mode, "Y" HR AMR supports this mode, and "Y" FR AMR can also do it. + * + * This means that whichever configuration is chosen from S0 thru S15, there are never more than four rates active. + * + * The spec praises S1 as the most desired configuration: "because it leads in all call cases to TFO/TrFO compatible + * connections with optimal voice quality." (Since HR AMR supports up to 7.95k, it seems that S14 would be more optimal + * voice quality, but it is not marked as supported by HR AMR.) + * + * For FR_AMR below, the default of 0x57ff means: + * 0x57ff = 0101 0111 1111 1111 + * ^14 ^10 ^0 + * allow config 0 thru 10, and configs 12 and 14. + * + * For HR_AMR, drop all those where there is no "Y" in the HR row: + * 0x073f = 0000 0111 0011 1111 + * ^15 ^11 ^6 ^0 + */ enum gsm0808_speech_codec_defaults { GSM0808_SC_CFG_DEFAULT_FR_AMR = 0x57ff, GSM0808_SC_CFG_DEFAULT_HR_AMR = 0x073f, @@ -573,9 +616,12 @@ GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB = 0x01, }; -/*! Default speech codec configurations broken down by reate. +/*! Default speech codec configurations broken down by rate. * See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for - * the Adaptive Multi-Rate Codec Types. */ + * the Adaptive Multi-Rate Codec Types. + * + * Set all Sn bits that have this rate listed as active. + */ enum gsm0808_speech_codec_rate_defaults { GSM0808_SC_CFG_DEFAULT_AMR_4_75 = 0xff03, GSM0808_SC_CFG_DEFAULT_AMR_5_15 = 0x0000, @@ -587,9 +633,12 @@ GSM0808_SC_CFG_DEFAULT_AMR_12_2 = 0xc082 }; -/*! Single speech codec configurations broken down by reate. +/*! Single speech codec configurations broken down by rate. * See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for - * the Adaptive Multi-Rate Codec Types. */ + * the Adaptive Multi-Rate Codec Types. + * + * Set bit Sn (S0 = 0x01), where Sn is identified by a descriptive name. + */ enum gsm0808_speech_codec_rate { GSM0808_SC_CFG_AMR_4_75 = 0x0001, GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 = 0x0002,
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_08_58.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_08_58.h
Changed
@@ -368,6 +368,7 @@ RSL_IE_OSMO_REP_ACCH_CAP = 0x60, RSL_IE_OSMO_TRAINING_SEQUENCE = 0x61, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP = 0x62, + RSL_IE_OSMO_OSMUX_CID = 0x63, /* ip.access */ RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_09_02.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_09_02.h
Changed
@@ -1,8 +1,7 @@ /*! \file gsm_09_02.h * GSM TS 09.02 definitions (MAP). */ -#ifndef PROTO_GSM_09_02_H -#define PROTO_GSM_09_02_H +#pragma once /* Section 17.7.4 */ /* SS-Status */ @@ -134,5 +133,3 @@ #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_C 0xDC #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_D 0xDD #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_E 0xDE - -#endif /* PROTO_GSM_09_02_H */
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/protocol/gsm_23_041.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/protocol/gsm_23_041.h
Changed
@@ -2,6 +2,16 @@ #include <osmocom/core/endian.h> +/* Section 9.3.24: Warning-Type */ +enum gsm23041_warning_type_value { + CBS_ETWS_WARN_TYPE_EARTHQUAKE = 0, + CBS_ETWS_WARN_TYPE_TSUNAMI = 1, + CBS_ETWS_WARN_TYPE_EARTHQUAKE_AND_TSUNAMI = 2, + CBS_ETWS_WARN_TYPE_TEST = 3, + CBS_ETWS_WARN_TYPE_OTHER = 4, + /* 0000101-1111111 Reserved for future use */ +}; + /* Section 9.4.1.2: GSM Message Format */ struct gsm23041_msg_param_gsm { uint16_t serial_nr;
View file
libosmocore_1.7.0.tar.xz/include/osmocom/gsm/tlv.h -> libosmocore_1.8.0.tar.xz/include/osmocom/gsm/tlv.h
Changed
@@ -322,7 +322,7 @@ } /*! put (append) a TV field */ -static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, +static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, uint8_t val) { *buf++ = tag; @@ -344,7 +344,7 @@ * \paramin tag Tag value * \paramin val Value (in host byte order!) */ -static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, +static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, uint16_t val) { *buf++ = tag; @@ -543,7 +543,7 @@ int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp, const uint8_t *tag_order, unsigned int tag_order_len); -#define TLVP_PRESENT(x, y) ((x)->lvy.val) +#define TLVP_PRESENT(x, y) (!!((x)->lvy.val)) #define TLVP_LEN(x, y) (x)->lvy.len #define TLVP_VAL(x, y) (x)->lvy.val
View file
libosmocore_1.8.0.tar.xz/include/osmocom/isdn
Added
+(directory)
View file
libosmocore_1.8.0.tar.xz/include/osmocom/isdn/Makefile.am
Added
@@ -0,0 +1,6 @@ +osmoisdn_HEADERS = \ + i460_mux.h \ + lapd_core.h \ + $(NULL) + +osmoisdndir = $(includedir)/osmocom/isdn
View file
libosmocore_1.8.0.tar.xz/include/osmocom/isdn/i460_mux.h
Added
@@ -0,0 +1,116 @@ +/*! \file i460_mux.h + * ITU-T I.460 sub-channel multiplexer + demultiplexer */ +/* + * (C) 2020 by Harald Welte <laforge@gnumonks.org> + * + * 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. + */ + +#pragma once +#include <stdint.h> +#include <osmocom/core/bits.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/msgb.h> + +/* I.460 sub-slot rate */ +enum osmo_i460_rate { + OSMO_I460_RATE_NONE, /* disabled */ + OSMO_I460_RATE_64k, + OSMO_I460_RATE_32k, + OSMO_I460_RATE_16k, + OSMO_I460_RATE_8k, +}; + +struct osmo_i460_subchan; + +typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data, + const ubit_t *bits, unsigned int num_bits); +typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data, + const uint8_t *bytes, unsigned int num_bytes); + +struct osmo_i460_subchan_demux { + /*! bit-buffer for output bits */ + uint8_t *out_bitbuf; + /*! size of out_bitbuf in bytes */ + unsigned int out_bitbuf_size; + /*! offset of next bit to be written in out_bitbuf */ + unsigned int out_idx; + /*! callback to be called once we have received out_bitbuf_size bits */ + out_cb_bits_t out_cb_bits; + out_cb_bytes_t out_cb_bytes; + void *user_data; +}; + +typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data); + +struct osmo_i460_subchan_mux { + /*! list of to-be-transmitted message buffers */ + struct llist_head tx_queue; + in_cb_queue_empty_t in_cb_queue_empty; + void *user_data; +}; + +struct osmo_i460_subchan { + struct osmo_i460_timeslot *ts; /* back-pointer */ + enum osmo_i460_rate rate; /* 8/16/32/64k */ + uint8_t bit_offset; /* bit offset inside each byte of the B-channel */ + struct osmo_i460_subchan_demux demux; + struct osmo_i460_subchan_mux mux; +}; + +struct osmo_i460_timeslot { + struct osmo_i460_subchan schan8; +}; + +/*! description of a sub-channel; passed by caller */ +struct osmo_i460_schan_desc { + enum osmo_i460_rate rate; + uint8_t bit_offset; + struct { + /* size (in bits) of the internal buffer; determines granularity */ + size_t num_bits; + /*! call-back function called whenever we received num_bits */ + out_cb_bits_t out_cb_bits; + /*! out_cb_bytes call-back function called whenever we received num_bits. + * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only + * out_cb_bits is provided, output data will always be provided as unpacked bits; if only + * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If + * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two, + * depending on what can be provided without extra conversion. */ + out_cb_bytes_t out_cb_bytes; + /* opaque user data pointer to pass to out_cb */ + void *user_data; + } demux; + + struct { + /* call-back function whenever the muxer requires more input data from the sub-channels, + * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */ + in_cb_queue_empty_t in_cb_queue_empty; + /* opaque user data pointer to pass to in_cb */ + void *user_data; + } mux; +}; + +void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len); + +void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg); +int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len); + +void osmo_i460_ts_init(struct osmo_i460_timeslot *ts); + +struct osmo_i460_subchan * +osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd); + +void osmo_i460_subchan_del(struct osmo_i460_subchan *schan); + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/include/osmocom/isdn/lapd_core.h
Added
@@ -0,0 +1,176 @@ +/*! \file lapd_core.h + * primitive related stuff + */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/timer.h> +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/prim.h> + +/*! \defgroup lapd LAPD implementation common part + * @{ + * \file lapd_core.h + */ + +#define LOGDL(dl, level, fmt, args...) \ + LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args) + +/*! LAPD related primitives (L2<->L3 SAP)*/ +enum osmo_dl_prim { + PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */ + PRIM_DL_DATA, /*!< DL-DATA */ + PRIM_DL_EST, /*!< DL-ESTABLISH */ + PRIM_DL_REL, /*!< DL-RLEEASE */ + PRIM_DL_SUSP, /*!< DL-SUSPEND */ + PRIM_DL_RES, /*!< DL-RESUME */ + PRIM_DL_RECON, /*!< DL-RECONNECT */ + PRIM_MDL_ERROR, /*!< MDL-ERROR */ +}; + +/* Uses the same values as RLL, so no conversion for GSM is required. */ +#define MDL_CAUSE_T200_EXPIRED 0x01 +#define MDL_CAUSE_REEST_REQ 0x02 +#define MDL_CAUSE_UNSOL_UA_RESP 0x03 +#define MDL_CAUSE_UNSOL_DM_RESP 0x04 +#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05 +#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06 +#define MDL_CAUSE_SEQ_ERR 0x07 +#define MDL_CAUSE_UFRM_INC_PARAM 0x08 +#define MDL_CAUSE_SFRM_INC_PARAM 0x09 +#define MDL_CAUSE_IFRM_INC_MBITS 0x0a +#define MDL_CAUSE_IFRM_INC_LEN 0x0b +#define MDL_CAUSE_FRM_UNIMPL 0x0c +#define MDL_CAUSE_SABM_MF 0x0d +#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e +#define MDL_CAUSE_FRMR 0x0f + +/*! for MDL-ERROR.ind */ +struct mdl_error_ind_param { + uint8_t cause; /*!< generic cause value */ +}; + +/*! for DL-REL.req */ +struct dl_rel_req_param { + uint8_t mode; /*!< release mode */ +}; + +/*! primitive header for LAPD DL-SAP primitives */ +struct osmo_dlsap_prim { + struct osmo_prim_hdr oph; /*!< generic primitive header */ + union { + struct mdl_error_ind_param error_ind; + struct dl_rel_req_param rel_req; + } u; /*!< request-specific data */ +}; + +/*! LAPD mode/role */ +enum lapd_mode { + LAPD_MODE_USER, /*!< behave like user */ + LAPD_MODE_NETWORK, /*!< behave like network */ +}; + +/*! LAPD state (Figure B.2/Q.921)*/ +enum lapd_state { + LAPD_STATE_NULL = 0, + LAPD_STATE_TEI_UNASS, + LAPD_STATE_ASS_TEI_WAIT, + LAPD_STATE_EST_TEI_WAIT, + LAPD_STATE_IDLE, + LAPD_STATE_SABM_SENT, + LAPD_STATE_DISC_SENT, + LAPD_STATE_MF_EST, + LAPD_STATE_TIMER_RECOV, +}; + +/*! LAPD message format (I / S / U) */ +enum lapd_format { + LAPD_FORM_UKN = 0, + LAPD_FORM_I, + LAPD_FORM_S, + LAPD_FORM_U, +}; + +/*! LAPD message context */ +struct lapd_msg_ctx { + struct lapd_datalink *dl; + int n201; + /* address */ + uint8_t cr; + uint8_t sapi; + uint8_t tei; + uint8_t lpd; + /* control */ + uint8_t format; + uint8_t p_f; /* poll / final bit */ + uint8_t n_send; + uint8_t n_recv; + uint8_t s_u; /* S or repectivly U function bits */ + /* length */ + int length; + uint8_t more; +}; + +struct lapd_cr_ent { + uint8_t cmd; + uint8_t resp; +}; + +struct lapd_history { + struct msgb *msg; /* message to be sent / NULL, if histoy is empty */ + int more; /* if message is fragmented */ +}; + +/*! LAPD datalink */ +struct lapd_datalink { + int (*send_dlsap)(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); + int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg); + int (*update_pending_frames)(struct lapd_msg_ctx *lctx); + struct { + /*! filled-in once we set the lapd_mode above */ + struct lapd_cr_ent loc2rem; + struct lapd_cr_ent rem2loc; + } cr; + enum lapd_mode mode; /*!< current mode of link */ + int use_sabme; /*!< use SABME instead of SABM */ + int reestablish; /*!< enable reestablish support */ + int n200, n200_est_rel; /*!< number of retranmissions */ + struct lapd_msg_ctx lctx; /*!< LAPD context */ + int maxf; /*!< maximum frame size (after defragmentation) */ + uint8_t k; /*!< maximum number of unacknowledged frames */ + uint8_t v_range; /*!< range of sequence numbers */ + uint8_t v_send; /*!< seq nr of next I frame to be transmitted */ + uint8_t v_ack; /*!< last frame ACKed by peer */ + uint8_t v_recv; /*!< seq nr of next I frame expected to be received */ + uint32_t state; /*!< LAPD state (\ref lapd_state) */ + int seq_err_cond; /*!< condition of sequence error */ + uint8_t own_busy; /*!< receiver busy on our side */ + uint8_t peer_busy; /*!< receiver busy on remote side */ + int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */ + int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */ + struct osmo_timer_list t200; /*!< T200 timer */ + struct osmo_timer_list t203; /*!< T203 timer */ + uint8_t retrans_ctr; /*!< re-transmission counter */ + struct llist_head tx_queue; /*!< frames to L1 */ + struct llist_head send_queue; /*!< frames from L3 */ + struct msgb *send_buffer; /*!< current frame transmitting */ + int send_out; /*!< how much was sent from send_buffer */ + struct lapd_history *tx_hist; /*!< tx history structure array */ + uint8_t range_hist; /*!< range of history buffer 2..2^n */ + struct msgb *rcv_buffer; /*!< buffer to assemble the received message */ + struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */ + char *name; /*!< user-provided name */ +}; + +void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf) + OSMO_DEPRECATED("Use lapd_dl_init2() instead"); +void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name); +void lapd_dl_set_name(struct lapd_datalink *dl, const char *name); +void lapd_dl_exit(struct lapd_datalink *dl); +void lapd_dl_reset(struct lapd_datalink *dl); +int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode); +int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx); +int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/include/osmocom/sim/Makefile.am
Added
@@ -0,0 +1,6 @@ +osmosim_HEADERS = \ + class_tables.h \ + sim.h + $(NULL) + +osmosimdir = $(includedir)/osmocom/sim
View file
libosmocore_1.7.0.tar.xz/include/osmocom/sim/sim.h -> libosmocore_1.8.0.tar.xz/include/osmocom/sim/sim.h
Changed
@@ -2,8 +2,7 @@ * Routines for helping with SIM (ISO/IEC 7816-4 more generally) communication. */ -#ifndef _OSMOCOM_SIM_H -#define _OSMOCOM_SIM_H +#pragma once #include <osmocom/core/msgb.h> #include <osmocom/core/linuxlist.h> @@ -445,4 +444,3 @@ struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto); int osim_card_reset(struct osim_card_hdl *card, bool cold_reset); int osim_card_close(struct osim_card_hdl *card); -#endif /* _OSMOCOM_SIM_H */
View file
libosmocore_1.8.0.tar.xz/include/osmocom/usb/Makefile.am
Added
@@ -0,0 +1,7 @@ +if ENABLE_LIBUSB +osmousb_HEADERS = \ + libusb.h \ + $(NULL) +endif + +osmousbdir = $(includedir)/osmocom/usb
View file
libosmocore_1.8.0.tar.xz/include/osmocom/vty/Makefile.am
Added
@@ -0,0 +1,17 @@ +if ENABLE_VTY +osmovty_HEADERS = \ + buffer.h \ + command.h \ + logging.h \ + stats.h \ + misc.h \ + telnet_interface.h \ + vector.h \ + vty.h \ + ports.h \ + cpu_sched_vty.h \ + tdef_vty.h \ + $(NULL) +endif + +osmovtydir = $(includedir)/osmocom/vty
View file
libosmocore_1.7.0.tar.xz/include/osmocom/vty/command.h -> libosmocore_1.8.0.tar.xz/include/osmocom/vty/command.h
Changed
@@ -442,8 +442,8 @@ vector cmd_make_strvec(const char *); int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p); void cmd_free_strvec(vector); -vector cmd_describe_command(); -char **cmd_complete_command(); +vector cmd_describe_command(vector vline, struct vty *vty, int *status); +char **cmd_complete_command(vector vline, struct vty *vty, int *status); const char *cmd_prompt(enum node_type); int config_from_file(struct vty *, FILE *); enum node_type node_parent(enum node_type);
View file
libosmocore_1.7.0.tar.xz/include/osmocom/vty/logging.h -> libosmocore_1.8.0.tar.xz/include/osmocom/vty/logging.h
Changed
@@ -6,7 +6,13 @@ #define FILTER_STR "Filter log messages\n" struct log_info; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +/* note this undefined argument declaration is intentional. There used + * to be an argument until 2017 which we no longer need .*/ void logging_vty_add_cmds(); +#pragma GCC diagnostic pop void logging_vty_add_deprecated_subsys(void *ctx, const char *name); struct vty; struct log_target *osmo_log_vty2tgt(struct vty *vty);
View file
libosmocore_1.7.0.tar.xz/include/osmocom/vty/ports.h -> libosmocore_1.8.0.tar.xz/include/osmocom/vty/ports.h
Changed
@@ -38,6 +38,7 @@ #define OSMO_VTY_PORT_CBC 4264 #define OSMO_VTY_PORT_UECUPS 4268 #define OSMO_VTY_PORT_E1D 4269 +#define OSMO_VTY_PORT_ISDNTAP 4270 #define OSMO_VTY_PORT_SMLC 4271 /* 4272 used by control interface */ #define OSMO_VTY_PORT_HNODEB 4273
View file
libosmocore_1.7.0.tar.xz/include/osmocom/vty/telnet_interface.h -> libosmocore_1.8.0.tar.xz/include/osmocom/vty/telnet_interface.h
Changed
@@ -18,6 +18,7 @@ #pragma once +#include <osmocom/core/defs.h> #include <osmocom/core/logging.h> #include <osmocom/core/select.h> @@ -41,10 +42,13 @@ struct log_target *dbg; }; -int telnet_init(void *tall_ctx, void *priv, int port); -int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port); int telnet_init_default(void *tall_ctx, void *priv, int default_port); +int telnet_init(void *tall_ctx, void *priv, int port) + OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead"); +int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) + OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead"); + void telnet_exit(void); /*! @} */
View file
libosmocore_1.7.0.tar.xz/libosmocore.pc.in -> libosmocore_1.8.0.tar.xz/libosmocore.pc.in
Changed
@@ -6,7 +6,7 @@ Name: Osmocom Core Library Description: C Utility Library Version: @VERSION@ -Requires: talloc +Requires: talloc @LIBMNL_PC@ Requires.private: @LIBSCTP_PC@ Libs: -L${libdir} -losmocore Libs.private: @PTHREAD_LIBS@ @LIBSCTP_LIBS@
View file
libosmocore_1.7.0.tar.xz/libosmogsm.pc.in -> libosmocore_1.8.0.tar.xz/libosmogsm.pc.in
Changed
@@ -6,6 +6,6 @@ Name: Osmocom GSM Core Library Description: GSM Core Library Version: @VERSION@ -Requires: talloc, libosmocore +Requires: talloc, libosmocore, libosmoisdn Libs: -L${libdir} -losmogsm Cflags: -I${includedir}/
View file
libosmocore_1.8.0.tar.xz/libosmoisdn.pc.in
Added
@@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom ISDN Library +Description: ISDN Library +Version: @VERSION@ +Requires: talloc, libosmocore +Libs: -L${libdir} -losmoisdn +Cflags: -I${includedir}/
View file
libosmocore_1.7.0.tar.xz/src/Makefile.am -> libosmocore_1.8.0.tar.xz/src/Makefile.am
Changed
@@ -1,108 +1,13 @@ -# This is _NOT_ the library release version, it's an API version. -# Please read chapter "Library interface versions" of the libtool documentation -# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=19:0:0 - -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS) - -if ENABLE_PSEUDOTALLOC -AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc -endif - -lib_LTLIBRARIES = libosmocore.la - -libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS) $(LIBRARY_RT) $(PTHREAD_LIBS) $(LIBSCTP_LIBS) -libosmocore_la_SOURCES = context.c timer.c timer_gettimeofday.c timer_clockgettime.c \ - select.c signal.c msgb.c bits.c \ - bitvec.c bitcomp.c counter.c fsm.c \ - write_queue.c utils.c socket.c \ - logging.c logging_syslog.c logging_gsmtap.c rate_ctr.c \ - gsmtap_util.c crc16.c panic.c backtrace.c \ - conv.c application.c rbtree.c strrb.c \ - loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \ - macaddr.c stat_item.c stats.c stats_statsd.c prim.c \ - stats_tcp.c \ - conv_acc.c conv_acc_generic.c sercomm.c prbs.c \ - isdnhdlc.c \ - tdef.c \ - thread.c \ - time_cc.c \ - sockaddr_str.c \ - use_count.c \ - exec.c \ - it_q.c \ - probes.d \ - base64.c \ - $(NULL) - -if HAVE_SSSE3 -libosmocore_la_SOURCES += conv_acc_sse.c -if HAVE_SSE4_1 -conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1 -else -conv_acc_sse.lo : AM_CFLAGS += -mssse3 -endif - -if HAVE_AVX2 -libosmocore_la_SOURCES += conv_acc_sse_avx.c -if HAVE_SSE4_1 -conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1 -else -conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -endif -endif -endif - -if HAVE_NEON -libosmocore_la_SOURCES += conv_acc_neon.c -# conv_acc_neon.lo : AM_CFLAGS += -mfpu=neon no, could as well be vfp with neon -endif - -BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c - -EXTRA_DIST = \ - conv_acc_sse_impl.h \ - conv_acc_neon_impl.h \ - crcXXgen.c.tpl \ - stat_item_internal.h \ - $(NULL) - -libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined - -if ENABLE_PLUGIN -libosmocore_la_SOURCES += plugin.c -libosmocore_la_LIBADD += $(LIBRARY_DLOPEN) -endif - -if ENABLE_MSGFILE -libosmocore_la_SOURCES += msgfile.c -endif - -if ENABLE_SERIAL -libosmocore_la_SOURCES += serial.c -endif - -if ENABLE_SYSTEMD_LOGGING -libosmocore_la_SOURCES += logging_systemd.c -libosmocore_la_LIBADD += $(SYSTEMD_LIBS) -endif - -if ENABLE_LIBMNL -libosmocore_la_SOURCES += mnl.c -libosmocore_la_LIBADD += $(LIBMNL_LIBS) -endif - -if ENABLE_SYSTEMTAP -probes.h: probes.d - $(DTRACE) -C -h -s $< -o $@ - -probes.lo: probes.d - $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@ - -BUILT_SOURCES += probes.h probes.lo -libosmocore_la_LIBADD += probes.lo -endif - -crc%gen.c: crcXXgen.c.tpl - $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ +SUBDIRS = \ + core \ + vty \ + isdn \ + codec \ + gsm \ + coding \ + gb \ + ctrl \ + pseudotalloc \ + sim \ + usb \ + $(NULL) \ No newline at end of file
View file
libosmocore_1.7.0.tar.xz/src/codec/Makefile.am -> libosmocore_1.8.0.tar.xz/src/codec/Makefile.am
Changed
@@ -3,8 +3,8 @@ # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html LIBVERSION=3:1:3 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) -AM_CFLAGS = -Wall +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) if ENABLE_PSEUDOTALLOC AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc @@ -15,4 +15,4 @@ libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c ecu.c ecu_fr.c libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -libosmocodec_la_LIBADD = $(top_builddir)/src/libosmocore.la +libosmocodec_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
View file
libosmocore_1.7.0.tar.xz/src/coding/Makefile.am -> libosmocore_1.8.0.tar.xz/src/coding/Makefile.am
Changed
@@ -6,8 +6,9 @@ AM_CPPFLAGS = \ -I"$(top_srcdir)/include" \ -I"$(top_builddir)/include" \ - $(TALLOC_CFLAGS) -AM_CFLAGS = -Wall + -I"$(top_builddir)" \ + $(NULL) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) if ENABLE_PSEUDOTALLOC AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc @@ -28,9 +29,11 @@ $(LIBVERSION) \ -no-undefined \ $(TALLOC_LIBS) + libosmocoding_la_LIBADD = \ - ../libosmocore.la \ - ../gsm/libosmogsm.la \ - ../codec/libosmocodec.la + $(top_builddir)/src/core/libosmocore.la \ + $(top_builddir)/src/gsm/libosmogsm.la \ + $(top_builddir)/src/codec/libosmocodec.la \ + $(NULL) EXTRA_DIST = libosmocoding.map
View file
libosmocore_1.8.0.tar.xz/src/core
Added
+(directory)
View file
libosmocore_1.8.0.tar.xz/src/core/Makefile.am
Added
@@ -0,0 +1,150 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read chapter "Library interface versions" of the libtool documentation +# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html +LIBVERSION=20:0:0 + +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS) + +if ENABLE_PSEUDOTALLOC +AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc +endif + +lib_LTLIBRARIES = libosmocore.la + +libosmocore_la_LIBADD = \ + $(BACKTRACE_LIB) \ + $(TALLOC_LIBS) \ + $(LIBRARY_RT) \ + $(PTHREAD_LIBS) \ + $(LIBSCTP_LIBS) \ + $(NULL) + +libosmocore_la_SOURCES = \ + application.c \ + backtrace.c \ + base64.c \ + bits.c \ + bitvec.c \ + bitcomp.c \ + context.c \ + conv.c \ + conv_acc.c \ + conv_acc_generic.c \ + counter.c \ + crc16.c \ + crc8gen.c \ + crc16gen.c \ + crc32gen.c \ + crc64gen.c \ + exec.c \ + fsm.c \ + gsmtap_util.c \ + isdnhdlc.c \ + it_q.c \ + logging.c \ + logging_syslog.c \ + logging_gsmtap.c \ + loggingrb.c \ + macaddr.c \ + msgb.c \ + netdev.c \ + netns.c \ + panic.c \ + prbs.c \ + prim.c \ + rate_ctr.c \ + rbtree.c \ + select.c \ + sercomm.c \ + signal.c \ + sockaddr_str.c \ + socket.c \ + stat_item.c \ + stats.c \ + stats_statsd.c \ + stats_tcp.c \ + strrb.c \ + tdef.c \ + thread.c \ + time_cc.c \ + timer.c \ + timer_gettimeofday.c \ + timer_clockgettime.c \ + tun.c \ + use_count.c \ + utils.c \ + write_queue.c \ + probes.d \ + $(NULL) + +if HAVE_SSSE3 +libosmocore_la_SOURCES += conv_acc_sse.c +if HAVE_SSE4_1 +conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1 +else +conv_acc_sse.lo : AM_CFLAGS += -mssse3 +endif + +if HAVE_AVX2 +libosmocore_la_SOURCES += conv_acc_sse_avx.c +if HAVE_SSE4_1 +conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1 +else +conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 +endif +endif +endif + +if HAVE_NEON +libosmocore_la_SOURCES += conv_acc_neon.c +# conv_acc_neon.lo : AM_CFLAGS += -mfpu=neon no, could as well be vfp with neon +endif + +BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c + +EXTRA_DIST = \ + conv_acc_sse_impl.h \ + conv_acc_neon_impl.h \ + crcXXgen.c.tpl \ + stat_item_internal.h \ + $(NULL) + +libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined + +if ENABLE_PLUGIN +libosmocore_la_SOURCES += plugin.c +libosmocore_la_LIBADD += $(LIBRARY_DLOPEN) +endif + +if ENABLE_MSGFILE +libosmocore_la_SOURCES += msgfile.c +endif + +if ENABLE_SERIAL +libosmocore_la_SOURCES += serial.c +endif + +if ENABLE_SYSTEMD_LOGGING +libosmocore_la_SOURCES += logging_systemd.c +libosmocore_la_LIBADD += $(SYSTEMD_LIBS) +endif + +if ENABLE_LIBMNL +libosmocore_la_SOURCES += mnl.c +libosmocore_la_LIBADD += $(LIBMNL_LIBS) +endif + +if ENABLE_SYSTEMTAP +probes.h: probes.d + $(DTRACE) -C -h -s $< -o $@ + +probes.lo: probes.d + $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@ + +BUILT_SOURCES += probes.h probes.lo +libosmocore_la_LIBADD += probes.lo +endif + +crc%gen.c: crcXXgen.c.tpl + $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
View file
libosmocore_1.8.0.tar.xz/src/core/application.c
Changed
(renamed from src/application.c)
View file
libosmocore_1.8.0.tar.xz/src/core/backtrace.c
Changed
(renamed from src/backtrace.c)
View file
libosmocore_1.8.0.tar.xz/src/core/base64.c
Changed
(renamed from src/base64.c)
View file
libosmocore_1.8.0.tar.xz/src/core/bitcomp.c
Changed
(renamed from src/bitcomp.c)
View file
libosmocore_1.8.0.tar.xz/src/core/bits.c
Added
@@ -0,0 +1,313 @@ +/* + * (C) 2011 by Harald Welte <laforge@gnumonks.org> + * (C) 2011 by Sylvain Munaut <tnt@246tNt.com> + * + * 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 <osmocom/core/bits.h> + +/*! \addtogroup bits + * @{ + * Osmocom bit level support code. + * + * This module implements the notion of different bit-fields, such as + * - unpacked bits (\ref ubit_t), i.e. 1 bit per byte + * - packed bits (\ref pbit_t), i.e. 8 bits per byte + * - soft bits (\ref sbit_t), 1 bit per byte from -127 to 127 + * + * \file bits.c */ + +/*! convert unpacked bits to packed bits, return length in bytes + * \paramout out output buffer of packed bits + * \paramin in input buffer of unpacked bits + * \paramin num_bits number of bits + */ +int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits) +{ + unsigned int i; + uint8_t curbyte = 0; + pbit_t *outptr = out; + + for (i = 0; i < num_bits; i++) { + uint8_t bitnum = 7 - (i % 8); + + curbyte |= (ini << bitnum); + + if(i % 8 == 7){ + *outptr++ = curbyte; + curbyte = 0; + } + } + /* we have a non-modulo-8 bitcount */ + if (i % 8) + *outptr++ = curbyte; + + return outptr - out; +} + +/*! Shift unaligned input to octet-aligned output + * \paramout out output buffer, unaligned + * \paramin in input buffer, octet-aligned + * \paramin num_nibbles number of nibbles + */ +void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles) +{ + unsigned int i, num_whole_bytes = num_nibbles / 2; + if (!num_whole_bytes) + return; + + /* first byte: upper nibble empty, lower nibble from src */ + out0 = (in0 >> 4); + + /* bytes 1.. */ + for (i = 1; i < num_whole_bytes; i++) + outi = ((ini - 1 & 0xF) << 4) | (ini >> 4); + + /* shift the last nibble, in case there's an odd count */ + i = num_whole_bytes; + if (num_nibbles & 1) + outi = ((ini - 1 & 0xF) << 4) | (ini >> 4); + else + outi = (ini - 1 & 0xF) << 4; +} + +/*! Shift unaligned input to octet-aligned output + * \paramout out output buffer, octet-aligned + * \paramin in input buffer, unaligned + * \paramin num_nibbles number of nibbles + */ +void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles) +{ + unsigned int i, num_whole_bytes = num_nibbles / 2; + if (!num_whole_bytes) + return; + + for (i = 0; i < num_whole_bytes; i++) + outi = ((ini & 0xF) << 4) | (ini + 1 >> 4); + + /* shift the last nibble, in case there's an odd count */ + i = num_whole_bytes; + if (num_nibbles & 1) + outi = (ini & 0xF) << 4; +} + +/*! convert unpacked bits to soft bits + * \paramout out output buffer of soft bits + * \paramin in input buffer of unpacked bits + * \paramin num_bits number of bits + */ +void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits) +{ + unsigned int i; + for (i = 0; i < num_bits; i++) + outi = ini ? -127 : 127; +} + +/*! convert soft bits to unpacked bits + * \paramout out output buffer of unpacked bits + * \paramin in input buffer of soft bits + * \paramin num_bits number of bits + */ +void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits) +{ + unsigned int i; + for (i = 0; i < num_bits; i++) + outi = ini < 0; +} + +/*! convert packed bits to unpacked bits, return length in bytes + * \paramout out output buffer of unpacked bits + * \paramin in input buffer of packed bits + * \paramin num_bits number of bits + * \return number of bytes used in \ref out + */ +int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits) +{ + unsigned int i; + ubit_t *cur = out; + ubit_t *limit = out + num_bits; + + for (i = 0; i < (num_bits/8)+1; i++) { + pbit_t byte = ini; + *cur++ = (byte >> 7) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 6) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 5) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 4) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 3) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 2) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 1) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 0) & 1; + if (cur >= limit) + break; + } + return cur - out; +} + +/*! convert unpacked bits to packed bits (extended options) + * \paramout out output buffer of packed bits + * \paramin out_ofs offset into output buffer + * \paramin in input buffer of unpacked bits + * \paramin in_ofs offset into input buffer + * \paramin num_bits number of bits + * \paramin lsb_mode Encode bits in LSB order instead of MSB + * \returns length in bytes (max written offset of output buffer + 1) + */ +int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, + const ubit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + unsigned int i, op, bn; + for (i=0; i<num_bits; i++) { + op = out_ofs + i; + bn = lsb_mode ? (op&7) : (7-(op&7)); + if (inin_ofs+i) + outop>>3 |= 1 << bn; + else + outop>>3 &= ~(1 << bn); + } + return ((out_ofs + num_bits - 1) >> 3) + 1; +} + +/*! convert packed bits to unpacked bits (extended options) + * \paramout out output buffer of unpacked bits + * \paramin out_ofs offset into output buffer + * \paramin in input buffer of packed bits + * \paramin in_ofs offset into input buffer + * \paramin num_bits number of bits + * \paramin lsb_mode Encode bits in LSB order instead of MSB + * \returns length in bytes (max written offset of output buffer + 1) + */ +int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, + const pbit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + unsigned int i, ip, bn; + for (i=0; i<num_bits; i++) { + ip = in_ofs + i; + bn = lsb_mode ? (ip&7) : (7-(ip&7)); + outout_ofs+i = !!(inip>>3 & (1<<bn)); + } + return out_ofs + num_bits; +} + +/* look-up table for bit-reversal within a byte. Generated using: + int i,k; + for (i = 0 ; i < 256 ; i++) { + uint8_t sample = 0 ; + for (k = 0; k<8; k++) { + if ( i & 1 << k ) sample |= 0x80 >> k; + } + flip_tablei = sample; + } + */ +static const uint8_t flip_table256 = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +/*! generalized bit reversal function + * \paramin x the 32bit value to be reversed + * \paramin k the type of reversal requested + * \returns the reversed 32bit dword + * + * This function reverses the bit order within a 32bit word. Depending + * on "k", it either reverses all bits in a 32bit dword, or the bytes in + * the dword, or the bits in each byte of a dword, or simply swaps the + * two 16bit words in a dword. See Chapter 7 "Hackers Delight" + */ +uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k) +{ + if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; + if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; + if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; + + return x; +} + +/*! reverse the bit-order in each byte of a dword + * \paramin x 32bit input value + * \returns 32bit value where bits of each byte have been reversed + * + * See Chapter 7 "Hackers Delight" + */ +uint32_t osmo_revbytebits_32(uint32_t x) +{ + x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; + x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + + return x; +} + +/*! reverse the bit order in a byte + * \paramin x 8bit input value + * \returns 8bit value where bits order has been reversed + */ +uint32_t osmo_revbytebits_8(uint8_t x) +{ + return flip_tablex; +} + +/*! reverse bit-order of each byte in a buffer + * \paramin buf buffer containing bytes to be bit-reversed + * \paramin len length of buffer in bytes + * + * This function reverses the bits in each byte of the buffer + */ +void osmo_revbytebits_buf(uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + bufi = flip_tablebufi; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/bitvec.c
Changed
(renamed from src/bitvec.c)
View file
libosmocore_1.8.0.tar.xz/src/core/context.c
Added
@@ -0,0 +1,47 @@ +/*! \file context.c + * talloc context handling. + * + * (C) 2019 by Harald Welte <laforge@gnumonks.org> + * 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 <string.h> +#include <errno.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +__thread struct osmo_talloc_contexts *osmo_ctx; + +int osmo_ctx_init(const char *id) +{ + osmo_ctx = talloc_named(NULL, sizeof(*osmo_ctx), "global-%s", id); + if (!osmo_ctx) + return -ENOMEM; + memset(osmo_ctx, 0, sizeof(*osmo_ctx)); + osmo_ctx->global = osmo_ctx; + osmo_ctx->select = talloc_named_const(osmo_ctx->global, 0, "select"); + if (!osmo_ctx->select) { + talloc_free(osmo_ctx); + return -ENOMEM; + } + return 0; +} + +/* initialize osmo_ctx on main tread */ +static __attribute__((constructor)) void on_dso_load_ctx(void) +{ + OSMO_ASSERT(osmo_ctx_init("main") == 0); +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/conv.c
Changed
(renamed from src/conv.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc.c
Changed
(renamed from src/conv_acc.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_generic.c
Changed
(renamed from src/conv_acc_generic.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_neon.c
Changed
(renamed from src/conv_acc_neon.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_neon_impl.h
Changed
(renamed from src/conv_acc_neon_impl.h)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_sse.c
Changed
(renamed from src/conv_acc_sse.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_sse_avx.c
Changed
(renamed from src/conv_acc_sse_avx.c)
View file
libosmocore_1.8.0.tar.xz/src/core/conv_acc_sse_impl.h
Changed
(renamed from src/conv_acc_sse_impl.h)
View file
libosmocore_1.8.0.tar.xz/src/core/counter.c
Added
@@ -0,0 +1,108 @@ +/*! \file counter.c + * utility routines for keeping some statistics. */ +/* + * (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * 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 <string.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/counter.h> + +static LLIST_HEAD(counters); + +/*! Global talloc context for all osmo_counter allocations. */ +void *tall_ctr_ctx; + +/*! Allocate a new counter with given name. Allocates from tall_ctr_ctx + * \paramin name Human-readable string name for the counter + * \returns Allocated counter on success; NULL on error */ +struct osmo_counter *osmo_counter_alloc(const char *name) +{ + struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter); + + if (!ctr) + return NULL; + + ctr->name = name; + llist_add_tail(&ctr->list, &counters); + + return ctr; +} + +/*! Release/Destroy a given counter + * \paramin ctr Counter to be destroyed */ +void osmo_counter_free(struct osmo_counter *ctr) +{ + llist_del(&ctr->list); + talloc_free(ctr); +} + +/*! Iterate over all counters; call \a handle_cunter call-back for each. + * \paramin handle_counter Call-back to be called for each counter; aborts if rc < 0 + * \paramin data Opaque data passed through to \a handle_counter function + * \returns 0 if all \a handle_counter calls successfull; negative on error */ +int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), + void *data) +{ + struct osmo_counter *ctr; + int rc = 0; + + llist_for_each_entry(ctr, &counters, list) { + rc = handle_counter(ctr, data); + if (rc < 0) + return rc; + } + + return rc; +} + +/*! Counts the registered counter + * \returns amount of counters */ +int osmo_counters_count(void) +{ + return llist_count(&counters); +} + +/*! Find a counter by its name. + * \paramin name Name used to look-up/search counter + * \returns Counter on success; NULL if not found */ +struct osmo_counter *osmo_counter_get_by_name(const char *name) +{ + struct osmo_counter *ctr; + + llist_for_each_entry(ctr, &counters, list) { + if (!strcmp(ctr->name, name)) + return ctr; + } + return NULL; +} + +/*! Compute difference between current and previous counter value. + * \paramin ctr Counter of which the difference is to be computed + * \returns Delta value between current counter and previous counter. Please + * note that the actual counter values are unsigned long, while the + * difference is computed as signed integer! */ +int osmo_counter_difference(struct osmo_counter *ctr) +{ + int delta = ctr->value - ctr->previous; + ctr->previous = ctr->value; + + return delta; +}
View file
libosmocore_1.8.0.tar.xz/src/core/crc16.c
Changed
(renamed from src/crc16.c)
View file
libosmocore_1.8.0.tar.xz/src/core/crcXXgen.c.tpl
Changed
(renamed from src/crcXXgen.c.tpl)
View file
libosmocore_1.8.0.tar.xz/src/core/exec.c
Changed
(renamed from src/exec.c)
View file
libosmocore_1.8.0.tar.xz/src/core/fsm.c
Added
@@ -0,0 +1,1046 @@ +/*! \file fsm.c + * Osmocom generic Finite State Machine implementation. */ +/* + * (C) 2016-2019 by Harald Welte <laforge@gnumonks.org> + * + * 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 <errno.h> +#include <stdbool.h> +#include <string.h> +#include <inttypes.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> + +/*! \addtogroup fsm + * @{ + * Finite State Machine abstraction + * + * This is a generic C-language abstraction for implementing finite + * state machines within the Osmocom framework. It is intended to + * replace existing hand-coded or even only implicitly existing FSMs + * all over the existing code base. + * + * An libosmocore FSM is described by its \ref osmo_fsm description, + * which in turn refers to an array of \ref osmo_fsm_state descriptor, + * each describing a single state in the FSM. + * + * The general idea is that all actions performed within one state are + * located at one position in the code (the state's action function), + * as opposed to the 'message-centric' view of e.g. the existing + * state machines of the LAPD(m) core, where there is one message for + * each possible event (primitive), and the function then needs to + * concern itself on how to handle that event over all possible states. + * + * For each state, there is a bit-mask of permitted input events for + * this state, as well as a bit-mask of permitted new output states to + * which the state can change. Furthermore, there is a function + * pointer implementing the actual handling of the input events + * occurring whilst in that state. + * + * Furthermore, each state offers a function pointer that can be + * executed just before leaving a state, and another one just after + * entering a state. + * + * When transitioning into a new state, an optional timer number and + * time-out can be passed along. The timer is started just after + * entering the new state, and will call the \ref osmo_fsm timer_cb + * function once it expires. This is intended to be used in telecom + * state machines where a given timer (identified by a certain number) + * is started to terminate the fsm or terminate the fsm once expected + * events are not happening before timeout expiration. + * + * As there can often be many concurrent FSMs of one given class, we + * introduce the concept of \ref osmo_fsm_inst, i.e. an FSM instance. + * The instance keeps the actual state, while the \ref osmo_fsm + * descriptor contains the static/const descriptor of the FSM's states + * and possible transitions. + * + * osmo_fsm are integrated with the libosmocore logging system. The + * logging sub-system is determined by the FSM descriptor, as we assume + * one FSM (let's say one related to a location update procedure) is + * inevitably always tied to a sub-system. The logging level however + * is configurable for each FSM instance, to ensure that e.g. DEBUG + * logging can be used for the LU procedure of one subscriber, while + * NOTICE level is used for all other subscribers. + * + * In order to attach private state to the \ref osmo_fsm_inst, it + * offers an opaque private pointer. + * + * \file fsm.c */ + +LLIST_HEAD(osmo_g_fsms); +static bool fsm_log_addr = true; +static bool fsm_log_timeouts = false; +/*! See osmo_fsm_term_safely(). */ +static bool fsm_term_safely_enabled = false; + +/*! Internal state for FSM instance termination cascades. */ +static __thread struct { + /*! The first FSM instance that invoked osmo_fsm_inst_term() in the current cascade. */ + struct osmo_fsm_inst *root_fi; + /*! 2 if a secondary FSM terminates, 3 if a secondary FSM causes a tertiary FSM to terminate, and so on. */ + unsigned int depth; + /*! Talloc context to collect all deferred deallocations (FSM instances, and talloc objects if any). */ + void *collect_ctx; + /*! See osmo_fsm_set_dealloc_ctx() */ + void *fsm_dealloc_ctx; +} fsm_term_safely; + +/*! Internal call to free an FSM instance, which redirects to the context set by osmo_fsm_set_dealloc_ctx() if any. + */ +static void fsm_free_or_steal(void *talloc_object) +{ + if (fsm_term_safely.fsm_dealloc_ctx) + talloc_steal(fsm_term_safely.fsm_dealloc_ctx, talloc_object); + else + talloc_free(talloc_object); +} + +/*! specify if FSM instance addresses should be logged or not + * + * By default, the FSM name includes the pointer address of the \ref + * osmo_fsm_inst. This behavior can be disabled (and re-enabled) + * using this function. + * + * \paramin log_addr Indicate if FSM instance address shall be logged + */ +void osmo_fsm_log_addr(bool log_addr) +{ + fsm_log_addr = log_addr; +} + +/*! Enable or disable logging of timeout values for FSM instance state changes. + * + * By default, state changes are logged by state name only, omitting the timeout. When passing true, each state change + * will also log the T number (or Osmocom-specific X number) and the chosen timeout in seconds. + * osmo_fsm_inst_state_chg_keep_timer() will log remaining timeout in millisecond precision. + * + * The default for this is false to reflect legacy behavior. Since various C tests that verify logging output already + * existed prior to this option, keeping timeout logging off makes sure that they continue to pass. Particularly, + * osmo_fsm_inst_state_chg_keep_timer() may cause non-deterministic logging of remaining timeout values. + * + * For any program that does not explicitly require deterministic logging output, i.e. anything besides regression tests + * involving FSM instances, it is recommended to call osmo_fsm_log_timeouts(true). + * + * \paramin log_timeouts Pass true to log timeouts on state transitions, false to omit timeouts. + */ +void osmo_fsm_log_timeouts(bool log_timeouts) +{ + fsm_log_timeouts = log_timeouts; +} + +/*! Enable safer way to deallocate cascades of terminating FSM instances. + * + * Note, using osmo_fsm_set_dealloc_ctx() is a more general solution to this same problem. + * Particularly, in a program using osmo_select_main_ctx(), the simplest solution to avoid most use-after-free problems + * from FSM instance deallocation is using osmo_fsm_set_dealloc_ctx(OTC_SELECT). + * + * When enabled, an FSM instance termination detects whether another FSM instance is already terminating, and instead of + * deallocating immediately, collects all terminating FSM instances in a talloc context, to be bulk deallocated once all + * event handling and termination cascades are done. + * + * For example, if an FSM's cleanup() sends an event to some "other" FSM, which in turn causes the FSM's parent to + * deallocate, then the parent would talloc_free() the child's memory, causing a use-after-free. There are infinite + * constellations like this, which all are trivially solved with this feature enabled. + * + * For illustration, see fsm_dealloc_test.c. + * + * When enabled, this feature changes the order of logging, which may break legacy unit test expectations, and changes + * the order of deallocation to after the parent term event is dispatched. + * + * \paramin term_safely Pass true to switch to safer FSM instance termination behavior. + */ +void osmo_fsm_term_safely(bool term_safely) +{ + fsm_term_safely_enabled = term_safely; +} + +/*! Instead of deallocating FSM instances, move them to the given talloc context. + * + * It is the caller's responsibility to clear this context to actually free the memory of terminated FSM instances. + * Make sure to not talloc_free(ctx) itself before setting a different osmo_fsm_set_dealloc_ctx(). To clear a ctx + * without the need to call osmo_fsm_set_dealloc_ctx() again, rather use talloc_free_children(ctx). + * + * For example, to defer deallocation to the next osmo_select_main_ctx() iteration, set this to OTC_SELECT. + * + * Deferring deallocation is the simplest solution to avoid most use-after-free problems from FSM instance deallocation. + * This is a simpler and more general solution than osmo_fsm_term_safely(). + * + * To disable the feature again, pass NULL as ctx. + * + * Both osmo_fsm_term_safely() and osmo_fsm_set_dealloc_ctx() can be enabled at the same time, which will result in + * first collecting deallocated FSM instances in fsm_term_safely.collect_ctx, and finally reparenting that to the ctx + * passed here. However, in practice, it does not really make sense to enable both at the same time. + * + * \param ctxin Instead of talloc_free()int, talloc_steal() all future deallocated osmo_fsm_inst instances to this + * ctx. If NULL, go back to talloc_free() as usual. + */ +void osmo_fsm_set_dealloc_ctx(void *ctx) +{ + fsm_term_safely.fsm_dealloc_ctx = ctx; +} + +/*! talloc_free() the given object immediately, or once ongoing FSM terminations are done. + * + * If an FSM deallocation cascade is ongoing, talloc_steal() the given talloc_object into the talloc context that is + * freed once the cascade is done. If no FSM deallocation cascade is ongoing, or if osmo_fsm_term_safely() is disabled, + * immediately talloc_free the object. + * + * This can be useful if some higher order talloc object, which is the talloc parent for FSM instances or their priv + * objects, is not itself tied to an FSM instance. This function allows safely freeing it without affecting ongoing FSM + * termination cascades. + * + * Once passed to this function, the talloc_object should be considered as already freed. Only FSM instance pre_term() + * and cleanup() functions as well as event handling caused by these may safely assume that it is still valid memory. + * + * The talloc_object should not have multiple parents. + * + * (This function may some day move to public API, which might be redundant if we introduce a select-loop volatile + * context mechanism to defer deallocation instead.) + * + * \paramin talloc_object Object pointer to free. + */ +static void osmo_fsm_defer_free(void *talloc_object) +{ + if (!fsm_term_safely.depth) { + fsm_free_or_steal(talloc_object); + return; + } + + if (!fsm_term_safely.collect_ctx) { + /* This is actually the first other object / FSM instance besides the root terminating inst. Create the + * ctx to collect this and possibly more objects to free. Avoid talloc parent loops: don't make this ctx + * the child of the root inst or anything like that. */ + fsm_term_safely.collect_ctx = talloc_named_const(NULL, 0, "fsm_term_safely.collect_ctx"); + OSMO_ASSERT(fsm_term_safely.collect_ctx); + } + talloc_steal(fsm_term_safely.collect_ctx, talloc_object); +} + +struct osmo_fsm *osmo_fsm_find_by_name(const char *name) +{ + struct osmo_fsm *fsm; + llist_for_each_entry(fsm, &osmo_g_fsms, list) { + if (!strcmp(name, fsm->name)) + return fsm; + } + return NULL; +} + +struct osmo_fsm_inst *osmo_fsm_inst_find_by_name(const struct osmo_fsm *fsm, + const char *name) +{ + struct osmo_fsm_inst *fi; + + if (!name) + return NULL; + + llist_for_each_entry(fi, &fsm->instances, list) { + if (!fi->name) + continue; + if (!strcmp(name, fi->name)) + return fi; + } + return NULL; +} + +struct osmo_fsm_inst *osmo_fsm_inst_find_by_id(const struct osmo_fsm *fsm, + const char *id) +{ + struct osmo_fsm_inst *fi; + + llist_for_each_entry(fi, &fsm->instances, list) { + if (!strcmp(id, fi->id)) + return fi; + } + return NULL; +} + +/*! register a FSM with the core + * + * A FSM descriptor needs to be registered with the core before any + * instances can be created for it. + * + * \paramin fsm Descriptor of Finite State Machine to be registered + * \returns 0 on success; negative on error + */ +int osmo_fsm_register(struct osmo_fsm *fsm) +{ + if (!osmo_identifier_valid(fsm->name)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Attempting to register FSM with illegal identifier '%s'\n", fsm->name); + return -EINVAL; + } + if (osmo_fsm_find_by_name(fsm->name)) + return -EEXIST; + if (fsm->event_names == NULL) + LOGP(DLGLOBAL, LOGL_ERROR, "FSM '%s' has no event names! Please fix!\n", fsm->name); + llist_add_tail(&fsm->list, &osmo_g_fsms); + INIT_LLIST_HEAD(&fsm->instances); + + return 0; +} + +/*! unregister a FSM from the core + * + * Once the FSM descriptor is unregistered, active instances can still + * use it, but no new instances may be created for it. + * + * \paramin fsm Descriptor of Finite State Machine to be removed + */ +void osmo_fsm_unregister(struct osmo_fsm *fsm) +{ + llist_del(&fsm->list); +} + +/* small wrapper function around timer expiration (for logging) */ +static void fsm_tmr_cb(void *data) +{ + struct osmo_fsm_inst *fi = data; + struct osmo_fsm *fsm = fi->fsm; + int32_t T = fi->T; + + LOGPFSM(fi, "Timeout of " OSMO_T_FMT "\n", OSMO_T_FMT_ARGS(fi->T)); + + if (fsm->timer_cb) { + int rc = fsm->timer_cb(fi); + if (rc != 1) + /* We don't actually know whether fi exists anymore. + * Make sure to not access it and return right away. */ + return; + /* The timer_cb told us to terminate, so we can safely assume + * that fi still exists. */ + LOGPFSM(fi, "timer_cb requested termination\n"); + } else + LOGPFSM(fi, "No timer_cb, automatic termination\n"); + + /* if timer_cb returns 1 or there is no timer_cb */ + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_TIMEOUT, &T); +} + +/*! Change id of the FSM instance + * \paramin fi FSM instance + * \paramin id new ID + * \returns 0 if the ID was updated, otherwise -EINVAL + */ +int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id) +{ + if (!id) + return osmo_fsm_inst_update_id_f(fi, NULL); + else + return osmo_fsm_inst_update_id_f(fi, "%s", id); +} + +static void update_name(struct osmo_fsm_inst *fi) +{ + if (fi->name) + talloc_free((char*)fi->name); + + if (!fsm_log_addr) { + if (fi->id) + fi->name = talloc_asprintf(fi, "%s(%s)", fi->fsm->name, fi->id); + else + fi->name = talloc_asprintf(fi, "%s", fi->fsm->name); + } else { + if (fi->id) + fi->name = talloc_asprintf(fi, "%s(%s)%p", fi->fsm->name, fi->id, fi); + else + fi->name = talloc_asprintf(fi, "%s%p", fi->fsm->name, fi); + } +} + +/*! Change id of the FSM instance using a string format. + * \paramin fi FSM instance. + * \paramin fmt format string to compose new ID. + * \paramin ... variable argument list for format string. + * \returns 0 if the ID was updated, otherwise -EINVAL. + */ +int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...) +{ + char *id = NULL; + + if (fmt) { + va_list ap; + + va_start(ap, fmt); + id = talloc_vasprintf(fi, fmt, ap); + va_end(ap); + + if (!osmo_identifier_valid(id)) { + LOGP(DLGLOBAL, LOGL_ERROR, + "Attempting to set illegal id for FSM instance of type '%s': %s\n", + fi->fsm->name, osmo_quote_str(id, -1)); + talloc_free(id); + return -EINVAL; + } + } + + if (fi->id) + talloc_free((char*)fi->id); + fi->id = id; + + update_name(fi); + return 0; +} + +/*! Change id of the FSM instance using a string format, and ensuring a valid id. + * Replace any characters that are not permitted as FSM identifier with replace_with. + * \paramin fi FSM instance. + * \paramin replace_with Character to use instead of non-permitted FSM id characters. + * Make sure to choose a legal character, e.g. '-'. + * \paramin fmt format string to compose new ID. + * \paramin ... variable argument list for format string. + * \returns 0 if the ID was updated, otherwise -EINVAL. + */ +int osmo_fsm_inst_update_id_f_sanitize(struct osmo_fsm_inst *fi, char replace_with, const char *fmt, ...) +{ + char *id = NULL; + va_list ap; + int rc; + + if (!fmt) + return osmo_fsm_inst_update_id(fi, NULL); + + va_start(ap, fmt); + id = talloc_vasprintf(fi, fmt, ap); + va_end(ap); + + osmo_identifier_sanitize_buf(id, NULL, replace_with); + + rc = osmo_fsm_inst_update_id(fi, id); + talloc_free(id); + return rc; +} + +/*! allocate a new instance of a specified FSM + * \paramin fsm Descriptor of the FSM + * \paramin ctx talloc context from which to allocate memory + * \paramin priv private data reference store in fsm instance + * \paramin log_level The log level for events of this FSM + * \paramin id The name/ID of the FSM instance + * \returns newly-allocated, initialized and registered FSM instance + */ +struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv, + int log_level, const char *id) +{ + struct osmo_fsm_inst *fi = talloc_zero(ctx, struct osmo_fsm_inst); + + fi->fsm = fsm; + fi->priv = priv; + fi->log_level = log_level; + osmo_timer_setup(&fi->timer, fsm_tmr_cb, fi); + + if (osmo_fsm_inst_update_id(fi, id) < 0) { + fsm_free_or_steal(fi); + return NULL; + } + + INIT_LLIST_HEAD(&fi->proc.children); + INIT_LLIST_HEAD(&fi->proc.child); + llist_add(&fi->list, &fsm->instances); + + LOGPFSM(fi, "Allocated\n"); + + return fi; +} + +/*! allocate a new instance of a specified FSM as child of + * other FSM instance + * + * This is like \ref osmo_fsm_inst_alloc but using the parent FSM as + * talloc context, and inheriting the log level of the parent. + * + * \paramin fsm Descriptor of the to-be-allocated FSM + * \paramin parent Parent FSM instance + * \paramin parent_term_event Event to be sent to parent when terminating + * \returns newly-allocated, initialized and registered FSM instance + */ +struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, + struct osmo_fsm_inst *parent, + uint32_t parent_term_event) +{ + struct osmo_fsm_inst *fi; + + fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, + parent->id); + if (!fi) { + /* indicate immediate termination to caller */ + osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); + return NULL; + } + + LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); + + osmo_fsm_inst_change_parent(fi, parent, parent_term_event); + + return fi; +} + +/*! unlink child FSM from its parent FSM. + * \paramin fi Descriptor of the child FSM to unlink. + * \paramin ctx New talloc context + * + * Never call this function from the cleanup callback, because at that time + * the child FSMs will already be terminated. If unlinking should be performed + * on FSM termination, use the grace callback instead. */ +void osmo_fsm_inst_unlink_parent(struct osmo_fsm_inst *fi, void *ctx) +{ + if (fi->proc.parent) { + talloc_steal(ctx, fi); + fi->proc.parent = NULL; + fi->proc.parent_term_event = 0; + llist_del(&fi->proc.child); + } +} + +/*! change parent instance of an FSM. + * \paramin fi Descriptor of the to-be-allocated FSM. + * \paramin new_parent New parent FSM instance. + * \paramin new_parent_term_event Event to be sent to parent when terminating. + * + * Never call this function from the cleanup callback! + * (see also osmo_fsm_inst_unlink_parent()).*/ +void osmo_fsm_inst_change_parent(struct osmo_fsm_inst *fi, + struct osmo_fsm_inst *new_parent, + uint32_t new_parent_term_event) +{ + /* Make sure a possibly existing old parent is unlinked first + * (new_parent can be NULL) */ + osmo_fsm_inst_unlink_parent(fi, new_parent); + + /* Add new parent */ + if (new_parent) { + fi->proc.parent = new_parent; + fi->proc.parent_term_event = new_parent_term_event; + llist_add(&fi->proc.child, &new_parent->proc.children); + } +} + +/*! delete a given instance of a FSM + * \paramin fi FSM instance to be un-registered and deleted + */ +void osmo_fsm_inst_free(struct osmo_fsm_inst *fi) +{ + osmo_timer_del(&fi->timer); + llist_del(&fi->list); + + if (fsm_term_safely.depth) { + /* Another FSM instance has caused this one to free and is still busy with its termination. Don't free + * yet, until the other FSM instance is done. */ + osmo_fsm_defer_free(fi); + /* The root_fi can't go missing really, but to be safe... */ + if (fsm_term_safely.root_fi) + LOGPFSM(fi, "Deferring: will deallocate with %s\n", fsm_term_safely.root_fi->name); + else + LOGPFSM(fi, "Deferring deallocation\n"); + + /* Don't free anything yet. Exit. */ + return; + } + + /* fsm_term_safely.depth == 0. + * - If fsm_term_safely is enabled, this is the original FSM instance that started terminating first. Free this + * and along with it all other collected terminated FSM instances. + * - If fsm_term_safely is disabled, this is just any FSM instance deallocating. */ + + if (fsm_term_safely.collect_ctx) { + /* The fi may be a child of any other FSM instances or objects collected in the collect_ctx. Don't + * deallocate separately to avoid use-after-free errors, put it in there and deallocate all at once. */ + LOGPFSM(fi, "Deallocated, including all deferred deallocations\n"); + osmo_fsm_defer_free(fi); + fsm_free_or_steal(fsm_term_safely.collect_ctx); + fsm_term_safely.collect_ctx = NULL; + } else { + LOGPFSM(fi, "Deallocated\n"); + fsm_free_or_steal(fi); + } + fsm_term_safely.root_fi = NULL; +} + +/*! get human-readable name of FSM event + * \paramin fsm FSM descriptor of event + * \paramin event Event integer value + * \returns string rendering of the event + */ +const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event) +{ + static __thread char buf32; + if (!fsm->event_names) { + snprintf(buf, sizeof(buf), "%"PRIu32, event); + return buf; + } else + return get_value_string(fsm->event_names, event); +} + +/*! get human-readable name of FSM instance + * \paramin fi FSM instance + * \returns string rendering of the FSM identity + */ +const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi) +{ + if (!fi) + return "NULL"; + + if (fi->name) + return fi->name; + else + return fi->fsm->name; +} + +/*! get human-readable name of FSM state + * \paramin fsm FSM descriptor + * \paramin state FSM state number + * \returns string rendering of the FSM state + */ +const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state) +{ + static __thread char buf32; + if (state >= fsm->num_states) { + snprintf(buf, sizeof(buf), "unknown %"PRIu32, state); + return buf; + } else + return fsm->statesstate.name; +} + +static int state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, + bool keep_timer, unsigned long timeout_ms, int T, + const char *file, int line) +{ + struct osmo_fsm *fsm = fi->fsm; + uint32_t old_state = fi->state; + const struct osmo_fsm_state *st = &fsm->statesfi->state; + struct timeval remaining; + + if (fi->proc.terminating) { + LOGPFSMSRC(fi, file, line, + "FSM instance already terminating, not changing state to %s\n", + osmo_fsm_state_name(fsm, new_state)); + return -EINVAL; + } + + /* validate if new_state is a valid state */ + if (!(st->out_state_mask & (1 << new_state))) { + LOGPFSMLSRC(fi, LOGL_ERROR, file, line, + "transition to state %s not permitted!\n", + osmo_fsm_state_name(fsm, new_state)); + return -EPERM; + } + + if (!keep_timer) { + /* delete the old timer */ + osmo_timer_del(&fi->timer); + } + + if (st->onleave) + st->onleave(fi, new_state); + + if (fsm_log_timeouts) { + char trailer64; + trailer0 = '\0'; + if (keep_timer && fi->timer.active) { + /* This should always give us a timeout, but just in case the return value indicates error, omit + * logging the remaining time. */ + if (osmo_timer_remaining(&fi->timer, NULL, &remaining)) + snprintf(trailer, sizeof(trailer), "(keeping " OSMO_T_FMT ")", + OSMO_T_FMT_ARGS(fi->T)); + else + snprintf(trailer, sizeof(trailer), "(keeping " OSMO_T_FMT + ", %ld.%03lds remaining)", OSMO_T_FMT_ARGS(fi->T), + (long) remaining.tv_sec, remaining.tv_usec / 1000); + } else if (timeout_ms) { + if (timeout_ms % 1000 == 0) + /* keep log output legacy compatible to avoid autotest failures */ + snprintf(trailer, sizeof(trailer), "(" OSMO_T_FMT ", %lus)", + OSMO_T_FMT_ARGS(T), timeout_ms/1000); + else + snprintf(trailer, sizeof(trailer), "(" OSMO_T_FMT ", %lums)", + OSMO_T_FMT_ARGS(T), timeout_ms); + } else + snprintf(trailer, sizeof(trailer), "(no timeout)"); + + LOGPFSMSRC(fi, file, line, "State change to %s %s\n", + osmo_fsm_state_name(fsm, new_state), trailer); + } else { + LOGPFSMSRC(fi, file, line, "state_chg to %s\n", + osmo_fsm_state_name(fsm, new_state)); + } + + fi->state = new_state; + st = &fsm->statesnew_state; + + if (!keep_timer + || (keep_timer && !osmo_timer_pending(&fi->timer))) { + fi->T = T; + if (timeout_ms) { + osmo_timer_schedule(&fi->timer, + /* seconds */ (timeout_ms / 1000), + /* microseconds */ (timeout_ms % 1000) * 1000); + } + } + + /* Call 'onenter' last, user might terminate FSM from there */ + if (st->onenter) + st->onenter(fi, old_state); + + return 0; +} + +/*! perform a state change of the given FSM instance + * + * Best invoke via the osmo_fsm_inst_state_chg() macro which logs the source + * file where the state change was effected. Alternatively, you may pass \a + * file as NULL to use the normal file/line indication instead. + * + * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* + * function. It verifies that the existing state actually permits a + * transition to new_state. + * + * If timeout_secs is 0, stay in the new state indefinitely, without a timeout + * (stop the FSM instance's timer if it was runnning). + * + * If timeout_secs > 0, start or reset the FSM instance's timer with this + * timeout. On expiry, invoke the FSM instance's timer_cb -- if no timer_cb is + * set, an expired timer immediately terminates the FSM instance with + * OSMO_FSM_TERM_TIMEOUT. + * + * The value of T is stored in fi->T and is then available for query in + * timer_cb. If passing timeout_secs == 0, it is recommended to also pass T == + * 0, so that fi->T is reset to 0 when no timeout is invoked. + * + * Positive values for T are considered to be 3GPP spec compliant and appear in + * logging and VTY as "T1234", while negative values are considered to be + * Osmocom specific timers, represented in logging and VTY as "X1234". + * + * See also osmo_tdef_fsm_inst_state_chg() from the osmo_tdef API, which + * provides a unified way to configure and apply GSM style Tnnnn timers to FSM + * state transitions. + * + * \paramin fi FSM instance whose state is to change + * \paramin new_state The new state into which we should change + * \paramin timeout_secs Timeout in seconds (if !=0), maximum-clamped to 2147483647 seconds. + * \paramin T Timer number, where positive numbers are considered to be 3GPP spec compliant timer numbers and are + * logged as "T1234", while negative numbers are considered Osmocom specific timer numbers logged as + * "X1234". + * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) + * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) + * \returns 0 on success; negative on error + */ +int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, + unsigned long timeout_secs, int T, + const char *file, int line) +{ + return state_chg(fi, new_state, false, timeout_secs*1000, T, file, line); +} +int _osmo_fsm_inst_state_chg_ms(struct osmo_fsm_inst *fi, uint32_t new_state, + unsigned long timeout_ms, int T, + const char *file, int line) +{ + return state_chg(fi, new_state, false, timeout_ms, T, file, line); +} + +/*! perform a state change while keeping the current timer running. + * + * This is useful to keep a timeout across several states (without having to round the + * remaining time to seconds). + * + * Best invoke via the osmo_fsm_inst_state_chg_keep_timer() macro which logs the source + * file where the state change was effected. Alternatively, you may pass \a + * file as NULL to use the normal file/line indication instead. + * + * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* + * function. It verifies that the existing state actually permits a + * transition to new_state. + * + * \paramin fi FSM instance whose state is to change + * \paramin new_state The new state into which we should change + * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) + * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) + * \returns 0 on success; negative on error + */ +int _osmo_fsm_inst_state_chg_keep_timer(struct osmo_fsm_inst *fi, uint32_t new_state, + const char *file, int line) +{ + return state_chg(fi, new_state, true, 0, 0, file, line); +} + +/*! perform a state change while keeping the current timer if running, or starting a timer otherwise. + * + * This is useful to keep a timeout across several states, but to make sure that some timeout is actually running. + * + * Best invoke via the osmo_fsm_inst_state_chg_keep_or_start_timer() macro which logs the source file where the state + * change was effected. Alternatively, you may pass file as NULL to use the normal file/line indication instead. + * + * All changes to the FSM instance state must be made via an osmo_fsm_inst_state_chg_* + * function. It verifies that the existing state actually permits a + * transition to new_state. + * + * \paramin fi FSM instance whose state is to change + * \paramin new_state The new state into which we should change + * \paramin timeout_secs If no timer is running yet, set this timeout in seconds (if !=0), maximum-clamped to + * 2147483647 seconds. + * \paramin T Timer number, where positive numbers are considered to be 3GPP spec compliant timer numbers and are + * logged as "T1234", while negative numbers are considered Osmocom specific timer numbers logged as + * "X1234". + * \paramin file Calling source file (from osmo_fsm_inst_state_chg macro) + * \paramin line Calling source line (from osmo_fsm_inst_state_chg macro) + * \returns 0 on success; negative on error + */ +int _osmo_fsm_inst_state_chg_keep_or_start_timer(struct osmo_fsm_inst *fi, uint32_t new_state, + unsigned long timeout_secs, int T, + const char *file, int line) +{ + return state_chg(fi, new_state, true, timeout_secs*1000, T, file, line); +} +int _osmo_fsm_inst_state_chg_keep_or_start_timer_ms(struct osmo_fsm_inst *fi, uint32_t new_state, + unsigned long timeout_ms, int T, + const char *file, int line) +{ + return state_chg(fi, new_state, true, timeout_ms, T, file, line); +} + + +/*! dispatch an event to an osmocom finite state machine instance + * + * Best invoke via the osmo_fsm_inst_dispatch() macro which logs the source + * file where the event was effected. Alternatively, you may pass \a file as + * NULL to use the normal file/line indication instead. + * + * Any incoming events to \ref osmo_fsm instances must be dispatched to + * them via this function. It verifies, whether the event is permitted + * based on the current state of the FSM. If not, -1 is returned. + * + * \paramin fi FSM instance + * \paramin event Event to send to FSM instance + * \paramin data Data to pass along with the event + * \paramin file Calling source file (from osmo_fsm_inst_dispatch macro) + * \paramin line Calling source line (from osmo_fsm_inst_dispatch macro) + * \returns 0 in case of success; negative on error + */ +int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data, + const char *file, int line) +{ + struct osmo_fsm *fsm; + const struct osmo_fsm_state *fs; + + if (!fi) { + LOGPSRC(DLGLOBAL, LOGL_ERROR, file, line, + "Trying to dispatch event %"PRIu32" to non-existent" + " FSM instance!\n", event); + osmo_log_backtrace(DLGLOBAL, LOGL_ERROR); + return -ENODEV; + } + + fsm = fi->fsm; + + if (fi->proc.terminating) { + LOGPFSMSRC(fi, file, line, + "FSM instance already terminating, not dispatching event %s\n", + osmo_fsm_event_name(fsm, event)); + return -EINVAL; + } + + OSMO_ASSERT(fi->state < fsm->num_states); + fs = &fi->fsm->statesfi->state; + + LOGPFSMSRC(fi, file, line, + "Received Event %s\n", osmo_fsm_event_name(fsm, event)); + + if (((1 << event) & fsm->allstate_event_mask) && fsm->allstate_action) { + fsm->allstate_action(fi, event, data); + return 0; + } + + if (!((1 << event) & fs->in_event_mask)) { + LOGPFSMLSRC(fi, LOGL_ERROR, file, line, + "Event %s not permitted\n", + osmo_fsm_event_name(fsm, event)); + return -1; + } + + if (fs->action) + fs->action(fi, event, data); + + return 0; +} + +/*! Terminate FSM instance with given cause + * + * This safely terminates the given FSM instance by first iterating + * over all children and sending them a termination event. Next, it + * calls the FSM descriptors cleanup function (if any), followed by + * releasing any memory associated with the FSM instance. + * + * Finally, the parent FSM instance (if any) is notified using the + * parent termination event configured at time of FSM instance start. + * + * \paramin fi FSM instance to be terminated + * \paramin cause Cause / reason for termination + * \paramin data Opaque event data to be passed with the parent term event + * \paramin file Calling source file (from osmo_fsm_inst_term macro) + * \paramin line Calling source line (from osmo_fsm_inst_term macro) + */ +void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause, void *data, + const char *file, int line) +{ + struct osmo_fsm_inst *parent; + uint32_t parent_term_event = fi->proc.parent_term_event; + + if (fi->proc.terminating) { + LOGPFSMSRC(fi, file, line, "Ignoring trigger to terminate: already terminating\n"); + return; + } + fi->proc.terminating = true; + + /* Start termination cascade handling only if the feature is enabled. Also check the current depth: though + * unlikely, theoretically the fsm_term_safely_enabled flag could be toggled in the middle of a cascaded + * termination, so make sure to continue if it already started. */ + if (fsm_term_safely_enabled || fsm_term_safely.depth) { + fsm_term_safely.depth++; + /* root_fi is just for logging, so no need to be extra careful about it. */ + if (!fsm_term_safely.root_fi) + fsm_term_safely.root_fi = fi; + } + + if (fsm_term_safely.depth > 1) { + /* fsm_term_safely is enabled and this is a secondary FSM instance terminated, caused by the root_fi. */ + LOGPFSMSRC(fi, file, line, "Terminating in cascade, depth %d (cause = %s, caused by: %s)\n", + fsm_term_safely.depth, osmo_fsm_term_cause_name(cause), + fsm_term_safely.root_fi ? fsm_term_safely.root_fi->name : "unknown"); + /* The root_fi can't go missing really, but to be safe, log "unknown" in that case. */ + } else { + /* fsm_term_safely is disabled, or this is the root_fi. */ + LOGPFSMSRC(fi, file, line, "Terminating (cause = %s)\n", osmo_fsm_term_cause_name(cause)); + } + + /* graceful exit (optional) */ + if (fi->fsm->pre_term) + fi->fsm->pre_term(fi, cause); + + _osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL, + file, line); + + /* delete ourselves from the parent */ + parent = fi->proc.parent; + if (parent) { + LOGPFSMSRC(fi, file, line, "Removing from parent %s\n", + osmo_fsm_inst_name(parent)); + llist_del(&fi->proc.child); + } + + /* call destructor / clean-up function */ + if (fi->fsm->cleanup) + fi->fsm->cleanup(fi, cause); + + /* Fetch parent again in case it has changed. */ + parent = fi->proc.parent; + + /* Legacy behavior if fsm_term_safely is disabled: free before dispatching parent event. (If fsm_term_safely is + * enabled, depth will *always* be > 0 here.) Pivot on depth instead of the enabled flag in case the enabled + * flag is toggled in the middle of an FSM term. */ + if (!fsm_term_safely.depth) { + LOGPFSMSRC(fi, file, line, "Freeing instance\n"); + osmo_fsm_inst_free(fi); + } + + /* indicate our termination to the parent */ + if (parent && cause != OSMO_FSM_TERM_PARENT) + _osmo_fsm_inst_dispatch(parent, parent_term_event, data, + file, line); + + /* Newer, safe deallocation: free only after the parent_term_event was dispatched, to catch all termination + * cascades, and free all FSM instances at once. (If fsm_term_safely is enabled, depth will *always* be > 0 + * here.) osmo_fsm_inst_free() will do the defer magic depending on the fsm_term_safely.depth. */ + if (fsm_term_safely.depth) { + fsm_term_safely.depth--; + osmo_fsm_inst_free(fi); + } +} + +/*! Terminate all child FSM instances of an FSM instance. + * + * Iterate over all children and send them a termination event, with the given + * cause. Pass OSMO_FSM_TERM_PARENT to avoid dispatching events from the + * terminated child FSMs. + * + * \paramin fi FSM instance that should be cleared of child FSMs + * \paramin cause Cause / reason for termination (OSMO_FSM_TERM_PARENT) + * \paramin data Opaque event data to be passed with the parent term events + * \paramin file Calling source file (from osmo_fsm_inst_term_children macro) + * \paramin line Calling source line (from osmo_fsm_inst_term_children macro) + */ +void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause, + void *data, + const char *file, int line) +{ + struct osmo_fsm_inst *first_child, *last_seen_first_child; + + /* iterate over all children, starting from the beginning every time: + * terminating an FSM may emit events that cause other FSMs to also + * terminate and remove themselves from this list. */ + last_seen_first_child = NULL; + while (!llist_empty(&fi->proc.children)) { + first_child = llist_entry(fi->proc.children.next, + typeof(*first_child), + proc.child); + + /* paranoia: do not loop forever */ + if (first_child == last_seen_first_child) { + LOGPFSMLSRC(fi, LOGL_ERROR, file, line, + "Internal error while terminating child" + " FSMs: a child FSM is stuck\n"); + break; + } + last_seen_first_child = first_child; + + /* terminate child */ + _osmo_fsm_inst_term(first_child, cause, data, + file, line); + } +} + +/*! Broadcast an event to all the FSMs children. + * + * Iterate over all children and send them the specified event. + * + * \paramin fi FSM instance of the parent + * \paramin event Event to send to children of FSM instance + * \paramin data Data to pass along with the event + * \paramin file Calling source file (from osmo_fsm_inst_dispatch macro) + * \paramin line Calling source line (from osmo_fsm_inst_dispatch macro) + */ +void _osmo_fsm_inst_broadcast_children(struct osmo_fsm_inst *fi, + uint32_t event, void *data, + const char *file, int line) +{ + struct osmo_fsm_inst *child, *tmp; + llist_for_each_entry_safe(child, tmp, &fi->proc.children, proc.child) { + _osmo_fsm_inst_dispatch(child, event, data, file, line); + } +} + +const struct value_string osmo_fsm_term_cause_names = { + OSMO_VALUE_STRING(OSMO_FSM_TERM_PARENT), + OSMO_VALUE_STRING(OSMO_FSM_TERM_REQUEST), + OSMO_VALUE_STRING(OSMO_FSM_TERM_REGULAR), + OSMO_VALUE_STRING(OSMO_FSM_TERM_ERROR), + OSMO_VALUE_STRING(OSMO_FSM_TERM_TIMEOUT), + { 0, NULL } +}; + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/gsmtap_util.c
Added
@@ -0,0 +1,554 @@ +/*! \file gsmtap_util.c + * GSMTAP support code in libosmocore. */ +/* + * (C) 2010-2017 by Harald Welte <laforge@gnumonks.org> + * + * 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 "config.h" + +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/byteswap.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/rsl.h> + +#include <sys/types.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +/*! \addtogroup gsmtap + * @{ + * GSMTAP utility routines. Encapsulates GSM messages over UDP. + * + * \file gsmtap_util.c */ + + +/*! convert RSL channel number to GSMTAP channel type + * \paramin rsl_chantype RSL channel type + * \paramin link_id RSL link identifier + * \paramin user_plane Is this voice/csd user plane (1) or signaling (0) + * \returns GSMTAP channel type + */ +uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_plane) +{ + uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; + + switch (rsl_chantype) { + case RSL_CHAN_Bm_ACCHs: + case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs: + if (user_plane) + ret = GSMTAP_CHANNEL_VOICE_F; + else + ret = GSMTAP_CHANNEL_FACCH_F; + break; + case RSL_CHAN_Lm_ACCHs: + case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs: + if (user_plane) + ret = GSMTAP_CHANNEL_VOICE_H; + else + ret = GSMTAP_CHANNEL_FACCH_H; + break; + case RSL_CHAN_SDCCH4_ACCH: + ret = GSMTAP_CHANNEL_SDCCH4; + break; + case RSL_CHAN_SDCCH8_ACCH: + ret = GSMTAP_CHANNEL_SDCCH8; + break; + case RSL_CHAN_BCCH: + ret = GSMTAP_CHANNEL_BCCH; + break; + case RSL_CHAN_RACH: + ret = GSMTAP_CHANNEL_RACH; + break; + case RSL_CHAN_PCH_AGCH: + /* it could also be AGCH... */ + ret = GSMTAP_CHANNEL_PCH; + break; + case RSL_CHAN_OSMO_PDCH: + ret = GSMTAP_CHANNEL_PDCH; + break; + case RSL_CHAN_OSMO_CBCH4: + ret = GSMTAP_CHANNEL_CBCH51; + break; + case RSL_CHAN_OSMO_CBCH8: + ret = GSMTAP_CHANNEL_CBCH52; + break; + } + + if (link_id & 0x40) + ret |= GSMTAP_CHANNEL_ACCH; + + return ret; +} + +/*! convert RSL channel number to GSMTAP channel type + * \paramin rsl_chantype RSL channel type + * \paramin link_id RSL link identifier + * \returns GSMTAP channel type + */ +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) +{ + return chantype_rsl2gsmtap2(rsl_chantype, link_id, false); +} + +/*! convert GSMTAP channel type to RSL channel number + Link ID + * \paramin gsmtap_chantype GSMTAP channel type + * \paramout rsl_chantype RSL channel mumber + * \paramout link_id RSL link identifier + */ +void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype, + uint8_t *link_id) +{ + switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) { + case GSMTAP_CHANNEL_FACCH_F: + case GSMTAP_CHANNEL_VOICE_F: // TCH/F + *rsl_chantype = RSL_CHAN_Bm_ACCHs; + break; + case GSMTAP_CHANNEL_FACCH_H: + case GSMTAP_CHANNEL_VOICE_H: // TCH/H + *rsl_chantype = RSL_CHAN_Lm_ACCHs; + break; + case GSMTAP_CHANNEL_SDCCH4: // SDCCH/4 + *rsl_chantype = RSL_CHAN_SDCCH4_ACCH; + break; + case GSMTAP_CHANNEL_SDCCH8: // SDCCH/8 + *rsl_chantype = RSL_CHAN_SDCCH8_ACCH; + break; + case GSMTAP_CHANNEL_BCCH: // BCCH + *rsl_chantype = RSL_CHAN_BCCH; + break; + case GSMTAP_CHANNEL_RACH: // RACH + *rsl_chantype = RSL_CHAN_RACH; + break; + case GSMTAP_CHANNEL_PCH: // PCH + case GSMTAP_CHANNEL_AGCH: // AGCH + *rsl_chantype = RSL_CHAN_PCH_AGCH; + break; + case GSMTAP_CHANNEL_PDCH: + *rsl_chantype = RSL_CHAN_OSMO_PDCH; + break; + } + + *link_id = gsmtap_chantype & GSMTAP_CHANNEL_ACCH ? 0x40 : 0x00; +} + +/*! create an arbitrary type GSMTAP message + * \paramin type The GSMTAP_TYPE_xxx constant of the message to create + * \paramin arfcn GSM ARFCN (Channel Number) + * \paramin ts GSM time slot + * \paramin chan_type Channel Type + * \paramin ss Sub-slot + * \paramin fn GSM Frame Number + * \paramin signal_dbm Signal Strength (dBm) + * \paramin snr Signal/Noise Ratio (SNR) + * \paramin data Pointer to data buffer + * \paramin len Length of \ref data + * \return dynamically allocated message buffer containing data + * + * This function will allocate a new msgb and fill it with a GSMTAP + * header containing the information + */ +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, + int8_t snr, const uint8_t *data, unsigned int len) +{ + struct msgb *msg; + struct gsmtap_hdr *gh; + uint8_t *dst; + + msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx"); + if (!msg) + return NULL; + + gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->version = GSMTAP_VERSION; + gh->hdr_len = sizeof(*gh)/4; + gh->type = type; + gh->timeslot = ts; + gh->sub_slot = ss; + gh->arfcn = osmo_htons(arfcn); + gh->snr_db = snr; + gh->signal_dbm = signal_dbm; + gh->frame_number = osmo_htonl(fn); + gh->sub_type = chan_type; + gh->antenna_nr = 0; + + dst = msgb_put(msg, len); + memcpy(dst, data, len); + + return msg; +} + +/*! create L1/L2 data and put it into GSMTAP + * \paramin arfcn GSM ARFCN (Channel Number) + * \paramin ts GSM time slot + * \paramin chan_type Channel Type + * \paramin ss Sub-slot + * \paramin fn GSM Frame Number + * \paramin signal_dbm Signal Strength (dBm) + * \paramin snr Signal/Noise Ratio (SNR) + * \paramin data Pointer to data buffer + * \paramin len Length of \ref data + * \return message buffer or NULL in case of error + * + * This function will allocate a new msgb and fill it with a GSMTAP + * header containing the information + */ +struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, + uint8_t ss, uint32_t fn, int8_t signal_dbm, + int8_t snr, const uint8_t *data, unsigned int len) +{ + return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type, + ss, fn, signal_dbm, snr, data, len); +} + +#ifdef HAVE_SYS_SOCKET_H + +#include <sys/socket.h> +#include <netinet/in.h> + +/*! Create a new (sending) GSMTAP source socket + * \paramin host host name or IP address in string format + * \paramin port UDP port number in host byte order + * \return file descriptor of the new socket + * + * Opens a GSMTAP source (sending) socket, conncet it to host/port and + * return resulting fd. If \a host is NULL, the destination address + * will be localhost. If \a port is 0, the default \ref + * GSMTAP_UDP_PORT will be used. + * */ +int gsmtap_source_init_fd(const char *host, uint16_t port) +{ + if (port == 0) + port = GSMTAP_UDP_PORT; + if (host == NULL) + host = "localhost"; + + return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, + OSMO_SOCK_F_CONNECT); +} + +/*! Add a local sink to an existing GSMTAP source and return fd + * \paramin gsmtap_fd file descriptor of the gsmtap socket + * \returns file descriptor of locally bound receive socket + * + * In case the GSMTAP socket is connected to a local destination + * IP/port, this function creates a corresponding receiving socket + * bound to that destination IP + port. + * + * In case the gsmtap socket is not connected to a local IP/port, or + * creation of the receiving socket fails, a negative error code is + * returned. + */ +int gsmtap_source_add_sink_fd(int gsmtap_fd) +{ + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + int rc; + + rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len); + if (rc < 0) + return rc; + + if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { + rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, + IPPROTO_UDP, + OSMO_SOCK_F_BIND | + OSMO_SOCK_F_UDP_REUSEADDR); + if (rc >= 0) + return rc; + } + + return -ENODEV; +} + +/*! Send a \ref msgb through a GSMTAP source + * \paramin gti GSMTAP instance + * \paramin msg message buffer + * \return 0 in case of success; negative in case of error + * NOTE: in case of nonzero return value, the *caller* must free the msg! + * (This enables the caller to attempt re-sending the message.) + * If 0 is returned, the msgb was freed by this function. + */ +int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg) +{ + if (!gti) + return -ENODEV; + + if (gti->ofd_wq_mode) + return osmo_wqueue_enqueue(>i->wq, msg); + else { + /* try immediate send and return error if any */ + int rc; + + rc = write(gsmtap_inst_fd(gti), msg->data, msg->len); + if (rc < 0) { + return rc; + } else if (rc >= msg->len) { + msgb_free(msg); + return 0; + } else { + /* short write */ + return -EIO; + } + } +} + +/*! Send a \ref msgb through a GSMTAP source; free the message even if tx queue full. + * \paramin gti GSMTAP instance + * \paramin msg message buffer; always freed, caller must not reference it later. + * \return 0 in case of success; negative in case of error + */ +int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg) +{ + int rc; + rc = gsmtap_sendmsg(gti, msg); + if (rc < 0) + msgb_free(msg); + return rc; +} + +/*! send an arbitrary type through GSMTAP. + * See \ref gsmtap_makemsg_ex for arguments + */ +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, int8_t snr, const uint8_t *data, + unsigned int len) +{ + struct msgb *msg; + int rc; + + if (!gti) + return -ENODEV; + + msg = gsmtap_makemsg_ex(type, arfcn, ts, chan_type, ss, fn, signal_dbm, + snr, data, len); + if (!msg) + return -ENOMEM; + + rc = gsmtap_sendmsg(gti, msg); + if (rc) + msgb_free(msg); + return rc; +} + +/*! send a message from L1/L2 through GSMTAP. + * See \ref gsmtap_makemsg for arguments + */ +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, int8_t snr, const uint8_t *data, + unsigned int len) +{ + return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn, + signal_dbm, snr, data, len); +} + +/* Callback from select layer if we can write to the socket */ +static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + int rc; + + rc = write(ofd->fd, msg->data, msg->len); + if (rc < 0) { + return rc; + } + if (rc != msg->len) { + return -EIO; + } + + return 0; +} + +/* Callback from select layer if we can read from the sink socket */ +static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) +{ + int rc; + uint8_t buf4096; + + if (!(flags & OSMO_FD_READ)) + return 0; + + rc = read(fd->fd, buf, sizeof(buf)); + if (rc < 0) { + return rc; + } + /* simply discard any data arriving on the socket */ + + return 0; +} + +/*! Add a local sink to an existing GSMTAP source and return fd + * \paramin gti existing GSMTAP source + * \returns file descriptor of locally bound receive socket + * + * In case the GSMTAP socket is connected to a local destination + * IP/port, this function creates a corresponding receiving socket + * bound to that destination IP + port. + * + * In case the gsmtap socket is not connected to a local IP/port, or + * creation of the receiving socket fails, a negative error code is + * returned. + * + * The file descriptor of the receiving socket is automatically added + * to the libosmocore select() handling. + */ +int gsmtap_source_add_sink(struct gsmtap_inst *gti) +{ + int fd, rc; + + fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti)); + if (fd < 0) + return fd; + + if (gti->ofd_wq_mode) { + struct osmo_fd *sink_ofd; + + sink_ofd = >i->sink_ofd; + sink_ofd->fd = fd; + sink_ofd->when = OSMO_FD_READ; + sink_ofd->cb = gsmtap_sink_fd_cb; + + rc = osmo_fd_register(sink_ofd); + if (rc < 0) { + close(fd); + return rc; + } + } + + return fd; +} + + +/*! Open GSMTAP source socket, connect and register osmo_fd + * \paramin host host name or IP address in string format + * \paramin port UDP port number in host byte order + * \paramin ofd_wq_mode Register \ref osmo_wqueue (1) or not (0) + * \return callee-allocated \ref gsmtap_inst + * + * Open GSMTAP source (sending) socket, connect it to host/port, + * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue + * registration. + */ +struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, + int ofd_wq_mode) +{ + struct gsmtap_inst *gti; + int fd, rc; + + fd = gsmtap_source_init_fd(host, port); + if (fd < 0) + return NULL; + + gti = talloc_zero(NULL, struct gsmtap_inst); + gti->ofd_wq_mode = ofd_wq_mode; + gti->wq.bfd.fd = fd; + gti->sink_ofd.fd = -1; + + if (ofd_wq_mode) { + osmo_wqueue_init(>i->wq, 64); + gti->wq.write_cb = &gsmtap_wq_w_cb; + + rc = osmo_fd_register(>i->wq.bfd); + if (rc < 0) { + talloc_free(gti); + close(fd); + return NULL; + } + } + + return gti; +} + +void gsmtap_source_free(struct gsmtap_inst *gti) +{ + if (gti->ofd_wq_mode) { + osmo_fd_unregister(>i->wq.bfd); + osmo_wqueue_clear(>i->wq); + + if (gti->sink_ofd.fd != -1) { + osmo_fd_unregister(>i->sink_ofd); + close(gti->sink_ofd.fd); + } + } + + close(gti->wq.bfd.fd); + talloc_free(gti); +} + +#endif /* HAVE_SYS_SOCKET_H */ + +const struct value_string gsmtap_gsm_channel_names = { + { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" }, + { GSMTAP_CHANNEL_BCCH, "BCCH" }, + { GSMTAP_CHANNEL_CCCH, "CCCH" }, + { GSMTAP_CHANNEL_RACH, "RACH" }, + { GSMTAP_CHANNEL_AGCH, "AGCH" }, + { GSMTAP_CHANNEL_PCH, "PCH" }, + { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, + { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" }, + { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" }, + { GSMTAP_CHANNEL_FACCH_F, "FACCH/F" }, + { GSMTAP_CHANNEL_FACCH_H, "FACCH/H" }, + { GSMTAP_CHANNEL_PACCH, "PACCH" }, + { GSMTAP_CHANNEL_CBCH52, "CBCH" }, + { GSMTAP_CHANNEL_PDCH, "PDCH" } , + { GSMTAP_CHANNEL_PTCCH, "PTTCH" }, + { GSMTAP_CHANNEL_CBCH51, "CBCH" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH, "LSACCH" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH4, "SACCH/4" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_SDCCH8, "SACCH/8" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_F, "SACCH/F" }, + { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_H, "SACCH/H" }, + { GSMTAP_CHANNEL_VOICE_F, "TCH/F" }, + { GSMTAP_CHANNEL_VOICE_H, "TCH/H" }, + { 0, NULL } +}; + +/* for debugging */ +const struct value_string gsmtap_type_names = { + { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" }, + { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" }, + { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" }, + { GSMTAP_TYPE_SIM, "SIM Card" }, + { GSMTAP_TYPE_TETRA_I1, "TETRA V+D" }, + { GSMTAP_TYPE_TETRA_I1_BURST, "TETRA bursts" }, + { GSMTAP_TYPE_WMX_BURST, "WiMAX burst" }, + { GSMTAP_TYPE_GMR1_UM, "GMR-1 air interfeace (MES-MS<->GTS)"}, + { GSMTAP_TYPE_UMTS_RLC_MAC, "UMTS RLC/MAC" }, + { GSMTAP_TYPE_UMTS_RRC, "UMTS RRC" }, + { GSMTAP_TYPE_LTE_RRC, "LTE RRC" }, + { GSMTAP_TYPE_LTE_MAC, "LTE MAC" }, + { GSMTAP_TYPE_LTE_MAC_FRAMED, "LTE MAC with context hdr" }, + { GSMTAP_TYPE_OSMOCORE_LOG, "libosmocore logging" }, + { GSMTAP_TYPE_QC_DIAG, "Qualcomm DIAG" }, + { 0, NULL } +}; + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/isdnhdlc.c
Added
@@ -0,0 +1,612 @@ +/* + * isdnhdlc.c -- General purpose ISDN HDLC decoder. + * + * Copyright (C) + * 2009 Karsten Keil <keil@b1-systems.de> + * 2002 Wolfgang Mües <wolfgang@iksw-muees.de> + * 2001 Frode Isaksen <fisaksen@bewan.com> + * 2001 Kai Germaschewski <kai.germaschewski@gmx.de> + * + * slightly adapted for use in userspace / osmocom envrionment by Harald Welte + * + * 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 <string.h> + +#include <osmocom/core/crc16.h> +#include <osmocom/core/bits.h> +#include <osmocom/core/isdnhdlc.h> + +enum { + HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7, + HDLC_GET_DATA, HDLC_FAST_FLAG +}; + +enum { + HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG, + HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG, + HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0, + HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE +}; + +#define crc_ccitt_byte osmo_crc16_ccitt_byte + +void osmo_isdnhdlc_rcv_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t features) +{ + memset(hdlc, 0, sizeof(*hdlc)); + hdlc->state = HDLC_GET_DATA; + if (features & OSMO_HDLC_F_56KBIT) + hdlc->do_adapt56 = 1; + if (features & OSMO_HDLC_F_BITREVERSE) + hdlc->do_bitreverse = 1; +} + +void osmo_isdnhdlc_out_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t features) +{ + memset(hdlc, 0, sizeof(*hdlc)); + if (features & OSMO_HDLC_F_DCHANNEL) { + hdlc->dchannel = 1; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + hdlc->dchannel = 0; + hdlc->state = HDLC_SEND_FAST_FLAG; + hdlc->ffvalue = 0x7e; + } + hdlc->cbin = 0x7e; + if (features & OSMO_HDLC_F_56KBIT) { + hdlc->do_adapt56 = 1; + hdlc->state = HDLC_SENDFLAG_B0; + } else + hdlc->data_bits = 8; + if (features & OSMO_HDLC_F_BITREVERSE) + hdlc->do_bitreverse = 1; +} + +static int +check_frame(struct osmo_isdnhdlc_vars *hdlc) +{ + int status; + + if (hdlc->dstpos < 2) /* too small - framing error */ + status = -OSMO_HDLC_FRAMING_ERROR; + else if (hdlc->crc != 0xf0b8) /* crc error */ + status = -OSMO_HDLC_CRC_ERROR; + else { + /* remove CRC */ + hdlc->dstpos -= 2; + /* good frame */ + status = hdlc->dstpos; + } + return status; +} + +/*! decodes HDLC frames from a transparent bit stream. + + The source buffer is scanned for valid HDLC frames looking for + flags (01111110) to indicate the start of a frame. If the start of + the frame is found, the bit stuffing is removed (0 after 5 1's). + When a new flag is found, the complete frame has been received + and the CRC is checked. + If a valid frame is found, the function returns the frame length + excluding the CRC. + If the beginning of a valid frame is found, the function returns + the length. + If a framing error is found (too many 1s and not a flag) the function + returns -OSMO_HDLC_FRAMING_ERROR. + If a CRC error is found the function returns -OSMO_HDLC_CRC_ERROR. + If the frame length exceeds the destination buffer size, the function + returns the length with the bit OSMO_HDLC_LENGTH_ERROR set. + + \paramin src source buffer + \paramin slen source buffer length + \paramout count number of bytes removed (decoded) from the source buffer + \paramout dst destination buffer + \paramin dsize destination buffer size + \returns number of decoded bytes in the destination buffer and status flag. +*/ +int osmo_isdnhdlc_decode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src, int slen, + int *count, uint8_t *dst, int dsize) +{ + int status = 0; + + static const unsigned char fast_flag = { + 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f + }; + + static const unsigned char fast_flag_value = { + 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f + }; + + static const unsigned char fast_abort = { + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff + }; + +#define handle_fast_flag(h) \ + do { \ + if (h->cbin == fast_flagh->bit_shift) { \ + h->ffvalue = fast_flag_valueh->bit_shift; \ + h->state = HDLC_FAST_FLAG; \ + h->ffbit_shift = h->bit_shift; \ + h->bit_shift = 1; \ + } else { \ + h->state = HDLC_GET_DATA; \ + h->data_received = 0; \ + } \ + } while (0) + +#define handle_abort(h) \ + do { \ + h->shift_reg = fast_aborth->ffbit_shift - 1; \ + h->hdlc_bits1 = h->ffbit_shift - 2; \ + if (h->hdlc_bits1 < 0) \ + h->hdlc_bits1 = 0; \ + h->data_bits = h->ffbit_shift - 1; \ + h->state = HDLC_GET_DATA; \ + h->data_received = 0; \ + } while (0) + + *count = slen; + + while (slen > 0) { + if (hdlc->bit_shift == 0) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + hdlc->cbin = osmo_revbytebits_8(*src++); + else + hdlc->cbin = *src++; + slen--; + hdlc->bit_shift = 8; + if (hdlc->do_adapt56) + hdlc->bit_shift--; + } + + switch (hdlc->state) { + case STOPPED: + return 0; + case HDLC_FAST_IDLE: + if (hdlc->cbin == 0xff) { + hdlc->bit_shift = 0; + break; + } + hdlc->state = HDLC_GET_FLAG_B0; + hdlc->hdlc_bits1 = 0; + hdlc->bit_shift = 8; + break; + case HDLC_GET_FLAG_B0: + if (!(hdlc->cbin & 0x80)) { + hdlc->state = HDLC_GETFLAG_B1A6; + hdlc->hdlc_bits1 = 0; + } else { + if ((!hdlc->do_adapt56) && + (++hdlc->hdlc_bits1 >= 8) && + (hdlc->bit_shift == 1)) + hdlc->state = HDLC_FAST_IDLE; + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GETFLAG_B1A6: + if (hdlc->cbin & 0x80) { + hdlc->hdlc_bits1++; + if (hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_GETFLAG_B7; + } else + hdlc->hdlc_bits1 = 0; + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GETFLAG_B7: + if (hdlc->cbin & 0x80) { + hdlc->state = HDLC_GET_FLAG_B0; + } else { + hdlc->state = HDLC_GET_DATA; + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->hdlc_bits1 = 0; + hdlc->data_bits = 0; + hdlc->data_received = 0; + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GET_DATA: + if (hdlc->cbin & 0x80) { + hdlc->hdlc_bits1++; + switch (hdlc->hdlc_bits1) { + case 6: + break; + case 7: + if (hdlc->data_received) + /* bad frame */ + status = -OSMO_HDLC_FRAMING_ERROR; + if (!hdlc->do_adapt56) { + if (hdlc->cbin == fast_abort + hdlc->bit_shift + 1) { + hdlc->state = + HDLC_FAST_IDLE; + hdlc->bit_shift = 1; + break; + } + } else + hdlc->state = HDLC_GET_FLAG_B0; + break; + default: + hdlc->shift_reg >>= 1; + hdlc->shift_reg |= 0x80; + hdlc->data_bits++; + break; + } + } else { + switch (hdlc->hdlc_bits1) { + case 5: + break; + case 6: + if (hdlc->data_received) + status = check_frame(hdlc); + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->data_bits = 0; + if (!hdlc->do_adapt56) + handle_fast_flag(hdlc); + else { + hdlc->state = HDLC_GET_DATA; + hdlc->data_received = 0; + } + break; + default: + hdlc->shift_reg >>= 1; + hdlc->data_bits++; + break; + } + hdlc->hdlc_bits1 = 0; + } + if (status) { + hdlc->dstpos = 0; + *count -= slen; + hdlc->cbin <<= 1; + hdlc->bit_shift--; + return status; + } + if (hdlc->data_bits == 8) { + hdlc->data_bits = 0; + hdlc->data_received = 1; + hdlc->crc = crc_ccitt_byte(hdlc->crc, + hdlc->shift_reg); + + /* good byte received */ + if (hdlc->dstpos < dsize) + dsthdlc->dstpos++ = hdlc->shift_reg; + else { + /* frame too long */ + status = -OSMO_HDLC_LENGTH_ERROR; + hdlc->dstpos = 0; + } + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_FAST_FLAG: + if (hdlc->cbin == hdlc->ffvalue) { + hdlc->bit_shift = 0; + break; + } else { + if (hdlc->cbin == 0xff) { + hdlc->state = HDLC_FAST_IDLE; + hdlc->bit_shift = 0; + } else if (hdlc->ffbit_shift == 8) { + hdlc->state = HDLC_GETFLAG_B7; + break; + } else + handle_abort(hdlc); + } + break; + default: + break; + } + } + *count -= slen; + return 0; +} +/*! encodes HDLC frames to a transparent bit stream. + + The bit stream starts with a beginning flag (01111110). After + that each byte is added to the bit stream with bit stuffing added + (0 after 5 1's). + When the last byte has been removed from the source buffer, the + CRC (2 bytes is added) and the frame terminates with the ending flag. + For the dchannel, the idle character (all 1's) is also added at the end. + If this function is called with empty source buffer (slen=0), flags or + idle character will be generated. + + \paramin src source buffer + \paramin slen source buffer length + \paramout count number of bytes removed (encoded) from source buffer + \paramout dst destination buffer + \paramin dsize destination buffer size + \returns - number of encoded bytes in the destination buffer +*/ +int osmo_isdnhdlc_encode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src, uint16_t slen, + int *count, uint8_t *dst, int dsize) +{ + static const unsigned char xfast_flag_value = { + 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e + }; + + int len = 0; + + *count = slen; + + /* special handling for one byte frames */ + if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG)) + hdlc->state = HDLC_SENDFLAG_ONE; + while (dsize > 0) { + if (hdlc->bit_shift == 0) { + if (slen && !hdlc->do_closing) { + hdlc->shift_reg = *src++; + slen--; + if (slen == 0) + /* closing sequence, CRC + flag(s) */ + hdlc->do_closing = 1; + hdlc->bit_shift = 8; + } else { + if (hdlc->state == HDLC_SEND_DATA) { + if (hdlc->data_received) { + hdlc->state = HDLC_SEND_CRC1; + hdlc->crc ^= 0xffff; + hdlc->bit_shift = 8; + hdlc->shift_reg = + hdlc->crc & 0xff; + } else if (!hdlc->do_adapt56) + hdlc->state = + HDLC_SEND_FAST_FLAG; + else + hdlc->state = + HDLC_SENDFLAG_B0; + } + + } + } + + switch (hdlc->state) { + case STOPPED: + while (dsize--) + *dst++ = 0xff; + return dsize; + case HDLC_SEND_FAST_FLAG: + hdlc->do_closing = 0; + if (slen == 0) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = osmo_revbytebits_8(hdlc->ffvalue); + else + *dst++ = hdlc->ffvalue; + len++; + dsize--; + break; + } + /* fall through */ + case HDLC_SENDFLAG_ONE: + if (hdlc->bit_shift == 8) { + hdlc->cbin = hdlc->ffvalue >> + (8 - hdlc->data_bits); + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SENDFLAG_B0: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->hdlc_bits1 = 0; + hdlc->state = HDLC_SENDFLAG_B1A6; + break; + case HDLC_SENDFLAG_B1A6: + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->cbin++; + if (++hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_SENDFLAG_B7; + break; + case HDLC_SENDFLAG_B7: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (slen == 0) { + hdlc->state = HDLC_SENDFLAG_B0; + break; + } + if (hdlc->bit_shift == 8) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SEND_FIRST_FLAG: + hdlc->data_received = 1; + if (hdlc->data_bits == 8) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + break; + } + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + } + break; + case HDLC_SEND_DATA: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->bit_shift == 8) + hdlc->crc = crc_ccitt_byte(hdlc->crc, + hdlc->shift_reg); + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + break; + case HDLC_SEND_CRC1: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if (hdlc->bit_shift == 0) { + hdlc->shift_reg = (hdlc->crc >> 8); + hdlc->state = HDLC_SEND_CRC2; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CRC2: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if (hdlc->bit_shift == 0) { + hdlc->shift_reg = 0x7e; + hdlc->state = HDLC_SEND_CLOSING_FLAG; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CLOSING_FLAG: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->ffvalue = + xfast_flag_valuehdlc->data_bits; + if (hdlc->dchannel) { + hdlc->ffvalue = 0x7e; + hdlc->state = HDLC_SEND_IDLE1; + hdlc->bit_shift = 8-hdlc->data_bits; + if (hdlc->bit_shift == 0) + hdlc->state = + HDLC_SEND_FAST_IDLE; + } else { + if (!hdlc->do_adapt56) { + hdlc->state = + HDLC_SEND_FAST_FLAG; + hdlc->data_received = 0; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + hdlc->data_received = 0; + } + /* Finished this frame, send flags */ + if (dsize > 1) + dsize = 1; + } + } + break; + case HDLC_SEND_IDLE1: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->state = HDLC_SEND_FAST_IDLE; + hdlc->bit_shift = 0; + } + break; + case HDLC_SEND_FAST_IDLE: + hdlc->do_closing = 0; + hdlc->cbin = 0xff; + hdlc->data_bits = 8; + if (hdlc->bit_shift == 8) { + hdlc->cbin = 0x7e; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = osmo_revbytebits_8(hdlc->cbin); + else + *dst++ = hdlc->cbin; + hdlc->bit_shift = 0; + hdlc->data_bits = 0; + len++; + dsize = 0; + } + break; + default: + break; + } + if (hdlc->do_adapt56) { + if (hdlc->data_bits == 7) { + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + } + } + if (hdlc->data_bits == 8) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = osmo_revbytebits_8(hdlc->cbin); + else + *dst++ = hdlc->cbin; + hdlc->data_bits = 0; + len++; + dsize--; + } + } + *count -= slen; + + return len; +}
View file
libosmocore_1.8.0.tar.xz/src/core/it_q.c
Added
@@ -0,0 +1,272 @@ +/*! \file it_q.c + * Osmocom Inter-Thread queue implementation */ +/* (C) 2019 by Harald Welte <laforge@gnumonks.org> + * 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. + */ + +/*! \addtogroup it_q + * @{ + * Inter-Thread Message Queue. + * + * This implements a general-purpose queue between threads. It uses + * user-provided data types (containing a llist_head as initial member) + * as elements in the queue and an eventfd-based notification mechanism. + * Hence, it can be used for pretty much anything, including but not + * limited to msgbs, including msgb-wrapped osmo_prim. + * + * The idea is that the sending thread simply calls osmo_it_q_enqueue(). + * The receiving thread is woken up from its osmo_select_main() loop by eventfd, + * and a general osmo_fd callback function for the eventfd will dequeue each item + * and call a queue-specific callback function. + */ + +#include "config.h" + +#ifdef HAVE_SYS_EVENTFD_H + +#include <pthread.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/eventfd.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/it_q.h> + +/* "increment" the eventfd by specified 'inc' */ +static int eventfd_increment(int fd, uint64_t inc) +{ + int rc; + + rc = write(fd, &inc, sizeof(inc)); + if (rc != sizeof(inc)) + return -1; + + return 0; +} + +/* global (for all threads) list of message queues in a program + associated lock */ +static LLIST_HEAD(it_queues); +static pthread_rwlock_t it_queues_rwlock = PTHREAD_RWLOCK_INITIALIZER; + +/* resolve it-queue by its globally unique name; must be called with rwlock held */ +static struct osmo_it_q *_osmo_it_q_by_name(const char *name) +{ + struct osmo_it_q *q; + llist_for_each_entry(q, &it_queues, entry) { + if (!strcmp(q->name, name)) + return q; + } + return NULL; +} + +/*! resolve it-queue by its globally unique name */ +struct osmo_it_q *osmo_it_q_by_name(const char *name) +{ + struct osmo_it_q *q; + pthread_rwlock_rdlock(&it_queues_rwlock); + q = _osmo_it_q_by_name(name); + pthread_rwlock_unlock(&it_queues_rwlock); + return q; +} + +/* osmo_fd call-back when eventfd is readable */ +static int osmo_it_q_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct osmo_it_q *q = (struct osmo_it_q *) ofd->data; + uint64_t val; + int i, rc; + + if (!(what & OSMO_FD_READ)) + return 0; + + rc = read(ofd->fd, &val, sizeof(val)); + if (rc < sizeof(val)) + return rc; + + for (i = 0; i < val; i++) { + struct llist_head *item = _osmo_it_q_dequeue(q); + /* in case the user might have called osmo_it_q_flush() we may + * end up in the eventfd-dispatch but without any messages left in the queue, + * otherwise I'd have loved to OSMO_ASSERT(msg) here. */ + if (!item) + break; + q->read_cb(q, item); + } + + return 0; +} + +/*! Allocate a new inter-thread message queue. + * \paramin ctx talloc context from which to allocate the queue + * \paramin name human-readable string name of the queue; function creates a copy. + * \paramin read_cb call-back function to be called for each de-queued message; may be + * NULL in case you don't want eventfd/osmo_select integration and + * will manually take care of noticing if and when to dequeue. + * \returns a newly-allocated inter-thread message queue; NULL in case of error */ +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) +{ + struct osmo_it_q *q; + int fd; + + q = talloc_zero(ctx, struct osmo_it_q); + if (!q) + return NULL; + q->data = data; + q->name = talloc_strdup(q, name); + q->current_length = 0; + q->max_length = max_length; + q->read_cb = read_cb; + INIT_LLIST_HEAD(&q->list); + pthread_mutex_init(&q->mutex, NULL); + q->event_ofd.fd = -1; + + if (q->read_cb) { + /* create eventfd *if* the user has provided a read_cb function */ + fd = eventfd(0, 0); + if (fd < 0) { + talloc_free(q); + return NULL; + } + + /* initialize BUT NOT REGISTER the osmo_fd. The receiving thread must + * take are to select/poll/read/... on it */ + osmo_fd_setup(&q->event_ofd, fd, OSMO_FD_READ, osmo_it_q_fd_cb, q, 0); + } + + /* add to global list of queues, checking for duplicate names */ + pthread_rwlock_wrlock(&it_queues_rwlock); + if (_osmo_it_q_by_name(q->name)) { + pthread_rwlock_unlock(&it_queues_rwlock); + if (q->event_ofd.fd >= 0) + osmo_fd_close(&q->event_ofd); + talloc_free(q); + return NULL; + } + llist_add_tail(&q->entry, &it_queues); + pthread_rwlock_unlock(&it_queues_rwlock); + + return q; +} + +static void *item_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + if (lh) { + llist_del(lh); + return lh; + } else + return NULL; +} + +/*! Flush all messages currently present in queue */ +static void _osmo_it_q_flush(struct osmo_it_q *q) +{ + void *item; + while ((item = item_dequeue(&q->list))) { + talloc_free(item); + } + q->current_length = 0; +} + +/*! Flush all messages currently present in queue */ +void osmo_it_q_flush(struct osmo_it_q *q) +{ + OSMO_ASSERT(q); + + pthread_mutex_lock(&q->mutex); + _osmo_it_q_flush(q); + pthread_mutex_unlock(&q->mutex); +} + +/*! Destroy a message queue */ +void osmo_it_q_destroy(struct osmo_it_q *q) +{ + OSMO_ASSERT(q); + + /* first remove from global list of queues */ + pthread_rwlock_wrlock(&it_queues_rwlock); + llist_del(&q->entry); + pthread_rwlock_unlock(&it_queues_rwlock); + /* next, close the eventfd */ + if (q->event_ofd.fd >= 0) + osmo_fd_close(&q->event_ofd); + /* flush all messages still present */ + osmo_it_q_flush(q); + pthread_mutex_destroy(&q->mutex); + /* and finally release memory */ + talloc_free(q); +} + +/*! Thread-safe en-queue to an inter-thread message queue. + * \paramin queue Inter-thread queue on which to enqueue + * \paramin item Item to enqueue. Must have llist_head as first member! + * \returns 0 on success; negative on error */ +int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item) +{ + OSMO_ASSERT(queue); + OSMO_ASSERT(item); + + pthread_mutex_lock(&queue->mutex); + if (queue->current_length+1 > queue->max_length) { + pthread_mutex_unlock(&queue->mutex); + return -ENOSPC; + } + llist_add_tail(item, &queue->list); + queue->current_length++; + pthread_mutex_unlock(&queue->mutex); + /* increment eventfd counter by one */ + if (queue->event_ofd.fd >= 0) + eventfd_increment(queue->event_ofd.fd, 1); + return 0; +} + + +/*! Thread-safe de-queue from an inter-thread message queue. + * \paramin queue Inter-thread queue from which to dequeue + * \returns dequeued message buffer; NULL if none available + */ +struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue) +{ + struct llist_head *l; + OSMO_ASSERT(queue); + + pthread_mutex_lock(&queue->mutex); + + if (llist_empty(&queue->list)) + l = NULL; + l = queue->list.next; + OSMO_ASSERT(l); + llist_del(l); + queue->current_length--; + + pthread_mutex_unlock(&queue->mutex); + + return l; +} + + +#endif /* HAVE_SYS_EVENTFD_H */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/logging.c
Added
@@ -0,0 +1,1532 @@ +/*! \file logging.c + * Debugging/Logging support code. */ +/* + * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * 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. + * + */ + +/*! \addtogroup logging + * @{ + * libosmocore Logging sub-system + * + * \file logging.c */ + +#include "config.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#ifdef HAVE_SYSTEMTAP +/* include the generated probes header and put markers in code */ +#include "probes.h" +#define TRACE(probe) probe +#define TRACE_ENABLED(probe) probe ## _ENABLED() +#else +/* Wrap the probe to allow it to be removed when no systemtap available */ +#define TRACE(probe) +#define TRACE_ENABLED(probe) (0) +#endif /* HAVE_SYSTEMTAP */ + +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/thread.h> +#include <osmocom/core/select.h> +#include <osmocom/core/write_queue.h> +#include <osmocom/core/gsmtap_util.h> + +#include <osmocom/vty/logging.h> /* for LOGGING_STR. */ + +/* maximum length of the log string of a single log event (typically line) */ +#define MAX_LOG_SIZE 4096 + +/* maximum number of log statements we queue in file/stderr target write queue */ +#define LOG_WQUEUE_LEN 156 + +osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx), + enum_logging_ctx_items_fit_in_struct_log_context); +osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data), + enum_logging_filters_fit_in_log_target_filter_data); +osmo_static_assert(_LOG_FLT_COUNT <= 8*sizeof(((struct log_target*)NULL)->filter_map), + enum_logging_filters_fit_in_log_target_filter_map); + +struct log_info *osmo_log_info; + +static struct log_context log_context; +void *tall_log_ctx = NULL; +LLIST_HEAD(osmo_log_target_list); + +static __thread long int logging_tid; + +#if (!EMBEDDED) +/*! This mutex must be held while using osmo_log_target_list or any of its + log_targets in a multithread program. Prevents race conditions between threads + like producing unordered timestamps or VTY deleting a target while another + thread is writing to it */ +static pthread_mutex_t osmo_log_tgt_mutex; +static bool osmo_log_tgt_mutex_on = false; + +/*! Enable multithread support (mutex) in libosmocore logging system. + * Must be called by processes willing to use logging subsystem from several + * threads. Once enabled, it's not possible to disable it again. + */ +void log_enable_multithread(void) { + if (osmo_log_tgt_mutex_on) + return; + pthread_mutex_init(&osmo_log_tgt_mutex, NULL); + osmo_log_tgt_mutex_on = true; +} + +/*! Acquire the osmo_log_tgt_mutex. Don't use this function directly, always use + * macro log_tgt_mutex_lock() instead. + */ +void log_tgt_mutex_lock_impl(void) { + /* These lines are useful to debug scenarios where there's only 1 thread + and a double lock appears, for instance during startup and some + unlock() missing somewhere: + if (osmo_log_tgt_mutex_on && pthread_mutex_trylock(&osmo_log_tgt_mutex) != 0) + osmo_panic("acquiring already locked mutex!\n"); + return; + */ + + if (osmo_log_tgt_mutex_on) + pthread_mutex_lock(&osmo_log_tgt_mutex); +} + +/*! Release the osmo_log_tgt_mutex. Don't use this function directly, always use + * macro log_tgt_mutex_unlock() instead. + */ +void log_tgt_mutex_unlock_impl(void) { + if (osmo_log_tgt_mutex_on) + pthread_mutex_unlock(&osmo_log_tgt_mutex); +} + +#else /* if (!EMBEDDED) */ +#pragma message ("logging multithread support disabled in embedded build") +void log_enable_multithread(void) {} +void log_tgt_mutex_lock_impl(void) {} +void log_tgt_mutex_unlock_impl(void) {} +#endif /* if (!EMBEDDED) */ + +const struct value_string loglevel_strs = { + { LOGL_DEBUG, "DEBUG" }, + { LOGL_INFO, "INFO" }, + { LOGL_NOTICE, "NOTICE" }, + { LOGL_ERROR, "ERROR" }, + { LOGL_FATAL, "FATAL" }, + { 0, NULL }, +}; + +/* 256 color palette see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */ +#define INT2IDX(x) (-1*(x)-1) +static const struct log_info_cat internal_catOSMO_NUM_DLIB = { + INT2IDX(DLGLOBAL) = { /* -1 becomes 0 */ + .name = "DLGLOBAL", + .description = "Library-internal global log family", + .loglevel = LOGL_NOTICE, + .enabled = 1, + }, + INT2IDX(DLLAPD) = { /* -2 becomes 1 */ + .name = "DLLAPD", + .description = "LAPD in libosmogsm", + .loglevel = LOGL_NOTICE, + .enabled = 1, + .color = "\03338;5;12m", + }, + INT2IDX(DLINP) = { + .name = "DLINP", + .description = "A-bis Intput Subsystem", + .loglevel = LOGL_NOTICE, + .enabled = 1, + .color = "\03338;5;23m", + }, + INT2IDX(DLMUX) = { + .name = "DLMUX", + .description = "A-bis B-Subchannel TRAU Frame Multiplex", + .loglevel = LOGL_NOTICE, + .enabled = 1, + .color = "\03338;5;25m", + }, + INT2IDX(DLMI) = { + .name = "DLMI", + .description = "A-bis Input Driver for Signalling", + .enabled = 0, .loglevel = LOGL_NOTICE, + .color = "\03338;5;27m", + }, + INT2IDX(DLMIB) = { + .name = "DLMIB", + .description = "A-bis Input Driver for B-Channels (voice)", + .enabled = 0, .loglevel = LOGL_NOTICE, + .color = "\03338;5;29m", + }, + INT2IDX(DLSMS) = { + .name = "DLSMS", + .description = "Layer3 Short Message Service (SMS)", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;31m", + }, + INT2IDX(DLCTRL) = { + .name = "DLCTRL", + .description = "Control Interface", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;33m", + }, + INT2IDX(DLGTP) = { + .name = "DLGTP", + .description = "GPRS GTP library", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;35m", + }, + INT2IDX(DLSTATS) = { + .name = "DLSTATS", + .description = "Statistics messages and logging", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;37m", + }, + INT2IDX(DLGSUP) = { + .name = "DLGSUP", + .description = "Generic Subscriber Update Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;39m", + }, + INT2IDX(DLOAP) = { + .name = "DLOAP", + .description = "Osmocom Authentication Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;41m", + }, + INT2IDX(DLSS7) = { + .name = "DLSS7", + .description = "libosmo-sigtran Signalling System 7", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;43m", + }, + INT2IDX(DLSCCP) = { + .name = "DLSCCP", + .description = "libosmo-sigtran SCCP Implementation", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;45m", + }, + INT2IDX(DLSUA) = { + .name = "DLSUA", + .description = "libosmo-sigtran SCCP User Adaptation", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;47m", + }, + INT2IDX(DLM3UA) = { + .name = "DLM3UA", + .description = "libosmo-sigtran MTP3 User Adaptation", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;49m", + }, + INT2IDX(DLMGCP) = { + .name = "DLMGCP", + .description = "libosmo-mgcp Media Gateway Control Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;51m", + }, + INT2IDX(DLJIBUF) = { + .name = "DLJIBUF", + .description = "libosmo-netif Jitter Buffer", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;53m", + }, + INT2IDX(DLRSPRO) = { + .name = "DLRSPRO", + .description = "Remote SIM protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;55m", + }, + INT2IDX(DLNS) = { + .name = "DLNS", + .description = "GPRS NS layer", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;57m", + }, + INT2IDX(DLBSSGP) = { + .name = "DLBSSGP", + .description = "GPRS BSSGP layer", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;59m", + }, + INT2IDX(DLNSDATA) = { + .name = "DLNSDATA", + .description = "GPRS NS layer data PDU", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;61m", + }, + INT2IDX(DLNSSIGNAL) = { + .name = "DLNSSIGNAL", + .description = "GPRS NS layer signal PDU", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;63m", + }, + INT2IDX(DLIUUP) = { + .name = "DLIUUP", + .description = "Iu UP layer", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;65m", + }, + INT2IDX(DLPFCP) = { + .name = "DLPFCP", + .description = "libosmo-pfcp Packet Forwarding Control Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;51m", + }, + INT2IDX(DLCSN1) = { + .name = "DLCSN1", + .description = "libosmo-csn1 Concrete Syntax Notation 1 codec", + .enabled = 1, .loglevel = LOGL_NOTICE, + .color = "\03338;5;11m", + }, +}; + +void assert_loginfo(const char *src) +{ + if (!osmo_log_info) { + fprintf(stderr, "ERROR: osmo_log_info == NULL! " + "You must call log_init() before using logging in %s()!\n", src); + OSMO_ASSERT(osmo_log_info); + } +} + +/* special magic for negative (library-internal) log subsystem numbers */ +static int subsys_lib2index(int subsys) +{ + return (subsys * -1) + (osmo_log_info->num_cat_user-1); +} + +/*! Parse a human-readable log level into a numeric value + * \paramin lvl zero-terminated string containing log level name + * \returns numeric log level + */ +int log_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +/*! convert a numeric log level into human-readable string + * \paramin lvl numeric log level + * \returns zero-terminated string (log level name) + */ +const char *log_level_str(unsigned int lvl) +{ + return get_value_string(loglevel_strs, lvl); +} + +/*! parse a human-readable log category into numeric form + * \paramin category human-readable log category name + * \returns numeric category value, or -EINVAL otherwise + */ +int log_parse_category(const char *category) +{ + int i; + + assert_loginfo(__func__); + + for (i = 0; i < osmo_log_info->num_cat; ++i) { + if (osmo_log_info->cati.name == NULL) + continue; + if (!strcasecmp(osmo_log_info->cati.name+1, category)) + return i; + } + + return -EINVAL; +} + +/*! parse the log category mask + * \paramin target log target to be configured + * \paramin _mask log category mask string + * + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void log_parse_category_mask(struct log_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + assert_loginfo(__func__); + + /* Disable everything to enable it afterwards */ + for (i = 0; i < osmo_log_info->num_cat; ++i) + target->categoriesi.enabled = 0; + + category_token = strtok(mask, ":"); + OSMO_ASSERT(category_token); + do { + for (i = 0; i < osmo_log_info->num_cat; ++i) { + size_t length, cat_length; + char* colon = strstr(category_token, ","); + + if (!osmo_log_info->cati.name) + continue; + + length = strlen(category_token); + cat_length = strlen(osmo_log_info->cati.name); + + /* Use longest length not to match subocurrences. */ + if (cat_length > length) + length = cat_length; + + if (colon) + length = colon - category_token; + + if (strncasecmp(osmo_log_info->cati.name, + category_token, length) == 0) { + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categoriesi.enabled = 1; + target->categoriesi.loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + if (subsys < osmo_log_info->num_cat) + return osmo_log_info->catsubsys.color; + + return NULL; +} + +static const struct value_string level_colors = { + { LOGL_DEBUG, OSMO_LOGCOLOR_BLUE }, + { LOGL_INFO, OSMO_LOGCOLOR_GREEN }, + { LOGL_NOTICE, OSMO_LOGCOLOR_YELLOW }, + { LOGL_ERROR, OSMO_LOGCOLOR_RED }, + { LOGL_FATAL, OSMO_LOGCOLOR_RED }, + { 0, NULL } +}; + +static const char *level_color(int level) +{ + const char *c = get_value_string_or_null(level_colors, level); + if (!c) + return get_value_string(level_colors, LOGL_FATAL); + return c; +} + +const char* log_category_name(int subsys) +{ + if (subsys < osmo_log_info->num_cat) + return osmo_log_info->catsubsys.name; + + return NULL; +} + +static const char *const_basename(const char *path) +{ + const char *bn = strrchr(path, '/'); + if (!bn || !bn1) + return path; + return bn + 1; +} + +/*! main output formatting function for log lines. + * \paramout buf caller-allocated output buffer for the generated string + * \paramin buf_len number of bytes available in buf + * \paramin target log target for which the string is to be formatted + * \paramin subsys Log sub-system number + * \paramin level Log level + * \paramin file name of source code file generating the log + * \paramin line line source code line number within 'file' generating the log + * \paramin cont is this a continuation (true) or not (false) + * \paramin format format string + * \paramin ap variable argument list for format + * \returns number of bytes written to out */ +static int _output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys, + unsigned int level, const char *file, int line, int cont, + const char *format, va_list ap) +{ + int ret, len = 0, offset = 0, rem = buf_len; + const char *c_subsys = NULL; + + /* are we using color */ + if (target->use_color) { + c_subsys = color(subsys); + if (c_subsys) { + ret = snprintf(buf + offset, rem, "%s", c_subsys); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + } + if (!cont) { + if (target->print_ext_timestamp) { +#ifdef HAVE_LOCALTIME_R + struct tm tm; + struct timeval tv; + osmo_gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm); + ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (int)(tv.tv_usec / 1000)); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); +#endif + } else if (target->print_timestamp) { + time_t tm; + if ((tm = time(NULL)) == (time_t) -1) + goto err; + /* Get human-readable representation of time. + man ctime: we need at least 26 bytes in buf */ + if (rem < 26 || !ctime_r(&tm, buf + offset)) + goto err; + ret = strlen(buf + offset); + if (ret <= 0) + goto err; + /* Get rid of useless final '\n' added by ctime_r. We want a space instead. */ + bufoffset + ret - 1 = ' '; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + if (target->print_tid) { + if (logging_tid == 0) + logging_tid = (long int)osmo_gettid(); + ret = snprintf(buf + offset, rem, "%ld ", logging_tid); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + if (target->print_category) { + ret = snprintf(buf + offset, rem, "%s%s%s%s ", + target->use_color ? level_color(level) : "", + log_category_name(subsys), + target->use_color ? OSMO_LOGCOLOR_END : "", + c_subsys ? c_subsys : ""); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + if (target->print_level) { + ret = snprintf(buf + offset, rem, "%s%s%s%s ", + target->use_color ? level_color(level) : "", + log_level_str(level), + target->use_color ? OSMO_LOGCOLOR_END : "", + c_subsys ? c_subsys : ""); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + if (target->print_category_hex) { + ret = snprintf(buf + offset, rem, "<%4.4x> ", subsys); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + + if (target->print_filename_pos == LOG_FILENAME_POS_HEADER_END) { + switch (target->print_filename2) { + case LOG_FILENAME_NONE: + break; + case LOG_FILENAME_PATH: + ret = snprintf(buf + offset, rem, "%s:%d ", file, line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + case LOG_FILENAME_BASENAME: + ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + } + } + } + ret = vsnprintf(buf + offset, rem, format, ap); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + /* For LOG_FILENAME_POS_LAST, print the source file info only when the caller ended the log + * message in '\n'. If so, nip the last '\n' away, insert the source file info and re-append an + * '\n'. All this to allow LOGP("start..."); LOGPC("...end\n") constructs. */ + if (target->print_filename_pos == LOG_FILENAME_POS_LINE_END + && offset > 0 && bufoffset - 1 == '\n') { + switch (target->print_filename2) { + case LOG_FILENAME_NONE: + break; + case LOG_FILENAME_PATH: + offset--; + len--; + ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + case LOG_FILENAME_BASENAME: + offset--; + len--; + ret = snprintf(buf + offset, rem, " (%s:%d)\n", const_basename(file), line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + break; + } + } + + if (target->use_color && c_subsys) { + ret = snprintf(buf + offset, rem, OSMO_LOGCOLOR_END); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } +err: + len = OSMO_MIN(buf_len - 1, len); + buflen = '\0'; + return len; +} + +/* Format the log line for given target; use a stack buffer and call target->output */ +static void _output(struct log_target *target, unsigned int subsys, + unsigned int level, const char *file, int line, int cont, + const char *format, va_list ap) +{ + char bufMAX_LOG_SIZE; + int rc; + + rc = _output_buf(buf, sizeof(buf), target, subsys, level, file, line, cont, format, ap); + if (rc > 0) + target->output(target, level, buf); +} + +/* Catch internal logging category indexes as well as out-of-bounds indexes. + * For internal categories, the ID is negative starting with -1; and internal + * logging categories are added behind the user categories. For out-of-bounds + * indexes, return the index of DLGLOBAL. The returned category index is + * guaranteed to exist in osmo_log_info, otherwise the program would abort, + * which should never happen unless even the DLGLOBAL category is missing. */ +static inline int map_subsys(int subsys) +{ + /* Note: comparing signed and unsigned integers */ + + if (subsys > 0 && ((unsigned int)subsys) >= osmo_log_info->num_cat_user) + subsys = DLGLOBAL; + + if (subsys < 0) + subsys = subsys_lib2index(subsys); + + if (subsys < 0 || subsys >= osmo_log_info->num_cat) + subsys = subsys_lib2index(DLGLOBAL); + + OSMO_ASSERT(!(subsys < 0 || subsys >= osmo_log_info->num_cat)); + + return subsys; +} + +static inline bool should_log_to_target(struct log_target *tar, int subsys, + int level) +{ + struct log_category *category; + + category = &tar->categoriessubsys; + + /* subsystem is not supposed to be logged */ + if (!category->enabled) + return false; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + return false; + + /* Check the category log level */ + if (tar->loglevel == 0 && category->loglevel != 0 && + level < category->loglevel) + return false; + + /* Apply filters here... if that becomes messy we will + * need to put filters in a list and each filter will + * say stop, continue, output */ + if ((tar->filter_map & (1 << LOG_FLT_ALL)) != 0) + return true; + + if (osmo_log_info->filter_fn) + return osmo_log_info->filter_fn(&log_context, tar); + + /* TODO: Check the filter/selector too? */ + return true; +} + +/*! vararg version of logging function + * \paramin subsys Logging sub-system + * \paramin level Log level + * \paramin file name of source code file + * \paramin cont continuation (1) or new line (0) + * \paramin format format string + * \paramin ap vararg-list containing format string arguments + */ +void osmo_vlogp(int subsys, int level, const char *file, int line, + int cont, const char *format, va_list ap) +{ + struct log_target *tar; + + subsys = map_subsys(subsys); + + log_tgt_mutex_lock(); + + llist_for_each_entry(tar, &osmo_log_target_list, entry) { + va_list bp; + + if (!should_log_to_target(tar, subsys, level)) + continue; + + /* According to the manpage, vsnprintf leaves the value of ap + * in undefined state. Since _output uses vsnprintf and it may + * be called several times, we have to pass a copy of ap. */ + va_copy(bp, ap); + if (tar->raw_output) + tar->raw_output(tar, subsys, level, file, line, cont, format, bp); + else + _output(tar, subsys, level, file, line, cont, format, bp); + va_end(bp); + } + + log_tgt_mutex_unlock(); +} + +/*! logging function used by DEBUGP() macro + * \paramin subsys Logging sub-system + * \paramin file name of source code file + * \paramin cont continuation (1) or new line (0) + * \paramin format format string + */ +void logp(int subsys, const char *file, int line, int cont, + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +/*! logging function used by LOGP() macro + * \paramin subsys Logging sub-system + * \paramin level Log level + * \paramin file name of source code file + * \paramin cont continuation (1) or new line (0) + * \paramin format format string + */ +void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + TRACE(LIBOSMOCORE_LOG_START()); + va_start(ap, format); + osmo_vlogp(subsys, level, file, line, cont, format, ap); + va_end(ap); + TRACE(LIBOSMOCORE_LOG_DONE()); +} + +/* This logging function is used as a fallback when the logging framework is + * not is not properly initialized. */ +void logp_stub(const char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + if (!cont) + fprintf(stderr, "%s:%d ", file, line); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +/*! Register a new log target with the logging core + * \paramin target Log target to be registered + */ +void log_add_target(struct log_target *target) +{ + llist_add_tail(&target->entry, &osmo_log_target_list); +} + +/*! Unregister a log target from the logging core + * \paramin target Log target to be unregistered + */ +void log_del_target(struct log_target *target) +{ + llist_del(&target->entry); +} + +/*! Reset (clear) the logging context */ +void log_reset_context(void) +{ + memset(&log_context, 0, sizeof(log_context)); +} + +/*! Set the logging context + * \paramin ctx_nr logging context number + * \paramin value value to which the context is to be set + * \returns 0 in case of success; negative otherwise + * + * A logging context is something like the subscriber identity to which + * the currently processed message relates, or the BTS through which it + * was received. As soon as this data is known, it can be set using + * this function. The main use of context information is for logging + * filters. + */ +int log_set_context(uint8_t ctx_nr, void *value) +{ + if (ctx_nr > LOG_MAX_CTX) + return -EINVAL; + + log_context.ctxctx_nr = value; + + return 0; +} + +/*! Enable the \ref LOG_FLT_ALL log filter + * \paramin target Log target to be affected + * \paramin all enable (1) or disable (0) the ALL filter + * + * When the \ref LOG_FLT_ALL filter is enabled, all log messages will be + * printed. It acts as a wildcard. Setting it to \a 1 means there is no + * filtering. + */ +void log_set_all_filter(struct log_target *target, int all) +{ + if (all) + target->filter_map |= (1 << LOG_FLT_ALL); + else + target->filter_map &= ~(1 << LOG_FLT_ALL); +} + +/*! Enable or disable the use of colored output + * \paramin target Log target to be affected + * \paramin use_color Use color (1) or don't use color (0) + */ +void log_set_use_color(struct log_target *target, int use_color) +{ + target->use_color = use_color; +} + +/*! Enable or disable printing of timestamps while logging + * \paramin target Log target to be affected + * \paramin print_timestamp Enable (1) or disable (0) timestamps + */ +void log_set_print_timestamp(struct log_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +/*! Enable or disable printing of extended timestamps while logging + * \paramin target Log target to be affected + * \paramin print_timestamp Enable (1) or disable (0) timestamps + * + * When both timestamp and extended timestamp is enabled then only + * the extended timestamp will be used. The format of the timestamp + * is YYYYMMDDhhmmssnnn. + */ +void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp) +{ + target->print_ext_timestamp = print_timestamp; +} + +/*! Enable or disable printing of timestamps while logging + * \paramin target Log target to be affected + * \paramin print_tid Enable (1) or disable (0) Thread ID logging + */ +void log_set_print_tid(struct log_target *target, int print_tid) +{ + target->print_tid = print_tid; +} + +/*! Use log_set_print_filename2() instead. + * Call log_set_print_filename2() with LOG_FILENAME_PATH or LOG_FILENAME_NONE, *as well as* call + * log_set_print_category_hex() with the argument passed to this function. This is to mirror legacy + * behavior, which combined the category in hex with the filename. For example, if the category-hex + * output were no longer affected by log_set_print_filename(), many unit tests (in libosmocore as well as + * dependent projects) would fail since they expect the category to disappear along with the filename. + * \paramin target Log target to be affected + * \paramin print_filename Enable (1) or disable (0) filenames + */ +void log_set_print_filename(struct log_target *target, int print_filename) +{ + log_set_print_filename2(target, print_filename ? LOG_FILENAME_PATH : LOG_FILENAME_NONE); + log_set_print_category_hex(target, print_filename); +} + +/*! Enable or disable printing of the filename while logging. + * \paramin target Log target to be affected. + * \paramin lft An LOG_FILENAME_* enum value. + * LOG_FILENAME_NONE omits the source file and line information from logs. + * LOG_FILENAME_PATH prints the entire source file path as passed to LOGP macros. + */ +void log_set_print_filename2(struct log_target *target, enum log_filename_type lft) +{ + target->print_filename2 = lft; +} + +/*! Set the position where on a log line the source file info should be logged. + * \paramin target Log target to be affected. + * \paramin pos A LOG_FILENAME_POS_* enum value. + * LOG_FILENAME_POS_DEFAULT logs just before the caller supplied log message. + * LOG_FILENAME_POS_LAST logs only at the end of a log line, where the caller issued an '\n' to end the + */ +void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos) +{ + target->print_filename_pos = pos; +} + +/*! Enable or disable printing of the category name + * \paramin target Log target to be affected + * \paramin print_category Enable (1) or disable (0) filenames + * + * Print the category/subsys name in front of every log message. + */ +void log_set_print_category(struct log_target *target, int print_category) +{ + target->print_category = print_category; +} + +/*! Enable or disable printing of the category number in hex ('<000b>'). + * \paramin target Log target to be affected. + * \paramin print_category_hex Enable (1) or disable (0) hex category. + */ +void log_set_print_category_hex(struct log_target *target, int print_category_hex) +{ + target->print_category_hex = print_category_hex; +} + +/*! Enable or disable printing of the log level name. + * \paramin target Log target to be affected + * \paramin print_level Enable (1) or disable (0) log level name + * + * Print the log level name in front of every log message. + */ +void log_set_print_level(struct log_target *target, int print_level) +{ + target->print_level = (bool)print_level; +} + +/*! Set the global log level for a given log target + * \paramin target Log target to be affected + * \paramin log_level New global log level + */ +void log_set_log_level(struct log_target *target, int log_level) +{ + target->loglevel = log_level; +} + +/*! Set a category filter on a given log target + * \paramin target Log target to be affected + * \paramin category Log category to be affected + * \paramin enable whether to enable or disable the filter + * \paramin level Log level of the filter + */ +void log_set_category_filter(struct log_target *target, int category, + int enable, int level) +{ + if (!target) + return; + category = map_subsys(category); + target->categoriescategory.enabled = !!enable; + target->categoriescategory.loglevel = level; +} + +#if (!EMBEDDED) +/* write-queue tells us we should write another msgb (log line) to the output fd */ +static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + int rc; + + rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); + if (rc < 0) + return rc; + if (rc != msgb_length(msg)) { + /* pull the number of bytes we have already written */ + msgb_pull(msg, rc); + /* ask write_queue to re-insert the msgb at the head of the queue */ + return -EAGAIN; + } + return 0; +} + +/* output via buffered, blocking stdio streams */ +static void _file_output_stream(struct log_target *target, unsigned int level, + const char *log) +{ + OSMO_ASSERT(target->tgt_file.out); + fputs(log, target->tgt_file.out); + fflush(target->tgt_file.out); +} + +/* output via non-blocking write_queue, doing internal buffering */ +static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file, + int line, int cont, const char *format, va_list ap) +{ + struct msgb *msg; + int rc; + + OSMO_ASSERT(target->tgt_file.wqueue); + msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg"); + if (!msg) + return; + + /* we simply enqueue the log message to a write queue here, to avoid any blocking + * writes on the output file. The write queue will tell us once the file is writable + * and call _file_wq_write_cb() */ + rc = _output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap); + msgb_put(msg, rc); + + /* attempt a synchronous, non-blocking write, if the write queue is empty */ + if (target->tgt_file.wqueue->current_length == 0) { + rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg); + if (rc == 0) { + /* the write was complete, we can exit early */ + msgb_free(msg); + return; + } + } + /* if we reach here, either we already had elements in the write_queue, or the synchronous write + * failed: enqueue the message to the write_queue (backlog) */ + if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) { + msgb_free(msg); + /* TODO: increment some counter so we can see that messages were dropped */ + } +} +#endif + +/*! Create a new log target skeleton + * \returns dynamically-allocated log target + * This funcition allocates a \ref log_target and initializes it + * with some default values. The newly created target is not + * registered yet. + */ +struct log_target *log_target_create(void) +{ + struct log_target *target; + unsigned int i; + + assert_loginfo(__func__); + + target = talloc_zero(tall_log_ctx, struct log_target); + if (!target) + return NULL; + + target->categories = talloc_zero_array(target, + struct log_category, + osmo_log_info->num_cat); + if (!target->categories) { + talloc_free(target); + return NULL; + } + + INIT_LLIST_HEAD(&target->entry); + + /* initialize the per-category enabled/loglevel from defaults */ + for (i = 0; i < osmo_log_info->num_cat; i++) { + struct log_category *cat = &target->categoriesi; + cat->enabled = osmo_log_info->cati.enabled; + cat->loglevel = osmo_log_info->cati.loglevel; + } + + /* global settings */ + target->use_color = 1; + target->print_timestamp = 0; + target->print_tid = 0; + target->print_filename2 = LOG_FILENAME_PATH; + target->print_category_hex = true; + + /* global log level */ + target->loglevel = 0; + return target; +} + +/*! Create the STDERR log target + * \returns dynamically-allocated \ref log_target for STDERR */ +struct log_target *log_target_create_stderr(void) +{ +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#if !EMBEDDED && defined(stderr) + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->type = LOG_TGT_TYPE_STDERR; + target->tgt_file.out = stderr; + target->output = _file_output_stream; + return target; +#else + return NULL; +#endif /* stderr */ +} + +#if (!EMBEDDED) +/*! Create a new file-based log target using buffered, blocking stream output + * \paramin fname File name of the new log file + * \returns Log target in case of success, NULL otherwise + */ +struct log_target *log_target_create_file_stream(const char *fname) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->type = LOG_TGT_TYPE_FILE; + target->tgt_file.out = fopen(fname, "a"); + if (!target->tgt_file.out) { + log_target_destroy(target); + return NULL; + } + target->output = _file_output_stream; + target->tgt_file.fname = talloc_strdup(target, fname); + + return target; +} + +/*! switch from non-blocking/write-queue to blocking + buffered stream output + * \paramin target log target which we should switch + * \return 0 on success; 1 if already switched before; negative on error + * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. + */ +int log_target_file_switch_to_stream(struct log_target *target) +{ + struct osmo_wqueue *wq; + + if (!target) + return -ENODEV; + + if (target->tgt_file.out) { + /* target has already been switched over */ + return 1; + } + + wq = target->tgt_file.wqueue; + OSMO_ASSERT(wq); + + /* re-open output as stream */ + if (target->type == LOG_TGT_TYPE_STDERR) + target->tgt_file.out = stderr; + else + target->tgt_file.out = fopen(target->tgt_file.fname, "a"); + if (!target->tgt_file.out) { + return -EIO; + } + + /* synchronously write anything left in the queue */ + while (!llist_empty(&wq->msg_queue)) { + struct msgb *msg = msgb_dequeue(&wq->msg_queue); + fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out); + msgb_free(msg); + } + + /* now that everything succeeded, we can finally close the old output fd */ + if (target->type == LOG_TGT_TYPE_FILE) { + osmo_fd_unregister(&wq->bfd); + close(wq->bfd.fd); + } + + /* release the queue itself */ + talloc_free(wq); + target->tgt_file.wqueue = NULL; + target->output = _file_output_stream; + target->raw_output = NULL; + + return 0; +} + +/*! switch from blocking + buffered file output to non-blocking write-queue based output. + * \paramin target log target which we should switch + * \return 0 on success; 1 if already switched before; negative on error + * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. + */ +int log_target_file_switch_to_wqueue(struct log_target *target) +{ + struct osmo_wqueue *wq; + int rc; + + if (!target) + return -ENODEV; + + if (!target->tgt_file.out) { + /* target has already been switched over */ + return 1; + } + + /* we create a ~640kB sized talloc pool within the write-queue to ensure individual + * log lines (stored as msgbs) will not put result in malloc() calls, and also to + * reduce the OOM probability within logging, as the pool is already allocated */ + wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, + LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); + if (!wq) + return -ENOMEM; + osmo_wqueue_init(wq, LOG_WQUEUE_LEN); + + fflush(target->tgt_file.out); + if (target->type == LOG_TGT_TYPE_FILE) { + rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); + if (rc < 0) { + talloc_free(wq); + return -errno; + } + } else { + rc = STDERR_FILENO; + } + wq->bfd.fd = rc; + wq->bfd.when = OSMO_FD_WRITE; + wq->write_cb = _file_wq_write_cb; + + rc = osmo_fd_register(&wq->bfd); + if (rc < 0) { + talloc_free(wq); + return -EIO; + } + target->tgt_file.wqueue = wq; + target->raw_output = _file_raw_output; + target->output = NULL; + + /* now that everything succeeded, we can finally close the old output stream */ + if (target->type == LOG_TGT_TYPE_FILE) + fclose(target->tgt_file.out); + target->tgt_file.out = NULL; + + return 0; +} + +/*! Create a new file-based log target using non-blocking write_queue + * \paramin fname File name of the new log file + * \returns Log target in case of success, NULL otherwise + */ +struct log_target *log_target_create_file(const char *fname) +{ + struct log_target *target; + struct osmo_wqueue *wq; + int rc; + + target = log_target_create(); + if (!target) + return NULL; + + target->type = LOG_TGT_TYPE_FILE; + /* we create a ~640kB sized talloc pool within the write-queue to ensure individual + * log lines (stored as msgbs) will not put result in malloc() calls, and also to + * reduce the OOM probability within logging, as the pool is already allocated */ + wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, + LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); + if (!wq) { + log_target_destroy(target); + return NULL; + } + osmo_wqueue_init(wq, LOG_WQUEUE_LEN); + wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); + if (wq->bfd.fd < 0) { + talloc_free(wq); + log_target_destroy(target); + return NULL; + } + wq->bfd.when = OSMO_FD_WRITE; + wq->write_cb = _file_wq_write_cb; + + rc = osmo_fd_register(&wq->bfd); + if (rc < 0) { + talloc_free(wq); + log_target_destroy(target); + return NULL; + } + + target->tgt_file.wqueue = wq; + target->raw_output = _file_raw_output; + target->tgt_file.fname = talloc_strdup(target, fname); + + return target; +} +#endif + +/*! Find a registered log target + * \paramin type Log target type + * \paramin fname File name + * \returns Log target (if found), NULL otherwise + * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock. + */ +struct log_target *log_target_find(enum log_target_type type, const char *fname) +{ + struct log_target *tgt; + + llist_for_each_entry(tgt, &osmo_log_target_list, entry) { + if (tgt->type != type) + continue; + switch (tgt->type) { + case LOG_TGT_TYPE_FILE: + if (!strcmp(fname, tgt->tgt_file.fname)) + return tgt; + break; + case LOG_TGT_TYPE_GSMTAP: + if (!strcmp(fname, tgt->tgt_gsmtap.hostname)) + return tgt; + break; + default: + return tgt; + } + } + return NULL; +} + +/*! Unregister, close and delete a log target + * \paramin target log target to unregister, close and delete */ +void log_target_destroy(struct log_target *target) +{ + /* just in case, to make sure we don't have any references */ + log_del_target(target); + +#if (!EMBEDDED) + struct osmo_wqueue *wq; + switch (target->type) { + case LOG_TGT_TYPE_FILE: + case LOG_TGT_TYPE_STDERR: + if (target->tgt_file.out) { + if (target->type == LOG_TGT_TYPE_FILE) + fclose(target->tgt_file.out); + target->tgt_file.out = NULL; + } + wq = target->tgt_file.wqueue; + if (wq) { + if (wq->bfd.fd >= 0) { + if (target->type == LOG_TGT_TYPE_FILE) + close(wq->bfd.fd); + wq->bfd.fd = -1; + } + osmo_fd_unregister(&wq->bfd); + osmo_wqueue_clear(wq); + talloc_free(wq); + target->tgt_file.wqueue = NULL; + } + talloc_free((void *)target->tgt_file.fname); + target->tgt_file.fname = NULL; + break; + case LOG_TGT_TYPE_GSMTAP: + gsmtap_source_free(target->tgt_gsmtap.gsmtap_inst); + break; +#ifdef HAVE_SYSLOG_H + case LOG_TGT_TYPE_SYSLOG: + closelog(); + break; +#endif /* HAVE_SYSLOG_H */ + default: + /* make GCC happy */ + break; + } +#endif + + talloc_free(target); +} + +/*! close and re-open a log file (for log file rotation) + * \paramin target log target to re-open + * \returns 0 in case of success; negative otherwise */ +int log_target_file_reopen(struct log_target *target) +{ + struct osmo_wqueue *wq; + int rc; + + OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR); + OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue); + + if (target->tgt_file.out) { + fclose(target->tgt_file.out); + target->tgt_file.out = fopen(target->tgt_file.fname, "a"); + if (!target->tgt_file.out) + return -errno; + } else { + wq = target->tgt_file.wqueue; + osmo_fd_unregister(&wq->bfd); + if (wq->bfd.fd >= 0) { + close(wq->bfd.fd); + wq->bfd.fd = -1; + } + + rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); + if (rc < 0) + return -errno; + wq->bfd.fd = rc; + rc = osmo_fd_register(&wq->bfd); + if (rc < 0) + return rc; + } + + return 0; +} + +/*! close and re-open all log files (for log file rotation) + * \returns 0 in case of success; negative otherwise */ +int log_targets_reopen(void) +{ + struct log_target *tar; + int rc = 0; + + log_tgt_mutex_lock(); + + llist_for_each_entry(tar, &osmo_log_target_list, entry) { + switch (tar->type) { + case LOG_TGT_TYPE_FILE: + if (log_target_file_reopen(tar) < 0) + rc = -1; + break; + default: + break; + } + } + + log_tgt_mutex_unlock(); + + return rc; +} + +/*! Initialize the Osmocom logging core + * \paramin inf Information regarding logging categories, could be NULL + * \paramin ctx talloc context for logging allocations + * \returns 0 in case of success, negative in case of error + * + * If inf is NULL then only library-internal categories are initialized. + */ +int log_init(const struct log_info *inf, void *ctx) +{ + int i; + struct log_info_cat *cat_ptr; + + /* Ensure that log_init is not called multiple times */ + OSMO_ASSERT(tall_log_ctx == NULL) + + tall_log_ctx = talloc_named_const(ctx, 1, "logging"); + if (!tall_log_ctx) + return -ENOMEM; + + osmo_log_info = talloc_zero(tall_log_ctx, struct log_info); + if (!osmo_log_info) + return -ENOMEM; + + osmo_log_info->num_cat = ARRAY_SIZE(internal_cat); + + if (inf) { + osmo_log_info->filter_fn = inf->filter_fn; + osmo_log_info->num_cat_user = inf->num_cat; + osmo_log_info->num_cat += inf->num_cat; + } + + cat_ptr = talloc_zero_array(osmo_log_info, struct log_info_cat, + osmo_log_info->num_cat); + if (!cat_ptr) { + talloc_free(osmo_log_info); + osmo_log_info = NULL; + return -ENOMEM; + } + + /* copy over the user part and sanitize loglevel */ + if (inf) { + for (i = 0; i < inf->num_cat; i++) { + memcpy(&cat_ptri, &inf->cati, + sizeof(struct log_info_cat)); + + /* Make sure that the loglevel is set to NOTICE in case + * no loglevel has been preset. */ + if (!cat_ptri.loglevel) { + cat_ptri.loglevel = LOGL_NOTICE; + } + } + } + + /* copy over the library part */ + for (i = 0; i < ARRAY_SIZE(internal_cat); i++) { + unsigned int cn = osmo_log_info->num_cat_user + i; + memcpy(&cat_ptrcn, &internal_cati, sizeof(struct log_info_cat)); + } + + osmo_log_info->cat = cat_ptr; + + return 0; +} + +/* De-initialize the Osmocom logging core + * This function destroys all targets and releases associated memory */ +void log_fini(void) +{ + struct log_target *tar, *tar2; + + log_tgt_mutex_lock(); + + llist_for_each_entry_safe(tar, tar2, &osmo_log_target_list, entry) + log_target_destroy(tar); + + talloc_free(osmo_log_info); + osmo_log_info = NULL; + talloc_free(tall_log_ctx); + tall_log_ctx = NULL; + + log_tgt_mutex_unlock(); +} + +/*! Check whether a log entry will be generated. + * \returns != 0 if a log entry might get generated by at least one target */ +int log_check_level(int subsys, unsigned int level) +{ + struct log_target *tar; + + assert_loginfo(__func__); + + subsys = map_subsys(subsys); + + /* TODO: The following could/should be cached (update on config) */ + + log_tgt_mutex_lock(); + + llist_for_each_entry(tar, &osmo_log_target_list, entry) { + if (!should_log_to_target(tar, subsys, level)) + continue; + + /* This might get logged (ignoring filters) */ + log_tgt_mutex_unlock(); + return 1; + } + + /* We are sure, that this will not be logged. */ + log_tgt_mutex_unlock(); + return 0; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/logging_gsmtap.c
Added
@@ -0,0 +1,161 @@ +/*! \file logging_gsmtap.c + * libosmocore log output encapsulated in GSMTAP. + * + * Encapsulating the log output inside GSMTAP frames allows us to + * observer protocol traces (of Um, Abis, A or any other interface in + * the Osmocom world) with synchronous interspersed log messages. + */ +/* + * (C) 2016 by Harald Welte <laforge@gnumonks.org> + * 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. + * + */ + +/*! \addtogroup logging + * @{ + * \file logging_gsmtap.c */ + +#include "config.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/byteswap.h> +#include <osmocom/core/thread.h> + +#define GSMTAP_LOG_MAX_SIZE 4096 + +static __thread uint32_t logging_gsmtap_tid; + +static void _gsmtap_raw_output(struct log_target *target, int subsys, + unsigned int level, const char *file, + int line, int cont, const char *format, + va_list ap) +{ + struct msgb *msg; + struct gsmtap_hdr *gh; + struct gsmtap_osmocore_log_hdr *golh; + const char *subsys_name = log_category_name(subsys); + struct timeval tv; + int rc; + const char *file_basename; + + /* get timestamp ASAP */ + osmo_gettimeofday(&tv, NULL); + + msg = msgb_alloc(sizeof(*gh)+sizeof(*golh)+GSMTAP_LOG_MAX_SIZE, + "GSMTAP logging"); + + /* GSMTAP header */ + gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); + memset(gh, 0, sizeof(*gh)); + gh->version = GSMTAP_VERSION; + gh->hdr_len = sizeof(*gh)/4; + gh->type = GSMTAP_TYPE_OSMOCORE_LOG; + + /* Logging header */ + golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh)); + OSMO_STRLCPY_ARRAY(golh->proc_name, target->tgt_gsmtap.ident); + if (logging_gsmtap_tid == 0) + osmo_store32be((uint32_t)osmo_gettid(), &logging_gsmtap_tid); + golh->pid = logging_gsmtap_tid; + if (subsys_name) + OSMO_STRLCPY_ARRAY(golh->subsys, subsys_name + 1); + else + golh->subsys0 = '\0'; + + /* strip all leading path elements from file, if any. */ + file_basename = strrchr(file, '/'); + file = (file_basename && file_basename1)? file_basename + 1 : file; + OSMO_STRLCPY_ARRAY(golh->src_file.name, file); + golh->src_file.line_nr = osmo_htonl(line); + golh->level = level; + /* we always store the timestamp in the message, irrespective + * of hat prrint_ext_timestamp say */ + golh->ts.sec = osmo_htonl(tv.tv_sec); + golh->ts.usec = osmo_htonl(tv.tv_usec); + + rc = vsnprintf((char *) msg->tail, msgb_tailroom(msg), format, ap); + if (rc < 0) { + msgb_free(msg); + return; + } else if (rc >= msgb_tailroom(msg)) { + /* If the output was truncated, vsnprintf() returns the + * number of characters which would have been written + * if enough space had been available (excluding '\0'). */ + rc = msgb_tailroom(msg); + msg->tailrc - 1 = '\0'; + } + msgb_put(msg, rc); + + rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg); + if (rc) + msgb_free(msg); +} + +/*! Create a new logging target for GSMTAP logging + * \paramin host remote host to send the logs to + * \paramin port remote port to send the logs to + * \paramin ident string identifier + * \paramin ofd_wq_mode register osmo_wqueue (1) or not (0) + * \paramin add_sink add GSMTAP sink or not + * \returns Log target in case of success, NULL in case of error + */ +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 *target; + struct gsmtap_inst *gti; + + target = log_target_create(); + if (!target) + return NULL; + + gti = gsmtap_source_init(host, port, ofd_wq_mode); + if (!gti) { + log_target_destroy(target); + return NULL; + } + + if (add_sink) + gsmtap_source_add_sink(gti); + + target->tgt_gsmtap.gsmtap_inst = gti; + target->tgt_gsmtap.ident = talloc_strdup(target, ident); + target->tgt_gsmtap.hostname = talloc_strdup(target, host); + + target->type = LOG_TGT_TYPE_GSMTAP; + target->raw_output = _gsmtap_raw_output; + + return target; +} + +/* @} */
View file
libosmocore_1.8.0.tar.xz/src/core/logging_syslog.c
Added
@@ -0,0 +1,89 @@ +/*! \file logging_syslog.c + * Syslog logging support code. */ +/* + * (C) 2011 by Harald Welte <laforge@gnumonks.org> + * 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. + * + */ + +/*! \addtogroup logging + * @{ + * \file logging_syslog.c */ + +#include "config.h" + +#ifdef HAVE_SYSLOG_H + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> + +static int logp2syslog_level(unsigned int level) +{ + if (level >= LOGL_FATAL) + return LOG_CRIT; + else if (level >= LOGL_ERROR) + return LOG_ERR; + else if (level >= LOGL_NOTICE) + return LOG_NOTICE; + else if (level >= LOGL_INFO) + return LOG_INFO; + else + return LOG_DEBUG; +} + +static void _syslog_output(struct log_target *target, + unsigned int level, const char *log) +{ + syslog(logp2syslog_level(level), "%s", log); +} + +/*! Create a new logging target for syslog logging + * \paramin ident syslog string identifier + * \paramin option syslog options + * \paramin facility syslog facility + * \returns Log target in case of success, NULL in case of error + */ +struct log_target *log_target_create_syslog(const char *ident, int option, + int facility) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->tgt_syslog.facility = facility; + target->type = LOG_TGT_TYPE_SYSLOG; + target->output = _syslog_output; + + openlog(ident, option, facility); + + return target; +} + +#endif /* HAVE_SYSLOG_H */ + +/* @} */
View file
libosmocore_1.8.0.tar.xz/src/core/logging_systemd.c
Changed
(renamed from src/logging_systemd.c)
View file
libosmocore_1.8.0.tar.xz/src/core/loggingrb.c
Changed
(renamed from src/loggingrb.c)
View file
libosmocore_1.8.0.tar.xz/src/core/macaddr.c
Changed
(renamed from src/macaddr.c)
View file
libosmocore_1.8.0.tar.xz/src/core/mnl.c
Added
@@ -0,0 +1,111 @@ +/*! \file mnl.c + * + * This code integrates libmnl (minimal netlink library) into the osmocom select + * loop abstraction. It allows other osmocom libraries or application code to + * create netlink sockets and subscribe to netlink events via libmnl. The completion + * handler / callbacks are dispatched via libosmocore select loop handling. + */ + +/* + * (C) 2020 by Harald Welte <laforge@gnumonks.org> + * 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 <osmocom/core/select.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/mnl.h> + +#include <libmnl/libmnl.h> + +#include <errno.h> +#include <string.h> + +/* osmo_fd call-back for when RTNL socket is readable */ +static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + uint8_t bufMNL_SOCKET_BUFFER_SIZE; + struct osmo_mnl *omnl = ofd->data; + int rc; + + if (!(what & OSMO_FD_READ)) + return 0; + + rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf)); + if (rc <= 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n", + strerror(errno)); + return -EIO; + } + + return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl); +} + +/*! create an osmocom-wrapped limnl netlink socket. + * \parmain ctx talloc context from which to allocate + * \paramin bus netlink socket bus ID (see NETLINK_* constants) + * \paramin groups groups of messages to bind/subscribe to + * \paramin mnl_cb callback function called for each incoming message + * \paramin priv opaque private user data + * \returns newly-allocated osmo_mnl or NULL in case of error. */ +struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv) +{ + struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl); + + if (!olm) + return NULL; + + olm->priv = priv; + olm->mnl_cb = mnl_cb; + olm->mnls = mnl_socket_open(bus); + if (!olm->mnls) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n", + bus, strerror(errno)); + goto out_free; + } + + if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n", + bus, groups, strerror(errno)); + goto out_close; + } + + osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0); + + if (osmo_fd_register(&olm->ofd)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n"); + goto out_close; + } + + return olm; + +out_close: + mnl_socket_close(olm->mnls); +out_free: + talloc_free(olm); + return NULL; +} + +/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free. + * \paramin omnl osmo_mnl socket previously returned by osmo_mnl_init() */ +void osmo_mnl_destroy(struct osmo_mnl *omnl) +{ + if (!omnl) + return; + + osmo_fd_unregister(&omnl->ofd); + mnl_socket_close(omnl->mnls); + talloc_free(omnl); +}
View file
libosmocore_1.8.0.tar.xz/src/core/msgb.c
Added
@@ -0,0 +1,607 @@ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * 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. + * + */ + +/*! \addtogroup msgb + * @{ + * + * libosmocore message buffers, inspired by Linux kernel skbuff + * + * Inspired by the 'struct skbuff' of the Linux kernel, we implement a + * 'struct msgb' which we use for handling network + * packets aka messages aka PDUs. + * + * A msgb consists of + * * a header with some metadata, such as + * * a linked list header for message queues or the like + * * pointers to the headers of various protocol layers inside + * the packet + * * a data section consisting of + * * headroom, i.e. space in front of the message, to allow + * for additional headers being pushed in front of the current + * data + * * the currently occupied data for the message + * * tailroom, i.e. space at the end of the message, to + * allow more data to be added after the end of the current + * data + * + * We have plenty of utility functions around the \ref msgb: + * * allocation / release + * * enqueue / dequeue from/to message queues + * * prepending (pushing) and appending (putting) data + * * copying / resizing + * * hex-dumping to a string for debug purposes + * + * \file msgb.c + */ + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <stdarg.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +/*! Allocate a new message buffer from given talloc context + * \paramin ctx talloc context from which to allocate + * \paramin size Length in octets, including headroom + * \paramin name Human-readable name to be associated with msgb + * \returns dynamically-allocated \ref msgb + * + * This function allocates a 'struct msgb' as well as the underlying + * memory buffer for the actual message data (size specified by \a size) + * using the talloc memory context previously set by \ref msgb_set_talloc_ctx + */ +struct msgb *msgb_alloc_c(const void *ctx, uint16_t size, const char *name) +{ + struct msgb *msg; + + msg = talloc_named_const(ctx, sizeof(*msg) + size, name); + if (!msg) { + LOGP(DLGLOBAL, LOGL_FATAL, "Unable to allocate a msgb: " + "name='%s', size=%u\n", name, size); + return NULL; + } + + /* Manually zero-initialize allocated memory */ + memset(msg, 0x00, sizeof(*msg) + size); + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + msg->head = msg->_data; + msg->tail = msg->_data; + + return msg; +} + +/* default msgb allocation context for msgb_alloc() */ +void *tall_msgb_ctx = NULL; + +/*! Allocate a new message buffer from tall_msgb_ctx + * \paramin size Length in octets, including headroom + * \paramin name Human-readable name to be associated with msgb + * \returns dynamically-allocated \ref msgb + * + * This function allocates a 'struct msgb' as well as the underlying + * memory buffer for the actual message data (size specified by \a size) + * using the talloc memory context previously set by \ref msgb_set_talloc_ctx + */ +struct msgb *msgb_alloc(uint16_t size, const char *name) +{ + return msgb_alloc_c(tall_msgb_ctx, size, name); +} + + +/*! Release given message buffer + * \paramin m Message buffer to be freed + */ +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +/*! Enqueue message buffer to tail of a queue + * \paramin queue linked list header of queue + * \paramin msg message buffer to be added to the queue + * + * The function will append the specified message buffer \a msg to the + * queue implemented by \ref llist_head \a queue + */ +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +/*! Dequeue message buffer from head of queue + * \paramin queue linked list header of queue + * \returns message buffer (if any) or NULL if queue empty + * + * The function will remove the first message buffer from the queue + * implemented by \ref llist_head \a queue. + */ +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + + if (lh) { + llist_del(lh); + return llist_entry(lh, struct msgb, list); + } else + return NULL; +} + +/*! Re-set all message buffer pointers + * \paramin msg message buffer that is to be resetted + * + * This will re-set the various internal pointers into the underlying + * message buffer, i.e. remove all headroom and treat the msgb as + * completely empty. It also initializes the control buffer to zero. + */ +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->data = msg->_data; + msg->head = msg->_data; + msg->tail = msg->_data; + + msg->trx = NULL; + msg->lchan = NULL; + msg->l2h = NULL; + msg->l3h = NULL; + msg->l4h = NULL; + + memset(&msg->cb, 0, sizeof(msg->cb)); +} + +/*! get pointer to data section of message buffer + * \paramin msg message buffer + * \returns pointer to data section of message buffer + */ +uint8_t *msgb_data(const struct msgb *msg) +{ + return msg->data; +} + +/*! Compare and print: check data in msgb against given data and print errors if any + * \paramin file text prefix, usually __FILE__, ignored if print == false + * \paramin line numeric prefix, usually __LINE__, ignored if print == false + * \paramin func text prefix, usually __func__, ignored if print == false + * \paramin level while layer (L1, L2 etc) data should be compared against + * \paramin msg message buffer + * \paramin data expected data + * \paramin len length of data + * \paramin print boolean indicating whether we should print anything to stdout + * \returns boolean indicating whether msgb content is equal to a given data + * + * This function is not intended to be called directly but rather used through corresponding macro wrappers. + */ +bool _msgb_eq(const char *file, size_t line, const char *func, uint8_t level, + const struct msgb *msg, const uint8_t *data, size_t len, bool print) +{ + const char *m_dump; + unsigned int m_len, i; + uint8_t *m_data; + + if (!msg) { + if (print) + LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, "%s() NULL msg comparison\n", func); + return false; + } + + if (!data) { + if (print) + LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, "%s() NULL comparison data\n", func); + return false; + } + + switch (level) { + case 0: + m_len = msgb_length(msg); + m_data = msgb_data(msg); + m_dump = print ? msgb_hexdump(msg) : NULL; + break; + case 1: + m_len = msgb_l1len(msg); + m_data = msgb_l1(msg); + m_dump = print ? msgb_hexdump_l1(msg) : NULL; + break; + case 2: + m_len = msgb_l2len(msg); + m_data = msgb_l2(msg); + m_dump = print ? msgb_hexdump_l2(msg) : NULL; + break; + case 3: + m_len = msgb_l3len(msg); + m_data = msgb_l3(msg); + m_dump = print ? msgb_hexdump_l3(msg) : NULL; + break; + case 4: + m_len = msgb_l4len(msg); + m_data = msgb_l4(msg); + m_dump = print ? msgb_hexdump_l4(msg) : NULL; + break; + default: + LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, + "%s() FIXME: unexpected comparison level %u\n", func, level); + return false; + } + + if (m_len != len) { + if (print) + LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, + "%s() Length mismatch: %d != %zu, %s\n", func, m_len, len, m_dump); + return false; + } + + if (memcmp(m_data, data, len) == 0) + return true; + + if (!print) + return false; + + LOGPSRC(DLGLOBAL, LOGL_FATAL, file, line, + "%s() L%u data mismatch:\nexpected %s\n ", func, level, osmo_hexdump(data, len)); + + for(i = 0; i < len; i++) + if (datai != m_datai) { + LOGPC(DLGLOBAL, LOGL_FATAL, "!!\n"); + break; + } else + LOGPC(DLGLOBAL, LOGL_FATAL, ".. "); + + LOGPC(DLGLOBAL, LOGL_FATAL, " msgb %s\n", osmo_hexdump(m_data, len)); + + return false; +} + +/*! get length of message buffer + * \paramin msg message buffer + * \returns length of data section in message buffer + */ +uint16_t msgb_length(const struct msgb *msg) +{ + return msg->len; +} + +/*! Set the talloc context for \ref msgb_alloc + * Deprecated, use msgb_talloc_ctx_init() instead. + * \paramin ctx talloc context to be used as root for msgb allocations + */ +void msgb_set_talloc_ctx(void *ctx) +{ + tall_msgb_ctx = ctx; +} + +/*! Initialize a msgb talloc context for \ref msgb_alloc. + * Create a talloc context called "msgb". If \a pool_size is 0, create a named + * const as msgb talloc context. If \a pool_size is nonzero, create a talloc + * pool, possibly for faster msgb allocations (see talloc_pool()). + * \paramin root_ctx talloc context used as parent for the new "msgb" ctx. + * \paramin pool_size if nonzero, create a talloc pool of this size. + * \returns the new msgb talloc context, e.g. for reporting + */ +void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size) +{ + if (!pool_size) + tall_msgb_ctx = talloc_size(root_ctx, 0); + else + tall_msgb_ctx = talloc_pool(root_ctx, pool_size); + talloc_set_name_const(tall_msgb_ctx, "msgb"); + return tall_msgb_ctx; +} + +/*! Copy an msgb with memory reallocation. + * + * This function allocates a new msgb with new_len size, copies the data buffer of msg, + * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied. + * \paramin ctx talloc context on which allocation happens + * \paramin msg The old msgb object + * \paramin new_len The length of new msgb object + * \paramin name Human-readable name to be associated with new msgb + */ +struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name) +{ + struct msgb *new_msg; + + if (new_len < msgb_length(msg)) { + LOGP(DLGLOBAL, LOGL_ERROR, + "Data from old msgb (%u bytes) won't fit into new msgb (%u bytes) after reallocation\n", + msgb_length(msg), new_len); + return NULL; + } + + new_msg = msgb_alloc_c(ctx, new_len, name); + if (!new_msg) + return NULL; + + /* copy header */ + new_msg->len = msg->len; + new_msg->data += msg->data - msg->_data; + new_msg->head += msg->head - msg->_data; + new_msg->tail += msg->tail - msg->_data; + + /* copy data */ + memcpy(new_msg->data, msg->data, msgb_length(msg)); + + if (msg->l1h) + new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); + if (msg->l2h) + new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); + if (msg->l3h) + new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); + if (msg->l4h) + new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data); + + return new_msg; +} + +/*! Copy an msgb with memory reallocation. + * + * This function allocates a new msgb with new_len size, copies the data buffer of msg, + * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied. + * \paramin msg The old msgb object + * \paramin name Human-readable name to be associated with new msgb + */ +struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name) +{ + return msgb_copy_resize_c(tall_msgb_ctx, msg, new_len, name); +} + +/*! Copy an msgb. + * + * This function allocates a new msgb, copies the data buffer of msg, + * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part + * is not copied. + * \paramin ctx talloc context on which allocation happens + * \paramin msg The old msgb object + * \paramin name Human-readable name to be associated with msgb + */ +struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name) +{ + return msgb_copy_resize_c(ctx, msg, msg->data_len, name); +} + +/*! Copy an msgb. + * + * This function allocates a new msgb, copies the data buffer of msg, + * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part + * is not copied. + * \paramin msg The old msgb object + * \paramin name Human-readable name to be associated with msgb + */ +struct msgb *msgb_copy(const struct msgb *msg, const char *name) +{ + return msgb_copy_c(tall_msgb_ctx, msg, name); +} + +/*! Resize an area within an msgb + * + * This resizes a sub area of the msgb data and adjusts the pointers (incl + * l1h-l4h) accordingly. The cb part is not updated. If the area is extended, + * the contents of the extension is undefined. The complete sub area must be a + * part of data,tail. + * + * \paraminout msg The msgb object + * \paramin area A pointer to the sub-area + * \paramin old_size The old size of the sub-area + * \paramin new_size The new size of the sub-area + * \returns 0 on success, -1 if there is not enough space to extend the area + */ +int msgb_resize_area(struct msgb *msg, uint8_t *area, + int old_size, int new_size) +{ + int rc; + uint8_t *post_start = area + old_size; + int pre_len = area - msg->data; + int post_len = msg->len - old_size - pre_len; + int delta_size = new_size - old_size; + + if (old_size < 0 || new_size < 0) + MSGB_ABORT(msg, "Negative sizes are not allowed\n"); + if (area < msg->data || post_start > msg->tail) + MSGB_ABORT(msg, "Sub area is not fully contained in the msg data\n"); + + if (delta_size == 0) + return 0; + + if (delta_size > 0) { + rc = msgb_trim(msg, msg->len + delta_size); + if (rc < 0) + return rc; + } + + memmove(area + new_size, area + old_size, post_len); + + if (msg->l1h >= post_start) + msg->l1h += delta_size; + if (msg->l2h >= post_start) + msg->l2h += delta_size; + if (msg->l3h >= post_start) + msg->l3h += delta_size; + if (msg->l4h >= post_start) + msg->l4h += delta_size; + + if (delta_size < 0) + msgb_trim(msg, msg->len + delta_size); + + return 0; +} + + +/*! fill user-provided buffer with hexdump of the msg. + * \paramout buf caller-allocated buffer for output string + * \paramin buf_len length of buf + * \paramin msg message buffer to be dumped + * \returns buf + */ +char *msgb_hexdump_buf(char *buf, size_t buf_len, const struct msgb *msg) +{ + unsigned int buf_offs = 0; + int nchars; + const unsigned char *start = msg->data; + const unsigned char *lxhs4; + unsigned int i; + + lxhs0 = msg->l1h; + lxhs1 = msg->l2h; + lxhs2 = msg->l3h; + lxhs3 = msg->l4h; + + for (i = 0; i < ARRAY_SIZE(lxhs); i++) { + if (!lxhsi) + continue; + + if (lxhsi < msg->head) + continue; + if (lxhsi > msg->head + msg->data_len) + continue; + if (lxhsi > msg->tail) + continue; + if (lxhsi < msg->data || lxhsi > msg->tail) { + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "(L%d=data%+" PRIdPTR ") ", + i+1, lxhsi - msg->data); + buf_offs += nchars; + continue; + } + if (lxhsi < start) { + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "(L%d%+" PRIdPTR ") ", i+1, + start - lxhsi); + buf_offs += nchars; + continue; + } + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "%sL%d> ", + osmo_hexdump(start, lxhsi - start), + i+1); + if (nchars < 0 || nchars + buf_offs >= buf_len) + return "ERROR"; + + buf_offs += nchars; + start = lxhsi; + } + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "%s", osmo_hexdump(start, msg->tail - start)); + if (nchars < 0 || nchars + buf_offs >= buf_len) + return "ERROR"; + + buf_offs += nchars; + + for (i = 0; i < ARRAY_SIZE(lxhs); i++) { + if (!lxhsi) + continue; + + if (lxhsi < msg->head || lxhsi > msg->head + msg->data_len) { + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "(L%d out of range) ", i+1); + } else if (lxhsi <= msg->data + msg->data_len && + lxhsi > msg->tail) { + nchars = snprintf(buf + buf_offs, buf_len - buf_offs, + "(L%d=tail%+" PRIdPTR ") ", + i+1, lxhsi - msg->tail); + } else + continue; + + if (nchars < 0 || nchars + buf_offs >= buf_len) + return "ERROR"; + buf_offs += nchars; + } + + return buf; +} + +/*! Return a (static) buffer containing a hexdump of the msg. + * \paramin msg message buffer + * \returns a pointer to a static char array + */ +const char *msgb_hexdump(const struct msgb *msg) +{ + static __thread char buf4100; + return msgb_hexdump_buf(buf, sizeof(buf), msg); +} + +/*! Return a dynamically allocated buffer containing a hexdump of the msg + * \paramin ctx talloc context from where to allocate the output string + * \paramin msg message buffer + * \returns a pointer to a static char array + */ +char *msgb_hexdump_c(const void *ctx, const struct msgb *msg) +{ + size_t buf_len = msgb_length(msg) * 3 + 100; + char *buf = talloc_size(ctx, buf_len); + if (!buf) + return NULL; + return msgb_hexdump_buf(buf, buf_len, msg); +} + +/*! Print a string to the end of message buffer. + * \paramin msgb message buffer. + * \paramin format format string. + * \returns 0 on success, -EINVAL on error. + * + * The resulting string is printed to the msgb without a trailing nul + * character. A nul following the data tail may be written as an implementation + * detail, but a trailing nul is never part of the msgb data in terms of + * msgb_length(). + * + * Note: the tailroom must always be one byte longer than the string to be + * written. The msgb is filled only up to tailroom=1. This is an implementation + * detail that allows leaving a nul character behind the valid data. + * + * In case of error, the msgb remains unchanged, though data may have been + * written to the (unused) memory after the tail pointer. + */ +int msgb_printf(struct msgb *msgb, const char *format, ...) +{ + va_list args; + int str_len; + int rc = 0; + + OSMO_ASSERT(msgb); + OSMO_ASSERT(format); + + /* Regardless of what we plan to add to the buffer, we must at least + * be able to store a string terminator (nullstring) */ + if (msgb_tailroom(msgb) < 1) + return -EINVAL; + + va_start(args, format); + + str_len = + vsnprintf((char *)msgb->tail, msgb_tailroom(msgb), format, args); + + if (str_len >= msgb_tailroom(msgb) || str_len < 0) { + rc = -EINVAL; + } else + msgb_put(msgb, str_len); + + va_end(args); + return rc; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/msgfile.c
Changed
(renamed from src/msgfile.c)
View file
libosmocore_1.8.0.tar.xz/src/core/netdev.c
Added
@@ -0,0 +1,962 @@ + +/* network device (interface) functions. + * (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de> + * Author: Pau Espin Pedrol <pespin@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 "config.h" + +/*! \addtogroup netdev + * @{ + * network device (interface) convenience functions + * + * \file netdev.c + * + * Example lifecycle use of the API: + * + * struct osmo_sockaddr_str osa_str = {}; + * struct osmo_sockaddr osa = {}; + * + * // Allocate object: + * struct osmo_netdev *netdev = osmo_netdev_alloc(parent_talloc_ctx, name); + * OSMO_ASSERT(netdev); + * + * // Configure object (before registration): + * rc = osmo_netdev_set_netns_name(netdev, "some_netns_name_or_null"); + * rc = osmo_netdev_set_ifindex(netdev, if_nametoindex("eth0")); + * + * // Register object: + * rc = osmo_netdev_register(netdev); + * // The network interface is now being monitored and the network interface + * // can be operated (see below) + * + * // Add a local IPv4 address: + * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1"); + * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas); + * rc = osmo_netdev_add_addr(netdev, &osa, 24); + * + * // Bring network interface up: + * rc = osmo_netdev_ifupdown(netdev, true); + * + * // Add default route (0.0.0.0/0): + * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0"); + * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas); + * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL); + * + * // Unregister (can be freed directly too): + * rc = osmo_netdev_unregister(netdev); + * // Free the object: + * osmo_netdev_free(netdev); + */ + +#if (!EMBEDDED) + +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <net/if.h> +#include <net/route.h> + +#if defined(__linux__) +#include <linux/if_link.h> +#include <linux/rtnetlink.h> +#else +#error "Unknown platform!" +#endif + +#include <osmocom/core/utils.h> +#include <osmocom/core/select.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/netns.h> +#include <osmocom/core/netdev.h> + +#if ENABLE_LIBMNL +#include <osmocom/core/mnl.h> +#endif + +#define IFINDEX_UNUSED 0 + +#define LOGNETDEV(netdev, lvl, fmt, args ...) \ + LOGP(DLGLOBAL, lvl, "NETDEV(%s,if=%s/%u,ns=%s): " fmt, \ + (netdev)->name, osmo_netdev_get_dev_name(netdev) ? : "", \ + (netdev)->ifindex, (netdev)->netns_name ? : "", ## args) + +static struct llist_head g_netdev_netns_ctx_list = LLIST_HEAD_INIT(g_netdev_netns_ctx_list); +static struct llist_head g_netdev_list = LLIST_HEAD_INIT(g_netdev_list); + +/* One per netns, shared by all osmo_netdev in a given netns: */ +struct netdev_netns_ctx { + struct llist_head entry; /* entry in g_netdev_netns_ctx_list */ + unsigned int refcount; /* Number of osmo_netdev currently registered on this netns */ + const char *netns_name; /* default netns has empty string "" (never NULL!) */ + int netns_fd; /* FD to the netns with name "netns_name" above */ +#if ENABLE_LIBMNL + struct osmo_mnl *omnl; +#endif +}; + +static struct netdev_netns_ctx *netdev_netns_ctx_alloc(void *ctx, const char *netns_name) +{ + struct netdev_netns_ctx *netns_ctx; + OSMO_ASSERT(netns_name); + + netns_ctx = talloc_zero(ctx, struct netdev_netns_ctx); + if (!netns_ctx) + return NULL; + + netns_ctx->netns_name = talloc_strdup(netns_ctx, netns_name); + netns_ctx->netns_fd = -1; + + llist_add_tail(&netns_ctx->entry, &g_netdev_netns_ctx_list); + return netns_ctx; + +} + +static void netdev_netns_ctx_free(struct netdev_netns_ctx *netns_ctx) +{ + if (!netns_ctx) + return; + + llist_del(&netns_ctx->entry); + +#if ENABLE_LIBMNL + if (netns_ctx->omnl) { + osmo_mnl_destroy(netns_ctx->omnl); + netns_ctx->omnl = NULL; + } +#endif + + if (netns_ctx->netns_fd != -1) { + close(netns_ctx->netns_fd); + netns_ctx->netns_fd = -1; + } + talloc_free(netns_ctx); +} + +#if ENABLE_LIBMNL +static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data); +#endif + +static int netdev_netns_ctx_init(struct netdev_netns_ctx *netns_ctx) +{ + struct osmo_netns_switch_state switch_state; + int rc; + + if (netns_ctx->netns_name0 != '\0') { + LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Switch to netns '%s'\n", netns_ctx->netns_name); + netns_ctx->netns_fd = osmo_netns_open_fd(netns_ctx->netns_name); + if (netns_ctx->netns_fd < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n", + netns_ctx->netns_name, strerror(errno), errno); + return netns_ctx->netns_fd; + } + + /* temporarily switch to specified namespace to create netlink socket */ + rc = osmo_netns_switch_enter(netns_ctx->netns_fd, &switch_state); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n", + netns_ctx->netns_name, strerror(errno), errno); + /* netns_ctx->netns_fd will be freed by future call to netdev_netns_ctx_free() */ + return rc; + } + } + +#if ENABLE_LIBMNL + netns_ctx->omnl = osmo_mnl_init(NULL, NETLINK_ROUTE, RTMGRP_LINK, netdev_netns_ctx_mnl_cb, netns_ctx); + rc = (netns_ctx->omnl ? 0 : -EFAULT); +#else + rc = 0; +#endif + + /* switch back to default namespace */ + if (netns_ctx->netns_name0 != '\0') { + int rc2 = osmo_netns_switch_exit(&switch_state); + if (rc2 < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch back from netns '%s': %s\n", + netns_ctx->netns_name, strerror(errno)); + return rc2; + } + LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Back from netns '%s'\n", + netns_ctx->netns_name); + } + return rc; +} + +static struct netdev_netns_ctx *netdev_netns_ctx_find_by_netns_name(const char *netns_name) +{ + struct netdev_netns_ctx *netns_ctx; + + llist_for_each_entry(netns_ctx, &g_netdev_netns_ctx_list, entry) { + if (strcmp(netns_ctx->netns_name, netns_name)) + continue; + return netns_ctx; + } + + return NULL; +} + +static struct netdev_netns_ctx *netdev_netns_ctx_get(const char *netns_name) +{ + struct netdev_netns_ctx *netns_ctx; + int rc; + + OSMO_ASSERT(netns_name); + netns_ctx = netdev_netns_ctx_find_by_netns_name(netns_name); + if (!netns_ctx) { + netns_ctx = netdev_netns_ctx_alloc(NULL, netns_name); + if (!netns_ctx) + return NULL; + rc = netdev_netns_ctx_init(netns_ctx); + if (rc < 0) { + netdev_netns_ctx_free(netns_ctx); + return NULL; + } + } + netns_ctx->refcount++; + return netns_ctx; +} + +static void netdev_netns_ctx_put(struct netdev_netns_ctx *netns_ctx) +{ + OSMO_ASSERT(netns_ctx); + netns_ctx->refcount--; + + if (netns_ctx->refcount == 0) + netdev_netns_ctx_free(netns_ctx); +} + +struct osmo_netdev { + /* entry in g_netdev_list */ + struct llist_head entry; + + /* Pointer to struct shared (refcounted) by all osmo_netdev in the same netns: */ + struct netdev_netns_ctx *netns_ctx; + + /* Name used to identify the osmo_netdev */ + char *name; + + /* ifindex of the network interface (address space is per netns) */ + unsigned int ifindex; + + /* Network interface name. Can change over lifetime of the interface. */ + char *dev_name; + + /* netns name where the netdev interface is created (NULL = default netns) */ + char *netns_name; + + /* API user private data */ + void *priv_data; + + /* Whether the netdev is in operation (managing the netdev interface) */ + bool registered; + + /* Called by netdev each time a new up/down state change is detected. Can be NULL. */ + osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb; + + /* Called by netdev each time the registered network interface is renamed by the system. Can be NULL. */ + osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb; + + /* Called by netdev each time the configured MTU changes in registered network interface. Can be NULL. */ + osmo_netdev_mtu_chg_cb_t mtu_chg_cb; + + /* Whether the netdev interface is UP */ + bool if_up; + /* Whether we know the interface updown state (aka if if_up holds information)*/ + bool if_up_known; + + /* The netdev interface MTU size */ + uint32_t if_mtu; + /* Whether we know the interface MTU size (aka if if_mtu holds information)*/ + bool if_mtu_known; +}; + +#define NETDEV_NETNS_ENTER(netdev, switch_state, str_prefix) \ + do { \ + if ((netdev)->netns_name) { \ + LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Switch to netns '%s'\n", \ + (netdev)->netns_name); \ + int rc2 = osmo_netns_switch_enter((netdev)->netns_ctx->netns_fd, switch_state); \ + if (rc2 < 0) { \ + LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch to netns '%s': %s (%d)\n", \ + (netdev)->netns_name, strerror(errno), errno); \ + return -EACCES; \ + } \ + } \ + } while (0) + +#define NETDEV_NETNS_EXIT(netdev, switch_state, str_prefix) \ + do { \ + if ((netdev)->netns_name) { \ + int rc2 = osmo_netns_switch_exit(switch_state); \ + if (rc2 < 0) { \ + LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch back from netns '%s': %s\n", \ + (netdev)->netns_name, strerror(errno)); \ + return rc2; \ + } \ + LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Back from netns '%s'\n", \ + (netdev)->netns_name); \ + } \ + } while (0) + +#if ENABLE_LIBMNL +/* validate the netlink attributes */ +static int netdev_mnl_data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case IFLA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + return MNL_CB_ERROR; + break; + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tbtype = attr; + return MNL_CB_OK; +} + +static void netdev_mnl_check_mtu_change(struct osmo_netdev *netdev, uint32_t mtu) +{ + if (netdev->if_mtu_known && netdev->if_mtu == mtu) + return; + + LOGNETDEV(netdev, LOGL_NOTICE, "MTU changed: %u\n", mtu); + if (netdev->mtu_chg_cb) + netdev->mtu_chg_cb(netdev, mtu); + + netdev->if_mtu_known = true; + netdev->if_mtu = mtu; +} + +static void netdev_mnl_check_link_state_change(struct osmo_netdev *netdev, bool if_up) +{ + if (netdev->if_up_known && netdev->if_up == if_up) + return; + + LOGNETDEV(netdev, LOGL_NOTICE, "Physical link state changed: %s\n", + if_up ? "UP" : "DOWN"); + if (netdev->ifupdown_ind_cb) + netdev->ifupdown_ind_cb(netdev, if_up); + + netdev->if_up_known = true; + netdev->if_up = if_up; +} + +static int netdev_mnl_cb(struct osmo_netdev *netdev, struct ifinfomsg *ifm, struct nlattr **tb) +{ + char ifnamebufIF_NAMESIZE; + const char *ifname = NULL; + bool if_running; + + if (tbIFLA_IFNAME) { + ifname = mnl_attr_get_str(tbIFLA_IFNAME); + LOGNETDEV(netdev, LOGL_DEBUG, "%s(): ifname=%s\n", __func__, ifname); + } else { + /* Try harder to obtain the ifname. This code path should in + * general not be triggered since usually IFLA_IFNAME is there */ + struct osmo_netns_switch_state switch_state; + NETDEV_NETNS_ENTER(netdev, &switch_state, "if_indextoname"); + ifname = if_indextoname(ifm->ifi_index, ifnamebuf); + NETDEV_NETNS_EXIT(netdev, &switch_state, "if_indextoname"); + } + if (ifname) { + /* Update dev_name if it changed: */ + if (strcmp(netdev->dev_name, ifname) != 0) { + if (netdev->dev_name_chg_cb) + netdev->dev_name_chg_cb(netdev, ifname); + osmo_talloc_replace_string(netdev, &netdev->dev_name, ifname); + } + } + + if (tbIFLA_MTU) { + uint32_t mtu = mnl_attr_get_u32(tbIFLA_MTU); + LOGNETDEV(netdev, LOGL_DEBUG, "%s(): mtu=%u\n", __func__, mtu); + netdev_mnl_check_mtu_change(netdev, mtu); + } + if (tbIFLA_ADDRESS) { + uint8_t *hwaddr = mnl_attr_get_payload(tbIFLA_ADDRESS); + uint16_t hwaddr_len = mnl_attr_get_payload_len(tbIFLA_ADDRESS); + LOGNETDEV(netdev, LOGL_DEBUG, "%s(): hwaddress=%s\n", + __func__, osmo_hexdump(hwaddr, hwaddr_len)); + } + + if_running = !!(ifm->ifi_flags & IFF_RUNNING); + LOGNETDEV(netdev, LOGL_DEBUG, "%s(): up=%u running=%u\n", + __func__, !!(ifm->ifi_flags & IFF_UP), if_running); + netdev_mnl_check_link_state_change(netdev, if_running); + + return MNL_CB_OK; +} + +static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data) +{ + struct osmo_mnl *omnl = data; + struct netdev_netns_ctx *netns_ctx = (struct netdev_netns_ctx *)omnl->priv; + struct nlattr *tbIFLA_MAX+1 = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + struct osmo_netdev *netdev; + + OSMO_ASSERT(omnl); + OSMO_ASSERT(ifm); + + mnl_attr_parse(nlh, sizeof(*ifm), netdev_mnl_data_attr_cb, tb); + + LOGP(DLGLOBAL, LOGL_DEBUG, + "%s(): index=%d type=%d flags=0x%x family=%d\n", __func__, + ifm->ifi_index, ifm->ifi_type, ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_index == IFINDEX_UNUSED) + return MNL_CB_ERROR; + + /* Find the netdev (if any) using key <netns,ifindex>. + * Different users of the API may have its own osmo_netdev object + * tracking potentially same netif, hence we need to iterate the whole list + * and dispatch to all matches: + */ + bool found_any = false; + llist_for_each_entry(netdev, &g_netdev_list, entry) { + if (!netdev->registered) + continue; + if (netdev->ifindex != ifm->ifi_index) + continue; + if (strcmp(netdev->netns_ctx->netns_name, netns_ctx->netns_name)) + continue; + found_any = true; + netdev_mnl_cb(netdev, ifm, &tb0); + } + + if (!found_any) { + LOGP(DLGLOBAL, LOGL_DEBUG, "%s(): device with ifindex %u on netns %s not registered\n", __func__, + ifm->ifi_index, netns_ctx->netns_name); + } + return MNL_CB_OK; +} + +/* Trigger an initial dump of the iface to get link information */ +static int netdev_mnl_request_initial_dump(struct osmo_mnl *omnl, unsigned int if_index) +{ + char bufMNL_SOCKET_BUFFER_SIZE; + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + struct ifinfomsg *ifm; + + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_index = if_index; + + if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n"); + return -EIO; + } + + return 0; +} + +static int netdev_mnl_set_ifupdown(struct osmo_mnl *omnl, unsigned int if_index, + const char *dev_name, bool up) +{ + char bufMNL_SOCKET_BUFFER_SIZE; + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + struct ifinfomsg *ifm; + unsigned int change = 0; + unsigned int flags = 0; + + if (up) { + change |= IFF_UP; + flags |= IFF_UP; + } else { + change |= IFF_UP; + flags &= ~IFF_UP; + } + + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_change = change; + ifm->ifi_flags = flags; + ifm->ifi_index = if_index; + + if (dev_name) + mnl_attr_put_str(nlh, IFLA_IFNAME, dev_name); + + if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n"); + return -EIO; + } + + return 0; +} + +static int netdev_mnl_add_addr(struct osmo_mnl *omnl, unsigned int if_index, const struct osmo_sockaddr *osa, uint8_t prefix) +{ + char bufMNL_SOCKET_BUFFER_SIZE; + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + struct ifaddrmsg *ifm; + + nlh->nlmsg_type = RTM_NEWADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + nlh->nlmsg_seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifa_family = osa->u.sa.sa_family; + ifm->ifa_prefixlen = prefix; + ifm->ifa_flags = IFA_F_PERMANENT; + ifm->ifa_scope = RT_SCOPE_UNIVERSE; + ifm->ifa_index = if_index; + + /* + * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend + * on the address family being used and the device type. + * For broadcast devices (like the interfaces we use), + * for IPv4 we specify both and they are used interchangeably. + * For IPv6, only IFA_ADDRESS needs to be set. + */ + switch (osa->u.sa.sa_family) { + case AF_INET: + mnl_attr_put_u32(nlh, IFA_LOCAL, osa->u.sin.sin_addr.s_addr); + mnl_attr_put_u32(nlh, IFA_ADDRESS, osa->u.sin.sin_addr.s_addr); + break; + case AF_INET6: + mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), &osa->u.sin6.sin6_addr); + break; + default: + return -EINVAL; + } + + if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n"); + return -EIO; + } + + return 0; +} + +static int netdev_mnl_add_route(struct osmo_mnl *omnl, + unsigned int if_index, + const struct osmo_sockaddr *dst_osa, + uint8_t dst_prefix, + const struct osmo_sockaddr *gw_osa) +{ + char bufMNL_SOCKET_BUFFER_SIZE; + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + struct rtmsg *rtm; + + nlh->nlmsg_type = RTM_NEWROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + nlh->nlmsg_seq = time(NULL); + + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm)); + rtm->rtm_family = dst_osa->u.sa.sa_family; + rtm->rtm_dst_len = dst_prefix; + rtm->rtm_src_len = 0; + rtm->rtm_tos = 0; + rtm->rtm_protocol = RTPROT_STATIC; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_type = RTN_UNICAST; + rtm->rtm_scope = gw_osa ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK; + rtm->rtm_flags = 0; + + switch (dst_osa->u.sa.sa_family) { + case AF_INET: + mnl_attr_put_u32(nlh, RTA_DST, dst_osa->u.sin.sin_addr.s_addr); + break; + case AF_INET6: + mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst_osa->u.sin6.sin6_addr); + break; + default: + return -EINVAL; + } + + mnl_attr_put_u32(nlh, RTA_OIF, if_index); + + if (gw_osa) { + switch (gw_osa->u.sa.sa_family) { + case AF_INET: + mnl_attr_put_u32(nlh, RTA_GATEWAY, gw_osa->u.sin.sin_addr.s_addr); + break; + case AF_INET6: + mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), &gw_osa->u.sin6.sin6_addr); + break; + default: + return -EINVAL; + } + } + + if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n"); + return -EIO; + } + + return 0; +} +#endif /* if ENABLE_LIBMNL */ + +/*! Allocate a new netdev object. + * \paramin ctx talloc context to use as a parent when allocating the netdev object + * \paramin name A name providen to identify the netdev object + * \returns newly allocated netdev object on success; NULL on error + */ +struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name) +{ + struct osmo_netdev *netdev; + + netdev = talloc_zero(ctx, struct osmo_netdev); + if (!netdev) + return NULL; + + netdev->name = talloc_strdup(netdev, name); + + llist_add_tail(&netdev->entry, &g_netdev_list); + return netdev; +} + +/*! Free an allocated netdev object. + * \paramin netdev The netdev object to free + */ +void osmo_netdev_free(struct osmo_netdev *netdev) +{ + if (!netdev) + return; + if (osmo_netdev_is_registered(netdev)) + osmo_netdev_unregister(netdev); + llist_del(&netdev->entry); + talloc_free(netdev); +} + +/*! Start managing the network device referenced by the netdev object. + * \paramin netdev The netdev object to open + * \returns 0 on success; negative on error + */ +int osmo_netdev_register(struct osmo_netdev *netdev) +{ + char ifnamebufIF_NAMESIZE; + struct osmo_netns_switch_state switch_state; + int rc = 0; + + if (netdev->registered) + return -EALREADY; + + netdev->netns_ctx = netdev_netns_ctx_get(netdev->netns_name ? : ""); + if (!netdev->netns_ctx) + return -EFAULT; + + NETDEV_NETNS_ENTER(netdev, &switch_state, "register"); + + if (!if_indextoname(netdev->ifindex, ifnamebuf)) { + rc = -ENODEV; + goto err_put_exit; + } + osmo_talloc_replace_string(netdev, &netdev->dev_name, ifnamebuf); + +#if ENABLE_LIBMNL + rc = netdev_mnl_request_initial_dump(netdev->netns_ctx->omnl, netdev->ifindex); +#endif + + NETDEV_NETNS_EXIT(netdev, &switch_state, "register"); + + netdev->registered = true; + return rc; + +err_put_exit: + NETDEV_NETNS_EXIT(netdev, &switch_state, "register"); + netdev_netns_ctx_put(netdev->netns_ctx); + return rc; +} + +/*! Unregister the netdev object (stop managing /moniutoring the interface) + * \paramin netdev The netdev object to close + * \returns 0 on success; negative on error + */ +int osmo_netdev_unregister(struct osmo_netdev *netdev) +{ + if (!netdev->registered) + return -EALREADY; + + netdev->if_up_known = false; + netdev->if_mtu_known = false; + + netdev_netns_ctx_put(netdev->netns_ctx); + netdev->registered = false; + return 0; +} + +/*! Retrieve whether the netdev object is in "registered" state. + * \paramin netdev The netdev object to check + * \returns true if in state "registered"; false otherwise + */ +bool osmo_netdev_is_registered(struct osmo_netdev *netdev) +{ + return netdev->registered; +} + +/*! Set private user data pointer on the netdev object. + * \paramin netdev The netdev object where the field is set + */ +void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data) +{ + netdev->priv_data = priv_data; +} + +/*! Get private user data pointer from the netdev object. + * \paramin netdev The netdev object from where to retrieve the field + * \returns The current value of the priv_data field. + */ +void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev) +{ + return netdev->priv_data; +} + +/*! Set data_ind_cb callback, called when a new packet is received on the network interface. + * \paramin netdev The netdev object where the field is set + * \paramin data_ind_cb the user provided function to be called when the link status (UP/DOWN) changes + */ +void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb) +{ + netdev->ifupdown_ind_cb = ifupdown_ind_cb; +} + +/*! Set dev_name_chg_cb callback, called when a change in the network name is detected + * \paramin netdev The netdev object where the field is set + * \paramin dev_name_chg_cb the user provided function to be called when a the interface is renamed + */ +void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb) +{ + netdev->dev_name_chg_cb = dev_name_chg_cb; +} + +/*! Set mtu_chg_cb callback, called when a change in the network name is detected + * \paramin netdev The netdev object where the field is set + * \paramin mtu_chg_cb the user provided function to be called when the configured MTU at the interface changes + */ +void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb) +{ + netdev->mtu_chg_cb = mtu_chg_cb; +} + +/*! Get name used to identify the netdev object. + * \paramin netdev The netdev object from where to retrieve the field + * \returns The current value of the name used to identify the netdev object + */ +const char *osmo_netdev_get_name(const struct osmo_netdev *netdev) +{ + return netdev->name; +} + +/*! Set (specify) interface index identifying the network interface to manage + * \paramin netdev The netdev object where the field is set + * \paramin ifindex The interface index identifying the interface + * \returns 0 on success; negative on error + * + * The ifindex, together with the netns_name (see + * osmo_netdev_netns_name_set()), form together the key identifiers of a + * network interface to manage. + * This field is used during osmo_netdev_register() time, and hence must be set + * before calling that API, and cannot be changed when the netdev object is in + * "registered" state. + */ +int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex) +{ + if (netdev->registered) + return -EALREADY; + netdev->ifindex = ifindex; + return 0; +} + +/*! Get interface index identifying the interface managed by netdev + * \paramin netdev The netdev object from where to retrieve the field + * \returns The current value of the configured netdev interface ifindex to use (0 = unset) + */ +unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev) +{ + return netdev->ifindex; +} + +/*! Set (specify) name of the network namespace where the network interface to manage is located + * \paramin netdev The netdev object where the field is set + * \paramin netns_name The network namespace where the network interface is located + * \returns 0 on success; negative on error + * + * The netns_name, together with the ifindex (see + * osmo_netdev_ifindex_set()), form together the key identifiers of a + * network interface to manage. + * This field is used during osmo_netdev_register() time, and hence must be set + * before calling that API, and cannot be changed when the netdev object is in + * "registered" state. + * If left as NULL (default), the management will be done in the current network namespace. + */ +int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns_name) +{ + if (netdev->registered) + return -EALREADY; + osmo_talloc_replace_string(netdev, &netdev->netns_name, netns_name); + return 0; +} + +/*! Get name of network namespace used when opening the netdev interface + * \paramin netdev The netdev object from where to retrieve the field + * \returns The current value of the configured network namespace + */ +const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev) +{ + return netdev->netns_name; +} + +/*! Get name used to name the network interface created by the netdev object + * \paramin netdev The netdev object from where to retrieve the field + * \returns The interface name (or NULL if unknown) + * + * This information is retrieved internally once the netdev object enters the + * "registered" state. Hence, when not registered NULL can be returned. + */ +const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev) +{ + return netdev->dev_name; +} + +/*! Bring netdev interface UP or DOWN. + * \paramin netdev The netdev object managing the netdev interface + * \paramin ifupdown true to set the interface UP, false to set it DOWN + * \returns 0 on succes; negative on error. + */ +int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown) +{ + struct osmo_netns_switch_state switch_state; + int rc; + + if (!netdev->registered) + return -ENODEV; + + LOGNETDEV(netdev, LOGL_NOTICE, "Bringing dev %s %s\n", + netdev->dev_name, ifupdown ? "UP" : "DOWN"); + + NETDEV_NETNS_ENTER(netdev, &switch_state, "ifupdown"); + +#if ENABLE_LIBMNL + rc = netdev_mnl_set_ifupdown(netdev->netns_ctx->omnl, netdev->ifindex, + netdev->dev_name, ifupdown); +#else + LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__); + rc = -ENOTSUP; +#endif + + NETDEV_NETNS_EXIT(netdev, &switch_state, "ifupdown"); + + return rc; +} + +/*! Add IP address to netdev interface + * \paramin netdev The netdev object managing the netdev interface + * \paramin addr The local address to set on the interface + * \paramin prefixlen The network prefix of addr + * \returns 0 on succes; negative on error. + */ +int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen) +{ + struct osmo_netns_switch_state switch_state; + char bufINET6_ADDRSTRLEN; + int rc; + + if (!netdev->registered) + return -ENODEV; + + LOGNETDEV(netdev, LOGL_NOTICE, "Adding address %s/%u to dev %s\n", + osmo_sockaddr_ntop(&addr->u.sa, buf), prefixlen, netdev->dev_name); + + NETDEV_NETNS_ENTER(netdev, &switch_state, "Add address"); + +#if ENABLE_LIBMNL + rc = netdev_mnl_add_addr(netdev->netns_ctx->omnl, netdev->ifindex, addr, prefixlen); +#else + LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__); + rc = -ENOTSUP; +#endif + + NETDEV_NETNS_EXIT(netdev, &switch_state, "Add address"); + + return rc; +} + +/*! Add IP route to netdev interface + * \paramin netdev The netdev object managing the netdev interface + * \paramin dst_addr The destination address of the route + * \paramin dst_prefixlen The network prefix of dst_addr + * \paramin gw_addr The gateway address. Optional, can be NULL. + * \returns 0 on succes; negative on error. + */ +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) +{ + struct osmo_netns_switch_state switch_state; + char buf_dstINET6_ADDRSTRLEN; + char buf_gwINET6_ADDRSTRLEN; + int rc; + + if (!netdev->registered) + return -ENODEV; + + LOGNETDEV(netdev, LOGL_NOTICE, "Adding route %s/%u%s%s dev %s\n", + osmo_sockaddr_ntop(&dst_addr->u.sa, buf_dst), dst_prefixlen, + gw_addr ? " via " : "", + gw_addr ? osmo_sockaddr_ntop(&gw_addr->u.sa, buf_gw) : "", + netdev->dev_name); + + NETDEV_NETNS_ENTER(netdev, &switch_state, "Add route"); + +#if ENABLE_LIBMNL + rc = netdev_mnl_add_route(netdev->netns_ctx->omnl, netdev->ifindex, dst_addr, dst_prefixlen, gw_addr); +#else + LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__); + rc = -ENOTSUP; +#endif + + NETDEV_NETNS_EXIT(netdev, &switch_state, "Add route"); + + return rc; +} + +#endif /* (!EMBEDDED) */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/netns.c
Added
@@ -0,0 +1,208 @@ + +/* Network namespace convenience functions + * (C) 2023 by sysmocom - s.m.f.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 "config.h" + +/*! \addtogroup netns + * @{ + * Network namespace convenience functions + * + * \file netns.c */ + +#if defined(__linux__) + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sched.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <fcntl.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/netns.h> + +#define NETNS_PREFIX_PATH "/var/run/netns" +#define NETNS_CURRENT_PATH "/proc/self/ns/net" + +/*! Open a file descriptor for the current network namespace. + * \returns fd of the current network namespace on success; negative in case of error + */ +static int netns_open_current_fd(void) +{ + int fd; + /* store the default namespace for later reference */ + if ((fd = open(NETNS_CURRENT_PATH, O_RDONLY)) < 0) + return -errno; + return fd; +} + +/*! switch to a (non-default) namespace, store existing signal mask in oldmask. + * \paramin nsfd file descriptor representing the namespace to which we shall switch + * \paramout state caller-provided memory location to which state of previous netns is stored + * \returns 0 on success; negative on error */ +int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state) +{ + sigset_t intmask; + int rc; + + state->prev_nsfd = -1; + + if (sigfillset(&intmask) < 0) + return -errno; + if ((rc = sigprocmask(SIG_BLOCK, &intmask, &state->prev_sigmask)) != 0) + return -rc; + state->prev_nsfd = netns_open_current_fd(); + + if (setns(nsfd, CLONE_NEWNET) < 0) { + /* restore old mask if we couldn't switch the netns */ + sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL); + close(state->prev_nsfd); + state->prev_nsfd = -1; + return -errno; + } + return 0; +} + +/*! switch back to the previous namespace, restoring signal mask. + * \paramin state information about previous netns, filled by osmo_netns_switch_enter() + * \returns 0 on successs; negative on error */ +int osmo_netns_switch_exit(struct osmo_netns_switch_state *state) +{ + if (state->prev_nsfd < 0) + return -EINVAL; + + int rc; + if (setns(state->prev_nsfd, CLONE_NEWNET) < 0) + return -errno; + + close(state->prev_nsfd); + state->prev_nsfd = -1; + + if ((rc = sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL)) != 0) + return -rc; + return 0; +} + +static int create_netns(const char *name) +{ + char pathMAXPATHLEN; + sigset_t intmask, oldmask; + int fd, prev_nsfd; + int rc, rc2; + + /* create /var/run/netns, if it doesn't exist already */ + rc = mkdir(NETNS_PREFIX_PATH, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); + if (rc < 0 && errno != EEXIST) + return rc; + + /* create /var/run/netns/name, if it doesn't exist already */ + rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name); + if (rc >= sizeof(path)) + return -ENAMETOOLONG; + fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0); + if (fd < 0) + return -errno; + if (close(fd) < 0) + return -errno; + + /* mask off all signals, store old signal mask */ + if (sigfillset(&intmask) < 0) + return -errno; + if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0) + return -rc; + + prev_nsfd = netns_open_current_fd(); + if (prev_nsfd < 0) + return prev_nsfd; + + /* create a new network namespace */ + if (unshare(CLONE_NEWNET) < 0) { + rc = -errno; + goto restore_sigmask; + } + if (mount(NETNS_CURRENT_PATH, path, "none", MS_BIND, NULL) < 0) { + rc = -errno; + goto restore_sigmask; + } + + /* switch back to previous namespace */ + if (setns(prev_nsfd, CLONE_NEWNET) < 0) { + rc = -errno; + goto restore_sigmask; + } + +restore_sigmask: + close(prev_nsfd); + + /* restore process mask */ + if ((rc2 = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) + return -rc2; + + /* might have been set above in case mount fails */ + if (rc < 0) + return rc; + + /* finally, open the created namespace file descriptor from previous ns */ + if ((fd = open(path, O_RDONLY)) < 0) + return -errno; + + return fd; +} + +/*! Open a file descriptor for the network namespace with provided name. + * Creates /var/run/netns/ directory if it doesn't exist already. + * \paramin name Name of the network namespace (in /var/run/netns/) + * \returns File descriptor of network namespace; negative in case of error + */ +int osmo_netns_open_fd(const char *name) +{ + int rc; + int fd; + char pathMAXPATHLEN; + + /* path = /var/run/netns/name */ + rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name); + if (rc >= sizeof(path)) + return -ENAMETOOLONG; + + /* If netns already exists, simply open it: */ + fd = open(path, O_RDONLY); + if (fd >= 0) + return fd; + + /* The netns doesn't exist yet, let's create it: */ + fd = create_netns(name); + return fd; +} + +#endif /* defined(__linux__) */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/panic.c
Added
@@ -0,0 +1,103 @@ +/*! \file panic.c + * Routines for panic handling. */ +/* + * (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * 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. + * + */ + +/*! \addtogroup utils + * @{ + * \file panic.c */ + +#include <unistd.h> +#include <osmocom/core/panic.h> +#include <osmocom/core/backtrace.h> + +#include "config.h" + + +static osmo_panic_handler_t osmo_panic_handler = (void*)0; + + +#ifndef PANIC_INFLOOP + +#include <stdio.h> +#include <stdlib.h> + +static void osmo_panic_default(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + osmo_generate_backtrace(); + abort(); +} + +#else + +static void osmo_panic_default(const char *fmt, va_list args) +{ + while (1); +} + +#endif + + +/*! Terminate the current program with a panic + * + * You can call this function in case some severely unexpected situation + * is detected and the program is supposed to terminate in a way that + * reports the fact that it terminates. + * + * The application can register a panic handler function using \ref + * osmo_set_panic_handler. If it doesn't, a default panic handler + * function is called automatically. + * + * The default function on most systems will generate a backtrace and + * then abort() the process. + */ +void osmo_panic(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (osmo_panic_handler) + osmo_panic_handler(fmt, args); + else + osmo_panic_default(fmt, args); + + va_end(args); + + /* not reached, but make compiler believe we really never return */ +#ifndef PANIC_INFLOOP + exit(2342); +#else + while (1) ; +#endif +} + +/*! Set the panic handler + * \paramin h New panic handler function + * + * This changes the panic handling function from the currently active + * function to a new call-back function supplied by the caller. + */ +void osmo_set_panic_handler(osmo_panic_handler_t h) +{ + osmo_panic_handler = h; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/plugin.c
Added
@@ -0,0 +1,71 @@ +/*! \file plugin.c + * Routines for loading and managing shared library plug-ins. */ +/* + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * 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. + * + */ + +/*! \addtogroup utils + * @{ + * \file plugin.c */ + +#include "config.h" + +#if HAVE_DLFCN_H + +#include <dirent.h> +#include <dlfcn.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> + +#include <osmocom/core/plugin.h> + +/*! Load all plugins available in given directory + * \paramin directory full path name of directory containing plug-ins + * \returns number of plugins loaded in case of success, negative in case of error + */ +int osmo_plugin_load_all(const char *directory) +{ + unsigned int num = 0; + char fnamePATH_MAX; + DIR *dir; + struct dirent *entry; + + dir = opendir(directory); + if (!dir) + return -errno; + + while ((entry = readdir(dir))) { + snprintf(fname, sizeof(fname), "%s/%s", directory, + entry->d_name); + if (dlopen(fname, RTLD_NOW)) + num++; + } + + closedir(dir); + + return num; +} +#else +int osmo_plugin_load_all(const char *directory) +{ + return 0; +} +#endif /* HAVE_DLFCN_H */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/prbs.c
Changed
(renamed from src/prbs.c)
View file
libosmocore_1.8.0.tar.xz/src/core/prim.c
Changed
(renamed from src/prim.c)
View file
libosmocore_1.8.0.tar.xz/src/core/probes.d
Changed
(renamed from src/probes.d)
View file
libosmocore_1.8.0.tar.xz/src/core/rate_ctr.c
Added
@@ -0,0 +1,499 @@ +/* (C) 2009-2017 by Harald Welte <laforge@gnumonks.org> + * + * 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. + * + */ + +/*! \addtogroup rate_ctr + * @{ + * Counters about events and their event rates. + * + * As \ref osmo_counter and \ref osmo_stat_item are concerned only with + * a single given value that may be increased/decreased, or the difference + * to one given previous value, this module adds some support for keeping + * long term information about a given event rate. + * + * A \ref rate_ctr keeps information on the amount of events per second, + * per minute, per hour and per day. + * + * \ref rate_ctr come in groups: An application describes a group of counters + * with their names and identities once in a (typically const) \ref + * rate_ctr_group_desc. + * + * As objects (such as e.g. a subscriber or a PDP context) are + * allocated dynamically at runtime, the application calls \ref + * rate_ctr_group_alloc with a refernce to the \ref + * rate_ctr_group_desc, which causes the library to allocate one set of + * \ref rate_ctr: One for each in the group. + * + * The application then uses functions like \ref rate_ctr_add or \ref + * rate_ctr_inc to increment the value as certain events (e.g. location + * update) happens. + * + * The library internally keeps a timer once per second which iterates + * over all registered counters and which updates the per-second, + * per-minute, per-hour and per-day averages based on the current + * value. + * + * The counters can be reported using \ref stats or by VTY + * introspection, as well as by any application-specific code accessing + * the \ref rate_ctr.intv array directly. + * + * \file rate_ctr.c */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/logging.h> + +static LLIST_HEAD(rate_ctr_groups); + +static void *tall_rate_ctr_ctx; + + +static bool rate_ctrl_group_desc_validate(const struct rate_ctr_group_desc *desc) +{ + unsigned int i; + const struct rate_ctr_desc *ctr_desc; + + if (!desc) { + LOGP(DLGLOBAL, LOGL_ERROR, "NULL is not a valid counter group descriptor\n"); + return false; + } + ctr_desc = desc->ctr_desc; + + DEBUGP(DLGLOBAL, "validating counter group %p(%s) with %u counters\n", desc, + desc->group_name_prefix, desc->num_ctr); + + if (!osmo_identifier_valid(desc->group_name_prefix)) { + LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter group identifier\n", + desc->group_name_prefix); + return false; + } + + for (i = 0; i < desc->num_ctr; i++) { + if (!osmo_identifier_valid(ctr_desci.name)) { + LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter identifier\n", + ctr_desci.name); + return false; + } + } + + return true; +} + +/* return 'in' if it doesn't contain any '.'; otherwise allocate a copy and + * replace all '.' with ':' */ +static char *mangle_identifier_ifneeded(const void *ctx, const char *in) +{ + char *out; + unsigned int i; + bool modified = false; + + if (!in) + return NULL; + + if (!strchr(in, '.')) + return (char *)in; + + out = talloc_strdup(ctx, in); + OSMO_ASSERT(out); + + for (i = 0; i < strlen(out); i++) { + if (outi == '.') { + outi = ':'; + modified = true; + } + } + + if (modified) + LOGP(DLGLOBAL, LOGL_NOTICE, "counter group name mangled: '%s' -> '%s'\n", + in, out); + + return out; +} + +/* "mangle" a rate counter group descriptor, i.e. replace any '.' with ':' */ +static struct rate_ctr_group_desc * +rate_ctr_group_desc_mangle(void *ctx, const struct rate_ctr_group_desc *desc) +{ + struct rate_ctr_group_desc *desc_new = talloc_zero(ctx, struct rate_ctr_group_desc); + int i; + + OSMO_ASSERT(desc_new); + + LOGP(DLGLOBAL, LOGL_INFO, "Needed to mangle counter group '%s' names: it is still using '.' as " + "separator, which is not allowed. please consider updating the application\n", + desc->group_name_prefix); + + /* mangle the name_prefix but copy/keep the rest */ + desc_new->group_name_prefix = mangle_identifier_ifneeded(desc_new, desc->group_name_prefix); + desc_new->group_description = desc->group_description; + desc_new->class_id = desc->class_id; + desc_new->num_ctr = desc->num_ctr; + desc_new->ctr_desc = talloc_array(desc_new, struct rate_ctr_desc, desc_new->num_ctr); + OSMO_ASSERT(desc_new->ctr_desc); + + for (i = 0; i < desc->num_ctr; i++) { + struct rate_ctr_desc *ctrd_new = (struct rate_ctr_desc *) desc_new->ctr_desc; + const struct rate_ctr_desc *ctrd = desc->ctr_desc; + + if (!ctrdi.name) { + LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s'%d == NULL, aborting\n", + desc->group_name_prefix, i); + goto err_free; + } + + ctrd_newi.name = mangle_identifier_ifneeded(desc_new->ctr_desc, ctrdi.name); + ctrd_newi.description = ctrdi.description; + } + + if (!rate_ctrl_group_desc_validate(desc_new)) { + /* simple mangling of identifiers ('.' -> ':') was not sufficient to render a valid + * descriptor, we have to bail out */ + LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' still invalid after mangling\n", + desc->group_name_prefix); + goto err_free; + } + + return desc_new; +err_free: + talloc_free(desc_new); + return NULL; +} + +/*! Find an unused index for this rate counter group. + * \paramin name Name of the counter group + * \returns the largest used index number + 1, or 0 if none exist yet. */ +static unsigned int rate_ctr_get_unused_name_idx(const char *name) +{ + unsigned int idx = 0; + struct rate_ctr_group *ctrg; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) { + if (!ctrg->desc) + continue; + + if (strcmp(ctrg->desc->group_name_prefix, name)) + continue; + + if (idx <= ctrg->idx) + idx = ctrg->idx + 1; + } + return idx; +} + +/*! Allocate a new group of counters according to description + * \paramin ctx parent talloc context + * \paramin desc Rate counter group description + * \paramin idx Index of new counter group + */ +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx) +{ + unsigned int size; + struct rate_ctr_group *group; + + if (rate_ctr_get_group_by_name_idx(desc->group_name_prefix, idx)) { + unsigned int new_idx = rate_ctr_get_unused_name_idx(desc->group_name_prefix); + LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' already exists for index %u," + " instead using index %u. This is a software bug that needs fixing.\n", + desc->group_name_prefix, idx, new_idx); + idx = new_idx; + } + + size = sizeof(struct rate_ctr_group) + + desc->num_ctr * sizeof(struct rate_ctr); + + if (!ctx) + ctx = tall_rate_ctr_ctx; + + group = talloc_zero_size(ctx, size); + if (!group) + return NULL; + + /* attempt to mangle all '.' in identifiers to ':' for backwards compat */ + if (!rate_ctrl_group_desc_validate(desc)) { + desc = rate_ctr_group_desc_mangle(group, desc); + if (!desc) { + talloc_free(group); + return NULL; + } + } + + group->desc = desc; + group->idx = idx; + + llist_add(&group->list, &rate_ctr_groups); + + return group; +} + +/*! Free the memory for the specified group of counters */ +void rate_ctr_group_free(struct rate_ctr_group *grp) +{ + if (!grp) + return; + + if (!llist_empty(&grp->list)) + llist_del(&grp->list); + talloc_free(grp); +} + +/*! Get rate counter from group, identified by index idx + * \paramin grp Rate counter group + * \paramin idx Index of the counter to retrieve + * \returns rate counter requested + */ +struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx) +{ + return &grp->ctridx; +} + +/*! Set a name for the group of counters be used instead of index value + at report time. + * \paramin grp Rate counter group + * \paramin name Name identifier to assign to the rate counter group + */ +void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name) +{ + osmo_talloc_replace_string(grp, &grp->name, name); +} + +/*! Add a number to the counter */ +void rate_ctr_add(struct rate_ctr *ctr, int inc) +{ + ctr->current += inc; +} + +/*! Return the counter difference since the last call to this function */ +int64_t rate_ctr_difference(struct rate_ctr *ctr) +{ + int64_t result = ctr->current - ctr->previous; + ctr->previous = ctr->current; + + return result; +} + +/* TODO: support update intervals > 1s */ +/* TODO: implement this as a special stats reporter */ + +static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) +{ + /* calculate rate over last interval */ + ctr->intvintv.rate = ctr->current - ctr->intvintv.last; + /* save current counter for next interval */ + ctr->intvintv.last = ctr->current; +} + +static struct osmo_fd rate_ctr_timer = { .fd = -1 }; +static uint64_t timer_ticks; + +/* The one-second interval has expired */ +static void rate_ctr_group_intv(struct rate_ctr_group *grp) +{ + unsigned int i; + + for (i = 0; i < grp->desc->num_ctr; i++) { + struct rate_ctr *ctr = &grp->ctri; + + interval_expired(ctr, RATE_CTR_INTV_SEC); + if ((timer_ticks % 60) == 0) + interval_expired(ctr, RATE_CTR_INTV_MIN); + if ((timer_ticks % (60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_HOUR); + if ((timer_ticks % (24*60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_DAY); + } +} + +static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct rate_ctr_group *ctrg; + uint64_t expire_count; + int rc; + + /* check that the timer has actually expired */ + if (!(what & OSMO_FD_READ)) + return 0; + + /* read from timerfd: number of expirations of periodic timer */ + rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count)); + if (rc < 0 && errno == EAGAIN) + return 0; + + OSMO_ASSERT(rc == sizeof(expire_count)); + + if (expire_count > 1) + LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n", + expire_count, expire_count - 1); + + do { /* Increment number of ticks before we calculate intervals, + * as a counter value of 0 would already wrap all counters */ + timer_ticks++; + llist_for_each_entry(ctrg, &rate_ctr_groups, list) + rate_ctr_group_intv(ctrg); + } while (--expire_count); + + return 0; +} + +/*! Initialize the counter module. Call this once from your application. + * \paramin tall_ctx Talloc context from which rate_ctr_group will be allocated + * \returns 0 on success; negative on error */ +int rate_ctr_init(void *tall_ctx) +{ + struct timespec ts_interval = { .tv_sec = 1, .tv_nsec = 0 }; + int rc; + + /* ignore repeated initialization */ + if (osmo_fd_is_registered(&rate_ctr_timer)) + return 0; + + tall_rate_ctr_ctx = tall_ctx; + + rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n", + rc, rate_ctr_timer.fd); + return rc; + } + + rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n", + rc, rate_ctr_timer.fd); + } + + return 0; +} + +/*! Search for counter group based on group name and index + * \paramin name Name of the counter group you're looking for + * \paramin idx Index inside the counter group + * \returns \ref rate_ctr_group or NULL in case of error */ +struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx) +{ + struct rate_ctr_group *ctrg; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) { + if (!ctrg->desc) + continue; + + if (!strcmp(ctrg->desc->group_name_prefix, name) && + ctrg->idx == idx) { + return ctrg; + } + } + return NULL; +} + +/*! Search for counter based on group + name + * \paramin ctrg pointer to \ref rate_ctr_group + * \paramin name name of counter inside group + * \returns \ref rate_ctr or NULL in case of error + */ +const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name) +{ + int i; + const struct rate_ctr_desc *ctr_desc; + + if (!ctrg->desc) + return NULL; + + for (i = 0; i < ctrg->desc->num_ctr; i++) { + ctr_desc = &ctrg->desc->ctr_desci; + + if (!strcmp(ctr_desc->name, name)) { + return &ctrg->ctri; + } + } + return NULL; +} + +/*! Iterate over each counter in group and call function + * \paramin ctrg counter group over which to iterate + * \paramin handle_counter function pointer + * \paramin data Data to hand transparently to handle_counter() + * \returns 0 on success; negative otherwise + */ +int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg, + rate_ctr_handler_t handle_counter, void *data) +{ + int rc = 0; + int i; + + for (i = 0; i < ctrg->desc->num_ctr; i++) { + struct rate_ctr *ctr = &ctrg->ctri; + rc = handle_counter(ctrg, + ctr, &ctrg->desc->ctr_desci, data); + if (rc < 0) + return rc; + } + + return rc; +} + +/*! Iterate over all counter groups + * \paramin handle_group function pointer of callback function + * \paramin data Data to hand transparently to handle_group() + * \returns 0 on success; negative otherwise + */ +int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data) +{ + struct rate_ctr_group *statg; + int rc = 0; + + llist_for_each_entry(statg, &rate_ctr_groups, list) { + rc = handle_group(statg, data); + if (rc < 0) + return rc; + } + + return rc; +} + +/*! Reset a rate counter back to zero + * \paramin ctr counter to reset + */ +void rate_ctr_reset(struct rate_ctr *ctr) +{ + memset(ctr, 0, sizeof(*ctr)); +} + +/*! Reset all counters in a group + * \paramin ctrg counter group to reset + */ +void rate_ctr_group_reset(struct rate_ctr_group *ctrg) +{ + int i; + + for (i = 0; i < ctrg->desc->num_ctr; i++) { + struct rate_ctr *ctr = &ctrg->ctri; + rate_ctr_reset(ctr); + } +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/rbtree.c
Changed
(renamed from src/rbtree.c)
View file
libosmocore_1.8.0.tar.xz/src/core/select.c
Added
@@ -0,0 +1,675 @@ +/*! \file select.c + * select filedescriptor handling. + * Taken from: + * userspace logging daemon for the iptables ULOG target + * of the linux 2.4 netfilter subsystem. */ +/* + * (C) 2000-2020 by Harald Welte <laforge@gnumonks.org> + * 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 <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> + +#include <osmocom/core/select.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/stat_item.h> +#include <osmocom/core/stats_tcp.h> + +#include "config.h" + +#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_POLL_H) +#include <sys/select.h> +#include <poll.h> + +/*! \addtogroup select + * @{ + * select() loop abstraction + * + * \file select.c */ + +/* keep a set of file descriptors per-thread, so that each thread can have its own + * distinct set of file descriptors to interact with */ +static __thread int maxfd = 0; +static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */ +static __thread int unregistered_count; + +#ifndef FORCE_IO_SELECT +struct poll_state { + /* array of pollfd */ + struct pollfd *poll; + /* number of entries in pollfd allocated */ + unsigned int poll_size; + /* number of osmo_fd registered */ + unsigned int num_registered; +}; +static __thread struct poll_state g_poll; +#endif /* FORCE_IO_SELECT */ + +/*! See osmo_select_shutdown_request() */ +static int _osmo_select_shutdown_requested = 0; +/*! See osmo_select_shutdown_request() */ +static bool _osmo_select_shutdown_done = false; + +/*! Set up an osmo-fd. Will not register it. + * \paraminout ofd Osmo FD to be set-up + * \paramin fd OS-level file descriptor number + * \paramin when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT} + * \paramin cb Call-back function to be called + * \paramin data Private context pointer + * \paramin priv_nr Private number + */ +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) +{ + ofd->fd = fd; + ofd->when = when; + ofd->cb = cb; + ofd->data = data; + ofd->priv_nr = priv_nr; +} + +/*! Update the 'when' field of osmo_fd. "ofd->when = (ofd->when & when_mask) | when". + * Use this function instead of directly modifying ofd->when, as the latter will be + * removed soon. */ +void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when) +{ + ofd->when &= when_mask; + ofd->when |= when; +} + +/*! Check if a file descriptor is already registered + * \paramin fd osmocom file descriptor to be checked + * \returns true if registered; otherwise false + */ +bool osmo_fd_is_registered(struct osmo_fd *fd) +{ + struct osmo_fd *entry; + llist_for_each_entry(entry, &osmo_fds, list) { + if (entry == fd) { + return true; + } + } + + return false; +} + +/*! Register a new file descriptor with select loop abstraction + * \paramin fd osmocom file descriptor to be registered + * \returns 0 on success; negative in case of error + */ +int osmo_fd_register(struct osmo_fd *fd) +{ + int flags; + + /* make FD nonblocking */ + flags = fcntl(fd->fd, F_GETFL); + if (flags < 0) + return flags; + flags |= O_NONBLOCK; + flags = fcntl(fd->fd, F_SETFL, flags); + if (flags < 0) + return flags; + + /* set close-on-exec flag */ + flags = fcntl(fd->fd, F_GETFD); + if (flags < 0) + return flags; + flags |= FD_CLOEXEC; + flags = fcntl(fd->fd, F_SETFD, flags); + if (flags < 0) + return flags; + + /* Register FD */ + if (fd->fd > maxfd) + maxfd = fd->fd; + +#ifdef OSMO_FD_CHECK + if (osmo_fd_is_registered(fd)) { + fprintf(stderr, "Adding a osmo_fd that is already in the list.\n"); + return 0; + } +#endif +#ifndef FORCE_IO_SELECT + if (g_poll.num_registered + 1 > g_poll.poll_size) { + struct pollfd *p; + unsigned int new_size = g_poll.poll_size ? g_poll.poll_size * 2 : 1024; + p = talloc_realloc(OTC_GLOBAL, g_poll.poll, struct pollfd, new_size); + if (!p) + return -ENOMEM; + memset(p + g_poll.poll_size, 0, new_size - g_poll.poll_size); + g_poll.poll = p; + g_poll.poll_size = new_size; + } + g_poll.num_registered++; +#endif /* FORCE_IO_SELECT */ + + llist_add_tail(&fd->list, &osmo_fds); + + return 0; +} + +/*! Unregister a file descriptor from select loop abstraction + * \paramin fd osmocom file descriptor to be unregistered + */ +void osmo_fd_unregister(struct osmo_fd *fd) +{ + /* Note: when fd is inside the osmo_fds list (not registered before) + * this function will crash! If in doubt, check file descriptor with + * osmo_fd_is_registered() */ + unregistered_count++; + llist_del(&fd->list); +#ifndef FORCE_IO_SELECT + g_poll.num_registered--; +#endif /* FORCE_IO_SELECT */ + + /* If existent, free any statistical data */ + osmo_stats_tcp_osmo_fd_unregister(fd); +} + +/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction + * \paramin fd osmocom file descriptor to be unregistered + closed + * + * If \a fd is registered, we unregister it from the select() loop + * abstraction. We then close the fd and set it to -1, as well as + * unsetting any 'when' flags */ +void osmo_fd_close(struct osmo_fd *fd) +{ + if (osmo_fd_is_registered(fd)) + osmo_fd_unregister(fd); + if (fd->fd != -1) + close(fd->fd); + fd->fd = -1; + fd->when = 0; +} + +/*! Populate the fd_sets and return the highest fd number + * \paramin _rset The readfds to populate + * \paramin _wset The wrtiefds to populate + * \paramin _eset The errorfds to populate + * + * \returns The highest file descriptor seen or 0 on an empty list + */ +inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset) +{ + fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset; + struct osmo_fd *ufd; + int highfd = 0; + + llist_for_each_entry(ufd, &osmo_fds, list) { + if (ufd->when & OSMO_FD_READ) + FD_SET(ufd->fd, readset); + + if (ufd->when & OSMO_FD_WRITE) + FD_SET(ufd->fd, writeset); + + if (ufd->when & OSMO_FD_EXCEPT) + FD_SET(ufd->fd, exceptset); + + if (ufd->fd > highfd) + highfd = ufd->fd; + } + + return highfd; +} + +inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset) +{ + struct osmo_fd *ufd, *tmp; + int work = 0; + fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset; + +restart: + unregistered_count = 0; + llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) { + int flags = 0; + + if (FD_ISSET(ufd->fd, readset)) { + flags |= OSMO_FD_READ; + FD_CLR(ufd->fd, readset); + } + + if (FD_ISSET(ufd->fd, writeset)) { + flags |= OSMO_FD_WRITE; + FD_CLR(ufd->fd, writeset); + } + + if (FD_ISSET(ufd->fd, exceptset)) { + flags |= OSMO_FD_EXCEPT; + FD_CLR(ufd->fd, exceptset); + } + + if (flags) { + work = 1; + /* make sure to clear any log context before processing the next incoming message + * as part of some file descriptor callback. This effectively prevents "context + * leaking" from processing of one message into processing of the next message as part + * of one iteration through the list of file descriptors here. See OS#3813 */ + log_reset_context(); + ufd->cb(ufd, flags); + } + /* ugly, ugly hack. If more than one filedescriptor was + * unregistered, they might have been consecutive and + * llist_for_each_entry_safe() is no longer safe */ + /* this seems to happen with the last element of the list as well */ + if (unregistered_count >= 1) + goto restart; + } + + return work; +} + + +#ifndef FORCE_IO_SELECT +/* fill g_poll.poll and return the number of entries filled */ +static unsigned int poll_fill_fds(void) +{ + struct osmo_fd *ufd; + unsigned int i = 0; + + llist_for_each_entry(ufd, &osmo_fds, list) { + struct pollfd *p; + + if (!ufd->when) + continue; + + p = &g_poll.polli++; + + p->fd = ufd->fd; + p->events = 0; + p->revents = 0; + + /* use the same mapping as the Linux kernel does in fs/select.c */ + if (ufd->when & OSMO_FD_READ) + p->events |= POLLIN | POLLHUP | POLLERR; + + if (ufd->when & OSMO_FD_WRITE) + p->events |= POLLOUT | POLLERR; + + if (ufd->when & OSMO_FD_EXCEPT) + p->events |= POLLPRI; + + } + + return i; +} + +/* iterate over first n_fd entries of g_poll.poll + dispatch */ +static int poll_disp_fds(unsigned int n_fd) +{ + struct osmo_fd *ufd; + unsigned int i; + int work = 0; + int shutdown_pending_writes = 0; + + for (i = 0; i < n_fd; i++) { + struct pollfd *p = &g_poll.polli; + int flags = 0; + + if (!p->revents) + continue; + + ufd = osmo_fd_get_by_fd(p->fd); + if (!ufd) { + /* FD might have been unregistered meanwhile */ + continue; + } + /* use the same mapping as the Linux kernel does in fs/select.c */ + if (p->revents & (POLLIN | POLLHUP | POLLERR)) + flags |= OSMO_FD_READ; + if (p->revents & (POLLOUT | POLLERR)) + flags |= OSMO_FD_WRITE; + if (p->revents & POLLPRI) + flags |= OSMO_FD_EXCEPT; + + /* make sure we never report more than the user requested */ + flags &= ufd->when; + + if (_osmo_select_shutdown_requested > 0) { + if (ufd->when & OSMO_FD_WRITE) + shutdown_pending_writes++; + } + + if (flags) { + work = 1; + /* make sure to clear any log context before processing the next incoming message + * as part of some file descriptor callback. This effectively prevents "context + * leaking" from processing of one message into processing of the next message as part + * of one iteration through the list of file descriptors here. See OS#3813 */ + log_reset_context(); + ufd->cb(ufd, flags); + } + } + + if (_osmo_select_shutdown_requested > 0 && !shutdown_pending_writes) + _osmo_select_shutdown_done = true; + + return work; +} + +static int _osmo_select_main(int polling) +{ + unsigned int n_poll; + int rc; + int timeout = 0; + + /* prepare read and write fdsets */ + n_poll = poll_fill_fds(); + + if (!polling) { + osmo_timers_prepare(); + timeout = osmo_timers_nearest_ms(); + + if (_osmo_select_shutdown_requested && timeout == -1) + timeout = 0; + } + + rc = poll(g_poll.poll, n_poll, timeout); + if (rc < 0) + return 0; + + /* fire timers */ + if (!_osmo_select_shutdown_requested) + osmo_timers_update(); + + OSMO_ASSERT(osmo_ctx->select); + + /* call registered callback functions */ + return poll_disp_fds(n_poll); +} +#else /* FORCE_IO_SELECT */ +/* the old implementation based on select, used 2008-2020 */ +static int _osmo_select_main(int polling) +{ + fd_set readset, writeset, exceptset; + int rc; + struct timeval no_time = {0, 0}; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + /* prepare read and write fdsets */ + osmo_fd_fill_fds(&readset, &writeset, &exceptset); + + if (!polling) + osmo_timers_prepare(); + rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest()); + if (rc < 0) + return 0; + + /* fire timers */ + osmo_timers_update(); + + OSMO_ASSERT(osmo_ctx->select); + + /* call registered callback functions */ + return osmo_fd_disp_fds(&readset, &writeset, &exceptset); +} +#endif /* FORCE_IO_SELECT */ + +/*! select main loop integration + * \paramin polling should we pollonly (1) or block on select (0) + * \returns 0 if no fd handled; 1 if fd handled; negative in case of error + */ +int osmo_select_main(int polling) +{ + int rc = _osmo_select_main(polling); +#ifndef EMBEDDED + if (talloc_total_size(osmo_ctx->select) != 0) { + osmo_panic("You cannot use the 'select' volatile " + "context if you don't use osmo_select_main_ctx()!\n"); + } +#endif + return rc; +} + +#ifndef EMBEDDED +/*! select main loop integration with temporary select-dispatch talloc context + * \paramin polling should we pollonly (1) or block on select (0) + * \returns 0 if no fd handled; 1 if fd handled; negative in case of error + */ +int osmo_select_main_ctx(int polling) +{ + int rc = _osmo_select_main(polling); + /* free all the children of the volatile 'select' scope context */ + talloc_free_children(osmo_ctx->select); + return rc; +} +#endif + +/*! find an osmo_fd based on the integer fd + * \paramin fd file descriptor to use as search key + * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */ +struct osmo_fd *osmo_fd_get_by_fd(int fd) +{ + struct osmo_fd *ofd; + + llist_for_each_entry(ofd, &osmo_fds, list) { + if (ofd->fd == fd) + return ofd; + } + return NULL; +} + +/*! initialize the osmocom select abstraction for the current thread */ +void osmo_select_init(void) +{ + INIT_LLIST_HEAD(&osmo_fds); +} + +/* ensure main thread always has pre-initialized osmo_fds */ +static __attribute__((constructor)) void on_dso_load_select(void) +{ + osmo_select_init(); +} + +#ifdef HAVE_SYS_TIMERFD_H +#include <sys/timerfd.h> + +/*! disable the osmocom-wrapped timerfd */ +int osmo_timerfd_disable(struct osmo_fd *ofd) +{ + const struct itimerspec its_null = { + .it_value = { 0, 0 }, + .it_interval = { 0, 0 }, + }; + return timerfd_settime(ofd->fd, 0, &its_null, NULL); +} + +/*! schedule the osmocom-wrapped timerfd to occur first at \a first, then periodically at \a interval + * \paramin ofd Osmocom wrapped timerfd + * \paramin first Relative time at which the timer should first execute (NULL = \a interval) + * \paramin interval Time interval at which subsequent timer shall fire + * \returns 0 on success; negative on error */ +int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first, + const struct timespec *interval) +{ + struct itimerspec its; + + if (ofd->fd < 0) + return -EINVAL; + + /* first expiration */ + if (first) + its.it_value = *first; + else + its.it_value = *interval; + /* repeating interval */ + its.it_interval = *interval; + + return timerfd_settime(ofd->fd, 0, &its, NULL); +} + +/*! setup osmocom-wrapped timerfd + * \paraminout ofd Osmocom-wrapped timerfd on which to operate + * \paramin cb Call-back function called when timerfd becomes readable + * \paramin data Opaque data to be passed on to call-back + * \returns 0 on success; negative on error + * + * We simply initialize the data structures here, but do not yet + * schedule the timer. + */ +int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data) +{ + ofd->cb = cb; + ofd->data = data; + ofd->when = OSMO_FD_READ; + + if (ofd->fd < 0) { + int rc; + + ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (ofd->fd < 0) + return ofd->fd; + + rc = osmo_fd_register(ofd); + if (rc < 0) { + osmo_fd_unregister(ofd); + close(ofd->fd); + ofd->fd = -1; + return rc; + } + } + return 0; +} + +#endif /* HAVE_SYS_TIMERFD_H */ + +#ifdef HAVE_SYS_SIGNALFD_H +#include <sys/signalfd.h> + +static int signalfd_callback(struct osmo_fd *ofd, unsigned int what) +{ + struct osmo_signalfd *osfd = ofd->data; + struct signalfd_siginfo fdsi; + int rc; + + rc = read(ofd->fd, &fdsi, sizeof(fdsi)); + if (rc < 0) { + osmo_fd_unregister(ofd); + close(ofd->fd); + ofd->fd = -1; + return rc; + } + + osfd->cb(osfd, &fdsi); + + return 0; +}; + +/*! create a signalfd and register it with osmocom select loop. + * \paramin ctx talloc context from which osmo_signalfd is to be allocated + * \paramin set of signals to be accept via this file descriptor + * \paramin cb call-back function to be called for each arriving signal + * \paramin data opaque user-provided data to pass to callback + * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */ +struct osmo_signalfd * +osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data) +{ + struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd)); + int fd, rc; + + if (!osfd) + return NULL; + + osfd->data = data; + osfd->sigset = set; + osfd->cb = cb; + + fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK); + if (fd < 0) { + talloc_free(osfd); + return NULL; + } + + osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0); + rc = osmo_fd_register(&osfd->ofd); + if (rc < 0) { + close(fd); + talloc_free(osfd); + return NULL; + } + + return osfd; +} + +#endif /* HAVE_SYS_SIGNALFD_H */ + +/*! Request osmo_select_* to only service pending OSMO_FD_WRITE requests. Once all writes are done, + * osmo_select_shutdown_done() returns true. This allows for example to send all outbound packets before terminating the + * process. + * + * Usage example: + * + * static void signal_handler(int signum) + * { + * fprintf(stdout, "signal %u received\n", signum); + * + * switch (signum) { + * case SIGINT: + * case SIGTERM: + * // If the user hits Ctrl-C the third time, just terminate immediately. + * if (osmo_select_shutdown_requested() >= 2) + * exit(-1); + * // Request write-only mode in osmo_select_main_ctx() + * osmo_select_shutdown_request(); + * break; + * ... + * } + * + * main() + * { + * signal(SIGINT, &signal_handler); + * signal(SIGTERM, &signal_handler); + * + * ... + * + * // After the signal_handler issued osmo_select_shutdown_request(), osmo_select_shutdown_done() returns true + * // as soon as all write queues are empty. + * while (!osmo_select_shutdown_done()) { + * osmo_select_main_ctx(0); + * } + * } + */ +void osmo_select_shutdown_request(void) +{ + _osmo_select_shutdown_requested++; +}; + +/*! Return the number of times osmo_select_shutdown_request() was called before. */ +int osmo_select_shutdown_requested(void) +{ + return _osmo_select_shutdown_requested; +}; + +/*! Return true after osmo_select_shutdown_requested() was called, and after an osmo_select poll loop found no more + * pending OSMO_FD_WRITE on any registered socket. */ +bool osmo_select_shutdown_done(void) { + return _osmo_select_shutdown_done; +}; + +/*! @} */ + +#endif /* _HAVE_SYS_SELECT_H */
View file
libosmocore_1.8.0.tar.xz/src/core/sercomm.c
Changed
(renamed from src/sercomm.c)
View file
libosmocore_1.8.0.tar.xz/src/core/serial.c
Changed
(renamed from src/serial.c)
View file
libosmocore_1.8.0.tar.xz/src/core/signal.c
Changed
(renamed from src/signal.c)
View file
libosmocore_1.8.0.tar.xz/src/core/sockaddr_str.c
Changed
(renamed from src/sockaddr_str.c)
View file
libosmocore_1.8.0.tar.xz/src/core/socket.c
Added
@@ -0,0 +1,2062 @@ +/* + * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org> + * + * 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 "config.h" + +/*! \addtogroup socket + * @{ + * Osmocom socket convenience functions. + * + * \file socket.c */ + +#ifdef HAVE_SYS_SOCKET_H + +#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <net/if.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <ifaddrs.h> + +#ifdef HAVE_LIBSCTP +#include <netinet/sctp.h> +#endif + +static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, bool passive) +{ + struct addrinfo hints, *result, *rp; + char portbuf6; + int rc; + + snprintf(portbuf, sizeof(portbuf), "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + if (type == SOCK_RAW) { + /* Workaround for glibc, that returns EAI_SERVICE (-8) if + * SOCK_RAW and IPPROTO_GRE is used. + * http://sourceware.org/bugzilla/show_bug.cgi?id=15015 + */ + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } else { + hints.ai_socktype = type; + hints.ai_protocol = proto; + } + + if (passive) + hints.ai_flags |= AI_PASSIVE; + + rc = getaddrinfo(host, portbuf, &hints, &result); + if (rc != 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo(%s, %u) failed: %s\n", + host, port, gai_strerror(rc)); + return NULL; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + /* Workaround for glibc again */ + if (type == SOCK_RAW) { + rp->ai_socktype = SOCK_RAW; + rp->ai_protocol = proto; + } + } + + return result; +} + +#ifdef HAVE_LIBSCTP +/*! Retrieve an array of addrinfo with specified hints, one for each host in the hosts array. + * \paramout addrinfo array of addrinfo pointers, will be filled by the function on success. + * Its size must be at least the one of hosts. + * \paramin family Socket family like AF_INET, AF_INET6. + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM. + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP. + * \paramin hosts array of char pointers (strings) containing the addresses to query. + * \paramin host_cnt length of the hosts array (in items). + * \paramin port port number in host byte order. + * \paramin passive whether to include the AI_PASSIVE flag in getaddrinfo() hints. + * \returns 0 is returned on success together with a filled addrinfo array; negative on error + */ +static int addrinfo_helper_multi(struct addrinfo **addrinfo, uint16_t family, uint16_t type, uint8_t proto, + const char **hosts, size_t host_cnt, uint16_t port, bool passive) +{ + unsigned int i, j; + + for (i = 0; i < host_cnt; i++) { + addrinfoi = addrinfo_helper(family, type, proto, hostsi, port, passive); + if (!addrinfoi) { + for (j = 0; j < i; j++) + freeaddrinfo(addrinfoj); + return -EINVAL; + } + } + return 0; +} +#endif /* HAVE_LIBSCTP*/ + +static int socket_helper_tail(int sfd, unsigned int flags) +{ + int rc, on = 1; + uint8_t dscp = GET_OSMO_SOCK_F_DSCP(flags); + uint8_t prio = GET_OSMO_SOCK_F_PRIO(flags); + + if (flags & OSMO_SOCK_F_NONBLOCK) { + if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot set this socket unblocking: %s\n", + strerror(errno)); + close(sfd); + return -EINVAL; + } + } + + if (dscp) { + rc = osmo_sock_set_dscp(sfd, dscp); + if (rc) { + LOGP(DLGLOBAL, LOGL_ERROR, "cannot set IP DSCP of socket to %u: %s\n", + dscp, strerror(errno)); + /* we consider this a non-fatal error */ + } + } + + if (prio) { + rc = osmo_sock_set_priority(sfd, prio); + if (rc) { + LOGP(DLGLOBAL, LOGL_ERROR, "cannot set priority of socket to %u: %s\n", + prio, strerror(errno)); + /* we consider this a non-fatal error */ + } + } + + return 0; +} + +static int socket_helper(const struct addrinfo *rp, unsigned int flags) +{ + int sfd, rc; + + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, + "unable to create socket: %s\n", strerror(errno)); + return sfd; + } + + rc = socket_helper_tail(sfd, flags); + if (rc < 0) + return rc; + + return sfd; +} + +static int socket_helper_osa(const struct osmo_sockaddr *addr, uint16_t type, uint8_t proto, unsigned int flags) +{ + int sfd, rc; + + sfd = socket(addr->u.sa.sa_family, type, proto); + if (sfd == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, + "unable to create socket: %s\n", strerror(errno)); + return sfd; + } + + rc = socket_helper_tail(sfd, flags); + if (rc < 0) + return rc; + + return sfd; +} + +#ifdef HAVE_LIBSCTP +/* Fill buf with a string representation of the address set, in the form: + * buf_len == 0: "()" + * buf_len == 1: "hostA" + * buf_len >= 2: (hostA|hostB|...|...) + */ +static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, size_t host_cnt) +{ + int len = 0, offset = 0, rem = buf_len; + size_t i; + int ret; + char *after; + + if (buf_len < 3) + return -EINVAL; + + if (host_cnt != 1) { + ret = snprintf(buf, rem, "("); + if (ret < 0) + return ret; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + for (i = 0; i < host_cnt; i++) { + if (host_cnt == 1) + after = ""; + else + after = (i == (host_cnt - 1)) ? ")" : "|"; + ret = snprintf(buf + offset, rem, "%s%s", hostsi ? : "0.0.0.0", after); + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + + return len; +} +#endif /* HAVE_LIBSCTP */ + +static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags) +{ + int rc; + + /* Make sure to call 'listen' on a bound, connection-oriented sock */ + if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) { + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + rc = listen(fd, 10); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n", + strerror(errno)); + return rc; + } + break; + } + } + + if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) { + rc = osmo_sock_mcast_loop_set(fd, false); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n", + strerror(errno)); + return rc; + } + } + + if (flags & OSMO_SOCK_F_NO_MCAST_ALL) { + rc = osmo_sock_mcast_all_set(fd, false); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n", + strerror(errno)); + /* do not abort here, as this is just an + * optional additional optimization that only + * exists on Linux only */ + } + } + return 0; +} + +/*! Initialize a socket (including bind and/or connect) + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin local_host local host name or IP address in string form + * \paramin local_port local port number in host byte order + * \paramin remote_host remote host name or IP address in string form + * \paramin remote_port remote port number in host byte order + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket file descriptor on success; negative on error + * + * This function creates a new socket of the designated \a family, \a + * type and \a proto and optionally binds it to the \a local_host and \a + * local_port as well as optionally connects it to the \a remote_host + * and \q remote_port, depending on the value * of \a flags parameter. + * + * As opposed to \ref osmo_sock_init(), this function allows to combine + * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This + * is useful if you want to connect to a remote host/port, but still + * want to bind that socket to either a specific local alias IP and/or a + * specific local source port. + * + * You must specify either \ref OSMO_SOCK_F_BIND, or \ref + * OSMO_SOCK_F_CONNECT, or both. + * + * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to + * non-blocking mode. + */ +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 addrinfo *local = NULL, *remote = NULL, *rp; + int sfd = -1, rc, on = 1; + + bool local_ipv4 = false, local_ipv6 = false; + bool remote_ipv4 = false, remote_ipv6 = false; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " + "BIND or CONNECT flags\n"); + return -EINVAL; + } + + /* figure out local address infos */ + if (flags & OSMO_SOCK_F_BIND) { + local = addrinfo_helper(family, type, proto, local_host, local_port, true); + if (!local) + return -EINVAL; + } + + /* figure out remote address infos */ + if (flags & OSMO_SOCK_F_CONNECT) { + remote = addrinfo_helper(family, type, proto, remote_host, remote_port, false); + if (!remote) { + if (local) + freeaddrinfo(local); + + return -EINVAL; + } + } + + /* It must do a full run to ensure AF_UNSPEC does not fail. + * In case first local valid entry is IPv4 and only remote valid entry + * is IPv6 or vice versa */ + if (family == AF_UNSPEC) { + for (rp = local; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + local_ipv4 = true; + break; + case AF_INET6: + local_ipv6 = true; + break; + } + } + + for (rp = remote; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + remote_ipv4 = true; + break; + case AF_INET6: + remote_ipv6 = true; + break; + } + } + + if ((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) { + /* prioritize ipv6 as per RFC */ + if (local_ipv6 && remote_ipv6) + family = AF_INET6; + else if (local_ipv4 && remote_ipv4) + family = AF_INET; + else { + if (local) + freeaddrinfo(local); + if (remote) + freeaddrinfo(remote); + LOGP(DLGLOBAL, LOGL_ERROR, + "Unable to find a common protocol (IPv4 or IPv6) " + "for local host: %s and remote host: %s.\n", + local_host, remote_host); + return -ENODEV; + } + } else if ((flags & OSMO_SOCK_F_BIND)) { + family = local_ipv6 ? AF_INET6 : AF_INET; + } else if ((flags & OSMO_SOCK_F_CONNECT)) { + family = remote_ipv6 ? AF_INET6 : AF_INET; + } + } + + /* figure out local side of socket */ + if (flags & OSMO_SOCK_F_BIND) { + for (rp = local; rp != NULL; rp = rp->ai_next) { + /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */ + if (rp->ai_family != family) + continue; + + sfd = socket_helper(rp, flags); + if (sfd < 0) + continue; + + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + local_host, local_port, + strerror(errno)); + close(sfd); + continue; + } + } + + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n", + local_host, local_port, strerror(errno)); + close(sfd); + continue; + } + break; + } + + freeaddrinfo(local); + if (rp == NULL) { + if (remote) + freeaddrinfo(remote); + LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n", + local_host, local_port); + return -ENODEV; + } + } + + /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it + was already closed and func returned. If OSMO_SOCK_F_BIND is not + set, then sfd = -1 */ + + /* figure out remote side of socket */ + if (flags & OSMO_SOCK_F_CONNECT) { + for (rp = remote; rp != NULL; rp = rp->ai_next) { + /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */ + if (rp->ai_family != family) + continue; + + if (sfd < 0) { + sfd = socket_helper(rp, flags); + if (sfd < 0) + continue; + } + + rc = connect(sfd, rp->ai_addr, rp->ai_addrlen); + if (rc != 0 && errno != EINPROGRESS) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n", + remote_host, remote_port, strerror(errno)); + /* We want to maintain the bind socket if bind was enabled */ + if (!(flags & OSMO_SOCK_F_BIND)) { + close(sfd); + sfd = -1; + } + continue; + } + break; + } + + freeaddrinfo(remote); + if (rp == NULL) { + LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n", + remote_host, remote_port); + if (sfd >= 0) + close(sfd); + return -ENODEV; + } + } + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + return sfd; +} + +#define _SOCKADDR_TO_STR(dest, sockaddr) do { \ + if (osmo_sockaddr_str_from_sockaddr(dest, &sockaddr->u.sas)) \ + osmo_strlcpy((dest)->ip, "Invalid IP", 11); \ + } while (0) + +/*! Initialize a socket (including bind and/or connect) + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin local local address + * \paramin remote remote address + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket file descriptor on success; negative on error + * + * This function creates a new socket of the + * \a type and \a proto and optionally binds it to the \a local + * as well as optionally connects it to the \a remote + * depending on the value * of \a flags parameter. + * + * As opposed to \ref osmo_sock_init(), this function allows to combine + * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This + * is useful if you want to connect to a remote host/port, but still + * want to bind that socket to either a specific local alias IP and/or a + * specific local source port. + * + * You must specify either \ref OSMO_SOCK_F_BIND, or \ref + * OSMO_SOCK_F_CONNECT, or both. + * + * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to + * non-blocking mode. + */ +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 sfd = -1, rc, on = 1; + struct osmo_sockaddr_str _sastr = {}; + struct osmo_sockaddr_str *sastr = &_sastr; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " + "BIND or CONNECT flags\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_BIND) && !local) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot BIND when local is NULL\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_CONNECT) && !remote) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot CONNECT when remote is NULL\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_BIND) && + (flags & OSMO_SOCK_F_CONNECT) && + local->u.sa.sa_family != remote->u.sa.sa_family) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: the family for " + "local and remote endpoint must be same.\n"); + return -EINVAL; + } + + /* figure out local side of socket */ + if (flags & OSMO_SOCK_F_BIND) { + sfd = socket_helper_osa(local, type, proto, flags); + if (sfd < 0) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: " OSMO_SOCKADDR_STR_FMT "\n", + OSMO_SOCKADDR_STR_FMT_ARGS(sastr)); + return -ENODEV; + } + + if (proto != IPPROTO_UDP || (flags & OSMO_SOCK_F_UDP_REUSEADDR)) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", + OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); + close(sfd); + return rc; + } + } + + if (bind(sfd, &local->u.sa, sizeof(struct osmo_sockaddr)) == -1) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", + OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); + close(sfd); + return -1; + } + } + + /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it + was already closed and func returned. If OSMO_SOCK_F_BIND is not + set, then sfd = -1 */ + + /* figure out remote side of socket */ + if (flags & OSMO_SOCK_F_CONNECT) { + if (sfd < 0) { + sfd = socket_helper_osa(remote, type, proto, flags); + if (sfd < 0) { + return sfd; + } + } + + rc = connect(sfd, &remote->u.sa, sizeof(struct osmo_sockaddr)); + if (rc != 0 && errno != EINPROGRESS) { + _SOCKADDR_TO_STR(sastr, remote); + LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: " OSMO_SOCKADDR_STR_FMT ": %s\n", + OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(errno)); + close(sfd); + return rc; + } + } + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + return sfd; +} + +#ifdef HAVE_LIBSCTP + +/* Check whether there's an IPv6 Addr as first option of any addrinfo item in the addrinfo set */ +static void addrinfo_has_v4v6addr(const struct addrinfo **result, size_t result_count, bool *has_v4, bool *has_v6) +{ + size_t host_idx; + *has_v4 = false; + *has_v6 = false; + + for (host_idx = 0; host_idx < result_count; host_idx++) { + if (resulthost_idx->ai_family == AF_INET) + *has_v4 = true; + else if (resulthost_idx->ai_family == AF_INET6) + *has_v6 = true; + } +} + +/* Check whether there's an IPv6 with IN6ADDR_ANY_INIT ("::") */ +static bool addrinfo_has_in6addr_any(const struct addrinfo **result, size_t result_count) +{ + size_t host_idx; + struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; + + for (host_idx = 0; host_idx < result_count; host_idx++) { + if (resulthost_idx->ai_family != AF_INET6) + continue; + if (memcmp(&((struct sockaddr_in6 *)resulthost_idx->ai_addr)->sin6_addr, + &in6addr_any, sizeof(in6addr_any)) == 0) + return true; + } + return false; +} + +static int socket_helper_multiaddr(uint16_t family, uint16_t type, uint8_t proto, unsigned int flags) +{ + int sfd, rc; + + sfd = socket(family, type, proto); + if (sfd == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, + "Unable to create socket: %s\n", strerror(errno)); + return sfd; + } + + rc = socket_helper_tail(sfd, flags); + if (rc < 0) + return rc; + + return sfd; +} + +/* Build array of addresses taking first addrinfo result of the requested family + * for each host in addrs_buf. */ +static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result, + const char **hosts, unsigned int host_cont, + uint8_t *addrs_buf, size_t addrs_buf_len) { + size_t host_idx, offset = 0; + const struct addrinfo *rp; + + for (host_idx = 0; host_idx < host_cont; host_idx++) { + /* Addresses are ordered based on RFC 3484, see man getaddrinfo */ + for (rp = resulthost_idx; rp != NULL; rp = rp->ai_next) { + if (family != AF_UNSPEC && rp->ai_family != family) + continue; + if (offset + rp->ai_addrlen > addrs_buf_len) { + LOGP(DLGLOBAL, LOGL_ERROR, "Output buffer to small: %zu\n", + addrs_buf_len); + return -ENOSPC; + } + memcpy(addrs_buf + offset, rp->ai_addr, rp->ai_addrlen); + offset += rp->ai_addrlen; + break; + } + if (!rp) { /* No addr could be bound for this host! */ + LOGP(DLGLOBAL, LOGL_ERROR, "No suitable remote address found for host: %s\n", + hostshost_idx); + return -ENODEV; + } + } + return 0; +} + +/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses. + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin local_hosts array of char pointers (strings), each containing local host name or IP address in string form + * \paramin local_hosts_cnt length of local_hosts (in items) + * \paramin local_port local port number in host byte order + * \paramin remote_host array of char pointers (strings), each containing remote host name or IP address in string form + * \paramin remote_hosts_cnt length of remote_hosts (in items) + * \paramin remote_port remote port number in host byte order + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket file descriptor on success; negative on error + * + * This function is similar to \ref osmo_sock_init2(), but can be passed an + * array of local or remote addresses for protocols supporting multiple + * addresses per socket, like SCTP (currently only one supported). This function + * should not be used by protocols not supporting this kind of features, but + * rather \ref osmo_sock_init2() should be used instead. + * See \ref osmo_sock_init2() for more information on flags and general behavior. + */ +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) + +{ + struct addrinfo *res_locOSMO_SOCK_MAX_ADDRS, *res_remOSMO_SOCK_MAX_ADDRS; + int sfd = -1, rc, on = 1; + unsigned int i; + bool loc_has_v4addr, rem_has_v4addr; + bool loc_has_v6addr, rem_has_v6addr; + struct sockaddr_in6 addrs_bufOSMO_SOCK_MAX_ADDRS; + char strbuf512; + + /* updated later in case of AF_UNSPEC */ + loc_has_v4addr = rem_has_v4addr = (family == AF_INET); + loc_has_v6addr = rem_has_v6addr = (family == AF_INET6); + + /* TODO: So far this function is only aimed for SCTP, but could be + reused in the future for other protocols with multi-addr support */ + if (proto != IPPROTO_SCTP) + return -ENOTSUP; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " + "BIND or CONNECT flags\n"); + return -EINVAL; + } + + if (((flags & OSMO_SOCK_F_BIND) && !local_hosts_cnt) || + ((flags & OSMO_SOCK_F_CONNECT) && !remote_hosts_cnt) || + local_hosts_cnt > OSMO_SOCK_MAX_ADDRS || + remote_hosts_cnt > OSMO_SOCK_MAX_ADDRS) + return -EINVAL; + + /* figure out local side of socket */ + if (flags & OSMO_SOCK_F_BIND) { + rc = addrinfo_helper_multi(res_loc, family, type, proto, local_hosts, + local_hosts_cnt, local_port, true); + if (rc < 0) + return -EINVAL; + /* Figure out if there's any IPV4 or IPv6 addr in the set */ + if (family == AF_UNSPEC) + addrinfo_has_v4v6addr((const struct addrinfo **)res_loc, local_hosts_cnt, + &loc_has_v4addr, &loc_has_v6addr); + } + /* figure out remote side of socket */ + if (flags & OSMO_SOCK_F_CONNECT) { + rc = addrinfo_helper_multi(res_rem, family, type, proto, remote_hosts, + remote_hosts_cnt, remote_port, false); + if (rc < 0) { + rc = -EINVAL; + goto ret_freeaddrinfo_loc; + } + /* Figure out if there's any IPv4 or IPv6 addr in the set */ + if (family == AF_UNSPEC) + addrinfo_has_v4v6addr((const struct addrinfo **)res_rem, remote_hosts_cnt, + &rem_has_v4addr, &rem_has_v6addr); + } + + if (((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) && + !addrinfo_has_in6addr_any((const struct addrinfo **)res_loc, local_hosts_cnt) && + (loc_has_v4addr != rem_has_v4addr || loc_has_v6addr != rem_has_v6addr)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Invalid v4 vs v6 in local vs remote addresses\n"); + rc = -EINVAL; + goto ret_freeaddrinfo; + } + + sfd = socket_helper_multiaddr(loc_has_v6addr ? AF_INET6 : AF_INET, + type, proto, flags); + if (sfd < 0) { + rc = sfd; + goto ret_freeaddrinfo; + } + + if (flags & OSMO_SOCK_F_BIND) { + /* Since so far we only allow IPPROTO_SCTP in this function, + no need to check below for "proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR" */ + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + strbuf, local_port, + strerror(errno)); + goto ret_close; + } + + /* Build array of addresses taking first entry for each host. + TODO: Ideally we should use backtracking storing last used + indexes and trying next combination if connect() fails .*/ + /* We could alternatively use v4v6 mapped addresses and call sctp_bindx once with an array od sockaddr_in6 */ + rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_loc, + local_hosts, local_hosts_cnt, + (uint8_t*)addrs_buf, sizeof(addrs_buf)); + if (rc < 0) { + rc = -ENODEV; + goto ret_close; + } + + rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, local_hosts_cnt, SCTP_BINDX_ADD_ADDR); + if (rc == -1) { + multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); + LOGP(DLGLOBAL, LOGL_NOTICE, "unable to bind socket: %s:%u: %s\n", + strbuf, local_port, strerror(errno)); + rc = -ENODEV; + goto ret_close; + } + } + + if (flags & OSMO_SOCK_F_CONNECT) { + /* Build array of addresses taking first of same family for each host. + TODO: Ideally we should use backtracking storing last used + indexes and trying next combination if connect() fails .*/ + rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_rem, + remote_hosts, remote_hosts_cnt, + (uint8_t*)addrs_buf, sizeof(addrs_buf)); + if (rc < 0) { + rc = -ENODEV; + goto ret_close; + } + + rc = sctp_connectx(sfd, (struct sockaddr *)addrs_buf, remote_hosts_cnt, NULL); + if (rc != 0 && errno != EINPROGRESS) { + multiaddr_snprintf(strbuf, sizeof(strbuf), remote_hosts, remote_hosts_cnt); + LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n", + strbuf, remote_port, strerror(errno)); + rc = -ENODEV; + goto ret_close; + } + } + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + rc = sfd; + goto ret_freeaddrinfo; + +ret_close: + if (sfd >= 0) + close(sfd); +ret_freeaddrinfo: + if (flags & OSMO_SOCK_F_CONNECT) { + for (i = 0; i < remote_hosts_cnt; i++) + freeaddrinfo(res_remi); + } +ret_freeaddrinfo_loc: + if (flags & OSMO_SOCK_F_BIND) { + for (i = 0; i < local_hosts_cnt; i++) + freeaddrinfo(res_loci); + } + return rc; +} +#endif /* HAVE_LIBSCTP */ + +/*! Initialize a socket (including bind/connect) + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin host remote host name or IP address in string form + * \paramin port remote port number in host byte order + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket file descriptor on success; negative on error + * + * This function creates a new socket of the designated \a family, \a + * type and \a proto and optionally binds or connects it, depending on + * the value of \a flags parameter. + */ +int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, unsigned int flags) +{ + struct addrinfo *result, *rp; + int sfd = -1; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */ + int on = 1; + int rc; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == + (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:" + " %s:%u\n", host, port); + return -EINVAL; + } + + result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND); + if (!result) + return -EINVAL; + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket_helper(rp, flags); + if (sfd == -1) + continue; + + if (flags & OSMO_SOCK_F_CONNECT) { + rc = connect(sfd, rp->ai_addr, rp->ai_addrlen); + if (rc != 0 && errno != EINPROGRESS) { + close(sfd); + continue; + } + } else { + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + host, port, strerror(errno)); + close(sfd); + continue; + } + } + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:" + "%s:%u: %s\n", + host, port, strerror(errno)); + close(sfd); + continue; + } + } + break; + } + freeaddrinfo(result); + + if (rp == NULL) { + LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n", + host, port); + return -ENODEV; + } + + if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket: %s:%u: %s\n", host, + port, strerror(errno)); + close(sfd); + sfd = -1; + } + } + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + return sfd; +} + +/*! fill \ref osmo_fd for a give sfd + * \paramout ofd file descriptor (will be filled in) + * \paramin sfd socket file descriptor + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function fills the \a ofd structure. + */ +static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd, unsigned int flags) +{ + int rc; + + if (sfd < 0) + return sfd; + + ofd->fd = sfd; + ofd->when = OSMO_FD_READ; + + /* if we're doing a non-blocking connect, the completion will be signaled + * by marking the fd as WRITE-able. So in this exceptional case, we're + * also interested in when the socket becomes write-able */ + if ((flags & (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) == + (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) { + ofd->when |= OSMO_FD_WRITE; + } + + rc = osmo_fd_register(ofd); + if (rc < 0) { + close(sfd); + return rc; + } + + return sfd; +} + +/*! Initialize a socket and fill \ref osmo_fd + * \paramout ofd file descriptor (will be filled in) + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin host remote host name or IP address in string form + * \paramin port remote port number in host byte order + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function creates (and optionall binds/connects) a socket using + * \ref osmo_sock_init, but also fills the \a ofd structure. + */ +int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, + const char *host, uint16_t port, unsigned int flags) +{ + return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags), flags); +} + +/*! Initialize a socket and fill \ref osmo_fd + * \paramout ofd file descriptor (will be filled in) + * \paramin family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin local_host local host name or IP address in string form + * \paramin local_port local port number in host byte order + * \paramin remote_host remote host name or IP address in string form + * \paramin remote_port remote port number in host byte order + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function creates (and optionall binds/connects) a socket using + * \ref osmo_sock_init2, but also fills the \a ofd structure. + */ +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) +{ + return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host, + local_port, remote_host, remote_port, flags), 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) +{ + return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags), flags); +} + +/*! Initialize a socket and fill \ref sockaddr + * \paramout ss socket address (will be filled in) + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function creates (and optionall binds/connects) a socket using + * \ref osmo_sock_init, but also fills the \a ss structure. + */ +int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, + uint8_t proto, unsigned int flags) +{ + char hostNI_MAXHOST; + uint16_t port; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int s, sa_len; + + /* determine port and host from ss */ + switch (ss->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *) ss; + sa_len = sizeof(struct sockaddr_in); + port = ntohs(sin->sin_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) ss; + sa_len = sizeof(struct sockaddr_in6); + port = ntohs(sin6->sin6_port); + break; + default: + return -EINVAL; + } + + s = getnameinfo(ss, sa_len, host, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + if (s != 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:" + " %s\n", strerror(errno)); + return s; + } + + return osmo_sock_init(ss->sa_family, type, proto, host, port, flags); +} + +static int sockaddr_equal(const struct sockaddr *a, + const struct sockaddr *b, unsigned int len) +{ + struct sockaddr_in *sin_a, *sin_b; + struct sockaddr_in6 *sin6_a, *sin6_b; + + if (a->sa_family != b->sa_family) + return 0; + + switch (a->sa_family) { + case AF_INET: + sin_a = (struct sockaddr_in *)a; + sin_b = (struct sockaddr_in *)b; + if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, + sizeof(struct in_addr))) + return 1; + break; + case AF_INET6: + sin6_a = (struct sockaddr_in6 *)a; + sin6_b = (struct sockaddr_in6 *)b; + if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, + sizeof(struct in6_addr))) + return 1; + break; + } + return 0; +} + +/* linux has a default route: +local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 +*/ +static int sockaddr_is_local_routed(const struct sockaddr *a) +{ +#if __linux__ + if (a->sa_family != AF_INET) + return 0; + + uint32_t address = ((struct sockaddr_in *)a)->sin_addr.s_addr; /* already BE */ + uint32_t eightmask = htonl(0xff000000); /* /8 mask */ + uint32_t local_prefix_127 = htonl(0x7f000000); /* 127.0.0.0 */ + + if ((address & eightmask) == local_prefix_127) + return 1; +#endif + return 0; +} + +/*! Determine if the given address is a local address + * \paramin addr Socket Address + * \paramin addrlen Length of socket address in bytes + * \returns 1 if address is local, 0 otherwise. + */ +int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen) +{ + struct ifaddrs *ifaddr, *ifa; + + if (sockaddr_is_local_routed(addr)) + return 1; + + if (getifaddrs(&ifaddr) == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:" + " %s\n", strerror(errno)); + return -EIO; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) { + freeifaddrs(ifaddr); + return 1; + } + } + + freeifaddrs(ifaddr); + return 0; +} + +/*! Determine if the given address is an ANY address ("0.0.0.0", "::"). Port is not checked. + * \paramin addr Socket Address + * \paramin addrlen Length of socket address in bytes + * \returns 1 if address is ANY, 0 otherwise. -1 is address family not supported/detected. + */ +int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr) +{ + switch (addr->u.sa.sa_family) { + case AF_INET6: { + struct in6_addr ip6_any = IN6ADDR_ANY_INIT; + return memcmp(&addr->u.sin6.sin6_addr, + &ip6_any, sizeof(ip6_any)) == 0; + } + case AF_INET: + return addr->u.sin.sin_addr.s_addr == INADDR_ANY; + default: + return -1; + } +} + +/*! Convert sockaddr_in to IP address as char string and port as uint16_t. + * \paramout addr String buffer to write IP address to, or NULL. + * \paramout addr_len Size of \a addr. + * \paramout port Pointer to uint16_t to write the port number to, or NULL. + * \paramin sin Sockaddr to convert. + * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL. + */ +size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, + const struct sockaddr_in *sin) +{ + if (port) + *port = ntohs(sin->sin_port); + + if (addr) + return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len); + + return 0; +} + +/*! Convert sockaddr to IP address as char string and port as uint16_t. + * \paramout addr String buffer to write IP address to, or NULL. + * \paramout addr_len Size of \a addr. + * \paramout port Pointer to uint16_t to write the port number to, or NULL. + * \paramin sa Sockaddr to convert. + * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL. + */ +unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port, + const struct sockaddr *sa) +{ + + const struct sockaddr_in6 *sin6; + + switch (sa->sa_family) { + case AF_INET: + return osmo_sockaddr_in_to_str_and_uint(addr, addr_len, port, + (const struct sockaddr_in *)sa); + case AF_INET6: + sin6 = (const struct sockaddr_in6 *)sa; + if (port) + *port = ntohs(sin6->sin6_port); + if (addr && inet_ntop(sa->sa_family, &sin6->sin6_addr, addr, addr_len)) + return strlen(addr); + break; + } + return 0; +} + +/*! inet_ntop() wrapper for a struct sockaddr. + * \paramin sa source sockaddr to get the address from. + * \paramout dst string buffer of at least INET6_ADDRSTRLEN size. + * \returns returns a non-null pointer to dst. NULL is returned if there was an + * error, with errno set to indicate the error. + */ +const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst) +{ + const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa; + return inet_ntop(osa->u.sa.sa_family, + osa->u.sa.sa_family == AF_INET6 ? + (const void *)&osa->u.sin6.sin6_addr : + (const void *)&osa->u.sin.sin_addr, + dst, INET6_ADDRSTRLEN); +} + +/*! Get sockaddr port content (in host byte order) + * \paramin sa source sockaddr to get the port from. + * \returns returns the sockaddr port in host byte order + */ +uint16_t osmo_sockaddr_port(const struct sockaddr *sa) +{ + const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa; + switch (osa->u.sa.sa_family) { + case AF_INET6: + return ntohs(osa->u.sin6.sin6_port); + case AF_INET: + return ntohs(osa->u.sin.sin_port); + } + return 0; +} + +/*! Set sockaddr port content (to network byte order). + * \paramout sa sockaddr to set the port of. + * \paramin port port nr to set. + */ +void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port) +{ + struct osmo_sockaddr *osa = (struct osmo_sockaddr *)sa; + switch (osa->u.sa.sa_family) { + case AF_INET6: + osa->u.sin6.sin6_port = htons(port); + return; + case AF_INET: + osa->u.sin.sin_port = htons(port); + return; + } +} + +static unsigned int in6_addr_netmask_to_prefixlen(const struct in6_addr *netmask) +{ + #if defined(__linux__) + #define ADDRFIELD(i) s6_addr32i + #else + #define ADDRFIELD(i) __u6_addr.__u6_addr32i + #endif + + unsigned int i, j, prefix = 0; + + for (j = 0; j < 4; j++) { + uint32_t bits = netmask->ADDRFIELD(j); + uint8_t *b = (uint8_t *)&bits; + for (i = 0; i < 4; i++) { + while (bi & 0x80) { + prefix++; + bi = bi << 1; + } + } + } + + #undef ADDRFIELD + + return prefix; +} + +static unsigned int in_addr_netmask_to_prefixlen(const struct in_addr *netmask) +{ + uint32_t bits = netmask->s_addr; + uint8_t *b = (uint8_t *)&bits; + unsigned int i, prefix = 0; + + for (i = 0; i < 4; i++) { + while (bi & 0x80) { + prefix++; + bi = bi << 1; + } + } + return prefix; +} + +/*! Convert netmask to prefix length representation + * \paramin netmask sockaddr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit) + * \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask), negative on error. + */ +int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *netmask) +{ + switch (netmask->u.sa.sa_family) { + case AF_INET6: + return in6_addr_netmask_to_prefixlen(&netmask->u.sin6.sin6_addr); + case AF_INET: + return in_addr_netmask_to_prefixlen(&netmask->u.sin.sin_addr); + default: + return -ENOTSUP; + } +} + +/*! Initialize a unix domain socket (including bind/connect) + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin socket_path path to identify the socket + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function creates a new unix domain socket, \a + * type and \a proto and optionally binds or connects it, depending on + * the value of \a flags parameter. + */ +#if defined(__clang__) && defined(SUN_LEN) +__attribute__((no_sanitize("undefined"))) +#endif +int osmo_sock_unix_init(uint16_t type, uint8_t proto, + const char *socket_path, unsigned int flags) +{ + struct sockaddr_un local; + int sfd, rc; + unsigned int namelen; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == + (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) + return -EINVAL; + + local.sun_family = AF_UNIX; + /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */ + if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n", + sizeof(local.sun_path), socket_path); + return -ENOSPC; + } + +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + sfd = socket(AF_UNIX, type, proto); + if (sfd < 0) + return -1; + + if (flags & OSMO_SOCK_F_CONNECT) { + rc = connect(sfd, (struct sockaddr *)&local, namelen); + if (rc < 0) + goto err; + } else { + unlink(local.sun_path); + rc = bind(sfd, (struct sockaddr *)&local, namelen); + if (rc < 0) + goto err; + } + + rc = socket_helper_tail(sfd, flags); + if (rc < 0) + return rc; + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + return sfd; +err: + close(sfd); + return -1; +} + +/*! Initialize a unix domain socket and fill \ref osmo_fd + * \paramout ofd file descriptor (will be filled in) + * \paramin type Socket type like SOCK_DGRAM, SOCK_STREAM + * \paramin proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \paramin socket_path path to identify the socket + * \paramin flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket fd on success; negative on error + * + * This function creates (and optionally binds/connects) a socket + * using osmo_sock_unix_init, but also fills the ofd structure. + */ +int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, + const char *socket_path, unsigned int flags) +{ + return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags), flags); +} + +/*! Get the IP and/or port number on socket in separate string buffers. + * \paramin fd file descriptor of socket + * \paramout ip IP address (will be filled in when not NULL) + * \paramin ip_len length of the ip buffer + * \paramout port number (will be filled in when not NULL) + * \paramin port_len length of the port buffer + * \paramin local (true) or remote (false) name will get looked at + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local) +{ + struct sockaddr_storage sa; + socklen_t len = sizeof(sa); + char ipbufINET6_ADDRSTRLEN, portbuf6; + int rc; + + rc = local ? getsockname(fd, (struct sockaddr*)&sa, &len) : getpeername(fd, (struct sockaddr*)&sa, &len); + if (rc < 0) + return rc; + + rc = getnameinfo((const struct sockaddr*)&sa, len, ipbuf, sizeof(ipbuf), + portbuf, sizeof(portbuf), + NI_NUMERICHOST | NI_NUMERICSERV); + if (rc < 0) + return rc; + + if (ip) + strncpy(ip, ipbuf, ip_len); + if (port) + strncpy(port, portbuf, port_len); + return 0; +} + +/*! Get local IP address on socket + * \paramin fd file descriptor of socket + * \paramout ip IP address (will be filled in) + * \paramin len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_local_ip(int fd, char *ip, size_t len) +{ + return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true); +} + +/*! Get local port on socket + * \paramin fd file descriptor of socket + * \paramout port number (will be filled in) + * \paramin len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_local_ip_port(int fd, char *port, size_t len) +{ + return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true); +} + +/*! Get remote IP address on socket + * \paramin fd file descriptor of socket + * \paramout ip IP address (will be filled in) + * \paramin len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_remote_ip(int fd, char *ip, size_t len) +{ + return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false); +} + +/*! Get remote port on socket + * \paramin fd file descriptor of socket + * \paramout port number (will be filled in) + * \paramin len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len) +{ + return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false); +} + +/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)". + * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a + * caller provided string buffer, to avoid the dynamic talloc allocation. + * \paramin ctx talloc context from which to allocate string buffer + * \paramin fd file descriptor of socket + * \returns string identifying the connection of this socket, talloc'd from ctx. + */ +char *osmo_sock_get_name(const void *ctx, int fd) +{ + char strOSMO_SOCK_NAME_MAXLEN; + int rc; + rc = osmo_sock_get_name_buf(str, sizeof(str), fd); + if (rc <= 0) + return NULL; + return talloc_asprintf(ctx, "(%s)", str); +} + +/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10". + * This does not include braces like osmo_sock_get_name(). + * \paramout str Destination string buffer. + * \paramin str_len sizeof(str). + * \paramin fd File descriptor of socket. + * \return String length as returned by snprintf(), or negative on error. + */ +int osmo_sock_get_name_buf(char *str, size_t str_len, int fd) +{ + char hostbuf_lINET6_ADDRSTRLEN, hostbuf_rINET6_ADDRSTRLEN; + char portbuf_l6, portbuf_r6; + int rc; + + /* get local */ + if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true))) { + osmo_strlcpy(str, "<error-in-getsockname>", str_len); + return rc; + } + + /* get remote */ + if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0) + return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l); + + return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l); +} + +/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10". + * This does not include braces like osmo_sock_get_name(). + * \paramin fd File descriptor of socket. + * \return Static string buffer containing the result. + */ +const char *osmo_sock_get_name2(int fd) +{ + static __thread char strOSMO_SOCK_NAME_MAXLEN; + osmo_sock_get_name_buf(str, sizeof(str), fd); + return str; +} + +/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10". + * This does not include braces like osmo_sock_get_name(). + * \paramin fd File descriptor of socket. + * \return Static string buffer containing the result. + */ +char *osmo_sock_get_name2_c(const void *ctx, int fd) +{ + char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN); + if (!str) + return NULL; + osmo_sock_get_name_buf(str, OSMO_SOCK_NAME_MAXLEN, fd); + return str; +} + +static int sock_get_domain(int fd) +{ + int domain; +#ifdef SO_DOMAIN + socklen_t dom_len = sizeof(domain); + int rc; + + rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len); + if (rc < 0) + return rc; +#else + /* This of course sucks, but what shall we do on OSs like + * FreeBSD that don't seem to expose a method by which one can + * learn the address family of a socket? */ + domain = AF_INET; +#endif + return domain; +} + + +/*! Activate or de-activate local loop-back of transmitted multicast packets + * \paramin fd file descriptor of related socket + * \paramin enable Enable (true) or disable (false) loop-back + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_loop_set(int fd, bool enable) +{ + int domain, loop = 0; + + if (enable) + loop = 1; + + domain = sock_get_domain(fd); + if (domain < 0) + return domain; + + switch (domain) { + case AF_INET: + return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); + case AF_INET6: + return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)); + default: + return -EINVAL; + } +} + +/*! Set the TTL of outbound multicast packets + * \paramin fd file descriptor of related socket + * \paramin ttl TTL of to-be-sent multicast packets + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl) +{ + int domain, ttli = ttl; + + domain = sock_get_domain(fd); + if (domain < 0) + return domain; + + switch (domain) { + case AF_INET: + return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli)); + case AF_INET6: + return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli)); + default: + return -EINVAL; + } +} + +/*! Set the network device to which we should bind the multicast socket + * \paramin fd file descriptor of related socket + * \paramin ifname name of network interface to user for multicast + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_iface_set(int fd, const char *ifname) +{ + unsigned int ifindex; + struct ip_mreqn mr; + + /* first, resolve interface name to ifindex */ + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -errno; + + /* next, configure kernel to use that ifindex for this sockets multicast traffic */ + memset(&mr, 0, sizeof(mr)); + mr.imr_ifindex = ifindex; + return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mr, sizeof(mr)); +} + + +/*! Enable/disable receiving all multicast packets, even for non-subscribed groups + * \paramin fd file descriptor of related socket + * \paramin enable Enable or Disable receiving of all packets + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_all_set(int fd, bool enable) +{ + int domain, all = 0; + + if (enable) + all = 1; + + domain = sock_get_domain(fd); + if (domain < 0) + return domain; + + switch (domain) { + case AF_INET: +#ifdef IP_MULTICAST_ALL + return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all)); +#endif + case AF_INET6: + /* there seems no equivalent ?!? */ + default: + return -EINVAL; + } +} + +/* FreeBSD calls the socket option differently */ +#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP) +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif + +/*! Subscribe to the given IP multicast group + * \paramin fd file descriptor of related scoket + * \paramin grp_addr ASCII representation of the multicast group address + * \returns 0 on success; negative otherwise */ +int osmo_sock_mcast_subscribe(int fd, const char *grp_addr) +{ + int rc, domain; + struct ip_mreq mreq; + struct ipv6_mreq mreq6; + struct in6_addr i6a; + + domain = sock_get_domain(fd); + if (domain < 0) + return domain; + + switch (domain) { + case AF_INET: + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = inet_addr(grp_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); +#ifdef IPV6_ADD_MEMBERSHIP + case AF_INET6: + memset(&mreq6, 0, sizeof(mreq6)); + rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a); + if (rc < 0) + return -EINVAL; + mreq6.ipv6mr_multiaddr = i6a; + return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)); +#endif + default: + return -EINVAL; + } +} + +/*! Determine the matching local IP-address for a given remote IP-Address. + * \paramout local_ip caller provided memory for resulting local IP-address + * \paramin remote_ip remote IP-address + * \returns 0 on success; negative otherwise + * + * The function accepts IPv4 and IPv6 address strings. The caller must provide + * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as + * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */ +int osmo_sock_local_ip(char *local_ip, const char *remote_ip) +{ + int sfd; + int rc; + struct addrinfo addrinfo_hint; + struct addrinfo *addrinfo = NULL; + struct sockaddr_storage local_addr; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + socklen_t local_addr_len; + uint16_t family; + + /* Find out the address family (AF_INET or AF_INET6?) */ + memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint)); + addrinfo_hint.ai_family = AF_UNSPEC; + addrinfo_hint.ai_flags = AI_NUMERICHOST; + rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo); + if (rc) + return -EINVAL; + family = addrinfo->ai_family; + freeaddrinfo(addrinfo); + + /* Connect a dummy socket to trick the kernel into determining the + * ip-address of the interface that would be used if we would send + * out an actual packet */ + sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT); + if (sfd < 0) + return -EINVAL; + + /* Request the IP address of the interface that the kernel has + * actually choosen. */ + memset(&local_addr, 0, sizeof(local_addr)); + local_addr_len = sizeof(local_addr); + rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len); + close(sfd); + if (rc < 0) + return -EINVAL; + + switch (local_addr.ss_family) { + case AF_INET: + sin = (struct sockaddr_in*)&local_addr; + if (!inet_ntop(AF_INET, &sin->sin_addr, local_ip, INET_ADDRSTRLEN)) + return -EINVAL; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6*)&local_addr; + if (!inet_ntop(AF_INET6, &sin6->sin6_addr, local_ip, INET6_ADDRSTRLEN)) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +/*! Determine the matching local address for a given remote address. + * \paramout local_ip caller provided memory for resulting local address + * \paramin remote_ip remote address + * \returns 0 on success; negative otherwise + */ +int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip, const struct osmo_sockaddr *remote_ip) +{ + int sfd; + int rc; + socklen_t local_ip_len; + + sfd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, NULL, remote_ip, OSMO_SOCK_F_CONNECT); + if (sfd < 0) + return -EINVAL; + + memset(local_ip, 0, sizeof(*local_ip)); + local_ip_len = sizeof(*local_ip); + rc = getsockname(sfd, (struct sockaddr *)local_ip, &local_ip_len); + close(sfd); + + return rc; +} + +/*! Copy the addr part, the IP address octets in network byte order, to a buffer. + * Useful for encoding network protocols. + * \paramout dst Write octets to this buffer. + * \paramin dst_maxlen Space available in buffer. + * \paramin os Sockaddr to copy IP of. + * \return nr of octets written on success, negative on error. + */ +int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os) +{ + const void *addr; + size_t len; + switch (os->u.sa.sa_family) { + case AF_INET: + addr = &os->u.sin.sin_addr; + len = sizeof(os->u.sin.sin_addr); + break; + case AF_INET6: + addr = &os->u.sin6.sin6_addr; + len = sizeof(os->u.sin6.sin6_addr); + break; + default: + return -ENOTSUP; + } + if (dst_maxlen < len) + return -ENOSPC; + memcpy(dst, addr, len); + return len; +} + +/*! Copy the addr part, the IP address octets in network byte order, from a buffer. + * Useful for decoding network protocols. + * \paramout os Write IP address to this sockaddr. + * \paramin src Source buffer to read IP address octets from. + * \paramin src_len Number of octets to copy. + * \return number of octets read on success, negative on error. + */ +int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len) +{ + void *addr; + size_t len; + *os = (struct osmo_sockaddr){0}; + switch (src_len) { + case sizeof(os->u.sin.sin_addr): + os->u.sa.sa_family = AF_INET; + addr = &os->u.sin.sin_addr; + len = sizeof(os->u.sin.sin_addr); + break; + case sizeof(os->u.sin6.sin6_addr): + os->u.sin6.sin6_family = AF_INET6; + addr = &os->u.sin6.sin6_addr; + len = sizeof(os->u.sin6.sin6_addr); + break; + default: + return -ENOTSUP; + } + memcpy(addr, src, len); + return len; +} + +/*! Compare two osmo_sockaddr. + * \paramin a + * \paramin b + * \return 0 if a and b are equal. Otherwise it follows memcmp() + */ +int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, + const struct osmo_sockaddr *b) +{ + if (a == b) + return 0; + if (!a) + return 1; + if (!b) + return -1; + + if (a->u.sa.sa_family != b->u.sa.sa_family) { + return OSMO_CMP(a->u.sa.sa_family, b->u.sa.sa_family); + } + + switch (a->u.sa.sa_family) { + case AF_INET: + return memcmp(&a->u.sin, &b->u.sin, sizeof(struct sockaddr_in)); + case AF_INET6: + return memcmp(&a->u.sin6, &b->u.sin6, sizeof(struct sockaddr_in6)); + default: + /* fallback to memcmp for remaining AF over the full osmo_sockaddr length */ + return memcmp(a, b, sizeof(struct osmo_sockaddr)); + } +} + +/*! string-format a given osmo_sockaddr address + * \paramin sockaddr the osmo_sockaddr to print + * \return pointer to the string on success; NULL on error + */ +const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr) +{ + /* INET6_ADDRSTRLEN contains already a null termination, + * adding '' '' ':' '16 bit port' */ + static __thread char bufINET6_ADDRSTRLEN + 8; + return osmo_sockaddr_to_str_buf(buf, sizeof(buf), sockaddr); +} + +/*! string-format a given osmo_sockaddr address into a user-supplied buffer. + * Same as osmo_sockaddr_to_str_buf() but returns a would-be length in snprintf() style. + * \paramin buf user-supplied output buffer + * \paramin buf_len size of the user-supplied output buffer in bytes + * \paramin sockaddr the osmo_sockaddr to print + * \return number of characters that would be written if the buffer is large enough, like snprintf(). + */ +int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; + uint16_t port = 0; + + if (!sockaddr) { + OSMO_STRBUF_PRINTF(sb, "NULL"); + return sb.chars_needed; + } + + switch (sockaddr->u.sa.sa_family) { + case AF_INET: + OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa); + if (port) + OSMO_STRBUF_PRINTF(sb, ":%u", port); + break; + case AF_INET6: + OSMO_STRBUF_PRINTF(sb, ""); + OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa); + OSMO_STRBUF_PRINTF(sb, ""); + if (port) + OSMO_STRBUF_PRINTF(sb, ":%u", port); + break; + default: + OSMO_STRBUF_PRINTF(sb, "unsupported family %d", sockaddr->u.sa.sa_family); + break; + } + + return sb.chars_needed; +} + +/*! string-format a given osmo_sockaddr address into a talloc allocated buffer. + * Like osmo_sockaddr_to_str_buf2() but returns a talloc allocated string. + * \paramin ctx talloc context to allocate from, e.g. OTC_SELECT. + * \paramin sockaddr the osmo_sockaddr to print. + * \return human readable string. + */ +char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr) +{ + OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sockaddr_to_str_buf2, sockaddr) +} + +/*! string-format a given osmo_sockaddr address into a user-supplied buffer. + * Like osmo_sockaddr_to_str_buf2() but returns buf, or NULL if too short. + * \paramin buf user-supplied output buffer + * \paramin buf_len size of the user-supplied output buffer in bytes + * \paramin sockaddr the osmo_sockaddr to print + * \return pointer to the string on success; NULL on error + */ +char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len, + const struct osmo_sockaddr *sockaddr) +{ + int chars_needed = osmo_sockaddr_to_str_buf2(buf, buf_len, sockaddr); + if (chars_needed >= buf_len) + return NULL; + return buf; +} + +/*! Set the DSCP (differentiated services code point) of a socket. + * \paramin dscp DSCP value in range 0..63 + * \returns 0 on success; negative on error. */ +int osmo_sock_set_dscp(int fd, uint8_t dscp) +{ + struct sockaddr_storage local_addr; + socklen_t local_addr_len = sizeof(local_addr); + uint8_t tos; + socklen_t tos_len = sizeof(tos); + int tclass; + socklen_t tclass_len = sizeof(tclass); + int rc; + + /* DSCP is a 6-bit value stored in the upper 6 bits of the 8-bit TOS */ + if (dscp > 63) + return -EINVAL; + + rc = getsockname(fd, (struct sockaddr *)&local_addr, &local_addr_len); + if (rc < 0) + return rc; + + switch (local_addr.ss_family) { + case AF_INET: + /* read the original value */ + rc = getsockopt(fd, IPPROTO_IP, IP_TOS, &tos, &tos_len); + if (rc < 0) + return rc; + /* mask-in the DSCP into the upper 6 bits */ + tos &= 0x03; + tos |= dscp << 2; + /* and write it back to the kernel */ + rc = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + break; + case AF_INET6: + /* read the original value */ + rc = getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, &tclass_len); + if (rc < 0) + return rc; + /* mask-in the DSCP into the upper 6 bits */ + tclass &= 0x03; + tclass |= dscp << 2; + /* and write it back to the kernel */ + rc = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); + break; + case AF_UNSPEC: + default: + LOGP(DLGLOBAL, LOGL_ERROR, "No DSCP support for socket family %u\n", + local_addr.ss_family); + rc = -1; + break; + } + + return rc; +} + +/*! Set the priority value of a socket. + * \paramin prio priority value. Values outside 0..6 require CAP_NET_ADMIN. + * \returns 0 on success; negative on error. */ +int osmo_sock_set_priority(int fd, int prio) +{ + /* and write it back to the kernel */ + return setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)); +} + +#endif /* HAVE_SYS_SOCKET_H */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/stat_item.c
Changed
(renamed from src/stat_item.c)
View file
libosmocore_1.8.0.tar.xz/src/core/stat_item_internal.h
Changed
(renamed from src/stat_item_internal.h)
View file
libosmocore_1.8.0.tar.xz/src/core/stats.c
Changed
(renamed from src/stats.c)
View file
libosmocore_1.8.0.tar.xz/src/core/stats_statsd.c
Changed
(renamed from src/stats_statsd.c)
View file
libosmocore_1.8.0.tar.xz/src/core/stats_tcp.c
Changed
(renamed from src/stats_tcp.c)
View file
libosmocore_1.8.0.tar.xz/src/core/strrb.c
Changed
(renamed from src/strrb.c)
View file
libosmocore_1.8.0.tar.xz/src/core/tdef.c
Added
@@ -0,0 +1,371 @@ +/*! \file tdef.c + * Implementation to define Tnnn timers globally and use for FSM state changes. + */ +/* + * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Neels Hofmeyr <neels@hofmeyr.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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <limits.h> +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/tdef.h> + +/*! \addtogroup Tdef + * + * Implementation to define Tnnn timers globally and use for FSM state changes. + * + * See also \ref Tdef_VTY + * + * osmo_tdef provides: + * + * - a list of Tnnnn (GSM) timers with description, unit and default value. + * - vty UI to allow users to configure non-default timeouts. + * - API to tie T timers to osmo_fsm states and set them on state transitions. + * + * - a few standard units (minute, second, millisecond) as well as a custom unit + * (which relies on the timer's human readable description to indicate the + * meaning of the value). + * - conversion for standard units: for example, some GSM timers are defined in + * minutes, while our FSM definitions need timeouts in seconds. Conversion is + * for convenience only and can be easily avoided via the custom unit. + * + * By keeping separate osmo_tdef arrays, several groups of timers can be kept + * separately. The VTY tests in tests/tdef/ showcase different schemes: + * + * - \ref tests/tdef/tdef_vty_config_root_test.c: + * Keep several timer definitions in separately named groups: showcase the + * osmo_tdef_vty_groups*() API. Each timer group exists exactly once. + * + * - \ref tests/tdef/tdef_vty_config_subnode_test.c: + * Keep a single list of timers without separate grouping. + * Put this list on a specific subnode below the CONFIG_NODE. + * There could be several separate subnodes with timers like this, i.e. + * continuing from this example, sets of timers could be separated by placing + * timers in specific config subnodes instead of using the global group name. + * + * - \ref tests/tdef/tdef_vty_dynamic_test.c: + * Dynamically allocate timer definitions per each new created object. + * Thus there can be an arbitrary number of independent timer definitions, one + * per allocated object. + * + * osmo_tdef was introduced because: + * + * - without osmo_tdef, each invocation of osmo_fsm_inst_state_chg() needs to be + * programmed with the right timeout value, for all code paths that invoke this + * state change. It is a likely source of errors to get one of them wrong. By + * defining a T timer exactly for an FSM state, the caller can merely invoke the + * state change and trust on the original state definition to apply the correct + * timeout. + * + * - it is helpful to have a standardized config file UI to provide user + * configurable timeouts, instead of inventing new VTY commands for each + * separate application of T timer numbers. See \ref tdef_vty.h. + * + * @{ + * \file tdef.c + */ + +/*! a = return_val * b. \return 0 if factor is below 1. */ +static unsigned long osmo_tdef_factor(enum osmo_tdef_unit a, enum osmo_tdef_unit b) +{ + if (b == a + || b == OSMO_TDEF_CUSTOM || a == OSMO_TDEF_CUSTOM) + return 1; + + switch (b) { + case OSMO_TDEF_US: + switch (a) { + case OSMO_TDEF_MS: + return 1000; + case OSMO_TDEF_S: + return 1000*1000; + case OSMO_TDEF_M: + return 60*1000*1000; + default: + return 0; + } + case OSMO_TDEF_MS: + switch (a) { + case OSMO_TDEF_S: + return 1000; + case OSMO_TDEF_M: + return 60*1000; + default: + return 0; + } + case OSMO_TDEF_S: + switch (a) { + case OSMO_TDEF_M: + return 60; + default: + return 0; + } + default: + return 0; + } +} + +/*! \return val in unit to_unit, rounded up to the next integer value and clamped to ULONG_MAX, or 0 if val == 0. */ +static unsigned long osmo_tdef_round(unsigned long val, enum osmo_tdef_unit from_unit, enum osmo_tdef_unit to_unit) +{ + unsigned long f; + if (!val) + return 0; + + f = osmo_tdef_factor(from_unit, to_unit); + if (f == 1) + return val; + if (f < 1) { + f = osmo_tdef_factor(to_unit, from_unit); + return (val / f) + (val % f? 1 : 0); + } + /* range checking */ + if (f > (ULONG_MAX / val)) + return ULONG_MAX; + return val * f; +} + +/*! Set all osmo_tdef values to the default_val. + * It is convenient to define a tdefs array by setting only the default_val, and calling osmo_tdefs_reset() once for + * program startup. (See also osmo_tdef_vty_init()). + * During call to this function, default values are verified to be inside valid range; process is aborted otherwise. + * \paramin tdefs Array of timer definitions, last entry being fully zero. + */ +void osmo_tdefs_reset(struct osmo_tdef *tdefs) +{ + struct osmo_tdef *t; + osmo_tdef_for_each(t, tdefs) { + if (!osmo_tdef_val_in_range(t, t->default_val)) { + char range_str64; + osmo_tdef_range_str_buf(range_str, sizeof(range_str), t); + osmo_panic("%s:%d Timer " OSMO_T_FMT " contains default value %lu not in range %s\n", + __FILE__, __LINE__, OSMO_T_FMT_ARGS(t->T), t->default_val, range_str); + } + t->val = t->default_val; + } +} + +/*! Return the value of a T timer from a list of osmo_tdef, in the given unit. + * If no such timer is defined, return the default value passed, or abort the program if default < 0. + * + * Round up any value match as_unit: 1100 ms as OSMO_TDEF_S becomes 2 seconds, as OSMO_TDEF_M becomes one minute. + * However, always return a value of zero as zero (0 ms as OSMO_TDEF_M still is 0 m). + * + * Range: even though the value range is unsigned long here, in practice, using ULONG_MAX as value for a timeout in + * seconds may actually wrap to negative or low timeout values (e.g. in struct timeval). It is recommended to stay below + * INT_MAX seconds. See also osmo_fsm_inst_state_chg(). + * + * Usage example: + * + * struct osmo_tdef global_T_defs = { + * { .T=7, .default_val=50, .desc="Water Boiling Timeout" }, // default is .unit=OSMO_TDEF_S == 0 + * { .T=8, .default_val=300, .desc="Tea brewing" }, + * { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" }, + * { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" }, + * {} // <-- important! last entry shall be zero + * }; + * osmo_tdefs_reset(global_T_defs); // make all values the default + * osmo_tdef_vty_init(global_T_defs, CONFIG_NODE); + * + * val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_S, -1); // -> 50 + * sleep(val); + * + * val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_M, -1); // 50 seconds becomes 1 minute -> 1 + * sleep_minutes(val); + * + * val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, 3); // not defined, returns 3 + * + * val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, -1); // not defined, program aborts! + * + * \paramin tdefs Array of timer definitions, last entry must be fully zero initialized. + * \paramin T Timer number to get the value for. + * \paramin as_unit Return timeout value in this unit. + * \paramin val_if_not_present Fallback value to return if no timeout is defined; if this is a negative number, a + * missing T timer definition aborts the program via OSMO_ASSERT(). + * \return Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present. + * If val_if_not_present is negative and no T timer is defined, trigger OSMO_ASSERT() and do not return. + */ +unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, long val_if_not_present) +{ + const struct osmo_tdef *t = osmo_tdef_get_entry((struct osmo_tdef*)tdefs, T); + if (!t) { + OSMO_ASSERT(val_if_not_present >= 0); + return val_if_not_present; + } + return osmo_tdef_round(t->val, t->unit, as_unit); +} + +/*! Find tdef entry matching T. + * This is useful for manipulation, which is usually limited to the VTY configuration. To retrieve a timeout value, + * most callers probably should use osmo_tdef_get() instead. + * \paramin tdefs Array of timer definitions, last entry being fully zero. + * \paramin T Timer number to get the entry for. + * \return osmo_tdef entry matching T in given array, or NULL if no match is found. + */ +struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T) +{ + struct osmo_tdef *t; + osmo_tdef_for_each(t, tdefs) { + if (t->T == T) + return t; + } + return NULL; +} + +/*! Set value in entry matching T, converting val from val_unit to unit of T. + * The converted value is rounded up to the next integer value of T's unit and clamped to ULONG_MAX, or 0 if val == 0. + * \paramin tdefs Array of timer definitions, last entry being fully zero. + * \paramin T Timer number to set the value for. + * \paramin val The new timer value to set. + * \paramin val_unit Units of value in parameter val. + * \return 0 on success, negative on error. + */ +int osmo_tdef_set(struct osmo_tdef *tdefs, int T, unsigned long val, enum osmo_tdef_unit val_unit) +{ + unsigned long new_val; + struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T); + if (!t) + return -EEXIST; + + new_val = osmo_tdef_round(val, val_unit, t->unit); + if (!osmo_tdef_val_in_range(t, new_val)) + return -ERANGE; + + t->val = new_val; + return 0; +} + +/*! Check if value new_val is in range of valid possible values for timer entry tdef. + * \paramin tdef Timer entry from a timer definition table. + * \paramin new_val The value whose validity to check, in units as per this timer entry. + * \return true if inside range, false otherwise. + */ +bool osmo_tdef_val_in_range(struct osmo_tdef *tdef, unsigned long new_val) +{ + return new_val >= tdef->min_val && (!tdef->max_val || new_val <= tdef->max_val); +} + +/*! Write string representation of osmo_tdef range into buf. + * \paramin buf The buffer where the string representation is stored. + * \paramin buf_len Length of buffer in bytes. + * \paramin tdef Timer entry from a timer definition table. + * \return The number of characters printed on success (or number of characters + * which would have been written to the final string if enough space + * had been available), negative on error. See snprintf(). + */ +int osmo_tdef_range_str_buf(char *buf, size_t buf_len, struct osmo_tdef *t) +{ + int ret, len = 0, offset = 0, rem = buf_len; + + buf0 = '\0'; + ret = snprintf(buf + offset, rem, "%lu .. ", t->min_val); + if (ret < 0) + return ret; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + if (t->max_val) + ret = snprintf(buf + offset, rem, "%lu", t->max_val); + else + ret = snprintf(buf + offset, rem, "inf"); + if (ret < 0) + return ret; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + return len; +} + +/*! Using osmo_tdef for osmo_fsm_inst: find a given state's osmo_tdef_state_timeout entry. + * + * The timeouts_array shall contain exactly 32 elements, regardless whether only some of them are actually populated + * with nonzero values. 32 corresponds to the number of states allowed by the osmo_fsm_* API. Lookup is by array index. + * Not populated entries imply a state change invocation without timeout. + * + * For example: + * + * struct osmo_tdef_state_timeout my_fsm_timeouts32 = { + * MY_FSM_STATE_3 = { .T = 423 }, // look up timeout configured for T423 + * MY_FSM_STATE_7 = { .keep_timer = true, .T = 235 }, // keep previous timer if running, or start T235 + * MY_FSM_STATE_8 = { .keep_timer = true }, // keep previous state's T number, continue timeout. + * // any state that is omitted will remain zero == no timeout + * }; + * osmo_tdef_get_state_timeout(MY_FSM_STATE_0, &my_fsm_timeouts) -> NULL, + * osmo_tdef_get_state_timeout(MY_FSM_STATE_7, &my_fsm_timeouts) -> { .T = 235 } + * + * The intention is then to obtain the timer like osmo_tdef_get(global_T_defs, T=235); see also + * fsm_inst_state_chg_T() below. + * + * \paramin state State constant to look up. + * \paramin timeouts_array Array32 of struct osmo_tdef_state_timeout defining which timer number to use per state. + * \return A struct osmo_tdef_state_timeout entry, or NULL if that entry is zero initialized. + */ +const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array) +{ + const struct osmo_tdef_state_timeout *t; + OSMO_ASSERT(state < 32); + t = &timeouts_arraystate; + if (!t->keep_timer && !t->T) + return NULL; + return t; +} + +/*! See invocation macro osmo_tdef_fsm_inst_state_chg() instead. + * \paramin file Source file name, like __FILE__. + * \paramin line Source file line number, like __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, long default_timeout, + const char *file, int line) +{ + const struct osmo_tdef_state_timeout *t = osmo_tdef_get_state_timeout(state, timeouts_array); + unsigned long val = 0; + + /* No timeout defined for this state? */ + if (!t) + return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line); + + if (t->T) + val = osmo_tdef_get(tdefs, t->T, OSMO_TDEF_S, default_timeout); + + if (t->keep_timer) { + if (t->T) + return _osmo_fsm_inst_state_chg_keep_or_start_timer(fi, state, val, t->T, file, line); + else + return _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line); + } + + /* val is always initialized here, because if t->keep_timer is false, t->T must be != 0. + * Otherwise osmo_tdef_get_state_timeout() would have returned NULL. */ + OSMO_ASSERT(t->T); + return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line); +} + +const struct value_string osmo_tdef_unit_names = { + { OSMO_TDEF_S, "s" }, + { OSMO_TDEF_MS, "ms" }, + { OSMO_TDEF_M, "m" }, + { OSMO_TDEF_CUSTOM, "custom-unit" }, + { OSMO_TDEF_US, "us" }, + {} +}; + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/thread.c
Changed
(renamed from src/thread.c)
View file
libosmocore_1.8.0.tar.xz/src/core/time_cc.c
Added
@@ -0,0 +1,228 @@ +/*! \file foo.c + * 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/>. + * + */ + +/*! \addtogroup time_cc + * + * Report the cumulative counter of time for which a flag is true as rate counter. + * + * Useful for reporting cumulative time counters as defined in 3GPP TS 52.402, for example allAvailableSDCCHAllocated, + * allAvailableTCHAllocated, availablePDCHAllocatedTime. + * + * For a usage example, see the description of struct osmo_time_cc. + * + * @{ + * \file time_cc.c + */ +#include "config.h" +#ifdef HAVE_CLOCK_GETTIME + +#include <limits.h> +#include <time.h> + +#include <osmocom/core/tdef.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/time_cc.h> + +#define GRAN_USEC(TIME_CC) ((TIME_CC)->cfg.gran_usec ? : 1000000) +#define ROUND_THRESHOLD_USEC(TIME_CC) ((TIME_CC)->cfg.round_threshold_usec ? \ + OSMO_MIN((TIME_CC)->cfg.round_threshold_usec, GRAN_USEC(TIME_CC)) \ + : (GRAN_USEC(TIME_CC) / 2)) + +static uint64_t time_now_usec(void) +{ + struct timespec tp; + if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp)) + return 0; + return (uint64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000; +} + +static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now); + +static void osmo_time_cc_update_from_tdef(struct osmo_time_cc *tc, uint64_t now) +{ + bool do_forget_sum = false; + if (!tc->cfg.T_defs) + return; + if (tc->cfg.T_gran) { + uint64_t was = GRAN_USEC(tc); + tc->cfg.gran_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_gran, OSMO_TDEF_US, -1); + if (was != GRAN_USEC(tc)) + do_forget_sum = true; + } + if (tc->cfg.T_round_threshold) + tc->cfg.round_threshold_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_round_threshold, + OSMO_TDEF_US, -1); + if (tc->cfg.T_forget_sum) { + uint64_t was = tc->cfg.forget_sum_usec; + tc->cfg.forget_sum_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_forget_sum, OSMO_TDEF_US, -1); + if (tc->cfg.forget_sum_usec && was != tc->cfg.forget_sum_usec) + do_forget_sum = true; + } + + if (do_forget_sum && tc->sum) + osmo_time_cc_forget_sum(tc, now); +} + +static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now); + +/*! Clear out osmo_timer and internal counting state of struct osmo_time_cc. The .cfg remains unaffected. After calling, + * the osmo_time_cc instance can be used again to accumulate state as if it had just been initialized. */ +void osmo_time_cc_cleanup(struct osmo_time_cc *tc) +{ + osmo_timer_del(&tc->timer); + *tc = (struct osmo_time_cc){ + .cfg = tc->cfg, + }; +} + +static void osmo_time_cc_start(struct osmo_time_cc *tc, uint64_t now) +{ + osmo_time_cc_cleanup(tc); + tc->start_time = now; + tc->last_counted_time = now; + osmo_time_cc_update_from_tdef(tc, now); + osmo_time_cc_schedule_timer(tc, now); +} + +static void osmo_time_cc_count_time(struct osmo_time_cc *tc, uint64_t now) +{ + uint64_t time_delta = now - tc->last_counted_time; + tc->last_counted_time = now; + if (!tc->flag_state) + return; + /* Flag is currently true, cumulate the elapsed time */ + tc->total_sum += time_delta; + tc->sum += time_delta; +} + +static void osmo_time_cc_report(struct osmo_time_cc *tc, uint64_t now) +{ + uint64_t delta; + uint64_t n; + /* We report a sum "rounded up", ahead of time. If the granularity period has not yet elapsed after the last + * reporting, do not report again yet. */ + if (tc->reported_sum > tc->sum) + return; + delta = tc->sum - tc->reported_sum; + /* elapsed full periods */ + n = delta / GRAN_USEC(tc); + /* If the delta has passed round_threshold (normally half of gran_usec), increment. */ + delta -= n * GRAN_USEC(tc); + if (delta >= ROUND_THRESHOLD_USEC(tc)) + n++; + if (!n) + return; + + /* integer sanity, since rate_ctr_add() takes an int argument. */ + if (n > INT_MAX) + n = INT_MAX; + if (tc->cfg.rate_ctr) + rate_ctr_add(tc->cfg.rate_ctr, n); + /* Store the increments of gran_usec that were counted. */ + tc->reported_sum += n * GRAN_USEC(tc); +} + +static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now) +{ + tc->reported_sum = 0; + tc->sum = 0; + + if (tc->last_counted_time < now) + tc->last_counted_time = now; +} + +/*! Initialize struct osmo_time_cc. Call this once before use, and before setting up the .cfg items. */ +void osmo_time_cc_init(struct osmo_time_cc *tc) +{ + *tc = (struct osmo_time_cc){0}; +} + +/*! Report state to be recorded by osmo_time_cc instance. Setting an unchanged state repeatedly has no effect. */ +void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag) +{ + uint64_t now = time_now_usec(); + if (!tc->start_time) + osmo_time_cc_start(tc, now); + /* No flag change == no effect */ + if (flag == tc->flag_state) + return; + /* Sum up elapsed time, report increments for that. */ + osmo_time_cc_count_time(tc, now); + osmo_time_cc_report(tc, now); + tc->flag_state = flag; + osmo_time_cc_schedule_timer(tc, now); +} + +static void osmo_time_cc_timer_cb(void *data) +{ + struct osmo_time_cc *tc = data; + uint64_t now = time_now_usec(); + + osmo_time_cc_update_from_tdef(tc, now); + + if (tc->flag_state) { + osmo_time_cc_count_time(tc, now); + osmo_time_cc_report(tc, now); + } else if (tc->cfg.forget_sum_usec && tc->sum + && (now >= tc->last_counted_time + tc->cfg.forget_sum_usec)) { + osmo_time_cc_forget_sum(tc, now); + } + osmo_time_cc_schedule_timer(tc, now); +} + +/*! Figure out the next time we should do anything, if the flag state remains unchanged. */ +static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now) +{ + uint64_t next_event = UINT64_MAX; + + osmo_time_cc_update_from_tdef(tc, now); + + /* If it is required, when will the next forget_sum happen? */ + if (tc->cfg.forget_sum_usec && !tc->flag_state && tc->sum > 0) { + uint64_t next_forget_time = tc->last_counted_time + tc->cfg.forget_sum_usec; + next_event = OSMO_MIN(next_event, next_forget_time); + } + /* Next rate_ctr increment? */ + if (tc->flag_state) { + uint64_t next_inc = now + (tc->reported_sum - tc->sum) + ROUND_THRESHOLD_USEC(tc); + next_event = OSMO_MIN(next_event, next_inc); + } + + /* No event coming up? */ + if (next_event == UINT64_MAX) + return; + + if (next_event <= now) + next_event = 0; + else + next_event -= now; + + osmo_timer_setup(&tc->timer, osmo_time_cc_timer_cb, tc); + osmo_timer_del(&tc->timer); + osmo_timer_schedule(&tc->timer, next_event / 1000000, next_event % 1000000); +} + +#endif /* HAVE_CLOCK_GETTIME */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/timer.c
Changed
(renamed from src/timer.c)
View file
libosmocore_1.8.0.tar.xz/src/core/timer_clockgettime.c
Changed
(renamed from src/timer_clockgettime.c)
View file
libosmocore_1.8.0.tar.xz/src/core/timer_gettimeofday.c
Changed
(renamed from src/timer_gettimeofday.c)
View file
libosmocore_1.8.0.tar.xz/src/core/tun.c
Added
@@ -0,0 +1,572 @@ + +/* TUN interface functions. + * (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de> + * Author: Pau Espin Pedrol <pespin@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 "config.h" + +/*! \addtogroup tun + * @{ + * tun network device (interface) convenience functions + * + * \file tundev.c + * + * Example lifecycle use of the API: + * + * struct osmo_sockaddr_str osa_str = {}; + * struct osmo_sockaddr osa = {}; + * + * // Allocate object: + * struct osmo_tundev *tundev = osmo_tundev_alloc(parent_talloc_ctx, name); + * OSMO_ASSERT(tundev); + * + * // Configure object (before opening): + * osmo_tundev_set_data_ind_cb(tun, tun_data_ind_cb); + * rc = osmo_tundev_set_dev_name(tun, "mytunnel0"); + * rc = osmo_tundev_set_netns_name(tun, "some_netns_name_or_null"); + * + * // Open the tundev object: + * rc = osmo_tundev_open(tundev); + * // The tunnel device is now created and an associatd netdev object + * // is available to operate the device: + * struct osmo_netdev *netdev = osmo_tundev_get_netdev(tundev); + * OSMO_ASSERT(netdev); + * + * // Add a local IPv4 address: + * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1"); + * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas); + * rc = osmo_netdev_add_addr(netdev, &osa, 24); + * + * // Bring network interface up: + * rc = osmo_netdev_ifupdown(netdev, true); + * + * // Add default route (0.0.0.0/0): + * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0"); + * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas); + * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL); + * + * // Close the tunnel (asssociated netdev object becomes unavailable) + * rc = osmo_tundev_close(tundev); + * // Free the object: + * osmo_tundev_free(tundev); + */ + +#if (!EMBEDDED) + +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <net/if.h> + +#if defined(__linux__) +#include <linux/if_tun.h> +#else +#error "Unknown platform!" +#endif + +#include <osmocom/core/utils.h> +#include <osmocom/core/select.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/write_queue.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/netns.h> +#include <osmocom/core/netdev.h> +#include <osmocom/core/tun.h> + +#define TUN_DEV_PATH "/dev/net/tun" +#define TUN_PACKET_MAX 8196 + +#define LOGTUN(tun, lvl, fmt, args ...) \ + LOGP(DLGLOBAL, lvl, "TUN(%s,if=%s/%u,ns=%s): " fmt, \ + (tun)->name, (tun)->dev_name ? : "", \ + (tun)->ifindex, (tun)->netns_name ? : "", ## args) + +struct osmo_tundev { + /* Name used to identify the osmo_tundev */ + char *name; + + /* netdev managing the tun interface: */ + struct osmo_netdev *netdev; + + /* ifindiex of the currently opened tunnel interface */ + unsigned int ifindex; + + /* Network interface name to use when setting up the tun device. + * NULL = let the system pick one. */ + char *dev_name; + /* Whether dev_name is set by user or dynamically allocated by system */ + bool dev_name_dynamic; + + /* Write queue used since tun fd is set non-blocking */ + struct osmo_wqueue wqueue; + + /* netns name where the tun interface is created (NULL = default netns) */ + char *netns_name; + + /* API user private data */ + void *priv_data; + + /* Called by tundev each time a new packet is received on the tun interface. Can be NULL. */ + osmo_tundev_data_ind_cb_t data_ind_cb; + + /* Whether the tundev is in opened state (managing the tun interface) */ + bool opened; +}; + +/* A new pkt arrived from the tun device, dispatch it to the API user */ +static int tundev_decaps(struct osmo_tundev *tundev) +{ + struct msgb *msg; + int rc; + + msg = msgb_alloc(TUN_PACKET_MAX, "tundev_rx"); + + if ((rc = read(tundev->wqueue.bfd.fd, msgb_data(msg), TUN_PACKET_MAX)) <= 0) { + LOGTUN(tundev, LOGL_ERROR, "read() failed: %s (%d)\n", strerror(errno), errno); + msgb_free(msg); + return -1; + } + msgb_put(msg, rc); + + if (tundev->data_ind_cb) + return tundev->data_ind_cb(tundev, msg); + + msgb_free(msg); + return 0; +} + +/* callback for tun device osmocom select loop integration */ +static int tundev_read_cb(struct osmo_fd *fd) +{ + struct osmo_tundev *tundev = fd->data; + return tundev_decaps(tundev); +} + +/* callback for tun device osmocom select loop integration */ +static int tundev_write_cb(struct osmo_fd *fd, struct msgb *msg) +{ + struct osmo_tundev *tundev = fd->data; + size_t pkt_len = msgb_length(msg); + + int rc; + rc = write(tundev->wqueue.bfd.fd, msgb_data(msg), pkt_len); + if (rc < 0) + LOGTUN(tundev, LOGL_ERROR, "write() failed: %s (%d)\n", strerror(errno), errno); + else if (rc < pkt_len) + LOGTUN(tundev, LOGL_ERROR, "short write() %d < %zu\n", rc, pkt_len); + return rc; +} + +static int tundev_ifupdown_ind_cb(struct osmo_netdev *netdev, bool ifupdown) +{ + struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev); + LOGTUN(tundev, LOGL_NOTICE, "Physical link state changed: %s\n", + ifupdown ? "UP" : "DOWN"); + + /* free any backlog, both on IFUP and IFDOWN. Keep the LMI, as it makes + * sense to get one out of the door ASAP. */ + osmo_wqueue_clear(&tundev->wqueue); + return 0; +} + +static int tundev_dev_name_chg_cb(struct osmo_netdev *netdev, const char *new_dev_name) +{ + struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev); + LOGTUN(tundev, LOGL_NOTICE, "netdev changed name: %s -> %s\n", + osmo_netdev_get_dev_name(netdev), new_dev_name); + + if (tundev->dev_name_dynamic) { + osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name); + } else { + /* TODO: in here we probably want to force the iface name back + * to tundev->dev_name one we have a osmo_netdev_set_ifname() API */ + osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name); + } + + return 0; +} + +static int tundev_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu) +{ + struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev); + LOGTUN(tundev, LOGL_NOTICE, "netdev changed MTU: %u\n", new_mtu); + + return 0; +} + +/*! Allocate a new tundev object. + * \paramin ctx talloc context to use as a parent when allocating the tundev object + * \paramin name A name providen to identify the tundev object + * \returns newly allocated tundev object on success; NULL on error + */ +struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name) +{ + struct osmo_tundev *tundev; + + tundev = talloc_zero(ctx, struct osmo_tundev); + if (!tundev) + return NULL; + + tundev->netdev = osmo_netdev_alloc(tundev, name); + if (!tundev->netdev) { + talloc_free(tundev); + return NULL; + } + osmo_netdev_set_priv_data(tundev->netdev, tundev); + osmo_netdev_set_ifupdown_ind_cb(tundev->netdev, tundev_ifupdown_ind_cb); + osmo_netdev_set_dev_name_chg_cb(tundev->netdev, tundev_dev_name_chg_cb); + osmo_netdev_set_mtu_chg_cb(tundev->netdev, tundev_mtu_chg_cb); + + tundev->name = talloc_strdup(tundev, name); + osmo_wqueue_init(&tundev->wqueue, 1000); + osmo_fd_setup(&tundev->wqueue.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, tundev, 0); + tundev->wqueue.read_cb = tundev_read_cb; + tundev->wqueue.write_cb = tundev_write_cb; + + return tundev; +} + +/*! Free an allocated tundev object. + * \paramin tundev The tundev object to free + */ +void osmo_tundev_free(struct osmo_tundev *tundev) +{ + if (!tundev) + return; + osmo_tundev_close(tundev); + osmo_netdev_free(tundev->netdev); + talloc_free(tundev); +} + +/*! Open and configure fd of the tunnel device. + * \paramin tundev The tundev object whose tunnel interface to open + * \paramin flags internal linux flags to pass when creating the device (not used yet) + * \returns 0 on success; negative on error + */ +static int tundev_open_fd(struct osmo_tundev *tundev, int flags) +{ + struct ifreq ifr; + int fd, rc; + + fd = open(TUN_DEV_PATH, O_RDWR); + if (fd < 0) { + LOGTUN(tundev, LOGL_ERROR, "Cannot open " TUN_DEV_PATH ": %s\n", strerror(errno)); + return fd; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI | flags; + if (tundev->dev_name) { + /* if a TUN interface name was specified, put it in the structure; otherwise, + the kernel will try to allocate the "next" device of the specified type */ + osmo_strlcpy(ifr.ifr_name, tundev->dev_name, IFNAMSIZ); + } + + /* try to create the device */ + rc = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (rc < 0) + goto close_ret; + + /* Read name back from device */ + if (!tundev->dev_name) { + ifr.ifr_nameIFNAMSIZ - 1 = '\0'; + tundev->dev_name = talloc_strdup(tundev, ifr.ifr_name); + tundev->dev_name_dynamic = true; + } + + /* Store interface index: + * (Note: there's a potential race condition here between creating the + * iface with a given name above and attempting to retrieve its ifindex based + * on that name. Someone (ie udev) could have the iface renamed in + * between here. It's a pity that TUNSETIFF doesn't copy back to us the + * newly allocated ifinidex as it does with ifname) + */ + tundev->ifindex = if_nametoindex(tundev->dev_name); + if (tundev->ifindex == 0) { + LOGTUN(tundev, LOGL_ERROR, "Unable to find ifinidex for dev %s\n", + tundev->dev_name); + rc = -ENODEV; + goto close_ret; + } + + LOGTUN(tundev, LOGL_INFO, "TUN device created\n"); + + /* set non-blocking: */ + rc = fcntl(fd, F_GETFL); + if (rc < 0) { + LOGTUN(tundev, LOGL_ERROR, "fcntl(F_GETFL) failed: %s (%d)\n", + strerror(errno), errno); + goto close_ret; + } + rc = fcntl(fd, F_SETFL, rc | O_NONBLOCK); + if (rc < 0) { + LOGTUN(tundev, LOGL_ERROR, "fcntl(F_SETFL, O_NONBLOCK) failed: %s (%d)\n", + strerror(errno), errno); + goto close_ret; + } + return fd; + +close_ret: + close(fd); + return rc; +} + +/*! Open the tunnel device owned by the tundev object. + * \paramin tundev The tundev object to open + * \returns 0 on success; negative on error + */ +int osmo_tundev_open(struct osmo_tundev *tundev) +{ + struct osmo_netns_switch_state switch_state; + int rc; + int netns_fd = -1; + + if (tundev->opened) + return -EALREADY; + + /* temporarily switch to specified namespace to create tun device */ + if (tundev->netns_name) { + LOGTUN(tundev, LOGL_INFO, "Open tun: Switch to netns '%s'\n", + tundev->netns_name); + netns_fd = osmo_netns_open_fd(tundev->netns_name); + if (netns_fd < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n", + tundev->netns_name, strerror(errno), errno); + return netns_fd; + } + rc = osmo_netns_switch_enter(netns_fd, &switch_state); + if (rc < 0) { + LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n", + tundev->netns_name, strerror(errno), errno); + goto err_close_netns_fd; + } + } + + tundev->wqueue.bfd.fd = tundev_open_fd(tundev, 0); + if (tundev->wqueue.bfd.fd < 0) { + LOGTUN(tundev, LOGL_ERROR, "Cannot open TUN device: %s\n", strerror(errno)); + rc = -ENODEV; + goto err_restore_ns; + } + + /* switch back to default namespace */ + if (tundev->netns_name) { + rc = osmo_netns_switch_exit(&switch_state); + if (rc < 0) { + LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch back from netns '%s': %s\n", + tundev->netns_name, strerror(errno)); + goto err_close_tun; + } + LOGTUN(tundev, LOGL_INFO, "Open tun: Back from netns '%s'\n", + tundev->netns_name); + } + + rc = osmo_netdev_set_netns_name(tundev->netdev, tundev->netns_name); + if (rc < 0) + goto err_close_tun; + rc = osmo_netdev_set_ifindex(tundev->netdev, tundev->ifindex); + if (rc < 0) + goto err_close_tun; + + rc = osmo_netdev_register(tundev->netdev); + if (rc < 0) + goto err_close_tun; + + osmo_fd_register(&tundev->wqueue.bfd); + tundev->opened = true; + return 0; + +err_close_tun: + close(tundev->wqueue.bfd.fd); + tundev->wqueue.bfd.fd = -1; +err_restore_ns: + if (tundev->netns_name) + osmo_netns_switch_exit(&switch_state); +err_close_netns_fd: + if (netns_fd >= 0) + close(netns_fd); + return rc; +} + +/*! Close the tunnel device owned by the tundev object. + * \paramin tundev The tundev object to close + * \returns 0 on success; negative on error + */ +int osmo_tundev_close(struct osmo_tundev *tundev) +{ + if (!tundev->opened) + return -EALREADY; + + osmo_wqueue_clear(&tundev->wqueue); + if (tundev->wqueue.bfd.fd != -1) { + osmo_fd_unregister(&tundev->wqueue.bfd); + close(tundev->wqueue.bfd.fd); + tundev->wqueue.bfd.fd = -1; + } + + osmo_netdev_unregister(tundev->netdev); + if (tundev->dev_name_dynamic) { + TALLOC_FREE(tundev->dev_name); + tundev->dev_name_dynamic = false; + } + tundev->opened = false; + return 0; +} + +/*! Retrieve whether the tundev object is in "opened" state. + * \paramin tundev The tundev object to check + * \returns true if in state "opened"; false otherwise + */ +bool osmo_tundev_is_open(struct osmo_tundev *tundev) +{ + return tundev->opened; +} + +/*! Set private user data pointer on the tundev object. + * \paramin tundev The tundev object where the field is set + */ +void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data) +{ + tundev->priv_data = priv_data; +} + +/*! Get private user data pointer from the tundev object. + * \paramin tundev The tundev object from where to retrieve the field + * \returns The current value of the priv_data field. + */ +void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev) +{ + return tundev->priv_data; +} + +/*! Set data_ind_cb callback, called when a new packet is received on the tun interface. + * \paramin tundev The tundev object where the field is set + * \paramin data_ind_cb the user provided function to be called when a new packet is received + */ +void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb) +{ + tundev->data_ind_cb = data_ind_cb; +} + +/*! Get name used to identify the tundev object. + * \paramin tundev The tundev object from where to retrieve the field + * \returns The current value of the name used to identify the tundev object + */ +const char *osmo_tundev_get_name(const struct osmo_tundev *tundev) +{ + return tundev->name; +} + +/*! Set name used to name the tunnel interface created by the tundev object. + * \paramin tundev The tundev object where the field is set + * \paramin dev_name The tunnel interface name to use + * \returns 0 on success; negative on error + * + * This is used during osmo_tundev_open() time, and hence shouldn't be changed + * when the tundev object is in "opened" state. + * If left as NULL (default), the system will pick a suitable name during + * osmo_tundev_open(), and the field will be updated to the system-selected + * name, which can be retrieved later with osmo_tundev_get_dev_name(). + */ +int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name) +{ + if (tundev->opened) + return -EALREADY; + osmo_talloc_replace_string(tundev, &tundev->dev_name, dev_name); + tundev->dev_name_dynamic = false; + return 0; +} + +/*! Get name used to name the tunnel interface created by the tundev object + * \paramin tundev The tundev object from where to retrieve the field + * \returns The current value of the configured tunnel interface name to use + */ +const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev) +{ + return tundev->dev_name; +} + +/*! Set name of the network namespace to use when opening the tunnel interface + * \paramin tundev The tundev object where the field is set + * \paramin netns_name The network namespace to use during tunnel interface creation + * \returns 0 on success; negative on error + * + * This is used during osmo_tundev_open() time, and hence shouldn't be changed + * when the tundev object is in "opened" state. + * If left as NULL (default), the system will stay in the current network namespace. + */ +int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns_name) +{ + if (tundev->opened) + return -EALREADY; + osmo_talloc_replace_string(tundev, &tundev->netns_name, netns_name); + return 0; +} + +/*! Get name of network namespace used when opening the tunnel interface + * \paramin tundev The tundev object from where to retrieve the field + * \returns The current value of the configured network namespace + */ +const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev) +{ + return tundev->netns_name; +} + +/*! Get netdev managing the tunnel interface of tundev + * \paramin tundev The tundev object from where to retrieve the field + * \returns The netdev objet managing the tun interface + */ +struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev) +{ + return tundev->netdev; +} + +/*! Submit a packet to the tunnel device managed by the tundev object + * \paramin tundev The tundev object owning the tunnel device where to inject the packet + * \paramin msg The msgb containg the packet to transfer + * \returns The current value of the configured network namespace + * + * This function takes the ownership of msg in all cases. + */ +int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg) +{ + int rc = osmo_wqueue_enqueue(&tundev->wqueue, msg); + if (rc < 0) { + LOGTUN(tundev, LOGL_ERROR, "Failed to enqueue the packet\n"); + msgb_free(msg); + return rc; + } + return rc; +} + + +#endif /* (!EMBEDDED) */ + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/use_count.c
Added
@@ -0,0 +1,306 @@ +/*! \file use_count.c + * Generic object usage counter Implementation (get, put and deallocate on zero count). + */ +/* + * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels@hofmeyr.de> + * + * 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 <errno.h> +#include <inttypes.h> +#include <string.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/use_count.h> + +/*! \addtogroup use_count + * + * Generic object usage counter (get, put and deallocate on zero count). + * + * For an example and a detailed description, see struct osmo_use_count. + * + * @{ + * \file use_count.c + */ + +/*! Add two int32_t but make sure to min- and max-clamp at INT32_MIN and INT32_MAX, respectively. */ +static inline bool count_safe(int32_t *val_p, int32_t add) +{ + int32_t val = *val_p; + + /* A simpler implementation would just let the integer overflow and compare with previous value afterwards, but + * that causes runtime errors in the address sanitizer. So let's just do this without tricks. */ + if (add < 0 && val < 0 && val - INT32_MIN < -add) { + *val_p = INT32_MIN; + return false; + } + + if (add > 0 && val > 0 && INT32_MAX - val < add) { + *val_p = INT32_MAX; + return false; + } + + *val_p = val + add; + return true; +} + +/*! Return the sum of all use counts, min- and max-clamped at INT32_MIN and INT32_MAX. + * \paramin uc Use counts to sum up. + * \return Accumulated counts, or 0 if uc is NULL. + */ +int32_t osmo_use_count_total(const struct osmo_use_count *uc) +{ + struct osmo_use_count_entry *e; + int32_t total = 0; + + if (!uc || !uc->use_counts.next) + return 0; + + llist_for_each_entry(e, &uc->use_counts, entry) { + count_safe(&total, e->count); + } + return total; +} + +/*! Return use count by a single use token. + * \paramin uc Use counts to look up in. + * \paramin use Use token. + * \return Use count, or 0 if uc is NULL or use token is not present. + */ +int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use) +{ + const struct osmo_use_count_entry *e; + if (!uc) + return 0; + e = osmo_use_count_find(uc, use); + if (!e) + return 0; + return e->count; +} + +/*! Write a comprehensive listing of use counts to a string buffer. + * Reads like "12 (3*barring,fighting,8*kungfoo)". + * \paraminout buf Destination buffer. + * \paramin buf_len sizeof(buf). + * \paramin uc Use counts to print. + * \return buf, always nul-terminated (except when buf_len < 1). + */ +const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc) +{ + osmo_use_count_to_str_buf(buf, buf_len, uc); + return buf; +} + +/*! Write a comprehensive listing of use counts to a string buffer. + * Reads like "12 (3*barring,fighting,8*kungfoo)". + * \paraminout buf Destination buffer. + * \paramin buf_len sizeof(buf). + * \paramin uc Use counts to print. + * \return number of bytes that would be written, like snprintf(). + */ +int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc) +{ + int32_t count = osmo_use_count_total(uc); + struct osmo_strbuf sb = { .buf = buf, .len = buf_len }; + struct osmo_use_count_entry *e; + bool first; + + OSMO_STRBUF_PRINTF(sb, "%" PRId32 " (", count); + + if (!uc->use_counts.next) + goto uninitialized; + + first = true; + llist_for_each_entry(e, &uc->use_counts, entry) { + if (!e->count) + continue; + if (!first) + OSMO_STRBUF_PRINTF(sb, ","); + first = false; + if (e->count != 1) + OSMO_STRBUF_PRINTF(sb, "%" PRId32 "*", e->count); + OSMO_STRBUF_PRINTF(sb, "%s", e->use ? : "NULL"); + } + if (first) + OSMO_STRBUF_PRINTF(sb, "-"); + +uninitialized: + OSMO_STRBUF_PRINTF(sb, ")"); + return sb.chars_needed; +} + +/*! Write a comprehensive listing of use counts to a talloc allocated string buffer. + * Reads like "12 (3*barring,fighting,8*kungfoo)". + * \paramin ctx talloc pool to allocate from. + * \paramin uc Use counts to print. + * \return buf, always nul-terminated. + */ +char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc) +{ + OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_use_count_to_str_buf, uc) +} + +/* Return a use token's use count entry -- probably you want osmo_use_count_by() instead. + * \paramin uc Use counts to look up in. + * \paramin use Use token. + * \return matching entry, or NULL if not present. + */ +struct osmo_use_count_entry *osmo_use_count_find(const struct osmo_use_count *uc, const char *use) +{ + struct osmo_use_count_entry *e; + if (!uc->use_counts.next) + return NULL; + llist_for_each_entry(e, &uc->use_counts, entry) { + if (e->use == use || (use && e->use && !strcmp(e->use, use))) + return e; + } + return NULL; +} + +/*! Find a use count entry that currently has zero count, and re-use that for this new use token. */ +static struct osmo_use_count_entry *osmo_use_count_repurpose_zero_entry(struct osmo_use_count *uc, const char *use) +{ + struct osmo_use_count_entry *e; + if (!uc->use_counts.next) + return NULL; + llist_for_each_entry(e, &uc->use_counts, entry) { + if (!e->count) { + e->use = use; + return e; + } + } + return NULL; +} + +/*! Allocate a new use count entry, happens implicitly in osmo_use_count_get_put(). */ +static struct osmo_use_count_entry *osmo_use_count_create(struct osmo_use_count *uc, const char *use) +{ + struct osmo_use_count_entry *e = talloc_zero(uc->talloc_object, struct osmo_use_count_entry); + if (!e) + return NULL; + *e = (struct osmo_use_count_entry){ + .use_count = uc, + .use = use, + }; + if (!uc->use_counts.next) + INIT_LLIST_HEAD(&uc->use_counts); + llist_add_tail(&e->entry, &uc->use_counts); + return e; +} + +/*! Deallocate a use count entry. + * Normally, this is not necessary -- it is ok and even desirable to leave use count entries around even when they reach + * a count of zero, until the use_count->talloc_object deallocates and removes all of them in one flush. This avoids + * repeated allocation and deallocation for use tokens, because use count entries that have reached zero count are + * repurposed for any other use tokens. A cleanup makes sense only if a very large number of differing use tokens surged + * at the same time, and the owning object will not be deallocated soon; if so, this should be done by the + * osmo_use_count_cb_t implementation. + * + * osmo_use_count_free() must *not* be called on use count entries that were added by + * osmo_use_count_make_static_entries(). This is the responsibility of the osmo_use_count_cb_t() implementation. + * + * \paramin use_count_entry Use count entry to unlist and free. + */ +void osmo_use_count_free(struct osmo_use_count_entry *use_count_entry) +{ + if (!use_count_entry) + return; + llist_del(&use_count_entry->entry); + talloc_free(use_count_entry); +} + +/*! Implementation for osmo_use_count_get_put(), which can also be directly invoked to pass source file information. For + * arguments besides file and line, see osmo_use_count_get_put(). + * \paramin file Source file path, as in __FILE__. + * \paramin line Source file line, as in __LINE__. + */ +int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t change, + const char *file, int line) +{ + struct osmo_use_count_entry *e; + int32_t old_use_count; + if (!uc) + return -EINVAL; + if (!change) + return 0; + + e = osmo_use_count_find(uc, use); + if (!e) + e = osmo_use_count_repurpose_zero_entry(uc, use); + if (!e) + e = osmo_use_count_create(uc, use); + if (!e) + return -ENOMEM; + + if (!e->count) { + /* move to end */ + llist_del(&e->entry); + llist_add_tail(&e->entry, &uc->use_counts); + } + + old_use_count = e->count; + if (!count_safe(&e->count, change)) { + e->count = old_use_count; + return -ERANGE; + } + + if (uc->use_cb) + return uc->use_cb(e, old_use_count, file, line); + return 0; +} + +/*! Add N static use token entries to avoid dynamic allocation of use count tokens. + * When not using this function, use count entries are talloc allocated from uc->talloc_object as talloc context. This + * means that there are small dynamic allocations for each use count token. osmo_use_count_get_put() normally leaves + * zero-count entries around and re-purposes them later, so the number of small allocations is at most the number of + * concurrent differently-named uses of the same object. If that is not enough, this function allows completely avoiding + * dynamic use count allocations, by adding N static entries with a zero count and a NULL use token. They will be used + * by osmo_use_count_get_put(), and, if the caller avoids using osmo_use_count_free(), the osmo_use_count implementation + * never deallocates them. The idea is that the entries are members of the uc->talloc_object, or that they will by other + * means be implicitly deallocated by the talloc_object. It is fine to call + * osmo_use_count_make_static_entries(buf_n_entries=N) and later have more than N concurrent uses, i.e. it is no problem + * to mix static and dynamic entries. To completely avoid dynamic use count entries, N has to >= the maximum number of + * concurrent differently-named uses that will occur in the lifetime of the talloc_object. + * + * struct my_object { + * struct osmo_use_count use_count; + * struct osmo_use_count_entry use_count_buf3; // planning for 3 concurrent users + * }; + * + * void example() { + * struct my_object *o = talloc_zero(ctx, struct my_object); + * osmo_use_count_make_static_entries(&o->use_count, o->use_count_buf, ARRAY_SIZE(o->use_count_buf)); + * } + */ +void osmo_use_count_make_static_entries(struct osmo_use_count *uc, struct osmo_use_count_entry *buf, + size_t buf_n_entries) +{ + size_t idx; + if (!uc->use_counts.next) + INIT_LLIST_HEAD(&uc->use_counts); + for (idx = 0; idx < buf_n_entries; idx++) { + struct osmo_use_count_entry *e = &bufidx; + *e = (struct osmo_use_count_entry){ + .use_count = uc, + }; + llist_add_tail(&e->entry, &uc->use_counts); + } +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/utils.c
Added
@@ -0,0 +1,1539 @@ +/* + * (C) 2011 by Harald Welte <laforge@gnumonks.org> + * (C) 2011 by Sylvain Munaut <tnt@246tNt.com> + * (C) 2014 by Nils O. Selåsdal <noselasd@fiane.dyndns.org> + * + * 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 <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> +#include <limits.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/bit64gen.h> + + +/*! \addtogroup utils + * @{ + * various utility routines + * + * \file utils.c */ + +static __thread char namebuf255; +/* shared by osmo_str_tolower() and osmo_str_toupper() */ +static __thread char capsbuf128; + +/*! get human-readable string for given value + * \paramin vs Array of value_string tuples + * \paramin val Value to be converted + * \returns pointer to human-readable string + * + * If val is found in vs, the array's string entry is returned. Otherwise, an + * "unknown" string containing the actual value is composed in a static buffer + * that is reused across invocations. + */ +const char *get_value_string(const struct value_string *vs, uint32_t val) +{ + const char *str = get_value_string_or_null(vs, val); + if (str) + return str; + + snprintf(namebuf, sizeof(namebuf), "unknown 0x%"PRIx32, val); + namebufsizeof(namebuf) - 1 = '\0'; + return namebuf; +} + +/*! get human-readable string or NULL for given value + * \paramin vs Array of value_string tuples + * \paramin val Value to be converted + * \returns pointer to human-readable string or NULL if val is not found + */ +const char *get_value_string_or_null(const struct value_string *vs, + uint32_t val) +{ + int i; + + if (!vs) + return NULL; + + for (i = 0;; i++) { + if (vsi.value == 0 && vsi.str == NULL) + break; + if (vsi.value == val) + return vsi.str; + } + + return NULL; +} + +/*! get numeric value for given human-readable string + * \paramin vs Array of value_string tuples + * \paramin str human-readable string + * \returns numeric value (>0) or negative numer in case of error + */ +int get_string_value(const struct value_string *vs, const char *str) +{ + int i; + + for (i = 0;; i++) { + if (vsi.value == 0 && vsi.str == NULL) + break; + if (!strcasecmp(vsi.str, str)) + return vsi.value; + } + return -EINVAL; +} + +/*! Convert BCD-encoded digit into printable character + * \paramin bcd A single BCD-encoded digit + * \returns single printable character + */ +char osmo_bcd2char(uint8_t bcd) +{ + if (bcd < 0xa) + return '0' + bcd; + else + return 'A' + (bcd - 0xa); +} + +/*! Convert number in ASCII to BCD value + * \paramin c ASCII character + * \returns BCD encoded value of character + */ +uint8_t osmo_char2bcd(char c) +{ + if (c >= '0' && c <= '9') + return c - 0x30; + else if (c >= 'A' && c <= 'F') + return 0xa + (c - 'A'); + else if (c >= 'a' && c <= 'f') + return 0xa + (c - 'a'); + else + return 0; +} + +/*! Convert BCD to string. + * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd0 & 0xf, nibble 1 is bcd0 >> 4, nibble + * 3 is bcd1 & 0xf, etc.. + * \paramout dst Output string buffer, is always nul terminated when dst_size > 0. + * \paramin dst_size sizeof() the output string buffer. + * \paramin bcd Binary coded data buffer. + * \paramin start_nibble Offset to start from, in nibbles, typically 1 to skip the first nibble. + * \paramin end_nibble Offset to stop before, in nibbles, e.g. sizeof(bcd)*2 - (bcd0 & GSM_MI_ODD? 0:1). + * \paramin allow_hex If false, return error if there are digits other than 0-9. If true, return those as A-F. + * \returns The strlen that would be written if the output buffer is large enough, excluding nul byte (like + * snprintf()), or -EINVAL if allow_hex is false and a digit > 9 is encountered. On -EINVAL, the conversion is + * still completed as if allow_hex were passed as true. Return -ENOMEM if dst is NULL or dst_size is zero. + * If end_nibble <= start_nibble, write an empty string to dst and return 0. + */ +int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex) +{ + char *dst_end = dst + dst_size - 1; + int nibble_i; + int rc = 0; + + if (!dst || dst_size < 1 || start_nibble < 0) + return -ENOMEM; + + for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) { + uint8_t nibble = bcdnibble_i >> 1; + if ((nibble_i & 1)) + nibble >>= 4; + nibble &= 0xf; + + if (!allow_hex && nibble > 9) + rc = -EINVAL; + + *dst = osmo_bcd2char(nibble); + } + *dst = '\0'; + + if (rc < 0) + return rc; + return OSMO_MAX(0, end_nibble - start_nibble); +} + +/*! Convert string to BCD. + * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd0 & 0x0f, nibble 1 is bcd0 & 0xf0, nibble + * 3 is bcd1 & 0x0f, etc.. + * \paramout dst Output BCD buffer. + * \paramin dst_size sizeof() the output string buffer. + * \paramin digits String containing decimal or hexadecimal digits in upper or lower case. + * \paramin start_nibble Offset to start from, in nibbles, typically 1 to skip the first (MI type) nibble. + * \paramin end_nibble Negative to write all digits found in str, followed by 0xf nibbles to fill any started octet. + * If >= 0, stop before this offset in nibbles, e.g. to get default behavior, pass + * start_nibble + strlen(str) + ((start_nibble + strlen(str)) & 1? 1 : 0) + 1. + * \paramin allow_hex If false, return error if there are hexadecimal digits (A-F). If true, write those to + * BCD. + * \returns The buffer size in octets that is used to place all bcd digits (including the skipped nibbles + * from 'start_nibble' and rounded up to full octets); -EINVAL on invalid digits; + * -ENOMEM if dst is NULL, if dst_size is too small to contain all nibbles, or if start_nibble is negative. + */ +int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex) +{ + const char *digit = digits; + int nibble_i; + + if (!dst || !dst_size || start_nibble < 0) + return -ENOMEM; + + if (end_nibble < 0) { + end_nibble = start_nibble + strlen(digits); + /* If the last octet is not complete, add another filler nibble */ + if (end_nibble & 1) + end_nibble++; + } + if ((unsigned int) (end_nibble / 2) > dst_size) + return -ENOMEM; + + for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) { + uint8_t nibble = 0xf; + int octet = nibble_i >> 1; + if (*digit) { + char c = *digit; + digit++; + if (c >= '0' && c <= '9') + nibble = c - '0'; + else if (allow_hex && c >= 'A' && c <= 'F') + nibble = 0xa + (c - 'A'); + else if (allow_hex && c >= 'a' && c <= 'f') + nibble = 0xa + (c - 'a'); + else + return -EINVAL; + } + nibble &= 0xf; + if ((nibble_i & 1)) + dstoctet = (nibble << 4) | (dstoctet & 0x0f); + else + dstoctet = (dstoctet & 0xf0) | nibble; + } + + /* floor(float(end_nibble) / 2) */ + return end_nibble / 2; +} + +/*! Parse a string containing hexadecimal digits + * \paramin str string containing ASCII encoded hexadecimal digits + * \paramout b output buffer + * \paramin max_len maximum space in output buffer + * \returns number of parsed octets, or -1 on error + */ +int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len) + +{ + char c; + uint8_t v; + const char *strpos; + unsigned int nibblepos = 0; + + memset(b, 0x00, max_len); + + for (strpos = str; (c = *strpos); strpos++) { + /* skip whitespace */ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + + /* If the buffer is too small, error out */ + if (nibblepos >= (max_len << 1)) + return -1; + + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = 10 + (c - 'a'); + else if (c >= 'A' && c <= 'F') + v = 10 + (c - 'A'); + else + return -1; + + bnibblepos >> 1 |= v << (nibblepos & 1 ? 0 : 4); + nibblepos ++; + } + + /* In case of uneven amount of digits, the last byte is not complete + * and that's an error. */ + if (nibblepos & 1) + return -1; + + return nibblepos >> 1; +} + +static __thread char hexd_buff4096; +static const char hex_chars = "0123456789abcdef"; + +/*! Convert binary sequence to hexadecimal ASCII string. + * \paramout out_buf Output buffer to write the resulting string to. + * \paramin out_buf_size sizeof(out_buf). + * \paramin buf Input buffer, pointer to sequence of bytes. + * \paramin len Length of input buf in number of bytes. + * \paramin delim String to separate each byte; NULL or "" for no delim. + * \paramin delim_after_last If true, end the string in delim (true: "1a:ef:d9:", false: "1a:ef:d9"); + * if out_buf has insufficient space, the string will always end in a delim. + * \returns out_buf, containing a zero-terminated string, or "" (empty string) if out_buf == NULL or out_buf_size < 1. + * + * This function will print a sequence of bytes as hexadecimal numbers, adding one delim between each byte (e.g. for + * delim passed as ":", return a string like "1a:ef:d9"). + * + * The delim_after_last argument exists to be able to exactly show the original osmo_hexdump() behavior, which always + * ends the string with a delimiter. + */ +const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned char *buf, int len, const char *delim, + bool delim_after_last) +{ + int i; + char *cur = out_buf; + size_t delim_len; + + if (!out_buf || !out_buf_size) + return ""; + + delim = delim ? : ""; + delim_len = strlen(delim); + + for (i = 0; i < len; i++) { + const char *delimp = delim; + int len_remain = out_buf_size - (cur - out_buf) - 1; + if (len_remain < (int) (2 + delim_len) + && !(!delim_after_last && i == (len - 1) && len_remain >= 2)) + break; + + *cur++ = hex_charsbufi >> 4; + *cur++ = hex_charsbufi & 0xf; + + if (i == (len - 1) && !delim_after_last) + break; + + while (len_remain > 1 && *delimp) { + *cur++ = *delimp++; + len_remain--; + } + } + *cur = '\0'; + return out_buf; +} + +/*! Convert a sequence of unpacked bits to ASCII string, in user-supplied buffer. + * \paramout buf caller-provided output string buffer + * \paramout buf_len size of buf in bytes + * \paramin bits A sequence of unpacked bits + * \paramin len Length of bits + * \return The output buffer (buf). + */ +char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len) +{ + unsigned int i; + + if (len > buf_len-1) + len = buf_len-1; + memset(buf, 0, buf_len); + + for (i = 0; i < len; i++) { + char outch; + switch (bitsi) { + case 0: + outch = '0'; + break; + case 0xff: + outch = '?'; + break; + case 1: + outch = '1'; + break; + default: + outch = 'E'; + break; + } + bufi = outch; + } + bufbuf_len-1 = 0; + return buf; +} + +/*! Convert a sequence of unpacked bits to ASCII string, in static buffer. + * \paramin bits A sequence of unpacked bits + * \paramin len Length of bits + * \returns string representation in static buffer. + */ +char *osmo_ubit_dump(const uint8_t *bits, unsigned int len) +{ + return osmo_ubit_dump_buf(hexd_buff, sizeof(hexd_buff), bits, len); +} + +/*! Convert binary sequence to hexadecimal ASCII string + * \paramin buf pointer to sequence of bytes + * \paramin len length of buf in number of bytes + * \returns pointer to zero-terminated string + * + * This function will print a sequence of bytes as hexadecimal numbers, + * adding one space character between each byte (e.g. "1a ef d9") + * + * The maximum size of the output buffer is 4096 bytes, i.e. the maximum + * number of input bytes that can be printed in one call is 1365! + */ +char *osmo_hexdump(const unsigned char *buf, int len) +{ + osmo_hexdump_buf(hexd_buff, sizeof(hexd_buff), buf, len, " ", true); + return hexd_buff; +} + +/*! Convert binary sequence to hexadecimal ASCII string + * \paramin ctx talloc context from where to allocate the output string + * \paramin buf pointer to sequence of bytes + * \paramin len length of buf in number of bytes + * \returns pointer to zero-terminated string + * + * This function will print a sequence of bytes as hexadecimal numbers, + * adding one space character between each byte (e.g. "1a ef d9") + */ +char *osmo_hexdump_c(const void *ctx, const unsigned char *buf, int len) +{ + size_t hexd_buff_len = len * 3 + 1; + char *hexd_buff = talloc_size(ctx, hexd_buff_len); + if (!hexd_buff) + return NULL; + osmo_hexdump_buf(hexd_buff, hexd_buff_len, buf, len, " ", true); + return hexd_buff; +} + +/*! Convert binary sequence to hexadecimal ASCII string + * \paramin buf pointer to sequence of bytes + * \paramin len length of buf in number of bytes + * \returns pointer to zero-terminated string + * + * This function will print a sequence of bytes as hexadecimal numbers, + * without any space character between each byte (e.g. "1aefd9") + * + * The maximum size of the output buffer is 4096 bytes, i.e. the maximum + * number of input bytes that can be printed in one call is 2048! + */ +char *osmo_hexdump_nospc(const unsigned char *buf, int len) +{ + osmo_hexdump_buf(hexd_buff, sizeof(hexd_buff), buf, len, "", true); + return hexd_buff; +} + +/*! Convert binary sequence to hexadecimal ASCII string + * \paramin ctx talloc context from where to allocate the output string + * \paramin buf pointer to sequence of bytes + * \paramin len length of buf in number of bytes + * \returns pointer to zero-terminated string + * + * This function will print a sequence of bytes as hexadecimal numbers, + * without any space character between each byte (e.g. "1aefd9") + */ +char *osmo_hexdump_nospc_c(const void *ctx, const unsigned char *buf, int len) +{ + size_t hexd_buff_len = len * 2 + 1; + char *hexd_buff = talloc_size(ctx, hexd_buff_len); + if (!hexd_buff) + return NULL; + osmo_hexdump_buf(hexd_buff, hexd_buff_len, buf, len, "", true); + return hexd_buff; +} + + +/* Compat with previous typo to preserve abi */ +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) +#if defined(__MACH__) && defined(__APPLE__) + ; +#else + __attribute__((weak, alias("osmo_hexdump_nospc"))); +#endif + +#include "config.h" +#ifdef HAVE_CTYPE_H +#include <ctype.h> +/*! Convert an entire string to lower case + * \paramout out output string, caller-allocated + * \paramin in input string + */ +void osmo_str2lower(char *out, const char *in) +{ + unsigned int i; + + for (i = 0; i < strlen(in); i++) + outi = tolower((const unsigned char)ini); + outstrlen(in) = '\0'; +} + +/*! Convert an entire string to upper case + * \paramout out output string, caller-allocated + * \paramin in input string + */ +void osmo_str2upper(char *out, const char *in) +{ + unsigned int i; + + for (i = 0; i < strlen(in); i++) + outi = toupper((const unsigned char)ini); + outstrlen(in) = '\0'; +} +#endif /* HAVE_CTYPE_H */ + +/*! Wishful thinking to generate a constant time compare + * \paramin exp Expected data + * \paramin rel Comparison value + * \paramin count Number of bytes to compare + * \returns 1 in case \a exp equals \a rel; zero otherwise + * + * Compare count bytes of exp to rel. Return 0 if they are identical, 1 + * otherwise. Do not return a mismatch on the first mismatching byte, + * but always compare all bytes, regardless. The idea is that the amount of + * matching bytes cannot be inferred from the time the comparison took. */ +int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) +{ + int x = 0, i; + + for (i = 0; i < count; ++i) + x |= expi ^ reli; + + /* if x is zero, all data was identical */ + return x? 1 : 0; +} + +/*! Generic retrieval of 1..8 bytes as big-endian uint64_t + * \paramin data Input data as byte-array + * \paramin data_len Length of \a data in octets + * \returns uint64_t of \a data interpreted as big-endian + * + * This is like osmo_load64be_ext, except that if data_len is less than + * sizeof(uint64_t), the data is interpreted as the least significant bytes + * (osmo_load64be_ext loads them as the most significant bytes into the + * returned uint64_t). In this way, any integer size up to 64 bits can be + * decoded conveniently by using sizeof(), without the need to call specific + * numbered functions (osmo_load16, 32, ...). */ +uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len) +{ + uint64_t value = 0; + + while (data_len > 0) { + value = (value << 8) + *data; + data += 1; + data_len -= 1; + } + + return value; +} + +/*! Generic big-endian encoding of big endian number up to 64bit + * \paramin value unsigned integer value to be stored + * \paramin data_len number of octets + * \returns static buffer containing big-endian stored value + * + * This is like osmo_store64be_ext, except that this returns a static buffer of + * the result (for convenience, but not threadsafe). If data_len is less than + * sizeof(uint64_t), only the least significant bytes of value are encoded. */ +uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len) +{ + static __thread uint8_t bufsizeof(uint64_t); + OSMO_ASSERT(data_len <= ARRAY_SIZE(buf)); + osmo_store64be_ext(value, buf, data_len); + return buf; +} + +/*! Copy a C-string into a sized buffer + * \paramin src source string + * \paramout dst destination string + * \paramin siz size of the \a dst buffer + * \returns length of \a src + * + * Copy at most \a siz bytes from \a src to \a dst, ensuring that the result is + * NUL terminated. The NUL character is included in \a siz, i.e. passing the + * actual sizeof(*dst) is correct. + * + * Note, a similar function that also limits the input buffer size is osmo_print_n(). + */ +size_t osmo_strlcpy(char *dst, const char *src, size_t siz) +{ + size_t ret = src ? strlen(src) : 0; + + if (siz) { + size_t len = OSMO_MIN(siz - 1, ret); + if (len) + memcpy(dst, src, len); + dstlen = '\0'; + } + return ret; +} + +/*! Find first occurence of a char in a size limited string. + * Like strchr() but with a buffer size limit. + * \paramin str String buffer to examine. + * \paramin str_size sizeof(str). + * \paramin c Character to look for. + * \return Pointer to the matched char, or NULL if not found. + */ +const char *osmo_strnchr(const char *str, size_t str_size, char c) +{ + const char *end = str + str_size; + const char *pos; + if (!str) + return NULL; + for (pos = str; pos < end; pos++) { + if (c == *pos) + return pos; + if (!*pos) + return NULL; + } + return NULL; +} + +/*! Validate that a given string is a hex string within given size limits. + * Note that each hex digit amounts to a nibble, so if checking for a hex + * string to result in N bytes, pass amount of digits as 2*N. + * \param str A nul-terminated string to validate, or NULL. + * \param min_digits least permitted amount of digits. + * \param max_digits most permitted amount of digits. + * \param require_even if true, require an even amount of digits. + * \returns true when the hex_str contains only hexadecimal digits (no + * whitespace) and matches the requested length; also true + * when min_digits <= 0 and str is NULL. + */ +bool osmo_is_hexstr(const char *str, int min_digits, int max_digits, + bool require_even) +{ + int len; + /* Use unsigned char * to avoid a compiler warning of + * "error: array subscript has type 'char' -Werror=char-subscripts" */ + const unsigned char *pos = (const unsigned char*)str; + if (!pos) + return min_digits < 1; + for (len = 0; *pos && len < max_digits; len++, pos++) + if (!isxdigit(*pos)) + return false; + if (len < min_digits) + return false; + /* With not too many digits, we should have reached *str == nul */ + if (*pos) + return false; + if (require_even && (len & 1)) + return false; + + return true; +} + +static const char osmo_identifier_illegal_chars = "., {}()<>|~\\^`'\"?=;/+*&%$#!"; + +/*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars + * \paramin str String to validate + * \paramin sep_chars Permitted separation characters between identifiers. + * \returns true in case \a str contains only valid identifiers and sep_chars, false otherwise + */ +bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars) +{ + /* characters that are illegal in names */ + unsigned int i; + size_t len; + + /* an empty string is not a valid identifier */ + if (!str || (len = strlen(str)) == 0) + return false; + + for (i = 0; i < len; i++) { + if (sep_chars && strchr(sep_chars, stri)) + continue; + /* check for 7-bit ASCII */ + if (stri & 0x80) + return false; + if (!isprint((int)stri)) + return false; + /* check for some explicit reserved control characters */ + if (strchr(osmo_identifier_illegal_chars, stri)) + return false; + } + + return true; +} + +/*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars + * \paramin str String to validate + * \returns true in case \a str contains valid identifier, false otherwise + */ +bool osmo_identifier_valid(const char *str) +{ + return osmo_separated_identifiers_valid(str, NULL); +} + +/*! Replace characters in the given string buffer so that it is guaranteed to pass osmo_separated_identifiers_valid(). + * To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in + * doubt, use '-'. + * \paraminout str Identifier to sanitize, must be nul terminated and in a writable buffer. + * \paramin sep_chars Additional characters that are to be replaced besides osmo_identifier_illegal_chars. + * \paramin replace_with Replace any illegal characters with this character. + */ +void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with) +{ + char *pos; + if (!str) + return; + for (pos = str; *pos; pos++) { + if (strchr(osmo_identifier_illegal_chars, *pos) + || (sep_chars && strchr(sep_chars, *pos))) + *pos = replace_with; + } +} + +/*! Like osmo_escape_str_buf2, but with unusual ordering of arguments, and may sometimes return string constants instead + * of writing to buf for error cases or empty input. + * Most *_buf() functions have the buffer and size as first arguments, here the arguments are last. + * In particular, this function signature doesn't work with OSMO_STRBUF_APPEND_NOLEN(). + * \paramin str A string that may contain any characters. + * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. + * \paraminout buf string buffer to write escaped characters to. + * \paramin bufsize size of \a buf. + * \returns buf containing an escaped representation, possibly truncated, + * or "(null)" if str == NULL, or "(error)" in case of errors. + */ +const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize) +{ + if (!str) + return "(null)"; + if (!buf || !bufsize) + return "(error)"; + return osmo_escape_str_buf2(buf, bufsize, str, in_len); +} + +/*! Copy N characters to a buffer with a function signature useful for OSMO_STRBUF_APPEND(). + * Similarly to snprintf(), the result is always nul terminated (except if buf is NULL or bufsize is 0). + * \paramout buf Target buffer. + * \paramin bufsize sizeof(buf). + * \paramin str String to copy. + * \paramin n Maximum number of non-nul characters to copy. + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n) +{ + size_t write_n; + + if (!str) + str = ""; + + n = strnlen(str, n); + + if (!buf || !bufsize) + return n; + write_n = n; + if (write_n >= bufsize) + write_n = bufsize - 1; + if (write_n) + strncpy(buf, str, write_n); + bufwrite_n = '\0'; + + return n; +} + +/*! Return the string with all non-printable characters escaped. + * This internal function is the implementation for all osmo_escape_str* and osmo_quote_str* API versions. + * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax, + * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). + * \paramin legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy + * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while + * the non-legacy format also escapes those as "\xNN" sequences. + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +static int _osmo_escape_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format) +{ + struct osmo_strbuf sb = { .buf = buf, .len = bufsize }; + int in_pos = 0; + int next_unprintable = 0; + + if (!str) + in_len = 0; + + if (in_len < 0) + in_len = strlen(str); + + /* Make sure of '\0' termination */ + if (!in_len) + OSMO_STRBUF_PRINTF(sb, "%s", ""); + + while (in_pos < in_len) { + for (next_unprintable = in_pos; + next_unprintable < in_len && isprint((int)strnext_unprintable) + && strnext_unprintable != '"' + && strnext_unprintable != '\\'; + next_unprintable++); + + OSMO_STRBUF_APPEND(sb, osmo_print_n, &strin_pos, next_unprintable - in_pos); + in_pos = next_unprintable; + + if (in_pos == in_len) + goto done; + + switch (strnext_unprintable) { +#define BACKSLASH_CASE(c, repr) \ + case c: \ + OSMO_STRBUF_PRINTF(sb, "\\%c", repr); \ + break + + BACKSLASH_CASE('\n', 'n'); + BACKSLASH_CASE('\r', 'r'); + BACKSLASH_CASE('\t', 't'); + BACKSLASH_CASE('\0', '0'); + BACKSLASH_CASE('\\', '\\'); + BACKSLASH_CASE('"', '"'); + + default: + if (legacy_format) { + switch (strnext_unprintable) { + BACKSLASH_CASE('\a', 'a'); + BACKSLASH_CASE('\b', 'b'); + BACKSLASH_CASE('\v', 'v'); + BACKSLASH_CASE('\f', 'f'); + default: + OSMO_STRBUF_PRINTF(sb, "\\%u", (unsigned char)strin_pos); + break; + } + break; + } + + OSMO_STRBUF_PRINTF(sb, "\\x%02x", (unsigned char)strin_pos); + break; + } + in_pos ++; +#undef BACKSLASH_CASE + } + +done: + return sb.chars_needed; +} + +/*! Return the string with all non-printable characters escaped. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len) +{ + return _osmo_escape_str_buf(buf, bufsize, str, in_len, false); +} + +/*! Return the string with all non-printable characters escaped. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). + * \return The output buffer (buf). + */ +char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len) +{ + _osmo_escape_str_buf(buf, bufsize, str, in_len, true); + return buf; +} + +/*! Return the string with all non-printable characters escaped. + * Call osmo_escape_str_buf() with a static buffer. + * \paramin str A string that may contain any characters. + * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns buf containing an escaped representation, possibly truncated, or str itself. + */ +const char *osmo_escape_str(const char *str, int in_len) +{ + return osmo_escape_str_buf(str, in_len, namebuf, sizeof(namebuf)); +} + +/*! Return the string with all non-printable characters escaped, in dynamically-allocated buffer. + * \paramin str A string that may contain any characters. + * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns dynamically-allocated output buffer, containing an escaped representation + */ +char *osmo_escape_str_c(const void *ctx, const char *str, int in_len) +{ + /* The string will be at least as long as in_len, but some characters might need escaping. + * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ + OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, true); +} + +/*! Return a quoted and escaped representation of the string. + * This internal function is the implementation for all osmo_quote_str* API versions. + * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax, + * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). + * \paramin legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy + * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while + * the non-legacy format also escapes those as "\xNN" sequences. + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +static size_t _osmo_quote_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format) +{ + struct osmo_strbuf sb = { .buf = buf, .len = bufsize }; + if (!str) + OSMO_STRBUF_PRINTF(sb, "NULL"); + else { + OSMO_STRBUF_PRINTF(sb, "\""); + OSMO_STRBUF_APPEND(sb, _osmo_escape_str_buf, str, in_len, legacy_format); + OSMO_STRBUF_PRINTF(sb, "\""); + } + return sb.chars_needed; +} + +/*! Like osmo_escape_str_buf3(), but returns double-quotes around a string, or "NULL" for a NULL string. + * This allows passing any char* value and get its C representation as string. + * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len) +{ + return _osmo_quote_str_buf(buf, bufsize, str, in_len, false); +} + +/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string. + * This allows passing any char* value and get its C representation as string. + * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \return The output buffer (buf). + */ +char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len) +{ + _osmo_quote_str_buf(buf, bufsize, str, in_len, true); + return buf; +} + +/*! Like osmo_quote_str_buf2, but with unusual ordering of arguments, and may sometimes return string constants instead + * of writing to buf for error cases or empty input. + * Most *_buf() functions have the buffer and size as first arguments, here the arguments are last. + * In particular, this function signature doesn't work with OSMO_STRBUF_APPEND_NOLEN(). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns buf containing a quoted and escaped representation, possibly truncated. + */ +const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize) +{ + if (!str) + return "NULL"; + if (!buf || !bufsize) + return "(error)"; + _osmo_quote_str_buf(buf, bufsize, str, in_len, true); + return buf; +} + +/*! Like osmo_quote_str_buf() but returns the result in a static buffer. + * The static buffer is shared with get_value_string() and osmo_escape_str(). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns static buffer containing a quoted and escaped representation, possibly truncated. + */ +const char *osmo_quote_str(const char *str, int in_len) +{ + _osmo_quote_str_buf(namebuf, sizeof(namebuf), str, in_len, true); + return namebuf; +} + +/*! Like osmo_quote_str_buf() but returns the result in a dynamically-allocated buffer. + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns dynamically-allocated buffer containing a quoted and escaped representation. + */ +char *osmo_quote_str_c(const void *ctx, const char *str, int in_len) +{ + /* The string will be at least as long as in_len, but some characters might need escaping. + * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ + OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, true); +} + +/*! Return the string with all non-printable characters escaped. + * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and + * this escapes characters in a way compatible with C string constant syntax. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars). + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +size_t osmo_escape_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len) +{ + return _osmo_escape_str_buf(buf, bufsize, str, in_len, false); +} + +/*! Return the string with all non-printable characters escaped, in dynamically-allocated buffer. + * In contrast to osmo_escape_str_c(), this escapes characters in a way compatible with C string constant syntax, and + * allocates sufficient memory in all cases. + * \paramin str A string that may contain any characters. + * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns dynamically-allocated buffer, containing an escaped representation. + */ +char *osmo_escape_cstr_c(void *ctx, const char *str, int in_len) +{ + /* The string will be at least as long as in_len, but some characters might need escaping. + * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ + OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, false); +} + +/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string. + * This allows passing any char* value and get its C representation as string. + * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN(). + * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and + * this escapes characters in a way compatible with C string constant syntax. + * \paramout buf string buffer to write escaped characters to. + * \paramin bufsize sizeof(buf). + * \paramin str A string that may contain any characters. + * \paramin in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()). + */ +size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len) +{ + return _osmo_quote_str_buf(buf, bufsize, str, in_len, false); +} + +/*! Return the string quoted and with all non-printable characters escaped, in dynamically-allocated buffer. + * In contrast to osmo_quote_str_c(), this escapes characters in a way compatible with C string constant syntax, and + * allocates sufficient memory in all cases. + * \paramin str A string that may contain any characters. + * \paramin len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns dynamically-allocated buffer, containing a quoted and escaped representation. + */ +char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len) +{ + /* The string will be at least as long as in_len plus two quotes, but some characters might need escaping. + * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */ + OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, false); +} + +/*! perform an integer square root operation on unsigned 32bit integer. + * This implementation is taken from "Hacker's Delight" Figure 11-1 "Integer square root, Newton's + * method", which can also be found at http://www.hackersdelight.org/hdcodetxt/isqrt.c.txt */ +uint32_t osmo_isqrt32(uint32_t x) +{ + uint32_t x1; + int s, g0, g1; + + if (x <= 1) + return x; + + s = 1; + x1 = x - 1; + if (x1 > 0xffff) { + s = s + 8; + x1 = x1 >> 16; + } + if (x1 > 0xff) { + s = s + 4; + x1 = x1 >> 8; + } + if (x1 > 0xf) { + s = s + 2; + x1 = x1 >> 4; + } + if (x1 > 0x3) { + s = s + 1; + } + + g0 = 1 << s; /* g0 = 2**s */ + g1 = (g0 + (x >> s)) >> 1; /* g1 = (g0 + x/g0)/2 */ + + /* converges after four to five divisions for arguments up to 16,785,407 */ + while (g1 < g0) { + g0 = g1; + g1 = (g0 + (x/g0)) >> 1; + } + return g0; +} + +/*! Convert a string to lowercase, while checking buffer size boundaries. + * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. + * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters + * length as well as nul terminated. + * Note: similar osmo_str2lower(), but safe to use for src strings of arbitrary length. + * \paramout dest Target buffer to write lowercase string. + * \paramin dest_len Maximum buffer size of dest (e.g. sizeof(dest)). + * \paramin src String to convert to lowercase. + * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. + */ +size_t osmo_str_tolower_buf(char *dest, size_t dest_len, const char *src) +{ + size_t rc; + if (dest == src) { + if (dest_len < 1) + return 0; + destdest_len - 1 = '\0'; + rc = strlen(dest); + } else { + if (dest_len < 1) + return strlen(src); + rc = osmo_strlcpy(dest, src, dest_len); + } + for (; *dest; dest++) + *dest = tolower(*dest); + return rc; +} + +/*! Convert a string to lowercase, using a static buffer. + * The resulting string may be truncated if the internally used static buffer is shorter than src. + * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a + * terminating nul. The static buffer returned is shared with osmo_str_toupper(). + * See also osmo_str_tolower_buf(). + * \paramin src String to convert to lowercase. + * \returns Resulting lowercase string in a static buffer, always nul terminated. + */ +const char *osmo_str_tolower(const char *src) +{ + osmo_str_tolower_buf(capsbuf, sizeof(capsbuf), src); + return capsbuf; +} + +/*! Convert a string to lowercase, dynamically allocating the output from given talloc context + * See also osmo_str_tolower_buf(). + * \paramin ctx talloc context from where to allocate the output string + * \paramin src String to convert to lowercase. + * \returns Resulting lowercase string in a dynamically allocated buffer, always nul terminated. + */ +char *osmo_str_tolower_c(const void *ctx, const char *src) +{ + size_t buf_len = strlen(src) + 1; + char *buf = talloc_size(ctx, buf_len); + if (!buf) + return NULL; + osmo_str_tolower_buf(buf, buf_len, src); + return buf; +} + +/*! Convert a string to uppercase, while checking buffer size boundaries. + * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. + * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters + * length as well as nul terminated. + * Note: similar osmo_str2upper(), but safe to use for src strings of arbitrary length. + * \paramout dest Target buffer to write uppercase string. + * \paramin dest_len Maximum buffer size of dest (e.g. sizeof(dest)). + * \paramin src String to convert to uppercase. + * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. + */ +size_t osmo_str_toupper_buf(char *dest, size_t dest_len, const char *src) +{ + size_t rc; + if (dest == src) { + if (dest_len < 1) + return 0; + destdest_len - 1 = '\0'; + rc = strlen(dest); + } else { + if (dest_len < 1) + return strlen(src); + rc = osmo_strlcpy(dest, src, dest_len); + } + for (; *dest; dest++) + *dest = toupper(*dest); + return rc; +} + +/*! Convert a string to uppercase, using a static buffer. + * The resulting string may be truncated if the internally used static buffer is shorter than src. + * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a + * terminating nul. The static buffer returned is shared with osmo_str_tolower(). + * See also osmo_str_toupper_buf(). + * \paramin src String to convert to uppercase. + * \returns Resulting uppercase string in a static buffer, always nul terminated. + */ +const char *osmo_str_toupper(const char *src) +{ + osmo_str_toupper_buf(capsbuf, sizeof(capsbuf), src); + return capsbuf; +} + +/*! Convert a string to uppercase, dynamically allocating the output from given talloc context + * See also osmo_str_tolower_buf(). + * \paramin ctx talloc context from where to allocate the output string + * \paramin src String to convert to uppercase. + * \returns Resulting uppercase string in a dynamically allocated buffer, always nul terminated. + */ +char *osmo_str_toupper_c(const void *ctx, const char *src) +{ + size_t buf_len = strlen(src) + 1; + char *buf = talloc_size(ctx, buf_len); + if (!buf) + return NULL; + osmo_str_toupper_buf(buf, buf_len, src); + return buf; +} + +/*! Calculate the Luhn checksum (as used for IMEIs). + * \paramin in Input digits in ASCII string representation. + * \paramin in_len Count of digits to use for the input (14 for IMEI). + * \returns checksum char (e.g. '3'); negative on error + */ +char osmo_luhn(const char* in, int in_len) +{ + int i, sum = 0; + + /* All input must be numbers */ + for (i = 0; i < in_len; i++) { + if (!isdigit((unsigned char)ini)) + return -EINVAL; + } + + /* Double every second digit and add it to sum */ + for (i = in_len - 1; i >= 0; i -= 2) { + int dbl = (ini - '0') * 2; + if (dbl > 9) + dbl -= 9; + sum += dbl; + } + + /* Add other digits to sum */ + for (i = in_len - 2; i >= 0; i -= 2) + sum += ini - '0'; + + /* Final checksum */ + return (sum * 9) % 10 + '0'; +} + +/*! Compare start of a string. + * This is an optimisation of 'strstr(str, startswith_str) == str' because it doesn't search through the entire string. + * \param str (Longer) string to compare. + * \param startswith_str (Shorter) string to compare with the start of str. + * \return true iff the first characters of str fully match startswith_str or startswith_str is empty. */ +bool osmo_str_startswith(const char *str, const char *startswith_str) +{ + if (!startswith_str || !*startswith_str) + return true; + if (!str) + return false; + return strncmp(str, startswith_str, strlen(startswith_str)) == 0; +} + +/*! Convert a string of a floating point number to a signed int, with a decimal factor (fixed-point precision). + * For example, with precision=3, convert "-1.23" to -1230. In other words, the float value is multiplied by + * 10 to-the-power-of precision to obtain the returned integer. + * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to + * reduce implementation complexity. See also utils_test.c. + * The advantage over using sscanf("%f") is guaranteed precision: float or double types may apply rounding in the + * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting + * back and forth between string and int. + * \paramout val Returned integer value. + * \paramin str String of a float, like '-12.345'. + * \paramin precision Fixed-point precision, or * \returns 0 on success, negative on error. + */ +int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision) +{ + const char *point; + char *endptr; + const char *p; + int64_t sign = 1; + int64_t integer = 0; + int64_t decimal = 0; + int64_t precision_factor; + int64_t integer_max; + int64_t decimal_max; + unsigned int i; + + OSMO_ASSERT(val); + *val = 0; + + if (!str) + return -EINVAL; + if (str0 == '-') { + str = str + 1; + sign = -1; + } else if (str0 == '+') { + str = str + 1; + } + if (!str0) + return -EINVAL; + + /* Validate entire string as purely digits and at most one decimal dot. If not doing this here in advance, + * parsing digits might stop early because of precision cut-off and miss validation of input data. */ + point = NULL; + for (p = str; *p; p++) { + if (*p == '.') { + if (point) + return -EINVAL; + point = p; + } else if (!isdigit((unsigned char)*p)) + return -EINVAL; + } + + /* Parse integer part if there is one. If the string starts with a point, there's nothing to parse for the + * integer part. */ + if (!point || point > str) { + errno = 0; + integer = strtoll(str, &endptr, 10); + if ((errno == ERANGE && (integer == LLONG_MAX || integer == LLONG_MIN)) + || (errno != 0 && integer == 0)) + return -ERANGE; + + if ((point && endptr != point) + || (!point && *endptr)) + return -EINVAL; + } + + /* Parse the fractional part if there is any, and if the precision is nonzero (if we even care about fractional + * digits) */ + if (precision && point && point1 != '\0') { + /* limit the number of digits parsed to 'precision'. + * If 'precision' is larger than the 19 digits representable in int64_t, skip some, to pick up lower + * magnitude digits. */ + unsigned int skip_digits = (precision < 20) ? 0 : precision - 20; + char decimal_strprecision + 1; + osmo_strlcpy(decimal_str, point+1, precision+1); + + /* fill with zeros to make exactly 'precision' digits */ + for (i = strlen(decimal_str); i < precision; i++) + decimal_stri = '0'; + decimal_strprecision = '\0'; + + for (i = 0; i < skip_digits; i++) { + /* When skipping digits because precision > nr-of-digits-in-int64_t, they must be zero; + * if there is a nonzero digit above the precision, it's -ERANGE. */ + if (decimal_stri != '0') + return -ERANGE; + } + errno = 0; + decimal = strtoll(decimal_str + skip_digits, &endptr, 10); + if ((errno == ERANGE && (decimal == LLONG_MAX || decimal == LLONG_MIN)) + || (errno != 0 && decimal == 0)) + return -ERANGE; + + if (*endptr) + return -EINVAL; + } + + if (precision > 18) { + /* Special case of returning more digits than fit in int64_t range, e.g. + * osmo_float_str_to_int("0.0000000012345678901234567", precision=25) -> 12345678901234567. */ + precision_factor = 0; + integer_max = 0; + decimal_max = INT64_MAX; + } else { + /* Do not surpass the resulting int64_t range. Depending on the amount of precision, the integer part + * and decimal part have specific ranges they must comply to. */ + precision_factor = 1; + for (i = 0; i < precision; i++) + precision_factor *= 10; + integer_max = INT64_MAX / precision_factor; + if (integer == integer_max) + decimal_max = INT64_MAX % precision_factor; + else + decimal_max = INT64_MAX; + } + + if (integer > integer_max) + return -ERANGE; + if (decimal > decimal_max) + return -ERANGE; + + *val = sign * (integer * precision_factor + decimal); + return 0; +} + +/*! Convert an integer to a floating point string using a decimal quotient (fixed-point precision). + * For example, with precision = 3, convert -1230 to "-1.23". + * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to + * reduce implementation complexity. See also utils_test.c. + * The advantage over using printf("%.6g") is guaranteed precision: float or double types may apply rounding in the + * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting + * back and forth between string and int. + * The resulting string omits trailing zeros in the fractional part (like "%g" would) but never applies rounding. + * \paramout buf Buffer to write string to. + * \paramin buflen sizeof(buf). + * \paramin val Value to convert to float. + * \returns number of chars that would be written, like snprintf(). + */ +int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + unsigned int i; + unsigned int w; + int64_t precision_factor; + if (val < 0) { + OSMO_STRBUF_PRINTF(sb, "-"); + if (val == INT64_MIN) { + OSMO_STRBUF_PRINTF(sb, "ERR"); + return sb.chars_needed; + } + val = -val; + } + + if (precision > 18) { + /* Special case of returning more digits than fit in int64_t range, e.g. + * osmo_int_to_float_str(12345678901234567, precision=25) -> "0.0000000012345678901234567". */ + if (!val) { + OSMO_STRBUF_PRINTF(sb, "0"); + return sb.chars_needed; + } + OSMO_STRBUF_PRINTF(sb, "0."); + for (i = 19; i < precision; i++) + OSMO_STRBUF_PRINTF(sb, "0"); + precision = 19; + } else { + precision_factor = 1; + for (i = 0; i < precision; i++) + precision_factor *= 10; + + OSMO_STRBUF_PRINTF(sb, "%" PRId64, val / precision_factor); + val %= precision_factor; + if (!val) + return sb.chars_needed; + OSMO_STRBUF_PRINTF(sb, "."); + } + + /* print fractional part, skip trailing zeros */ + w = precision; + while (!(val % 10)) { + val /= 10; + w--; + } + OSMO_STRBUF_PRINTF(sb, "%0*" PRId64, w, val); + return sb.chars_needed; +} + +/*! Convert an integer with a factor of a million to a floating point string. + * For example, convert -1230000 to "-1.23". + * \paramin ctx Talloc ctx to allocate string buffer from. + * \paramin val Value to convert to float. + * \returns resulting string, dynamically allocated. + */ +char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision) +{ + OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_int_to_float_str_buf, val, precision) +} + +/*! Convert a string of a number to int64_t, including all common strtoll() validity checks. + * It's not so trivial to call strtoll() and properly verify that the input string was indeed a valid number string. + * \paramout result Buffer for the resulting integer number, or NULL if the caller is only interested in the + * validation result (returned rc). + * \paramin str The string to convert. + * \paramin base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll(). + * \paramin min_val The smallest valid number expected in the string. + * \paramin max_val The largest valid number expected in the string. + * \return 0 on success, -EOVERFLOW if the number in the string exceeds int64_t, -ENOTSUPP if the base is not supported, + * -ERANGE if the converted number exceeds the range min_val..max_val but is still within int64_t range, -E2BIG if + * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and + * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is + * clamped to INT64_MIN..INT64_MAX. + */ +int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val) +{ + long long int val; + char *endptr; + if (result) + *result = 0; + if (!str || !*str) + return -EINVAL; + errno = 0; + val = strtoll(str, &endptr, base); + /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or + * LLONG_MAX. Make sure of the same here with respect to int64_t. */ + if (val < INT64_MIN) { + if (result) + *result = INT64_MIN; + return -ERANGE; + } + if (val > INT64_MAX) { + if (result) + *result = INT64_MAX; + return -ERANGE; + } + if (result) + *result = (int64_t)val; + switch (errno) { + case 0: + break; + case ERANGE: + return -EOVERFLOW; + default: + case EINVAL: + return -ENOTSUP; + } + if (!endptr || *endptr) { + /* No chars were converted */ + if (endptr == str) + return -EINVAL; + /* Or there are surplus chars after the converted number */ + return -E2BIG; + } + if (val < min_val || val > max_val) + return -ERANGE; + return 0; +} + +/*! Convert a string of a number to int, including all common strtoll() validity checks. + * Same as osmo_str_to_int64() but using the plain int data type. + * \paramout result Buffer for the resulting integer number, or NULL if the caller is only interested in the + * validation result (returned rc). + * \paramin str The string to convert. + * \paramin base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll(). + * \paramin min_val The smallest valid number expected in the string. + * \paramin max_val The largest valid number expected in the string. + * \return 0 on success, -EOVERFLOW if the number in the string exceeds int range, -ENOTSUPP if the base is not supported, + * -ERANGE if the converted number exceeds the range min_val..max_val but is still within int range, -E2BIG if + * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and + * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is + * clamped to INT_MIN..INT_MAX. + */ +int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val) +{ + int64_t val; + int rc = osmo_str_to_int64(&val, str, base, min_val, max_val); + /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or + * LLONG_MAX. Make sure of the same here with respect to int. */ + if (val < INT_MIN) { + if (result) + *result = INT_MIN; + return -EOVERFLOW; + } + if (val > INT_MAX) { + if (result) + *result = INT_MAX; + return -EOVERFLOW; + } + if (result) + *result = (int)val; + return rc; +} + +/*! Replace a string using talloc and release its prior content (if any). + * This is a format string capable equivalent of osmo_talloc_replace_string(). + * \paramin ctx Talloc context to use for allocation. + * \paramout dst Pointer to string, will be updated with ptr to new string. + * \paramin fmt Format string that will be copied to newly allocated string. */ +void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...) +{ + char *name = NULL; + + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + name = talloc_vasprintf(ctx, fmt, ap); + va_end(ap); + } + + talloc_free(*dst); + *dst = name; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/core/write_queue.c
Changed
(renamed from src/write_queue.c)
View file
libosmocore_1.7.0.tar.xz/src/ctrl/Makefile.am -> libosmocore_1.8.0.tar.xz/src/ctrl/Makefile.am
Changed
@@ -1,9 +1,10 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=7:0:7 +LIBVERSION=8:0:8 -AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) if ENABLE_CTRL lib_LTLIBRARIES = libosmoctrl.la @@ -12,7 +13,7 @@ libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) -version-info $(LIBVERSION) -no-undefined libosmoctrl_la_LIBADD = $(TALLOC_LIBS) \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ $(top_builddir)/src/vty/libosmovty.la
View file
libosmocore_1.7.0.tar.xz/src/ctrl/control_cmd.c -> libosmocore_1.8.0.tar.xz/src/ctrl/control_cmd.c
Changed
@@ -210,6 +210,16 @@ { vector cmds_vec; + /* If this assert triggers, it means the program forgot to initialize + * the CTRL interface first by calling ctrl_handle_alloc(2)() directly + * or indirectly through ctrl_interface_setup_dynip(2)() + */ + if (!ctrl_node_vec) { + LOGP(DLCTRL, LOGL_ERROR, + "ctrl_handle must be initialized prior to installing cmds.\n"); + return -ENODEV; + } + cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); if (!cmds_vec) {
View file
libosmocore_1.7.0.tar.xz/src/ctrl/control_if.c -> libosmocore_1.8.0.tar.xz/src/ctrl/control_if.c
Changed
@@ -47,6 +47,7 @@ #include <osmocom/ctrl/control_cmd.h> #include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_vty.h> #include <osmocom/core/msgb.h> #include <osmocom/core/rate_ctr.h> @@ -878,7 +879,7 @@ struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port, ctrl_cmd_lookup lookup) { - return ctrl_interface_setup_dynip(data, "127.0.0.1", port, lookup); + return ctrl_interface_setup2(data, port, lookup, 0); } static int ctrl_initialized = 0; @@ -1030,6 +1031,18 @@ return ctrl_interface_setup_dynip2(data, bind_addr, port, lookup, 0); } +/*! Initializes CTRL interface using the configured bind addr/port. + * \paramin data Pointer which will be made available to each set_..() get_..() verify_..() control command function + * \paramin default_port TCP port number to bind to if not explicitly configured + * \paramin lookup Lookup function pointer, can be NULL + * \paramin node_count Number of CTRL nodes to allocate, 0 for default. + */ +struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup, + unsigned int node_count) +{ + return ctrl_interface_setup_dynip2(data, ctrl_vty_get_bind_addr(), ctrl_vty_get_bind_port(default_port), lookup, + node_count); +} /*! Install a lookup helper function for control nodes * This function is used by e.g. library code to install lookup helpers
View file
libosmocore_1.7.0.tar.xz/src/ctrl/control_vty.c -> libosmocore_1.8.0.tar.xz/src/ctrl/control_vty.c
Changed
@@ -26,16 +26,20 @@ static void *ctrl_vty_ctx = NULL; static const char *ctrl_vty_bind_addr = NULL; +/* Port the CTRL should bind to: -1 means not configured */ +static int ctrl_bind_port = -1; DEFUN(cfg_ctrl_bind_addr, cfg_ctrl_bind_addr_cmd, - "bind A.B.C.D", + "bind A.B.C.D <0-65535>", "Set bind address to listen for Control connections\n" - "Local IP address (default 127.0.0.1)\n") + "Local IP address (default 127.0.0.1)\n" + "Local TCP port number\n") { talloc_free((char*)ctrl_vty_bind_addr); ctrl_vty_bind_addr = NULL; ctrl_vty_bind_addr = talloc_strdup(ctrl_vty_ctx, argv0); + ctrl_bind_port = argc > 1 ? atoi(argv1) : -1; return CMD_SUCCESS; } @@ -46,6 +50,11 @@ return ctrl_vty_bind_addr; } +uint16_t ctrl_vty_get_bind_port(uint16_t default_port) +{ + return ctrl_bind_port >= 0 ? ctrl_bind_port : default_port; +} + static struct cmd_node ctrl_node = { L_CTRL_NODE, "%s(config-ctrl)# ",
View file
libosmocore_1.7.0.tar.xz/src/ctrl/libosmoctrl.map -> libosmocore_1.8.0.tar.xz/src/ctrl/libosmoctrl.map
Changed
@@ -22,12 +22,14 @@ ctrl_handle_alloc2; /* could be removed? */ ctrl_handle_msg; /* only used in unit test */ ctrl_interface_setup; +ctrl_interface_setup2; ctrl_interface_setup_dynip; ctrl_interface_setup_dynip2; ctrl_lookup_register; ctrl_parse_get_num; ctrl_type_vals; ctrl_vty_get_bind_addr; +ctrl_vty_get_bind_port; ctrl_vty_init; osmo_ctrl_conn_alloc;
View file
libosmocore_1.7.0.tar.xz/src/gb/Makefile.am -> libosmocore_1.8.0.tar.xz/src/gb/Makefile.am
Changed
@@ -1,12 +1,11 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=14:0:0 +LIBVERSION=15:0:1 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing \ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall -fno-strict-aliasing \ $(TALLOC_CFLAGS) \ - $(LIBMNL_CFLAGS) \ $(NULL) # FIXME: this should eventually go into a milenage/Makefile.am @@ -15,9 +14,13 @@ if ENABLE_GB lib_LTLIBRARIES = libosmogb.la -libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION) +libosmogb_la_LDFLAGS = \ + $(LTLDFLAGS_OSMOGB) \ + -version-info $(LIBVERSION) \ + -no-undefined \ + $(NULL) libosmogb_la_LIBADD = $(TALLOC_LIBS) \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_bssgp.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_bssgp.c
Changed
@@ -1428,7 +1428,7 @@ /*! * \brief Flush the queues of all BSSGP contexts. */ -void bssgp_flush_all_queues() +void bssgp_flush_all_queues(void) { struct bssgp_bvc_ctx *bctx;
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_bssgp2.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_bssgp2.c
Changed
@@ -349,6 +349,35 @@ return msg; } +/*! Encode BSSGP FLUSH-LL PDU as per TS 48.018 Section 10.4.1. + * \paramin tlli - the TLLI of the MS + * \paramin old_bvci BVCI + * \paramin new_bvci2 optional BVCI - only encoded if non-NULL + * \paramin nsei optional - only encoded if non-NULL + * \returns encoded PDU or NULL in case of error */ +struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci, + const uint16_t *new_bvci, const uint16_t *nsei) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph; + + if (!msg) + return NULL; + + bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + bgph->pdu_type = BSSGP_PDUT_FLUSH_LL; + + msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, tlli); + msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, old_bvci); + if (new_bvci) + msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *new_bvci); + + if (nsei) + msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *nsei); + + return msg; +} + /*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.4. * \paramin tag the tag IE value to encode * \returns encoded PDU or NULL in case of error */
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_bssgp_rim.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_bssgp_rim.c
Changed
@@ -1064,7 +1064,6 @@ struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph; uint8_t rim_ri_bufBSSGP_RIM_ROUTING_INFO_MAXLEN; - uint8_t *rim_cont_buf; int rc; if (!msg) @@ -1105,7 +1104,7 @@ /* Put RIM container */ if (pdu->decoded_present) { - rim_cont_buf = talloc_zero_size(msg, msg->data_len); + uint8_t *rim_cont_buf = talloc_zero_size(msg, msg->data_len); if (!rim_cont_buf) goto error; @@ -1130,8 +1129,10 @@ /* The API user must set the iei properly! */ OSMO_ASSERT(false); } - if (rc < 0) + if (rc < 0) { + talloc_free(rim_cont_buf); goto error; + } msgb_tvlv_put(msg, pdu->rim_cont_iei, rc, rim_cont_buf); talloc_free(rim_cont_buf); @@ -1143,7 +1144,6 @@ return msg; error: - talloc_free(rim_cont_buf); msgb_free(msg); return 0; }
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_ns.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_ns.c
Changed
@@ -352,8 +352,7 @@ */ void gprs_nsvc_delete(struct gprs_nsvc *nsvc) { - if (osmo_timer_pending(&nsvc->timer)) - osmo_timer_del(&nsvc->timer); + osmo_timer_del(&nsvc->timer); llist_del(&nsvc->list); rate_ctr_group_free(nsvc->ctrg); osmo_stat_item_group_free(nsvc->statg); @@ -750,8 +749,7 @@ nsvc->nsei, get_value_string(timer_mode_strs, mode), seconds); - if (osmo_timer_pending(&nsvc->timer)) - osmo_timer_del(&nsvc->timer); + osmo_timer_del(&nsvc->timer); osmo_gettimeofday(&nsvc->timer_started, NULL); nsvc->timer_mode = mode;
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_ns2_fr.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_ns2_fr.c
Changed
@@ -55,13 +55,10 @@ #include <osmocom/core/timer.h> #include <osmocom/core/talloc.h> #include <osmocom/gprs/gprs_ns2.h> +#include <osmocom/core/netdev.h> #include <osmocom/gprs/protocol/gsm_08_16.h> #include <osmocom/gprs/protocol/gsm_08_18.h> -#ifdef ENABLE_LIBMNL -#include <osmocom/core/mnl.h> -#endif - #include "config.h" #include "common_vty.h" #include "gprs_ns2_internal.h" @@ -92,11 +89,12 @@ }; struct priv_bind { + struct osmo_netdev *netdev; char netifIFNAMSIZ; struct osmo_fr_link *link; int ifindex; bool if_running; - /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4993) */ + /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4995) */ struct { /* file-descriptor for AF_PACKET socket */ struct osmo_fd ofd; @@ -172,6 +170,7 @@ } msgb_free(priv->backlog.lmi_msg); + osmo_netdev_free(priv->netdev); osmo_fr_link_free(priv->link); osmo_fd_close(&priv->backlog.ofd); talloc_free(priv); @@ -515,60 +514,14 @@ return fd; } -#ifdef ENABLE_LIBMNL - -#include <osmocom/core/mnl.h> -#include <linux/if_link.h> -#include <linux/rtnetlink.h> - -#ifndef ARPHRD_FRAD -#define ARPHRD_FRAD 770 -#endif - -/* validate the netlink attributes */ -static int data_attr_cb(const struct nlattr *attr, void *data) -{ - const struct nlattr **tb = data; - int type = mnl_attr_get_type(attr); - - if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) - return MNL_CB_OK; - - switch (type) { - case IFLA_MTU: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) - return MNL_CB_ERROR; - break; - case IFLA_IFNAME: - if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) - return MNL_CB_ERROR; - break; - } - tbtype = attr; - return MNL_CB_OK; -} - -/* find the bind for the netdev (if any) */ -static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname) -{ - struct gprs_ns2_vc_bind *bind; - - llist_for_each_entry(bind, &nsi->binding, list) { - struct priv_bind *bpriv = bind->priv; - if (!strcmp(bpriv->netif, ifname)) - return bind; - } - - return NULL; -} - -static void link_state_change(struct gprs_ns2_vc_bind *bind, bool if_running) +static int gprs_n2_fr_ifupdown_ind_cb(struct osmo_netdev *netdev, bool if_running) { + struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev); struct priv_bind *bpriv = bind->priv; struct msgb *msg, *msg2; if (bpriv->if_running == if_running) - return; + return 0; LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n", bpriv->netif, if_running ? "UP" : "DOWN"); @@ -589,93 +542,36 @@ } bpriv->if_running = if_running; + return 0; } -static void mtu_change(struct gprs_ns2_vc_bind *bind, uint32_t mtu) +static int gprs_n2_fr_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu) { + struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev); struct priv_bind *bpriv = bind->priv; struct gprs_ns2_nse *nse; /* 2 byte DLCI header */ - if (mtu <= 2) - return; - mtu -= 2; + if (new_mtu <= 2) + return 0; + new_mtu -= 2; - if (mtu == bind->mtu) - return; + if (new_mtu == bind->mtu) + return 0; LOGBIND(bind, LOGL_INFO, "MTU changed from %d to %d.\n", - bind->mtu + 2, mtu + 2); + bind->mtu + 2, new_mtu + 2); - bind->mtu = mtu; + bind->mtu = new_mtu; if (!bpriv->if_running) - return; + return 0; llist_for_each_entry(nse, &bind->nsi->nse, list) { ns2_nse_update_mtu(nse); } + return 0; } -/* handle a single netlink message received via libmnl */ -static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data) -{ - struct osmo_mnl *omnl = data; - struct gprs_ns2_vc_bind *bind; - struct nlattr *tbIFLA_MAX+1 = {}; - struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); - struct gprs_ns2_inst *nsi; - const char *ifname; - bool if_running; - - OSMO_ASSERT(omnl); - OSMO_ASSERT(ifm); - - nsi = omnl->priv; - - if (ifm->ifi_type != ARPHRD_FRAD) - return MNL_CB_OK; - - mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); - - if (!tbIFLA_IFNAME) - return MNL_CB_OK; - ifname = mnl_attr_get_str(tbIFLA_IFNAME); - if_running = !!(ifm->ifi_flags & IFF_RUNNING); - - bind = bind4netdev(nsi, ifname); - if (!bind) - return MNL_CB_OK; - - if (tbIFLA_MTU) { - mtu_change(bind, mnl_attr_get_u32(tbIFLA_MTU)); - } - - link_state_change(bind, if_running); - - return MNL_CB_OK; -} - -/* trigger one initial dump of all link information */ -static void linkmon_initial_dump(struct osmo_mnl *omnl) -{ - char bufMNL_SOCKET_BUFFER_SIZE; - struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); - struct rtgenmsg *rt; - - nlh->nlmsg_type = RTM_GETLINK; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - nlh->nlmsg_seq = time(NULL); - rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); - rt->rtgen_family = AF_PACKET; - - if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) { - LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno)); - } - - /* the responses will be handled just like the events */ -} -#endif /* LIBMNL */ - static int set_ifupdown(const char *netif, bool up) { int sock, rc; @@ -854,16 +750,31 @@ goto err_fr; } + priv->netdev = osmo_netdev_alloc(bind, name); + if (!priv->netdev) { + rc = -ENOENT; + goto err_fr; + } + osmo_netdev_set_priv_data(priv->netdev, bind); + osmo_netdev_set_ifupdown_ind_cb(priv->netdev, gprs_n2_fr_ifupdown_ind_cb); + osmo_netdev_set_mtu_chg_cb(priv->netdev, gprs_n2_fr_mtu_chg_cb); + rc = osmo_netdev_set_ifindex(priv->netdev, priv->ifindex); + if (rc < 0) + goto err_free_netdev; + rc = osmo_netdev_register(priv->netdev); + if (rc < 0) + goto err_free_netdev; + /* set protocol frame relay and lmi */ rc = setup_device(priv->netif, bind); if(rc < 0) { LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif); - goto err_fr; + goto err_free_netdev; } rc = open_socket(priv->ifindex, bind); if (rc < 0) - goto err_fr; + goto err_free_netdev; priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */ osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind); osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0); @@ -871,16 +782,6 @@ if (rc < 0) goto err_fd; -#ifdef ENABLE_LIBMNL - if (!nsi->linkmon_mnl) - nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi); - - /* we get a new full dump after every bind. which is a bit excessive. But that's just once - * at start-up, so we can get away with it */ - if (nsi->linkmon_mnl) - linkmon_initial_dump(nsi->linkmon_mnl); -#endif - if (result) *result = bind; @@ -888,6 +789,9 @@ err_fd: close(priv->backlog.ofd.fd); +err_free_netdev: + osmo_netdev_free(priv->netdev); + priv->netdev = NULL; err_fr: osmo_fr_link_free(fr_link); priv->link = NULL;
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_ns2_internal.h -> libosmocore_1.8.0.tar.xz/src/gb/gprs_ns2_internal.h
Changed
@@ -163,9 +163,6 @@ /*! workaround for rate counter until rate counter accepts char str as index */ uint32_t nsvc_rate_ctr_idx; uint32_t bind_rate_ctr_idx; - - /*! libmnl netlink socket for link state monitoring */ - struct osmo_mnl *linkmon_mnl; }; @@ -271,6 +268,9 @@ /*! recursive anchor */ bool freed; + /*! if blocked by O&M/vty */ + bool om_blocked; + /*! when the NSVC became alive or dead */ struct timespec ts_alive_change; };
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_ns2_vc_fsm.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_ns2_vc_fsm.c
Changed
@@ -58,8 +58,6 @@ bool initiator; bool initiate_block; bool initiate_reset; - /* if blocked by O&M/vty */ - bool om_blocked; /* if unitdata is forwarded to the user */ bool accept_unitdata; @@ -268,7 +266,7 @@ struct gprs_ns2_inst *nsi = priv->nsvc->nse->nsi; priv->initiate_reset = priv->initiate_block = priv->initiator; - priv->om_blocked = false; + priv->nsvc->om_blocked = false; switch (event) { case GPRS_NS2_EV_REQ_START: @@ -345,7 +343,7 @@ } ns2_nse_notify_unblocked(priv->nsvc, false); - if (priv->om_blocked) { + if (priv->nsvc->om_blocked) { /* we are already blocked after a RESET */ if (old_state == GPRS_NS2_ST_RESET) { osmo_timer_del(&fi->timer); @@ -363,7 +361,7 @@ { struct gprs_ns2_vc_priv *priv = fi->priv; - if (priv->om_blocked) { + if (priv->nsvc->om_blocked) { switch (event) { case GPRS_NS2_EV_RX_BLOCK_ACK: priv->accept_unitdata = false; @@ -563,7 +561,7 @@ case GPRS_NS2_ST_BLOCKED: if (priv->initiate_block) { priv->N++; - if (priv->om_blocked) { + if (priv->nsvc->om_blocked) { if (priv->N <= nsi->timeoutNS_TOUT_TNS_BLOCK_RETRIES) { osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeoutNS_TOUT_TNS_BLOCK, 0); } else { @@ -717,14 +715,14 @@ case GPRS_NS2_EV_REQ_OM_BLOCK: /* vty cmd: block */ priv->initiate_block = true; - priv->om_blocked = true; + priv->nsvc->om_blocked = true; osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeoutNS_TOUT_TNS_BLOCK, 0); break; case GPRS_NS2_EV_REQ_OM_UNBLOCK: /* vty cmd: unblock*/ - if (!priv->om_blocked) + if (!priv->nsvc->om_blocked) return; - priv->om_blocked = false; + priv->nsvc->om_blocked = false; if (fi->state == GPRS_NS2_ST_BLOCKED) osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeoutNS_TOUT_TNS_BLOCK, 0); break; @@ -834,7 +832,7 @@ int ns2_vc_block(struct gprs_ns2_vc *nsvc) { struct gprs_ns2_vc_priv *priv = nsvc->fi->priv; - if (priv->om_blocked) + if (priv->nsvc->om_blocked) return -EALREADY; return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_BLOCK, NULL); @@ -846,7 +844,7 @@ int ns2_vc_unblock(struct gprs_ns2_vc *nsvc) { struct gprs_ns2_vc_priv *priv = nsvc->fi->priv; - if (!priv->om_blocked) + if (!priv->nsvc->om_blocked) return -EALREADY; return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_UNBLOCK, NULL);
View file
libosmocore_1.7.0.tar.xz/src/gb/gprs_ns2_vty.c -> libosmocore_1.8.0.tar.xz/src/gb/gprs_ns2_vty.c
Changed
@@ -1875,18 +1875,21 @@ void ns2_vty_dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats) { if (nsvc->nsvci_is_valid) - vty_out(vty, " NSVCI %05u: %s %s %s %s since ", nsvc->nsvci, + vty_out(vty, " NSVCI %05u: %s %s %s %s %ssince ", nsvc->nsvci, osmo_fsm_inst_state_name(nsvc->fi), nsvc->persistent ? "PERSIST" : "DYNAMIC", gprs_ns2_ll_str(nsvc), - ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD"); + ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD", + nsvc->om_blocked ? "(blocked by O&M/vty) " : + !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : ""); else - vty_out(vty, " %s %s sig_weight=%u data_weight=%u %s %s since ", + vty_out(vty, " %s %s sig_weight=%u data_weight=%u %s %s %ssince ", osmo_fsm_inst_state_name(nsvc->fi), nsvc->persistent ? "PERSIST" : "DYNAMIC", nsvc->sig_weight, nsvc->data_weight, gprs_ns2_ll_str(nsvc), - ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD"); + ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD", + !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : ""); vty_out_uptime(vty, &nsvc->ts_alive_change); vty_out_newline(vty); @@ -2069,6 +2072,31 @@ return CMD_SUCCESS; } +DEFUN(nse_restart_sns, nse_restart_sns_cmd, + "nse <0-65535> restart-sns", + "NSE specific commands\n" + "NS Entity ID (NSEI)\n" + "Restart SNS procedure\n") +{ + struct gprs_ns2_inst *nsi = vty_nsi; + struct gprs_ns2_nse *nse; + + uint16_t id = atoi(argv0); + nse = gprs_ns2_nse_by_nsei(nsi, id); + if (!nse) { + vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + if (nse->dialect != GPRS_NS2_DIALECT_SNS) { + vty_out(vty, "Given NSEI %u doesn't use IP-SNS%s", id, VTY_NEWLINE); + return CMD_WARNING; + } + + gprs_ns2_free_nsvcs(nse); + return CMD_SUCCESS; +} + DEFUN(nsvc_block, nsvc_block_cmd, "nsvc <0-65535> (block|unblock|reset)", "NS Virtual Connection\n" @@ -2236,6 +2264,7 @@ install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd); install_lib_element(ENABLE_NODE, &nsvc_block_cmd); + install_lib_element(ENABLE_NODE, &nse_restart_sns_cmd); install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd); install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
View file
libosmocore_1.7.0.tar.xz/src/gb/libosmogb.map -> libosmocore_1.8.0.tar.xz/src/gb/libosmogb.map
Changed
@@ -85,6 +85,7 @@ bssgp2_enc_fc_bvc_ack; bssgp2_enc_fc_ms; bssgp2_enc_fc_ms_ack; +bssgp2_enc_flush_ll; bssgp2_enc_status; bssgp_bvc_fsm_alloc_sig_bss;
View file
libosmocore_1.7.0.tar.xz/src/gsm/Makefile.am -> libosmocore_1.8.0.tar.xz/src/gsm/Makefile.am
Changed
@@ -1,10 +1,10 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=18:0:0 +LIBVERSION=19:0:1 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) -AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) if ENABLE_PSEUDOTALLOC AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc @@ -25,25 +25,25 @@ gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \ gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c gsm0414.c \ - lapd_core.c lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \ + lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \ auth_core.c auth_comp128v1.c auth_comp128v23.c auth_xor.c \ auth_milenage.c milenage/aes-encblock.c gea.c \ milenage/aes-internal.c milenage/aes-internal-enc.c \ milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \ gsup.c gsup_sms.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \ gsm23003.c gsm23236.c mncc.c bts_features.c oap_client.c \ - gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c i460_mux.c \ + gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c \ gad.c bsslap.c bssmap_le.c kdf.c iuup.c libgsmint_la_LDFLAGS = -no-undefined -libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la +libgsmint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/isdn/libosmoisdn.la libosmogsm_la_SOURCES = libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined libosmogsm_la_LIBADD = libgsmint.la $(TALLOC_LIBS) if ENABLE_GNUTLS -AM_CPPFLAGS += $(LIBGNUTLS_CFLAGS) +AM_CFLAGS += $(LIBGNUTLS_CFLAGS) libosmogsm_la_LIBADD += $(LIBGNUTLS_LIBS) else noinst_HEADERS += kdf/sha1.h kdf/sha256.h kdf/common.h kdf/sha1_i.h kdf/sha256_i.h
View file
libosmocore_1.7.0.tar.xz/src/gsm/bts_features.c -> libosmocore_1.8.0.tar.xz/src/gsm/bts_features.c
Changed
@@ -43,6 +43,9 @@ { BTS_FEAT_BCCH_POWER_RED, "BCCH carrier power reduction mode" }, { BTS_FEAT_DYN_TS_SDCCH8, "Dynamic Timeslot configuration as SDCCH8" }, { BTS_FEAT_ACCH_TEMP_OVP, "FACCH/SACCH Temporary overpower" }, + { BTS_FEAT_OSMUX, "Osmux (Osmocom RTP multiplexing)" }, + { BTS_FEAT_VBS, "Voice Broadcast Service" }, + { BTS_FEAT_VGCS, "Voice Group Call Service" }, { 0, NULL } }; @@ -77,5 +80,7 @@ { BTS_FEAT_ABIS_OSMO_PCU, "ABIS_OSMO_PCU" }, { BTS_FEAT_BCCH_POWER_RED, "BCCH_PWR_RED" }, { BTS_FEAT_DYN_TS_SDCCH8, "DYN_TS_SDCCH8" }, + { BTS_FEAT_ACCH_TEMP_OVP, "ACCH_TEMP_OVP" }, + { BTS_FEAT_OSMUX, "OSMUX" }, {} };
View file
libosmocore_1.7.0.tar.xz/src/gsm/cbsp.c -> libosmocore_1.8.0.tar.xz/src/gsm/cbsp.c
Changed
@@ -537,8 +537,8 @@ struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent); unsigned int len_remain = len - (cur - buf); OSMO_ASSERT(ent); - ent->id_discr = cur0; - rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1); + ent->id_discr = *cur++; + rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur, len_remain-1); if (rc < 0) { osmo_cbsp_errstr = "fail list: error decoding cell_id_union"; return rc; @@ -639,6 +639,7 @@ struct msgb *in, void *ctx) { unsigned int i; + int rc; /* check for mandatory IEs */ if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) || @@ -656,8 +657,10 @@ } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) { uint8_t num_of_pages; @@ -718,6 +721,8 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -733,14 +738,18 @@ INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -753,6 +762,8 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { @@ -768,21 +779,27 @@ } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { @@ -796,6 +813,8 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { @@ -807,8 +826,10 @@ out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -821,6 +842,8 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { @@ -833,14 +856,18 @@ INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -853,6 +880,8 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { @@ -864,21 +893,27 @@ out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { @@ -892,6 +927,8 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -899,8 +936,10 @@ } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -910,6 +949,8 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -917,9 +958,11 @@ } INIT_LLIST_HEAD(&out->loading_list.list); - cbsp_decode_loading_list(&out->loading_list, ctx, - TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), - TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + rc = cbsp_decode_loading_list(&out->loading_list, ctx, + TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), + TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -929,6 +972,8 @@ static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -936,25 +981,29 @@ } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); INIT_LLIST_HEAD(&out->loading_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) { - cbsp_decode_loading_list(&out->loading_list, ctx, - TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), - TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + rc = cbsp_decode_loading_list(&out->loading_list, ctx, + TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), + TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); } - return 0; + return rc; } /* 8.1.3.10 STATUS QUERY */ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || @@ -967,8 +1016,10 @@ out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -978,6 +1029,8 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) || @@ -990,9 +1043,11 @@ out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->num_compl_list.list); - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; } @@ -1001,6 +1056,8 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || @@ -1013,17 +1070,21 @@ out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } return 0; } @@ -1032,14 +1093,19 @@ static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; + return 0; } @@ -1047,14 +1113,19 @@ static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; + return 0; } @@ -1062,20 +1133,26 @@ static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } return 0; } @@ -1106,6 +1183,8 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) { @@ -1114,8 +1193,10 @@ } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE); out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND); @@ -1126,6 +1207,8 @@ static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -1133,9 +1216,11 @@ } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE); return 0; @@ -1176,6 +1261,7 @@ * \returns callee-allocated decoded representation of CBSP message; NULL on error */ struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in) { + OSMO_ASSERT(in->l1h != NULL && in->l2h != NULL); struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded); const struct cbsp_header *h = msgb_l1(in); struct tlv_parsed tp16; /* max. number of pages in a given CBS message */ @@ -1441,6 +1527,10 @@ needed = len - msgb_l2len(msg); if (needed > 0) { + if (needed > msgb_tailroom(msg)) { + rc = -ENOMEM; + goto discard_msg; + } rc = recv(fd, msg->tail, needed, 0); if (rc == 0) goto discard_msg;
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm0411_utils.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm0411_utils.c
Changed
@@ -27,7 +27,7 @@ * */ -#include "../../config.h" +#include "config.h" #include <time.h> #include <string.h>
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm0502.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm0502.c
Changed
@@ -34,7 +34,7 @@ #include <inttypes.h> unsigned int -gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi) +gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi) { int ccch_conf; int bs_cc_chans; @@ -179,7 +179,6 @@ uint8_t fn_cycle; uint8_t i; int sub = -1; - uint32_t fn_map; struct fn_remap_table *table; OSMO_ASSERT(channel < ARRAY_SIZE(fn_remap_table_ptr)); @@ -199,9 +198,7 @@ return fn; } - fn_map = (fn + GSM_MAX_FN - sub) % GSM_MAX_FN; - - return fn_map; + return GSM_TDMA_FN_SUB(fn, sub); } /* Magic numbers (RNTABLE) for pseudo-random hopping sequence generation. */
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm0808.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm0808.c
Changed
@@ -100,13 +100,19 @@ msgb_l3len(msg_l3), msg_l3->l3h); /* AoIP: add Codec List (BSS Supported) 3.2.2.103 */ - if (scl) - gsm0808_enc_speech_codec_list(msg, scl); + if (scl) { + if (gsm0808_enc_speech_codec_list2(msg, scl) < 0) + goto exit_free; + } /* push the bssmap header */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create "Complete L3 Info" for A, legacy implementation. @@ -373,7 +379,7 @@ if (config != GSM0808_LCLS_CFG_NA) msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, config); if (control != GSM0808_LCLS_CSC_NA) - msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, control); + msgb_tv_put(msg, GSM0808_IE_LCLS_CONN_STATUS_CTRL, control); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; @@ -418,7 +424,7 @@ /*! Create BSSMAP Classmark Request message * \returns callee-allocated msgb with BSSMAP Classmark Request message */ -struct msgb *gsm0808_create_classmark_request() +struct msgb *gsm0808_create_classmark_request(void) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "classmark-request"); @@ -530,8 +536,10 @@ } /* AoIP: Codec List (MSC Preferred) 3.2.2.103 */ - if (scl) - gsm0808_enc_speech_codec_list(msg, scl); + if (scl) { + if (gsm0808_enc_speech_codec_list2(msg, scl) < 0) + goto exit_free; + } /* AoIP: Call Identifier 3.2.2.105 */ if (ci) { @@ -552,6 +560,10 @@ msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1. @@ -616,12 +628,16 @@ gsm0808_enc_aoip_trasp_addr(msg, ss); /* AoIP: Speech Codec (Chosen) 3.2.2.104 */ - if (sc) - gsm0808_enc_speech_codec(msg, sc); + if (sc) { + if (gsm0808_enc_speech_codec2(msg, sc) < 0) + goto exit_free; + } /* AoIP: add Codec List (BSS Supported) 3.2.2.103 */ - if (scl) - gsm0808_enc_speech_codec_list(msg, scl); + if (scl) { + if (gsm0808_enc_speech_codec_list2(msg, scl) < 0) + goto exit_free; + } /* FIXME: write LSA identifier 3.2.2.15 - see 3GPP TS 43.073 */ @@ -632,6 +648,10 @@ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP Assignment Completed message @@ -693,13 +713,19 @@ /* Circuit pool list 3.2.2.46 */ /* AoIP: add Codec List (BSS Supported) 3.2.2.103 */ - if (scl) - gsm0808_enc_speech_codec_list(msg, scl); + if (scl) { + if (gsm0808_enc_speech_codec_list2(msg, scl) < 0) + goto exit_free; + } /* update the size */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP Assignment Failure message @@ -992,8 +1018,10 @@ if (params->aoip_transport_layer) gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer); - if (params->codec_list_msc_preferred) - gsm0808_enc_speech_codec_list(msg, params->codec_list_msc_preferred); + if (params->codec_list_msc_preferred) { + if (gsm0808_enc_speech_codec_list2(msg, params->codec_list_msc_preferred) < 0) + goto exit_free; + } if (params->call_id_present) { uint8_t val4; @@ -1013,6 +1041,10 @@ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP HANDOVER REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.10. @@ -1048,17 +1080,25 @@ /* AoIP: add Codec List (BSS Supported) 3.2.2.103. * (codec_list_bss_supported was added to struct gsm0808_handover_request_ack later than speech_codec_chosen * below, but it needs to come before it in the message coding). */ - if (params->more_items && params->codec_list_bss_supported.len) - gsm0808_enc_speech_codec_list(msg, ¶ms->codec_list_bss_supported); + if (params->more_items && params->codec_list_bss_supported.len) { + if (gsm0808_enc_speech_codec_list2(msg, ¶ms->codec_list_bss_supported) < 0) + goto exit_free; + } /* AoIP: Speech Codec (Chosen) 3.2.2.104 */ - if (params->speech_codec_chosen_present) - gsm0808_enc_speech_codec(msg, ¶ms->speech_codec_chosen); + if (params->speech_codec_chosen_present) { + if (gsm0808_enc_speech_codec2(msg, ¶ms->speech_codec_chosen) < 0) + goto exit_free; + } /* prepend header with final length */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Same as gsm0808_create_handover_request_ack2() but with less parameters. @@ -1110,7 +1150,7 @@ /*! Create BSSMAP HANDOVER DETECT message, 3GPP TS 48.008 3.2.1.40. * Sent from the MT BSC back to the MSC when the MS has sent a handover RACH request and the MT BSC has * received the Handover Detect message. */ -struct msgb *gsm0808_create_handover_detect() +struct msgb *gsm0808_create_handover_detect(void) { struct msgb *msg; @@ -1129,7 +1169,7 @@ /*! Create BSSMAP HANDOVER SUCCEEDED message, 3GPP TS 48.008 3.2.1.13. * Sent from the MSC back to the old BSS to notify that the MS has successfully accessed the new BSS. */ -struct msgb *gsm0808_create_handover_succeeded() +struct msgb *gsm0808_create_handover_succeeded(void) { struct msgb *msg; @@ -1164,12 +1204,16 @@ msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, ¶ms->rr_cause); /* AoIP: Speech Codec (Chosen) 3.2.2.104 */ - if (params->speech_codec_chosen_present) - gsm0808_enc_speech_codec(msg, ¶ms->speech_codec_chosen); + if (params->speech_codec_chosen_present) { + if (gsm0808_enc_speech_codec2(msg, ¶ms->speech_codec_chosen) < 0) + goto exit_free; + } /* AoIP: add Codec List (BSS Supported) 3.2.2.103 */ - if (params->codec_list_bss_supported.len) - gsm0808_enc_speech_codec_list(msg, ¶ms->codec_list_bss_supported); + if (params->codec_list_bss_supported.len) { + if (gsm0808_enc_speech_codec_list2(msg, ¶ms->codec_list_bss_supported) < 0) + goto exit_free; + } /* Chosen Encryption Algorithm 3.2.2.44 */ if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0) @@ -1183,6 +1227,10 @@ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP HANDOVER FAILURE message, 3GPP TS 48.008 3.2.1.16. @@ -1206,13 +1254,19 @@ msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, ¶ms->rr_cause); /* AoIP: add Codec List (BSS Supported) 3.2.2.103 */ - if (params->codec_list_bss_supported.len) - gsm0808_enc_speech_codec_list(msg, ¶ms->codec_list_bss_supported); + if (params->codec_list_bss_supported.len) { + if (gsm0808_enc_speech_codec_list2(msg, ¶ms->codec_list_bss_supported) < 0) + goto exit_free; + } /* prepend header with final length */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP HANDOVER PERFORMED message, 3GPP TS 48.008 3.2.1.25. @@ -1248,8 +1302,10 @@ msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, params->speech_version_chosen); /* AoIP: Speech Codec (chosen) 3.2.2.104 */ - if (params->speech_codec_chosen_present) - gsm0808_enc_speech_codec(msg, ¶ms->speech_codec_chosen); + if (params->speech_codec_chosen_present) { + if (gsm0808_enc_speech_codec2(msg, ¶ms->speech_codec_chosen) < 0) + goto exit_free; + } /* LCLS-BSS-Status 3.2.2.119 */ if (params->lcls_bss_status_present) @@ -1259,6 +1315,10 @@ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; + +exit_free: + msgb_free(msg); + return NULL; } /*! Create BSSMAP COMMON ID message, 3GPP TS 48.008 3.2.1.68. @@ -1728,7 +1788,7 @@ { BSS_MAP_MSG_UPLINK_APP_DATA, "UPLINK APP DATA" }, { BSS_MAP_MSG_LCLS_CONNECT_CTRL, "LCLS-CONNECT-CONTROL" }, - { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "CLS-CONNECT-CONTROL-ACK" }, + { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "LCLS-CONNECT-CONTROL-ACK" }, { BSS_MAP_MSG_LCLS_NOTIFICATION, "LCLS-NOTIFICATION" }, { 0, NULL }
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm0808_utils.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm0808_utils.c
Changed
@@ -78,7 +78,7 @@ /*! Encode TS 08.08 AoIP transport address IE * \paramout msg Message Buffer to which to append IE * \paramin ss Socket Address to be used in IE - * \returns number of bytes added to \a msg */ + * \returns number of bytes added to \a msg; 0 if msg is too small */ uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg, const struct sockaddr_storage *ss) { @@ -87,16 +87,25 @@ struct sockaddr_in6 *sin6; uint16_t port = 0; uint8_t *ptr; - uint8_t *old_tail; - uint8_t *tlv_len; + const uint8_t len_tl = 2; + uint8_t len_v = sizeof(port); - OSMO_ASSERT(msg); - OSMO_ASSERT(ss); OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6); + switch (ss->ss_family) { + case AF_INET: + len_v += IP_V4_ADDR_LEN; + break; + case AF_INET6: + len_v += IP_V6_ADDR_LEN; + break; + } + + if (msgb_tailroom(msg) < len_tl + len_v) + return 0; + msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR); - tlv_len = msgb_put(msg,1); - old_tail = msg->tail; + msgb_put_u8(msg, len_v); switch (ss->ss_family) { case AF_INET: @@ -114,9 +123,7 @@ } msgb_put_u16(msg, port); - - *tlv_len = (uint8_t) (msg->tail - old_tail); - return *tlv_len + 2; + return len_tl + len_v; } /*! Decode TS 08.08 AoIP transport address IE @@ -132,7 +139,6 @@ struct sockaddr_in6 sin6; const uint8_t *old_elem = elem; - OSMO_ASSERT(ss); if (!elem) return -EINVAL; if (len == 0) @@ -180,7 +186,6 @@ * \returns number of bytes parsed */ int gsm0808_dec_osmux_cid(uint8_t *cid, const uint8_t *elem, uint8_t len) { - OSMO_ASSERT(cid); if (!elem) return -EINVAL; if (len != 1) @@ -204,21 +209,21 @@ gsm48_decode_lai2(&lai, decoded); } -/* Helper function for gsm0808_enc_speech_codec() - * and gsm0808_enc_speech_codec_list() */ -static uint8_t enc_speech_codec(struct msgb *msg, - const struct gsm0808_speech_codec *sc) +/* Helper function for gsm0808_enc_speech_codec_list(). + * Returns number of bytes appended; negative on error. */ +static int enc_speech_codec(struct msgb *msg, + const struct gsm0808_speech_codec *sc) { /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */ uint8_t header = 0; uint8_t *old_tail; - bool type_extended = false; + bool type_extended; /* Note: Extended codec types are codec types that require 8 instead * of 4 bit to fully specify the selected codec. In the following, * we check if we work with an extended type or not. We also check * if the codec type is valid at all. */ - switch(sc->type) { + switch (sc->type) { case GSM0808_SCT_FR1: case GSM0808_SCT_FR2: case GSM0808_SCT_FR3: @@ -235,8 +240,7 @@ break; default: /* Invalid codec type specified */ - OSMO_ASSERT(false); - break; + return -EINVAL; } old_tail = msg->tail; @@ -255,7 +259,6 @@ msgb_put_u8(msg, header); msgb_put_u8(msg, sc->type); } else { - OSMO_ASSERT(sc->type < 0x0f); header |= sc->type; msgb_put_u8(msg, header); } @@ -273,11 +276,13 @@ case GSM0808_SCT_FR5: case GSM0808_SCT_HR4: case GSM0808_SCT_CSD: - OSMO_ASSERT((sc->cfg & 0xff00) == 0); + if (sc->cfg >> 8) + return -EINVAL; msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff); break; default: - OSMO_ASSERT(sc->cfg == 0); + if (sc->cfg != 0) + return -EINVAL; break; } @@ -287,27 +292,38 @@ /*! Encode TS 08.08 Speech Codec IE * \paramout msg Message Buffer to which IE will be appended * \paramin sc Speech Codec to be encoded into IE - * \returns number of bytes appended to \a msg */ -uint8_t gsm0808_enc_speech_codec(struct msgb *msg, - const struct gsm0808_speech_codec *sc) + * \returns number of bytes appended to \a msg; negative on error */ +int gsm0808_enc_speech_codec2(struct msgb *msg, + const struct gsm0808_speech_codec *sc) { /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */ - uint8_t *old_tail; uint8_t *tlv_len; - - OSMO_ASSERT(msg); - OSMO_ASSERT(sc); + int rc; msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC); tlv_len = msgb_put(msg, 1); - old_tail = msg->tail; - enc_speech_codec(msg, sc); + rc = enc_speech_codec(msg, sc); + if (rc < 0) + return rc; - *tlv_len = (uint8_t) (msg->tail - old_tail); + *tlv_len = rc; return *tlv_len + 2; } +/*! Deprecated: gsm0808_enc_speech_codec2() wrapper for backwards compatibility. + * Mimics the old behavior: crash on missing and/or invalid input. */ +uint8_t gsm0808_enc_speech_codec(struct msgb *msg, + const struct gsm0808_speech_codec *sc) +{ + int rc; + + rc = gsm0808_enc_speech_codec2(msg, sc); + OSMO_ASSERT(rc > 0); + + return rc; +} + /*! Decode TS 08.08 Speech Codec IE * \paramout sc Caller-allocated memory for Speech Codec * \paramin elem IE value to be decoded @@ -320,7 +336,6 @@ uint8_t header; const uint8_t *old_elem = elem; - OSMO_ASSERT(sc); if (!elem) return -EINVAL; if (len == 0) @@ -392,35 +407,46 @@ /*! Encode TS 08.08 Speech Codec list * \paramout msg Message Buffer to which IE is to be appended * \paramin scl Speech Codec List to be encoded into IE - * \returns number of bytes added to \a msg */ -uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg, - const struct gsm0808_speech_codec_list *scl) + * \returns number of bytes added to \a msg; negative on error */ +int gsm0808_enc_speech_codec_list2(struct msgb *msg, + const struct gsm0808_speech_codec_list *scl) { /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */ uint8_t *old_tail; uint8_t *tlv_len; unsigned int i; - uint8_t rc; unsigned int bytes_used = 0; - OSMO_ASSERT(msg); - OSMO_ASSERT(scl); - msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST); tlv_len = msgb_put(msg, 1); old_tail = msg->tail; for (i = 0; i < scl->len; i++) { - rc = enc_speech_codec(msg, &scl->codeci); - OSMO_ASSERT(rc >= 1); + int rc = enc_speech_codec(msg, &scl->codeci); + if (rc < 1) + return rc; bytes_used += rc; - OSMO_ASSERT(bytes_used <= 255); + if (bytes_used > 0xff) + return -EOVERFLOW; } *tlv_len = (uint8_t) (msg->tail - old_tail); return *tlv_len + 2; } +/*! Deprecated: gsm0808_enc_speech_codec_list2() wrapper for backwards compatibility. + * Mimics the old behavior: crash on missing and/or invalid input. */ +uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg, + const struct gsm0808_speech_codec_list *scl) +{ + int rc; + + rc = gsm0808_enc_speech_codec_list2(msg, scl); + OSMO_ASSERT(rc > 0); + + return rc; +} + /*! Decode TS 08.08 Speech Codec list IE * \paramout scl Caller-provided memory to store codec list * \paramin elem IE value to be decoded @@ -435,7 +461,6 @@ int rc; uint8_t decoded = 0; - OSMO_ASSERT(scl); if (!elem) return -EINVAL; @@ -472,8 +497,6 @@ uint8_t *old_tail; uint8_t *tlv_len; - OSMO_ASSERT(msg); - OSMO_ASSERT(ct); OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2); /* FIXME: Implement encoding support for Data @@ -514,7 +537,6 @@ uint8_t byte; const uint8_t *old_elem = elem; - OSMO_ASSERT(ct); if (!elem) return -EINVAL; if (len < 3 || len > 11) @@ -703,8 +725,6 @@ uint8_t *old_tail; uint8_t *tlv_len; - OSMO_ASSERT(msg); - OSMO_ASSERT(ei); OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key)); OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN); @@ -743,7 +763,6 @@ unsigned int perm_algo_len = 0; const uint8_t *old_elem = elem; - OSMO_ASSERT(ei); if (!elem) return -EINVAL; if (len == 0) @@ -1003,9 +1022,6 @@ unsigned int i; uint8_t id_discr; - OSMO_ASSERT(msg); - OSMO_ASSERT(cil); - msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST); tlv_len = msgb_put(msg, 1); old_tail = msg->tail; @@ -1047,9 +1063,6 @@ uint8_t *tlv_len; unsigned int i; - OSMO_ASSERT(msg); - OSMO_ASSERT(cil); - msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST); tlv_len = msgb_put(msg, 1); old_tail = msg->tail; @@ -1225,7 +1238,6 @@ size_t bytes_elem = 0; int list_len = 0; - OSMO_ASSERT(cil); if (!elem) return -EINVAL; if (len == 0) @@ -1293,7 +1305,6 @@ const uint8_t *old_elem = elem; unsigned int item_count = 0; - OSMO_ASSERT(cil); if (!elem) return -EINVAL; if (len == 0) @@ -1451,9 +1462,6 @@ .id_list_len = 1, }; - OSMO_ASSERT(msg); - OSMO_ASSERT(ci); - ie_tag = msg->tail; rc = gsm0808_enc_cell_id_list2(msg, &cil);
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm48.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm48.c
Changed
@@ -419,6 +419,29 @@ return get_value_string(rr_msg_names, msgtype); } +/* 3GPP TS 44.018 Table 10.4.2 */ +static const struct value_string rr_msg_type_short_names = { + { GSM48_MT_RR_SH_SI10, "System Information Type 10" }, + { GSM48_MT_RR_SH_FACCH, "Notification/FACCH" }, + { GSM48_MT_RR_SH_UL_FREE, "Uplink Free" }, + { GSM48_MT_RR_SH_MEAS_REP, "Enhanced Measurement Report (uplink)" }, + { GSM48_MT_RR_SH_MEAS_INFO, "Measurement Information (downlink)" }, + { GSM48_MT_RR_SH_VGCS_RECON, "VBS/VGCS Reconfigure" }, + { GSM48_MT_RR_SH_VGCS_RECON2, "VBS/VGCS Reconfigure2" }, + { GSM48_MT_RR_SH_VGCS_INFO, "VGCS Additional Information" }, + { GSM48_MT_RR_SH_VGCS_SMS, "VGCS SMS Information" }, + { GSM48_MT_RR_SH_SI10bis, "System Information Type 10bis" }, + { GSM48_MT_RR_SH_SI10ter, "System Information Type 10ter" }, + { GSM48_MT_RR_SH_VGCS_NEIGH, "VGCS Neighbour Cell Information" }, + { GSM48_MT_RR_SH_APP_DATA, "Notify Application Data" }, + { 0, NULL } +}; + +/*! return string representation of RR Message Type using the RR short protocol discriminator */ +const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype) +{ + return get_value_string(rr_msg_type_short_names, msgtype); +} const struct value_string gsm48_chan_mode_names = { { GSM48_CMODE_SIGN, "SIGNALLING" }, @@ -1361,7 +1384,7 @@ * Uses From Table 10.5.33 of GSM 04.08 to determine the number of * paging sub-channels in the given control channel configuration */ -int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc) +int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc) { unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc);
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm48_ie.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm48_ie.c
Changed
@@ -203,7 +203,24 @@ case GSM_MNCC_BCAP_SPEECH: i = 1; s = 0; - while(!(lvi & 0x80)) { + if ((lv1 & 0x80) != 0) { /* octet 3a is absent */ + switch (bcap->radio) { + case GSM48_BCAP_RRQ_FR_ONLY: + bcap->speech_vers++ = GSM48_BCAP_SV_FR; + break; + case GSM48_BCAP_RRQ_DUAL_HR: + bcap->speech_vers++ = GSM48_BCAP_SV_HR; + bcap->speech_vers++ = GSM48_BCAP_SV_FR; + break; + case GSM48_BCAP_RRQ_DUAL_FR: + bcap->speech_vers++ = GSM48_BCAP_SV_FR; + bcap->speech_vers++ = GSM48_BCAP_SV_HR; + break; + } + bcap->speech_vers = -1; /* end of list */ + return 0; + } + while (!(lvi & 0x80)) { i++; /* octet 3a etc */ if (in_len < i) return 0; @@ -218,7 +235,7 @@ case GSM_MNCC_BCAP_UNR_DIG: case GSM_MNCC_BCAP_FAX_G3: i = 1; - while(!(lvi & 0x80)) { + while (!(lvi & 0x80)) { i++; /* octet 3a etc */ if (in_len < i) return 0; @@ -232,7 +249,7 @@ return 0; bcap->data.rate_adaption = (lvi >> 3) & 3; bcap->data.sig_access = lvi & 7; - while(!(lvi & 0x80)) { + while (!(lvi & 0x80)) { i++; /* octet 5a etc */ if (in_len < i) return 0; @@ -863,8 +880,9 @@ * \paramin cd Cell Channel Description IE * \paramin len Length of \a cd in bytes * \returns 0 on success; negative on error */ -int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, - uint8_t len, uint8_t mask, uint8_t frqt) +int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, + const uint8_t *cd, uint8_t len, + uint8_t mask, uint8_t frqt) { int i;
View file
libosmocore_1.7.0.tar.xz/src/gsm/gsm_utils.c -> libosmocore_1.8.0.tar.xz/src/gsm/gsm_utils.c
Changed
@@ -92,7 +92,7 @@ #include <time.h> #include <unistd.h> -#include "../../config.h" +#include "config.h" #if (!EMBEDDED) /* FIXME: this can be removed once we bump glibc requirements to 2.25: */ @@ -887,8 +887,19 @@ * \returns GSM Frame Number */ uint32_t gsm_gsmtime2fn(struct gsm_time *time) { - /* TS 05.02 Chapter 4.3.3 TDMA frame number */ - return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); + uint32_t fn; + + /* See also: + * 3GPP TS 44.018, section 10.5.2.38, 3GPP TS 45.002 section 4.3.3, and + * 3GPP TS 48.058, section 9.3.8 */ + fn = 51 * OSMO_MOD_FLR((time->t3-time->t2), 26) + time->t3 + 51 * 26 * time->t1; + + /* Note: Corrupted input values may cause a resulting frame number + * larger then the maximum permitted value of GSM_MAX_FN. Even though + * the caller is expected to check the input values beforehand we must + * make sure that the result cannot exceed the value range of a valid + * GSM frame number. */ + return fn % GSM_MAX_FN; } char *osmo_dump_gsmtime_buf(char *buf, size_t buf_len, const struct gsm_time *tm)
View file
libosmocore_1.7.0.tar.xz/src/gsm/iuup.c -> libosmocore_1.8.0.tar.xz/src/gsm/iuup.c
Changed
@@ -476,6 +476,8 @@ dt.frame_nr = h1->frame_nr; dt.fqc = h1->fqc; break; + default: + OSMO_ASSERT(0); } /* pull up to the IuUP payload and push a new primitive header in front */
View file
libosmocore_1.7.0.tar.xz/src/gsm/kdf.c -> libosmocore_1.8.0.tar.xz/src/gsm/kdf.c
Changed
@@ -22,7 +22,7 @@ #include <stdint.h> #include <string.h> -#include "../../config.h" +#include "config.h" #if (USE_GNUTLS) #include <gnutls/gnutls.h> #include <gnutls/crypto.h>
View file
libosmocore_1.7.0.tar.xz/src/gsm/libosmogsm.map -> libosmocore_1.8.0.tar.xz/src/gsm/libosmogsm.map
Changed
@@ -212,8 +212,10 @@ gsm0808_dec_aoip_trasp_addr; gsm0808_dec_osmux_cid; gsm0808_enc_speech_codec; +gsm0808_enc_speech_codec2; gsm0808_dec_speech_codec; gsm0808_enc_speech_codec_list; +gsm0808_enc_speech_codec_list2; gsm0808_dec_speech_codec_list; gsm0808_enc_channel_type; gsm0808_dec_channel_type; @@ -328,6 +330,7 @@ osmo_gsm48_rest_octets_si13_encode; osmo_gsm48_rest_octets_si3_decode; osmo_gsm48_rest_octets_si4_decode; +gsm48_rr_short_pd_msg_name; gsm48_rr_msg_name; gsm48_cc_state_name; gsm48_construct_ra;
View file
libosmocore_1.7.0.tar.xz/src/gsm/mncc.c -> libosmocore_1.8.0.tar.xz/src/gsm/mncc.c
Changed
@@ -20,7 +20,7 @@ * */ -#include "../config.h" +#include "config.h" #ifdef HAVE_SYS_SOCKET_H
View file
libosmocore_1.7.0.tar.xz/src/gsm/rsl.c -> libosmocore_1.8.0.tar.xz/src/gsm/rsl.c
Changed
@@ -125,6 +125,7 @@ RSL_IE_OSMO_REP_ACCH_CAP = { TLV_TYPE_TLV }, RSL_IE_OSMO_TRAINING_SEQUENCE = { TLV_TYPE_TLV }, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP = { TLV_TYPE_TLV }, + RSL_IE_OSMO_OSMUX_CID = { TLV_TYPE_TLV }, RSL_IE_IPAC_SRTP_CONFIG = { TLV_TYPE_TLV }, RSL_IE_IPAC_PROXY_UDP = { TLV_TYPE_FIXED, 2 }, RSL_IE_IPAC_BSCMPL_TOUT = { TLV_TYPE_TV },
View file
libosmocore_1.7.0.tar.xz/src/gsm/sysinfo.c -> libosmocore_1.8.0.tar.xz/src/gsm/sysinfo.c
Changed
@@ -46,7 +46,7 @@ /* bs11 forgot the l2 len, 0-6 rest octets */ osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); - +osmo_static_assert(sizeof(struct gsm48_system_information_type_10) == 1, _si10_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); static const uint8_t sitype2rsl_MAX_SYSINFO_TYPE = {
View file
libosmocore_1.8.0.tar.xz/src/isdn
Added
+(directory)
View file
libosmocore_1.8.0.tar.xz/src/isdn/Makefile.am
Added
@@ -0,0 +1,25 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read chapter "Library interface versions" of the libtool documentation +# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html +LIBVERSION=0:0:0 + +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) + +if ENABLE_PSEUDOTALLOC +AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc +endif + +noinst_LTLIBRARIES = libisdnint.la +lib_LTLIBRARIES = libosmoisdn.la + +libisdnint_la_SOURCES = i460_mux.c lapd_core.c + +libisdnint_la_LDFLAGS = -no-undefined +libisdnint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la + +libosmoisdn_la_SOURCES = +libosmoisdn_la_LDFLAGS = $(LTLDFLAGS_OSMOISDN) -version-info $(LIBVERSION) -no-undefined +libosmoisdn_la_LIBADD = libisdnint.la $(TALLOC_LIBS) + +EXTRA_DIST = libosmoisdn.map
View file
libosmocore_1.8.0.tar.xz/src/isdn/i460_mux.c
Added
@@ -0,0 +1,388 @@ +/*! \file i460_mux.c + * ITU-T I.460 sub-channel multiplexer + demultiplexer */ +/* + * (C) 2020 by Harald Welte <laforge@gnumonks.org> + * + * 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 <errno.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/isdn/i460_mux.h> + +/* count the number of sub-channels in this I460 slot */ +static int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts) +{ + int i, num_used = 0; + + for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { + if (ts->schani.rate != OSMO_I460_RATE_NONE) + num_used++; + } + + return num_used; +} + +/* does this channel have no sub-streams (single 64k subchannel)? */ +static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts) +{ + if (osmo_i460_subchan_count(ts) != 1) + return false; + + if (ts->schan0.rate != OSMO_I460_RATE_64k) + return false; + + return true; +} + +/*********************************************************************** + * Demultiplexer + ***********************************************************************/ + +/* append a single bit to a sub-channel */ +static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit) +{ + struct osmo_i460_subchan_demux *demux = &schan->demux; + + OSMO_ASSERT(demux->out_bitbuf); + OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size); + + demux->out_bitbufdemux->out_idx++ = bit ? 1 : 0; + + if (demux->out_idx >= demux->out_bitbuf_size) { + if (demux->out_cb_bits) + demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx); + else { + /* pack bits into bytes */ + OSMO_ASSERT((demux->out_idx % 8) == 0); + unsigned int num_bytes = demux->out_idx / 8; + uint8_t bytesnum_bytes; + osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx); + demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes); + } + demux->out_idx = 0; + } +} + +/* extract those bits relevant to this schan of each byte in 'data' */ +static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len) +{ + int i; + + for (i = 0; i < data_len; i++) { + uint8_t inbyte = datai; + /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two + * most significant bits, hence we extract msb-first */ + uint8_t inbits = inbyte << schan->bit_offset; + + /* extract the bits relevant to the given schan */ + switch (schan->rate) { + case OSMO_I460_RATE_8k: + demux_subchan_append_bit(schan, inbits & 0x80); + break; + case OSMO_I460_RATE_16k: + demux_subchan_append_bit(schan, inbits & 0x80); + demux_subchan_append_bit(schan, inbits & 0x40); + break; + case OSMO_I460_RATE_32k: + demux_subchan_append_bit(schan, inbits & 0x80); + demux_subchan_append_bit(schan, inbits & 0x40); + demux_subchan_append_bit(schan, inbits & 0x20); + demux_subchan_append_bit(schan, inbits & 0x10); + break; + case OSMO_I460_RATE_64k: + demux_subchan_append_bit(schan, inbits & 0x80); + demux_subchan_append_bit(schan, inbits & 0x40); + demux_subchan_append_bit(schan, inbits & 0x20); + demux_subchan_append_bit(schan, inbits & 0x10); + demux_subchan_append_bit(schan, inbits & 0x08); + demux_subchan_append_bit(schan, inbits & 0x04); + demux_subchan_append_bit(schan, inbits & 0x02); + demux_subchan_append_bit(schan, inbits & 0x01); + break; + default: + OSMO_ASSERT(0); + } + } +} + +/*! Data from E1 timeslot into de-multiplexer + * \paramin ts timeslot state + * \paramin data input data bytes as received from E1/T1 + * \paramin data_len length of data in bytes */ +void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len) +{ + struct osmo_i460_subchan *schan; + struct osmo_i460_subchan_demux *demux; + int i; + + /* fast path if entire 64k slot is used */ + if (osmo_i460_has_single_64k_schan(ts)) { + schan = &ts->schan0; + demux = &schan->demux; + if (demux->out_cb_bytes) + demux->out_cb_bytes(schan, demux->user_data, data, data_len); + else { + ubit_t bitsdata_len*8; + osmo_pbit2ubit(bits, data, data_len*8); + demux->out_cb_bits(schan, demux->user_data, bits, data_len*8); + } + return; + } + + /* Slow path iterating over all lchans */ + for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { + schan = &ts->schani; + if (schan->rate == OSMO_I460_RATE_NONE) + continue; + demux_subchan_extract_bits(schan, data, data_len); + } +} + + +/*********************************************************************** + * Multiplexer + ***********************************************************************/ + +/*! enqueue a to-be-transmitted message buffer containing unpacked bits */ +void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg) +{ + OSMO_ASSERT(msgb_length(msg) > 0); + msgb_enqueue(&schan->mux.tx_queue, msg); +} + +/* mux: pull the next bit out of the given sub-channel */ +static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan) +{ + struct osmo_i460_subchan_mux *mux = &schan->mux; + struct msgb *msg; + ubit_t bit; + + /* if we don't have anything to transmit, return '1' bits */ + if (llist_empty(&mux->tx_queue)) { + /* User code now has a last chance to put something into the queue. */ + if (mux->in_cb_queue_empty) + mux->in_cb_queue_empty(schan, mux->user_data); + + /* If the queue is still empty, return idle bits */ + if (llist_empty(&mux->tx_queue)) + return 0x01; + } + msg = llist_entry(mux->tx_queue.next, struct msgb, list); + bit = msgb_pull_u8(msg); + + /* free msgb if we have pulled the last bit */ + if (msgb_length(msg) <= 0) { + llist_del(&msg->list); + talloc_free(msg); + } + + return bit; +} + +/*! provide one byte with the subchan-specific bits of given sub-channel. + * \paramin schan sub-channel that is to provide bits + * \parmaout mask bitmask of those bits filled in + * \returns bits of given sub-channel */ +static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask) +{ + uint8_t outbits = 0; + uint8_t outmask; + + /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two + * most significant bits, hence we provide msb-first */ + + switch (schan->rate) { + case OSMO_I460_RATE_8k: + outbits = mux_schan_provide_bit(schan) << 7; + outmask = 0x80; + break; + case OSMO_I460_RATE_16k: + outbits |= mux_schan_provide_bit(schan) << 7; + outbits |= mux_schan_provide_bit(schan) << 6; + outmask = 0xC0; + break; + case OSMO_I460_RATE_32k: + outbits |= mux_schan_provide_bit(schan) << 7; + outbits |= mux_schan_provide_bit(schan) << 6; + outbits |= mux_schan_provide_bit(schan) << 5; + outbits |= mux_schan_provide_bit(schan) << 4; + outmask = 0xF0; + break; + case OSMO_I460_RATE_64k: + outbits |= mux_schan_provide_bit(schan) << 7; + outbits |= mux_schan_provide_bit(schan) << 6; + outbits |= mux_schan_provide_bit(schan) << 5; + outbits |= mux_schan_provide_bit(schan) << 4; + outbits |= mux_schan_provide_bit(schan) << 3; + outbits |= mux_schan_provide_bit(schan) << 2; + outbits |= mux_schan_provide_bit(schan) << 1; + outbits |= mux_schan_provide_bit(schan) << 0; + outmask = 0xFF; + break; + default: + OSMO_ASSERT(0); + } + *mask = outmask >> schan->bit_offset; + return outbits >> schan->bit_offset; +} + +/* provide one byte of multiplexed I.460 bits */ +static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts) +{ + int i, count = 0; + uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */ + + for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { + struct osmo_i460_subchan *schan = &ts->schani; + uint8_t bits, mask; + + if (schan->rate == OSMO_I460_RATE_NONE) + continue; + count++; + bits = mux_subchan_provide_bits(schan, &mask); + ret &= ~mask; + ret |= bits; + } + + return ret; +} + + +/*! Data from E1 timeslot into de-multiplexer + * \paramin ts timeslot state + * \paramout out caller-provided buffer where to store generated output bytes + * \paramin out_len number of bytes to be stored at out + */ +int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len) +{ + int i; + + /* fast path if entire 64k slot is used */ + //if (osmo_i460_has_single_64k_schan(ts)) { } + + for (i = 0; i < out_len; i++) + outi = mux_timeslot_provide_bits(ts); + + return out_len; +} + + +/*********************************************************************** + * Initialization / Control + ***********************************************************************/ + + +static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits) +{ + struct osmo_i460_subchan_demux *demux = &schan->demux; + + talloc_free(demux->out_bitbuf); + demux->out_bitbuf = talloc_zero_size(ctx, num_bits); + if (!demux->out_bitbuf) + return -ENOMEM; + demux->out_bitbuf_size = num_bits; + + return 0; +} + + +static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { + const struct osmo_i460_subchan *schan = &ts->schani; + if (schan->rate == OSMO_I460_RATE_NONE) + return i; + } + return -1; +} + +/* reset subchannel struct into a defined state */ +static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time) +{ + /* Before we zero out the subchannel struct, we must be sure that the + * tx_queue is cleared and all dynamically allocated memory is freed. + * However, on an uninitalized subchannel struct we can not be sure + * that the pointers are valid. If the subchannel is reset the first + * time the caller must set first_time to true. */ + if (!first_time) { + if (schan->demux.out_bitbuf) + talloc_free(schan->demux.out_bitbuf); + msgb_queue_free(&schan->mux.tx_queue); + } + + /* Reset subchannel to a defined state */ + memset(schan, 0, sizeof(*schan)); + schan->rate = OSMO_I460_RATE_NONE; + INIT_LLIST_HEAD(&schan->mux.tx_queue); +} + +/*! initialize an I.460 timeslot */ +void osmo_i460_ts_init(struct osmo_i460_timeslot *ts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ts->schan); i++) { + struct osmo_i460_subchan *schan = &ts->schani; + schan->ts = ts; + subchan_reset(schan, true); + } +} + +/*! add a new sub-channel to the given timeslot + * \paramin ctx talloc context from where to allocate the internal buffer + * \paramin ts timeslot to which to add a sub-channel + * \paramin chd description of the sub-channel to be added + * \return pointer to sub-channel on success, NULL on error */ +struct osmo_i460_subchan * +osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd) +{ + struct osmo_i460_subchan *schan; + int idx, rc; + + idx = find_unused_subchan_idx(ts); + if (idx < 0) + return NULL; + + schan = &ts->schanidx; + + schan->rate = chd->rate; + schan->bit_offset = chd->bit_offset; + + schan->demux.out_cb_bits = chd->demux.out_cb_bits; + schan->demux.out_cb_bytes = chd->demux.out_cb_bytes; + schan->demux.user_data = chd->demux.user_data; + schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty; + schan->mux.user_data = chd->mux.user_data; + rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits); + if (rc < 0) { + subchan_reset(schan, false); + return NULL; + } + + /* return number of schan in use */ + return schan; +} + +/* remove a su-channel from the multiplex */ +void osmo_i460_subchan_del(struct osmo_i460_subchan *schan) +{ + subchan_reset(schan, false); +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/isdn/lapd_core.c
Added
@@ -0,0 +1,2237 @@ +/*! \file lapd_core.c + * LAPD core implementation */ +/* + * (C) 2010-2020 by Harald Welte <laforge@gnumonks.org> + * (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu> + * + * 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. + * + */ + +/*! \addtogroup lapd + * @{ + * + * Osmocom LAPD core, used for Q.921, LAPDm and others. + * + * Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue + * + * RX data is stored in the rcv_buffer (pointer). If the message is complete, it + * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is + * received while there is an incomplete rcv_buffer, it is appended to it. + * + * TX data is stored in the send_queue first. When transmitting a frame, + * the first message in the send_queue is moved to the send_buffer. There it + * resides until all fragments are acknowledged. Fragments to be sent by I + * frames are stored in the tx_hist buffer for resend, if required. Also the + * current fragment is copied into the tx_queue. There it resides until it is + * forwarded to layer 1. + * + * In case we have SAPI 0, we only have a window size of 1, so the unack- + * nowledged message resides always in the send_buffer. In case of a suspend, + * it can be written back to the first position of the send_queue. + * + * The layer 1 normally sends a PH-READY-TO-SEND. But because we use + * asynchronous transfer between layer 1 and layer 2 (serial link), we must + * send a frame before layer 1 reaches the right timeslot to send it. So we + * move the tx_queue to layer 1 when there is not already a pending frame, and + * wait until acknowledge after the frame has been sent. If we receive an + * acknowledge, we can send the next frame from the buffer, if any. + * + * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it + * will trigger next I frame, if possible. + * + * T203 is optional. It will be stated when entering MF EST state. It will also + * be started when I or S frame is received in that state . It will be + * restarted in the lapd_acknowledge() function, in case outstanding frames + * will not trigger T200. It will be stoped, when T200 is started in MF EST + * state. It will also be stoped when leaving MF EST state. + * + * \file lapd_core.c + */ + +/* Enable this to test content resolution on network side: + * - The first SABM is received, UA is dropped. + * - The phone repeats SABM, but it's content is wrong, so it is ignored + * - The phone repeats SABM again, content is right, so UA is sent. + */ +//#define TEST_CONTENT_RESOLUTION_NETWORK + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/isdn/lapd_core.h> +#include <osmocom/gsm/rsl.h> + +/* TS 04.06 Table 4 / Section 3.8.1 */ +#define LAPD_U_SABM 0x7 +#define LAPD_U_SABME 0xf +#define LAPD_U_DM 0x3 +#define LAPD_U_UI 0x0 +#define LAPD_U_DISC 0x8 +#define LAPD_U_UA 0xC +#define LAPD_U_FRMR 0x11 + +#define LAPD_S_RR 0x0 +#define LAPD_S_RNR 0x1 +#define LAPD_S_REJ 0x2 + +#define CR_USER2NET_CMD 0 +#define CR_USER2NET_RESP 1 +#define CR_NET2USER_CMD 1 +#define CR_NET2USER_RESP 0 + +#define LAPD_HEADROOM 56 +#define LAPD_TAILROOM 16 + +#define SBIT(a) (1 << a) +#define ALL_STATES 0xffffffff + +static void lapd_t200_cb(void *data); +static void lapd_t203_cb(void *data); +static int lapd_send_i(struct lapd_msg_ctx *lctx, int line); +static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); + +/* UTILITY FUNCTIONS */ + +struct msgb *lapd_msgb_alloc(int length, const char *name) +{ + /* adding space for padding, FIXME: add as an option */ + if (length < 21) + length = 21; + return msgb_alloc_headroom(length + LAPD_HEADROOM + LAPD_TAILROOM, LAPD_HEADROOM, name); +} + +static inline uint8_t do_mod(uint8_t x, uint8_t m) +{ + return x & (m - 1); +} + +static inline uint8_t inc_mod(uint8_t x, uint8_t m) +{ + return (x + 1) & (m - 1); +} + +static inline uint8_t add_mod(uint8_t x, uint8_t y, uint8_t m) +{ + return (x + y) & (m - 1); +} + +static inline uint8_t sub_mod(uint8_t x, uint8_t y, uint8_t m) +{ + return (x - y) & (m - 1); /* handle negative results correctly */ +} + +static void lapd_dl_flush_send(struct lapd_datalink *dl) +{ + struct msgb *msg; + + /* Flush send-queue */ + while ((msg = msgb_dequeue(&dl->send_queue))) + msgb_free(msg); + + /* Clear send-buffer */ + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; +} + +static void lapd_dl_flush_hist(struct lapd_datalink *dl) +{ + unsigned int i; + + if (!dl->range_hist || !dl->tx_hist) + return; + + for (i = 0; i < dl->range_hist; i++) { + if (dl->tx_histi.msg) { + msgb_free(dl->tx_histi.msg); + dl->tx_histi.msg = NULL; + } + } +} + +static void lapd_dl_flush_tx(struct lapd_datalink *dl) +{ + struct msgb *msg; + + while ((msg = msgb_dequeue(&dl->tx_queue))) + msgb_free(msg); + lapd_dl_flush_hist(dl); +} + +/* Figure B.2/Q.921 */ +const struct value_string lapd_state_names = { + OSMO_VALUE_STRING(LAPD_STATE_NULL), + OSMO_VALUE_STRING(LAPD_STATE_TEI_UNASS), + OSMO_VALUE_STRING(LAPD_STATE_ASS_TEI_WAIT), + OSMO_VALUE_STRING(LAPD_STATE_EST_TEI_WAIT), + OSMO_VALUE_STRING(LAPD_STATE_IDLE), + OSMO_VALUE_STRING(LAPD_STATE_SABM_SENT), + OSMO_VALUE_STRING(LAPD_STATE_DISC_SENT), + OSMO_VALUE_STRING(LAPD_STATE_MF_EST), + OSMO_VALUE_STRING(LAPD_STATE_TIMER_RECOV), + { 0, NULL } +}; + +static inline const char *lapd_state_name(enum lapd_state state) +{ + return get_value_string(lapd_state_names, state); +} + +static void lapd_start_t200(struct lapd_datalink *dl) +{ + if (osmo_timer_pending(&dl->t200)) + return; + LOGDL(dl, LOGL_INFO, "start T200 (timeout=%d.%06ds)\n", + dl->t200_sec, dl->t200_usec); + osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec); +} + +static void lapd_start_t203(struct lapd_datalink *dl) +{ + if (osmo_timer_pending(&dl->t203)) + return; + LOGDL(dl, LOGL_INFO, "start T203\n"); + osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec); +} + +static void lapd_stop_t200(struct lapd_datalink *dl) +{ + if (!osmo_timer_pending(&dl->t200)) + return; + LOGDL(dl, LOGL_INFO, "stop T200\n"); + osmo_timer_del(&dl->t200); +} + +static void lapd_stop_t203(struct lapd_datalink *dl) +{ + if (!osmo_timer_pending(&dl->t203)) + return; + LOGDL(dl, LOGL_INFO, "stop T203\n"); + osmo_timer_del(&dl->t203); +} + +static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state) +{ + LOGDL(dl, LOGL_INFO, "new state %s -> %s\n", + lapd_state_name(dl->state), lapd_state_name(state)); + + if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) { + /* stop T203 on leaving MF EST state, if running */ + lapd_stop_t203(dl); + /* remove content res. (network side) on leaving MF EST state */ + msgb_free(dl->cont_res); + dl->cont_res = NULL; + } + + /* start T203 on entering MF EST state, if enabled */ + if ((dl->t203_sec || dl->t203_usec) + && state == LAPD_STATE_MF_EST && dl->state != LAPD_STATE_MF_EST) + lapd_start_t203(dl); + + dl->state = state; +} + +void *tall_lapd_ctx = NULL; + +/*! Initialize LAPD datalink instance and allocate history + * \paramin dl caller-allocated datalink structure + * \paramin k maximum number of unacknowledged frames + * \paramin v_range range of sequence numbers + * \paramin maxf maximum frame size (after defragmentation) + * \paramin name human-readable name for this LAPD datalink */ +void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, + const char *name) +{ + int m; + + memset(dl, 0, sizeof(*dl)); + INIT_LLIST_HEAD(&dl->send_queue); + INIT_LLIST_HEAD(&dl->tx_queue); + dl->reestablish = 1; + dl->n200_est_rel = 3; + dl->n200 = 3; + dl->t200_sec = 1; + dl->t200_usec = 0; + osmo_timer_setup(&dl->t200, lapd_t200_cb, dl); + dl->t203_sec = 10; + dl->t203_usec = 0; + osmo_timer_setup(&dl->t203, lapd_t203_cb, dl); + dl->maxf = maxf; + if (k > v_range - 1) + k = v_range - 1; + dl->k = k; + dl->v_range = v_range; + + /* Calculate modulo for history array: + * - The history range must be at least k+1. + * - The history range must be 2^x, where x is as low as possible. + */ + k++; + for (m = 0x80; m; m >>= 1) { + if ((m & k)) { + if (k > m) + m <<= 1; + dl->range_hist = m; + break; + } + } + + if (!tall_lapd_ctx) { + tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context"); + OSMO_ASSERT(tall_lapd_ctx); + } + + talloc_free(dl->name); + if (name) + dl->name = talloc_strdup(tall_lapd_ctx, name); + else + dl->name = talloc_asprintf(tall_lapd_ctx, "dl=%p", dl); + + LOGDL(dl, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, " + "history range = %d\n", dl->v_range, dl->k, dl->range_hist); + + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + + dl->tx_hist = talloc_zero_array(tall_lapd_ctx, + struct lapd_history, dl->range_hist); +} + +/*! Initialize LAPD datalink instance and allocate history + * \paramin dl caller-allocated datalink structure + * \paramin k maximum number of unacknowledged frames + * \paramin v_range range of sequence numbers + * \paramin maxf maximum frame size (after defragmentation) */ +void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf) +{ + lapd_dl_init2(dl, k, v_range, maxf, NULL); +} + +void lapd_dl_set_name(struct lapd_datalink *dl, const char *name) +{ + if (!name) + return; + osmo_talloc_replace_string(tall_lapd_ctx, &dl->name, name); +} + +/* reset to IDLE state */ +void lapd_dl_reset(struct lapd_datalink *dl) +{ + LOGDL(dl, LOGL_INFO, "Resetting LAPD instance\n"); + /* enter idle state (and remove eventual cont_res) */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + /* flush buffer */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + /* Discard partly received L3 message */ + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + /* stop Timers */ + lapd_stop_t200(dl); + lapd_stop_t203(dl); + if (dl->state == LAPD_STATE_IDLE) + return; + /* enter idle state (and remove eventual cont_res) */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); +} + +/* reset and de-allocate history buffer */ +void lapd_dl_exit(struct lapd_datalink *dl) +{ + /* free all ressources except history buffer */ + lapd_dl_reset(dl); + + /* enter null state */ + lapd_dl_newstate(dl, LAPD_STATE_NULL); + + /* free history buffer list */ + talloc_free(dl->tx_hist); + dl->tx_hist = NULL; + talloc_free(dl->name); + dl->name = NULL; +} + +/*! Set the \ref lapdm_mode of a LAPDm entity */ +int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode) +{ + switch (mode) { + case LAPD_MODE_USER: + dl->cr.loc2rem.cmd = CR_USER2NET_CMD; + dl->cr.loc2rem.resp = CR_USER2NET_RESP; + dl->cr.rem2loc.cmd = CR_NET2USER_CMD; + dl->cr.rem2loc.resp = CR_NET2USER_RESP; + break; + case LAPD_MODE_NETWORK: + dl->cr.loc2rem.cmd = CR_NET2USER_CMD; + dl->cr.loc2rem.resp = CR_NET2USER_RESP; + dl->cr.rem2loc.cmd = CR_USER2NET_CMD; + dl->cr.rem2loc.resp = CR_USER2NET_RESP; + break; + default: + return -EINVAL; + } + dl->mode = mode; + + return 0; +} + +/* send DL message with optional msgb */ +static int send_dl_l3(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx, + struct msgb *msg) +{ + struct lapd_datalink *dl = lctx->dl; + struct osmo_dlsap_prim dp; + + osmo_prim_init(&dp.oph, 0, prim, op, msg); + return dl->send_dlsap(&dp, lctx); +} + +/* send simple DL message */ +static inline int send_dl_simple(uint8_t prim, uint8_t op, + struct lapd_msg_ctx *lctx) +{ + return send_dl_l3(prim, op, lctx, NULL); +} + +/* send MDL-ERROR INDICATION */ +static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct osmo_dlsap_prim dp; + + LOGDL(dl, LOGL_NOTICE, + "sending MDL-ERROR-IND cause %d from state %s\n", + cause, lapd_state_name(dl->state)); + osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL); + dp.u.error_ind.cause = cause; + return dl->send_dlsap(&dp, lctx); +} + +/* send UA response */ +static int lapd_send_ua(struct lapd_msg_ctx *lctx, uint8_t len, uint8_t *data) +{ + struct msgb *msg = lapd_msgb_alloc(len, "LAPD UA"); + struct lapd_msg_ctx nctx; + struct lapd_datalink *dl = lctx->dl; + + memcpy(&nctx, lctx, sizeof(nctx)); + msg->l3h = msgb_put(msg, len); + if (len) + memcpy(msg->l3h, data, len); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.resp; + nctx.format = LAPD_FORM_U; + nctx.s_u = LAPD_U_UA; + /* keep nctx.p_f */ + nctx.length = len; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +/* send DM response */ +static int lapd_send_dm(struct lapd_msg_ctx *lctx) +{ + struct msgb *msg = lapd_msgb_alloc(0, "LAPD DM"); + struct lapd_msg_ctx nctx; + struct lapd_datalink *dl = lctx->dl; + + memcpy(&nctx, lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.resp; + nctx.format = LAPD_FORM_U; + nctx.s_u = LAPD_U_DM; + /* keep nctx.p_f */ + nctx.length = 0; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +/* send RR response / command */ +static int lapd_send_rr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) +{ + struct msgb *msg = lapd_msgb_alloc(0, "LAPD RR"); + struct lapd_msg_ctx nctx; + struct lapd_datalink *dl = lctx->dl; + + memcpy(&nctx, lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; + nctx.format = LAPD_FORM_S; + nctx.s_u = LAPD_S_RR; + nctx.p_f = f_bit; + nctx.n_recv = dl->v_recv; + nctx.length = 0; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +/* send RNR response / command */ +static int lapd_send_rnr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) +{ + struct msgb *msg = lapd_msgb_alloc(0, "LAPD RNR"); + struct lapd_msg_ctx nctx; + struct lapd_datalink *dl = lctx->dl; + + memcpy(&nctx, lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; + nctx.format = LAPD_FORM_S; + nctx.s_u = LAPD_S_RNR; + nctx.p_f = f_bit; + nctx.n_recv = dl->v_recv; + nctx.length = 0; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +/* send REJ response */ +static int lapd_send_rej(struct lapd_msg_ctx *lctx, uint8_t f_bit) +{ + struct msgb *msg = lapd_msgb_alloc(0, "LAPD REJ"); + struct lapd_msg_ctx nctx; + struct lapd_datalink *dl = lctx->dl; + + memcpy(&nctx, lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.resp; + nctx.format = LAPD_FORM_S; + nctx.s_u = LAPD_S_REJ; + nctx.p_f = f_bit; + nctx.n_recv = dl->v_recv; + nctx.length = 0; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +/* resend SABM or DISC message */ +static int lapd_send_resend(struct lapd_datalink *dl) +{ + struct msgb *msg; + uint8_t h = do_mod(dl->v_send, dl->range_hist); + int length = dl->tx_histh.msg->len; + struct lapd_msg_ctx nctx; + + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_U; + if (dl->state == LAPD_STATE_SABM_SENT) + nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; + else + nctx.s_u = LAPD_U_DISC; + nctx.p_f = 1; + nctx.length = length; + nctx.more = 0; + + /* Resend SABM/DISC from tx_hist */ + msg = lapd_msgb_alloc(length, "LAPD resend"); + msg->l3h = msgb_put(msg, length); + if (length) + memcpy(msg->l3h, dl->tx_histh.msg->data, length); + + return dl->send_ph_data_req(&nctx, msg); +} + +/* reestablish link */ +static int lapd_reestablish(struct lapd_datalink *dl) +{ + struct osmo_dlsap_prim dp; + struct msgb *msg; + + LOGDL(dl, LOGL_DEBUG, "LAPD reestablish\n"); + + msg = lapd_msgb_alloc(0, "DUMMY"); + osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg); + + return lapd_est_req(&dp, &dl->lctx); +} + +/* Timer callback on T200 expiry */ +static void lapd_t200_cb(void *data) +{ + struct lapd_datalink *dl = data; + + LOGDL(dl, LOGL_INFO, "Timeout T200 state=%s\n", lapd_state_name(dl->state)); + + switch (dl->state) { + case LAPD_STATE_SABM_SENT: + /* 5.4.1.3 */ + if (dl->retrans_ctr >= dl->n200_est_rel + 1) { + /* flush tx and send buffers */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + /* go back to idle state */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + /* send MDL ERROR INIDCATION to L3 */ + mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); + /* send RELEASE INDICATION to L3 */ + send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, + &dl->lctx); + break; + } + /* retransmit SABM command */ + lapd_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + lapd_start_t200(dl); + break; + case LAPD_STATE_DISC_SENT: + /* 5.4.4.3 */ + if (dl->retrans_ctr >= dl->n200_est_rel + 1) { + /* send MDL ERROR INIDCATION to L3 */ + mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); + /* flush tx and send buffers */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + /* go back to idle state */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + /* send RELEASE INDICATION to L3 */ + send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); + break; + } + /* retransmit DISC command */ + lapd_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + lapd_start_t200(dl); + break; + case LAPD_STATE_MF_EST: + /* 5.5.7 */ + dl->retrans_ctr = 0; + lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); + /* fall through */ + case LAPD_STATE_TIMER_RECOV: + dl->retrans_ctr++; + if (dl->retrans_ctr <= dl->n200) { + uint8_t vs = sub_mod(dl->v_send, 1, dl->v_range); + uint8_t h = do_mod(vs, dl->range_hist); + /* retransmit I frame (V_s-1) with P=1, if any */ + if (dl->tx_histh.msg) { + struct msgb *msg; + int length = dl->tx_histh.msg->len; + struct lapd_msg_ctx nctx; + + LOGDL(dl, LOGL_INFO, "retransmit last frame V(S)=%d\n", vs); + /* Create I frame (segment) from tx_hist */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_I; + nctx.p_f = 1; + nctx.n_send = vs; + nctx.n_recv = dl->v_recv; + nctx.length = length; + nctx.more = dl->tx_histh.more; + msg = lapd_msgb_alloc(length, "LAPD I resend"); + msg->l3h = msgb_put(msg, length); + memcpy(msg->l3h, dl->tx_histh.msg->data, + length); + dl->send_ph_data_req(&nctx, msg); + } else { + /* OR send appropriate supervision frame with P=1 */ + if (!dl->own_busy && !dl->seq_err_cond) { + lapd_send_rr(&dl->lctx, 1, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + lapd_send_rnr(&dl->lctx, 1, 1); + } else { + LOGDL(dl, LOGL_INFO, "unhandled, pls. fix\n"); + } + } + /* restart T200 (PH-READY-TO-SEND) */ + lapd_start_t200(dl); + } else { + /* send MDL ERROR INIDCATION to L3 */ + mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); + /* reestablish */ + if (!dl->reestablish) + break; + LOGDL(dl, LOGL_NOTICE, "N200+1 reached, performingreestablishment\n"); + lapd_reestablish(dl); + } + break; + default: + LOGDL(dl, LOGL_INFO, "T200 expired in unexpected dl->state %s)\n", + lapd_state_name(dl->state)); + } +} + +/* Timer callback on T203 expiry */ +static void lapd_t203_cb(void *data) +{ + struct lapd_datalink *dl = data; + + LOGDL(dl, LOGL_INFO, "Timeout T203 state=%s\n", lapd_state_name(dl->state)); + + if (dl->state != LAPD_STATE_MF_EST) { + LOGDL(dl, LOGL_ERROR, "T203 fired outside MF EST state, please fix!\n"); + return; + } + + /* set retransmission counter to 0 */ + dl->retrans_ctr = 0; + /* enter timer recovery state */ + lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); + /* transmit a supervisory command with P bit set to 1 as follows: */ + if (!dl->own_busy) { + LOGDL(dl, LOGL_INFO, "transmit an RR poll command\n"); + /* Send RR with P=1 */ + lapd_send_rr(&dl->lctx, 1, 1); + } else { + LOGDL(dl, LOGL_INFO, "transmit an RNR poll command\n"); + /* Send RNR with P=1 */ + lapd_send_rnr(&dl->lctx, 1, 1); + } + /* start T200 */ + lapd_start_t200(dl); +} + +/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */ +static void lapd_acknowledge(struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + uint8_t nr = lctx->n_recv; + int s = 0, rej = 0; + bool t200_reset = false; + int i, h; + + /* supervisory frame ? */ + if (lctx->format == LAPD_FORM_S) + s = 1; + /* REJ frame ? */ + if (s && lctx->s_u == LAPD_S_REJ) + rej = 1; + + /* Flush all transmit buffers of acknowledged frames */ + for (i = dl->v_ack; i != nr; i = inc_mod(i, dl->v_range)) { + h = do_mod(i, dl->range_hist); + if (dl->tx_histh.msg) { + msgb_free(dl->tx_histh.msg); + dl->tx_histh.msg = NULL; + LOGDL(dl, LOGL_INFO, "ack frame %d\n", i); + } + } + + if (dl->state != LAPD_STATE_TIMER_RECOV) { + /* When not in the timer recovery condition, the data + * link layer entity shall reset the timer T200 on + * receipt of a valid I frame with N(R) higher than V(A), + * or an REJ with an N(R) equal to V(A). */ + if ((!rej && nr != dl->v_ack) || (rej && nr == dl->v_ack)) { + t200_reset = true; + lapd_stop_t200(dl); + /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */ + } + /* 5.7.4: N(R) sequence error + * N(R) is called valid, if and only if + * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8. + */ + if (sub_mod(nr, dl->v_ack, dl->v_range) > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) { + LOGDL(dl, LOGL_NOTICE, "N(R) sequence error\n"); + mdl_error(MDL_CAUSE_SEQ_ERR, lctx); + } + } + + /* V(A) shall be set to the value of N(R) */ + dl->v_ack = nr; + + /* If T200 has been stopped by the receipt of an I, RR or RNR frame, + * and if there are outstanding I frames, restart T200 */ + if (t200_reset && !rej) { + if (dl->tx_histsub_mod(dl->v_send, 1, dl->range_hist).msg) { + LOGDL(dl, LOGL_INFO, "start T200, due to unacked I frame(s)\n"); + lapd_start_t200(dl); + } + } + + /* This also does a restart, when I or S frame is received */ + + /* Stop T203, if running */ + lapd_stop_t203(dl); + /* Start T203, if T200 is not running in MF EST state, if enabled */ + if (!osmo_timer_pending(&dl->t200) + && (dl->t203_sec || dl->t203_usec) + && (dl->state == LAPD_STATE_MF_EST)) { + lapd_start_t203(dl); + } +} + +/* L1 -> L2 */ + +/* Receive a LAPD U SABM(E) message from L1 */ +static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int length = lctx->length; + int rc = 0; + uint8_t prim, op; + + prim = PRIM_DL_EST; + op = PRIM_OP_INDICATION; + + LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state)); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.resp) { + LOGDL(dl, LOGL_ERROR, "SABM response error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + + /* G.4.5 If SABM is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if (lctx->more || length > lctx->n201) { + LOGDL(dl, LOGL_ERROR, "SABM too large error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); + return -EIO; + } + + switch (dl->state) { + case LAPD_STATE_IDLE: + break; + case LAPD_STATE_TIMER_RECOV: + LOGDL(dl, LOGL_INFO, "SABM command, timer recovery state\n"); + /* If link is lost on the remote side, we start over + * and send DL-ESTABLISH indication again. */ + /* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment" */ + if (length) { + /* check for contention resoultion */ + LOGDL(dl, LOGL_ERROR, "SABM L>0 not expected in timer " + "recovery state\n"); + mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx); + lapd_send_dm(lctx); + msgb_free(msg); + return 0; + } + /* re-establishment, continue below */ + lapd_stop_t200(dl); + break; + case LAPD_STATE_MF_EST: + LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n"); + /* If link is lost on the remote side, we start over + * and send DL-ESTABLISH indication again. */ + /* Additionally, continue in case of content resoltion + * (GSM network). This happens, if the mobile has not + * yet received UA or another mobile (collision) tries + * to establish connection. The mobile must receive + * UA again. */ + /* 5.4.2.1 */ + if (!length) { + /* If no content resolution, this is a + * re-establishment. */ + LOGDL(dl, LOGL_INFO, "Remote reestablish\n"); + break; + } + if (!dl->cont_res) { + LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n", + lapd_state_name(dl->state)); + mdl_error(MDL_CAUSE_SABM_MF, lctx); + msgb_free(msg); + return 0; + } + /* Ignore SABM if content differs from first SABM. */ + if (dl->mode == LAPD_MODE_NETWORK && length) { +#ifdef TEST_CONTENT_RESOLUTION_NETWORK + dl->cont_res->data0 ^= 0x01; +#endif + if (memcmp(dl->cont_res->data, msg->data, + length)) { + LOGDL(dl, LOGL_INFO, "Another SABM with different content - " + "ignoring!\n"); + msgb_free(msg); + return 0; + } + } + /* send UA again */ + lapd_send_ua(lctx, length, msg->l3h); + msgb_free(msg); + return 0; + case LAPD_STATE_DISC_SENT: + /* 5.4.6.2 send DM with F=P */ + lapd_send_dm(lctx); + /* stop Timer T200 */ + lapd_stop_t200(dl); + msgb_free(msg); + return send_dl_simple(prim, op, lctx); + default: + /* collision: Send UA, but still wait for rx UA, then + * change to MF_EST state. + */ + /* check for contention resoultion */ + if (dl->tx_hist0.msg && dl->tx_hist0.msg->len) { + LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention " + "resolution (state=%s)\n", lapd_state_name(dl->state)); + mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx); + } + lapd_send_ua(lctx, length, msg->l3h); + msgb_free(msg); + return 0; + } + /* save message context for further use */ + memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); +#ifndef TEST_CONTENT_RESOLUTION_NETWORK + /* send UA response */ + lapd_send_ua(lctx, length, msg->l3h); +#endif + /* set Vs, Vr and Va to 0 */ + dl->v_send = dl->v_recv = dl->v_ack = 0; + /* clear tx_hist */ + lapd_dl_flush_hist(dl); + /* enter multiple-frame-established state */ + lapd_dl_newstate(dl, LAPD_STATE_MF_EST); + /* store content resolution data on network side + * Note: cont_res will be removed when changing state again, + * so it must be allocated AFTER lapd_dl_newstate(). */ + if (dl->mode == LAPD_MODE_NETWORK && length) { + dl->cont_res = lapd_msgb_alloc(length, "CONT RES"); + memcpy(msgb_put(dl->cont_res, length), msg->l3h, + length); + LOGDL(dl, LOGL_INFO, "Store content res.\n"); + } + /* send notification to L3 */ + if (length == 0) { + /* 5.4.1.2 Normal establishment procedures */ + rc = send_dl_simple(prim, op, lctx); + msgb_free(msg); + } else { + /* 5.4.1.4 Contention resolution establishment */ + msgb_trim(msg, length); + rc = send_dl_l3(prim, op, lctx, msg); + } + return rc; +} + +/* Receive a LAPD U DM message from L1 */ +static int lapd_rx_u_dm(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int rc = 0; + + LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state)); + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.cmd) { + LOGDL(dl, LOGL_ERROR, "DM command error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + if (!lctx->p_f) { + /* 5.4.1.2 DM responses with the F bit set to "0" + * shall be ignored. + */ + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPD_STATE_SABM_SENT: + break; + case LAPD_STATE_MF_EST: + if (lctx->p_f) { + LOGDL(dl, LOGL_INFO, "unsolicited DM response\n"); + mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx); + } else { + LOGDL(dl, LOGL_INFO, "unsolicited DM response, " + "multiple frame established state\n"); + mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); + /* reestablish */ + if (!dl->reestablish) { + msgb_free(msg); + return 0; + } + LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); + lapd_reestablish(dl); + } + msgb_free(msg); + return 0; + case LAPD_STATE_TIMER_RECOV: + /* FP = 0 (DM is normal in case PF = 1) */ + if (!lctx->p_f) { + LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame " + "established state\n"); + mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); + msgb_free(msg); + /* reestablish */ + if (!dl->reestablish) + return 0; + LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); + return lapd_reestablish(dl); + } + break; + case LAPD_STATE_DISC_SENT: + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* go to idle state */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); + msgb_free(msg); + return 0; + case LAPD_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n"); + msgb_free(msg); + return 0; + } + /* stop timer T200 */ + lapd_stop_t200(dl); + /* go to idle state */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); + msgb_free(msg); + return rc; +} + +/* Receive a LAPD U UI message from L1 */ +static int lapd_rx_u_ui(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int length = lctx->length; + + LOGDL(dl, LOGL_INFO, "UI received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.resp) { + LOGDL(dl, LOGL_ERROR, "UI indicates response error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + + /* G.4.5 If UI is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if (length > lctx->n201 || lctx->more) { + LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n", + length, lctx->n201, lctx->more); + msgb_free(msg); + mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); + return -EIO; + } + + /* do some length checks */ + if (length == 0) { + /* 5.3.3 UI frames received with the length indicator + * set to "0" shall be ignored + */ + LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + msgb_trim(msg, length); + return send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg); +} + +/* Receive a LAPD U DISC message from L1 */ +static int lapd_rx_u_disc(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int length = lctx->length; + int rc = 0; + uint8_t prim, op; + + prim = PRIM_DL_REL; + op = PRIM_OP_INDICATION; + + LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state)); + /* flush tx and send buffers */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.resp) { + LOGDL(dl, LOGL_ERROR, "DISC response error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + if (length > 0 || lctx->more) { + /* G.4.4 If a DISC or DM frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "U frame with incorrect + * parameters" is sent to the mobile management entity. + */ + LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); + return -EIO; + } + switch (dl->state) { + case LAPD_STATE_IDLE: + LOGDL(dl, LOGL_INFO, "DISC in idle state\n"); + /* send DM with F=P */ + msgb_free(msg); + return lapd_send_dm(lctx); + case LAPD_STATE_SABM_SENT: + LOGDL(dl, LOGL_INFO, "DISC in SABM state\n"); + /* 5.4.6.2 send DM with F=P */ + lapd_send_dm(lctx); + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* go to idle state */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + msgb_free(msg); + return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, + lctx); + case LAPD_STATE_MF_EST: + case LAPD_STATE_TIMER_RECOV: + LOGDL(dl, LOGL_INFO, "DISC in est state\n"); + break; + case LAPD_STATE_DISC_SENT: + LOGDL(dl, LOGL_INFO, "DISC in disc state\n"); + prim = PRIM_DL_REL; + op = PRIM_OP_CONFIRM; + break; + default: + lapd_send_ua(lctx, length, msg->l3h); + msgb_free(msg); + return 0; + } + /* send UA response */ + lapd_send_ua(lctx, length, msg->l3h); + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* enter idle state, keep tx-buffer with UA response */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + /* send notification to L3 */ + rc = send_dl_simple(prim, op, lctx); + msgb_free(msg); + return rc; +} + +/* Receive a LAPD U UA message from L1 */ +static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int length = lctx->length; + int rc = 0; + + LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state)); + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.cmd) { + LOGDL(dl, LOGL_ERROR, "UA indicates command error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + + /* G.4.5 If UA is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if (lctx->more || length > lctx->n201) { + LOGDL(dl, LOGL_ERROR, "UA too large error\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); + return -EIO; + } + + if (!lctx->p_f) { + /* 5.4.1.2 A UA response with the F bit set to "0" + * shall be ignored. + */ + LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPD_STATE_SABM_SENT: + break; + case LAPD_STATE_MF_EST: + case LAPD_STATE_TIMER_RECOV: + LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n"); + mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx); + msgb_free(msg); + return 0; + case LAPD_STATE_DISC_SENT: + LOGDL(dl, LOGL_INFO, "UA in disconnect state\n"); + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* go to idle state */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); + msgb_free(msg); + return 0; + case LAPD_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n"); + msgb_free(msg); + return 0; + } + LOGDL(dl, LOGL_INFO, "UA in SABM state\n"); + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* compare UA with SABME if contention resolution is applied */ + if (dl->tx_hist0.msg->len) { + if (length != (dl->tx_hist0.msg->len) + || !!memcmp(dl->tx_hist0.msg->data, msg->l3h, + length)) { + LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n"); + /* go to idle state */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); + msgb_free(msg); + return 0; + } + } + /* set Vs, Vr and Va to 0 */ + dl->v_send = dl->v_recv = dl->v_ack = 0; + /* clear tx_hist */ + lapd_dl_flush_hist(dl); + /* enter multiple-frame-established state */ + lapd_dl_newstate(dl, LAPD_STATE_MF_EST); + /* send outstanding frames, if any (resume / reconnect) */ + lapd_send_i(lctx, __LINE__); + /* send notification to L3 */ + rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx); + msgb_free(msg); + return rc; +} + +/* Receive a LAPD U FRMR message from L1 */ +static int lapd_rx_u_frmr(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + + LOGDL(dl, LOGL_NOTICE, "Frame reject received\n"); + /* send MDL ERROR INIDCATION to L3 */ + mdl_error(MDL_CAUSE_FRMR, lctx); + msgb_free(msg); + /* reestablish */ + if (!dl->reestablish) + return 0; + LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n"); + return lapd_reestablish(dl); +} + +/* Receive a LAPD U (Unnumbered) message from L1 */ +static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + switch (lctx->s_u) { + case LAPD_U_SABM: + case LAPD_U_SABME: + return lapd_rx_u_sabm(msg, lctx); + case LAPD_U_DM: + return lapd_rx_u_dm(msg, lctx); + case LAPD_U_UI: + return lapd_rx_u_ui(msg, lctx); + case LAPD_U_DISC: + return lapd_rx_u_disc(msg, lctx); + case LAPD_U_UA: + return lapd_rx_u_ua(msg, lctx); + case LAPD_U_FRMR: + return lapd_rx_u_frmr(msg, lctx); + default: + /* G.3.1 */ + LOGDL(lctx->dl, LOGL_NOTICE, "Unnumbered frame not allowed\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } +} + +/* Receive a LAPD S (Supervisory) message from L1 */ +static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int length = lctx->length; + + if (length > 0 || lctx->more) { + /* G.4.3 If a supervisory frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "S frame with incorrect + * parameters" is sent to the mobile management entity. */ + LOGDL(dl, LOGL_ERROR, "S frame with incorrect parameters\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx); + return -EIO; + } + + if (lctx->cr == dl->cr.rem2loc.resp + && lctx->p_f + && dl->state != LAPD_STATE_TIMER_RECOV) { + /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */ + LOGDL(dl, LOGL_NOTICE, "S frame response with F=1 error\n"); + mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); + } + + switch (dl->state) { + case LAPD_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (lctx->p_f) + lapd_send_dm(lctx); /* F=P */ + /* fall though */ + case LAPD_STATE_SABM_SENT: + case LAPD_STATE_DISC_SENT: + LOGDL(dl, LOGL_NOTICE, "S frame ignored in this state\n"); + msgb_free(msg); + return 0; + } + switch (lctx->s_u) { + case LAPD_S_RR: + LOGDL(dl, LOGL_INFO, "RR received in state %s\n", lapd_state_name(dl->state)); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapd_acknowledge(lctx); + + /* 5.5.3.2 */ + if (lctx->cr == dl->cr.rem2loc.cmd + && lctx->p_f) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and " + "we are not busy, so we reply with RR frame response\n"); + lapd_send_rr(lctx, 1, 0); + /* NOTE: In case of sequence error condition, + * the REJ frame has been transmitted when + * entering the condition, so it has not be + * done here + */ + } else if (dl->own_busy) { + LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and " + "we are busy, so we reply with RR frame response\n"); + lapd_send_rnr(lctx, 1, 0); + } + } else if (lctx->cr == dl->cr.rem2loc.resp + && lctx->p_f + && dl->state == LAPD_STATE_TIMER_RECOV) { + LOGDL(dl, LOGL_INFO, "RR response with F==1, and we are in timer recovery " + "state, so we leave that state\n"); + /* V(S) to the N(R) in the RR frame */ + dl->v_send = lctx->n_recv; + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* 5.5.7 Clear timer recovery condition */ + lapd_dl_newstate(dl, LAPD_STATE_MF_EST); + } + /* Send message, if possible due to acknowledged data */ + lapd_send_i(lctx, __LINE__); + + break; + case LAPD_S_RNR: + LOGDL(dl, LOGL_INFO, "RNR received in state %s\n", lapd_state_name(dl->state)); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapd_acknowledge(lctx); + + /* 5.5.5 */ + /* Set peer receiver busy condition */ + dl->peer_busy = 1; + + if (lctx->p_f) { + if (lctx->cr == dl->cr.rem2loc.cmd) { + if (!dl->own_busy) { + LOGDL(dl, LOGL_INFO, "RNR poll command and we are not busy, " + "so we reply with RR final response\n"); + /* Send RR with F=1 */ + lapd_send_rr(lctx, 1, 0); + } else { + LOGDL(dl, LOGL_INFO, "RNR poll command and we are busy, so " + "we reply with RNR final response\n"); + /* Send RNR with F=1 */ + lapd_send_rnr(lctx, 1, 0); + } + } else if (dl->state == LAPD_STATE_TIMER_RECOV) { + LOGDL(dl, LOGL_INFO, "RNR poll response and we in timer recovery " + "state, so we leave that state\n"); + /* 5.5.7 Clear timer recovery condition */ + lapd_dl_newstate(dl, LAPD_STATE_MF_EST); + /* V(S) to the N(R) in the RNR frame */ + dl->v_send = lctx->n_recv; + } + } else + LOGDL(dl, LOGL_INFO, "RNR not polling/final state received\n"); + + /* Send message, if possible due to acknowledged data */ + lapd_send_i(lctx, __LINE__); + + break; + case LAPD_S_REJ: + LOGDL(dl, LOGL_INFO, "REJ received in state %s\n", lapd_state_name(dl->state)); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapd_acknowledge(lctx); + + /* 5.5.4.1 */ + if (dl->state != LAPD_STATE_TIMER_RECOV) { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->v_send = dl->v_ack = lctx->n_recv; + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* 5.5.3.2 */ + if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery " + "state and not in own busy condition received, so we " + "respond with RR final response\n"); + lapd_send_rr(lctx, 1, 0); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery " + "state and in own busy condition received, so we " + "respond with RNR final response\n"); + lapd_send_rnr(lctx, 1, 0); + } + } else + LOGDL(dl, LOGL_INFO, "REJ response or not polling command not " + "in timer recovery state received\n"); + /* send MDL ERROR INIDCATION to L3 */ + if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { + LOGDL(dl, LOGL_ERROR, "unsolicited supervisory response!\n"); + mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); + } + + } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { + LOGDL(dl, LOGL_INFO, "REJ poll response in timer recovery state received\n"); + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->v_send = dl->v_ack = lctx->n_recv; + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* 5.5.7 Clear timer recovery condition */ + lapd_dl_newstate(dl, LAPD_STATE_MF_EST); + } else { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->v_send = dl->v_ack = lctx->n_recv; + /* 5.5.3.2 */ + if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery " + "state and not in own busy condition received, so we " + "respond with RR final response\n"); + lapd_send_rr(lctx, 1, 0); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery " + "state and in own busy condition received, so we " + "respond with RNR final response\n"); + lapd_send_rnr(lctx, 1, 0); + } + } else + LOGDL(dl, LOGL_INFO, "REJ response or not polling command in " + "timer recovery state received\n"); + } + + /* FIXME: 5.5.4.2 2) */ + + /* Send message, if possible due to acknowledged data */ + lapd_send_i(lctx, __LINE__); + + break; + default: + /* G.3.1 */ + LOGDL(dl, LOGL_ERROR, "Supervisory frame not allowed\n"); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + msgb_free(msg); + return 0; +} + +/* Receive a LAPD I (Information) message from L1 */ +static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + //uint8_t nr = lctx->n_recv; + uint8_t ns = lctx->n_send; + int length = lctx->length; + int rc; + + LOGDL(dl, LOGL_INFO, "I received in state %s on SAPI(%u)\n", + lapd_state_name(dl->state), lctx->sapi); + + /* G.2.2 Wrong value of the C/R bit */ + if (lctx->cr == dl->cr.rem2loc.resp) { + LOGDL(dl, LOGL_ERROR, "I frame response not allowed (state %s)\n", + lapd_state_name(dl->state)); + msgb_free(msg); + mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); + return -EINVAL; + } + + if (length == 0 || length > lctx->n201) { + /* G.4.2 If the length indicator of an I frame is set + * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION + * primitive with cause "I frame with incorrect length" + * is sent to the mobile management entity. */ + LOGDL(dl, LOGL_ERROR, "I frame length not allowed (state %s)\n", + lapd_state_name(dl->state)); + msgb_free(msg); + mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx); + return -EIO; + } + + /* G.4.2 If the numerical value of L is L<N201 and the M + * bit is set to "1", then an MDL-ERROR-INDICATION primitive with + * cause "I frame with incorrect use of M bit" is sent to the + * mobile management entity. */ + if (lctx->more && length < lctx->n201) { + LOGDL(dl, LOGL_ERROR, "I frame with M bit too short (state %s)\n", + lapd_state_name(dl->state)); + msgb_free(msg); + mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx); + return -EIO; + } + + switch (dl->state) { + case LAPD_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (lctx->p_f) + lapd_send_dm(lctx); /* F=P */ + /* fall though */ + case LAPD_STATE_SABM_SENT: + case LAPD_STATE_DISC_SENT: + LOGDL(dl, LOGL_NOTICE, "I frame ignored in state %s\n", lapd_state_name(dl->state)); + msgb_free(msg); + return 0; + } + + /* 5.7.1: N(s) sequence error */ + if (ns != dl->v_recv) { + LOGDL(dl, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, V(R)=%u (state %s)\n", + ns, dl->v_recv, lapd_state_name(dl->state)); + /* discard data */ + msgb_free(msg); + if (dl->seq_err_cond != 1) { + /* FIXME: help me understand what exactly todo here + */ + dl->seq_err_cond = 1; + lapd_send_rej(lctx, lctx->p_f); + } else { + /* If there are two subsequent sequence errors received, + * ignore it. (Ignore every second subsequent error.) + * This happens if our reply with the REJ is too slow, + * so the remote gets a T200 timeout and sends another + * frame with a sequence error. + * Test showed that replying with two subsequent REJ + * messages could the remote L2 process to abort. + * Replying too slow shouldn't happen, but may happen + * over serial link between BB and LAPD. + */ + dl->seq_err_cond = 2; + } + /* Even if N(s) sequence error, acknowledge to N(R)-1 */ + /* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */ + lapd_acknowledge(lctx); /* V(A) is also set here */ + + /* Send message, if possible due to acknowledged data */ + lapd_send_i(lctx, __LINE__); + + return 0; + } + dl->seq_err_cond = 0; + + /* Increment receiver state */ + dl->v_recv = inc_mod(dl->v_recv, dl->v_range); + LOGDL(dl, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv); + + /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */ + lapd_acknowledge(lctx); /* V(A) is also set here */ + + /* Only if we are not in own receiver busy condition */ + if (!dl->own_busy) { + /* if the frame carries a complete segment */ + if (!lctx->more && !dl->rcv_buffer) { + LOGDL(dl, LOGL_INFO, "message in single I frame\n"); + /* send a DATA INDICATION to L3 */ + msgb_trim(msg, length); + rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx, + msg); + } else { + /* create rcv_buffer */ + if (!dl->rcv_buffer) { + LOGDL(dl, LOGL_INFO, "message in multiple I frames (first message)\n"); + dl->rcv_buffer = lapd_msgb_alloc(dl->maxf, + "LAPD RX"); + dl->rcv_buffer->l3h = dl->rcv_buffer->data; + } + /* concat. rcv_buffer */ + if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) { + LOGDL(dl, LOGL_NOTICE, "Received frame overflow!\n"); + } else { + memcpy(msgb_put(dl->rcv_buffer, length), + msg->l3h, length); + } + /* if the last segment was received */ + if (!lctx->more) { + LOGDL(dl, LOGL_INFO, "message in multiple I frames (last message)\n"); + rc = send_dl_l3(PRIM_DL_DATA, + PRIM_OP_INDICATION, lctx, + dl->rcv_buffer); + dl->rcv_buffer = NULL; + } else + LOGDL(dl, LOGL_INFO, "message in multiple I frames (next message)\n"); + msgb_free(msg); + + } + /* the L3 or higher (called in-line above via send_dl_l3) might have destroyed the + * data link meanwhile. See OS#1761 */ + if (dl->state == LAPD_STATE_NULL) + return 0; + } else + LOGDL(dl, LOGL_INFO, "I frame ignored during own receiver busy condition\n"); + + /* Check for P bit */ + if (lctx->p_f) { + /* 5.5.2.1 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + LOGDL(dl, LOGL_INFO, "we are not busy, send RR\n"); + /* Send RR with F=1 */ + rc = lapd_send_rr(lctx, 1, 0); + } else { + LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=1 */ + rc = lapd_send_rnr(lctx, 1, 0); + } + } else { + /* 5.5.2.2 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + /* NOTE: V(R) is already set above */ + rc = lapd_send_i(lctx, __LINE__); + + /* if update_pending_iframe returns 0 it updated + * the lapd header of an iframe in the tx queue */ + if (rc && dl->update_pending_frames) + rc = dl->update_pending_frames(lctx); + + if (rc) { + LOGDL(dl, LOGL_INFO, "we are not busy and have no pending data, " + "send RR\n"); + /* Send RR with F=0 */ + return lapd_send_rr(lctx, 0, 0); + } + /* all I or one RR is sent, we are done */ + return 0; + } else { + LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=0 */ + rc = lapd_send_rnr(lctx, 0, 0); + } + } + + /* Send message, if possible due to acknowledged data */ + lapd_send_i(lctx, __LINE__); + + return rc; +} + +/* Receive a LAPD message from L1 */ +int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx) +{ + int rc; + + switch (lctx->format) { + case LAPD_FORM_U: + rc = lapd_rx_u(msg, lctx); + break; + case LAPD_FORM_S: + rc = lapd_rx_s(msg, lctx); + break; + case LAPD_FORM_I: + rc = lapd_rx_i(msg, lctx); + break; + default: + LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format 0x%02x\n", lctx->format); + msgb_free(msg); + rc = -EINVAL; + } + return rc; +} + +/* L3 -> L2 */ + +/* send unit data */ +static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + struct lapd_msg_ctx nctx; + + memcpy(&nctx, lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_U; + nctx.s_u = LAPD_U_UI; + /* keep nctx.p_f */ + nctx.length = msg->len; + nctx.more = 0; + + return dl->send_ph_data_req(&nctx, msg); +} + +static void msg_to_tx_hist(struct lapd_history *tx_hist, const struct msgb *msg, int length, int more) +{ + tx_hist->msg = lapd_msgb_alloc(msg->len, "HIST"); + tx_hist->more = more; + msgb_put(tx_hist->msg, msg->len); + if (length) + memcpy(tx_hist->msg->data, msg->l3h, msg->len); +} + +static void msg_to_tx_hist0(struct lapd_datalink *dl, const struct msgb *msg) +{ + return msg_to_tx_hist(&dl->tx_hist0, msg, msg->len, 0); +} + +/* request link establishment */ +static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + struct lapd_msg_ctx nctx; + + if (msg->len) + LOGDL(dl, LOGL_INFO, "perform establishment with content (SABM)\n"); + else + LOGDL(dl, LOGL_INFO, "perform normal establishm. (SABM)\n"); + + /* Flush send-queue */ + /* Clear send-buffer */ + lapd_dl_flush_send(dl); + /* be sure that history is empty */ + lapd_dl_flush_hist(dl); + + /* save message context for further use */ + memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); + + /* Discard partly received L3 message */ + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_U; + nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; + nctx.p_f = 1; + nctx.length = msg->len; + nctx.more = 0; + + /* Transmit-buffer carries exactly one segment */ + msg_to_tx_hist0(dl, msg); + + /* set Vs to 0, because it is used as index when resending SABM */ + dl->v_send = 0; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + dl->send_ph_data_req(&nctx, msg); + lapd_start_t200(dl); + + return 0; +} + +/* send data */ +static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + + if (msgb_l3len(msg) == 0) { + LOGDL(dl, LOGL_ERROR, "writing an empty message is not possible\n"); + msgb_free(msg); + return -1; + } + + LOGDL(dl, LOGL_INFO, "writing message to send-queue: l3len: %d\n", msgb_l3len(msg)); + + /* Write data into the send queue */ + msgb_enqueue(&dl->send_queue, msg); + + /* Send message, if possible */ + lapd_send_i(&dl->lctx, __LINE__); + + return 0; +} + +/* Send next I frame from queued/buffered data */ +static int lapd_send_i(struct lapd_msg_ctx *lctx, int line) +{ + struct lapd_datalink *dl = lctx->dl; + uint8_t k = dl->k; + uint8_t h; + struct msgb *msg; + int length, left; + int rc = - 1; /* we sent nothing */ + struct lapd_msg_ctx nctx; + + + LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line); + + next_frame: + + if (dl->peer_busy) { + LOGDL(dl, LOGL_INFO, "peer busy, not sending\n"); + return rc; + } + + if (dl->state == LAPD_STATE_TIMER_RECOV) { + LOGDL(dl, LOGL_INFO, "timer recovery, not sending\n"); + return rc; + } + + /* If the send state variable V(S) is equal to V(A) plus k + * (where k is the maximum number of outstanding I frames - see + * subclause 5.8.4), the data link layer entity shall not transmit any + * new I frames, but shall retransmit an I frame as a result + * of the error recovery procedures as described in subclauses 5.5.4 and + * 5.5.7. */ + if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) { + LOGDL(dl, LOGL_INFO, "k frames outstanding, not sending more " + "(k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send, dl->v_ack); + return rc; + } + + h = do_mod(dl->v_send, dl->range_hist); + + /* if we have no tx_hist yet, we create it */ + if (!dl->tx_histh.msg) { + /* Get next message into send-buffer, if any */ + if (!dl->send_buffer) { + next_message: + dl->send_out = 0; + dl->send_buffer = msgb_dequeue(&dl->send_queue); + /* No more data to be sent */ + if (!dl->send_buffer) + return rc; + LOGDL(dl, LOGL_INFO, "get message from send-queue\n"); + } + + /* How much is left in the send-buffer? */ + left = msgb_l3len(dl->send_buffer) - dl->send_out; + /* Segment, if data exceeds N201 */ + length = left; + if (length > lctx->n201) + length = lctx->n201; + LOGDL(dl, LOGL_INFO, "msg-len %d sent %d left %d N201 %d length %d " + "first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, + lctx->n201, length, dl->send_buffer->l3h0); + /* If message in send-buffer is completely sent */ + if (left == 0) { + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + goto next_message; + } + + LOGDL(dl, LOGL_INFO, "send I frame %sV(S)=%d\n", + (left > length) ? "segment " : "", dl->v_send); + + /* Create I frame (segment) and transmit-buffer content */ + msg = lapd_msgb_alloc(length, "LAPD I"); + msg->l3h = msgb_put(msg, length); + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_I; + nctx.p_f = 0; + nctx.n_send = dl->v_send; + nctx.n_recv = dl->v_recv; + nctx.length = length; + if (left > length) + nctx.more = 1; + else + nctx.more = 0; + if (length) + memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out, + length); + /* store in tx_hist */ + msg_to_tx_hist(&dl->tx_histh, msg, length, nctx.more); + + /* Add length to track how much is already in the tx buffer */ + dl->send_out += length; + } else { + LOGDL(dl, LOGL_INFO, "resend I frame from tx buffer V(S)=%d\n", dl->v_send); + + /* Create I frame (segment) from tx_hist */ + length = dl->tx_histh.msg->len; + msg = lapd_msgb_alloc(length, "LAPD I resend"); + msg->l3h = msgb_put(msg, length); + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_I; + nctx.p_f = 0; + nctx.n_send = dl->v_send; + nctx.n_recv = dl->v_recv; + nctx.length = length; + nctx.more = dl->tx_histh.more; + if (length) + memcpy(msg->l3h, dl->tx_histh.msg->data, length); + } + + /* The value of the send state variable V(S) shall be incremented by 1 + * at the end of the transmission of the I frame */ + dl->v_send = inc_mod(dl->v_send, dl->v_range); + + /* If timer T200 is not running at the time right before transmitting a + * frame, when the PH-READY-TO-SEND primitive is received from the + * physical layer., it shall be set. */ + if (!osmo_timer_pending(&dl->t200)) { + /* stop Timer T203, if running */ + lapd_stop_t203(dl); + /* start Timer T200 */ + lapd_start_t200(dl); + } + + dl->send_ph_data_req(&nctx, msg); + + rc = 0; /* we sent something */ + goto next_frame; +} + +/* request link suspension */ +static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + + LOGDL(dl, LOGL_INFO, "perform suspension\n"); + + /* put back the send-buffer to the send-queue (first position) */ + if (dl->send_buffer) { + LOGDL(dl, LOGL_INFO, "put frame in sendbuffer back to queue\n"); + llist_add(&dl->send_buffer->list, &dl->send_queue); + dl->send_buffer = NULL; + } else + LOGDL(dl, LOGL_INFO, "no frame in sendbuffer\n"); + + /* Clear transmit buffer, but keep send buffer */ + lapd_dl_flush_tx(dl); + /* Stop timers (there is no state change, so we must stop all timers */ + lapd_stop_t200(dl); + lapd_stop_t203(dl); + + msgb_free(msg); + + return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx); +} + +/* request, resume or reconnect of link */ +static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + struct lapd_msg_ctx nctx; + + LOGDL(dl, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", msg->len); + + /* be sure that history is empty */ + lapd_dl_flush_hist(dl); + + /* save message context for further use */ + memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); + + /* Replace message in the send-buffer (reconnect) */ + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + + dl->send_out = 0; + if (msg->len) { + /* Write data into the send buffer, to be sent first */ + dl->send_buffer = msg; + } else { + msgb_free(msg); + msg = NULL; + dl->send_buffer = NULL; + } + + /* Discard partly received L3 message */ + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + + /* Create new msgb (old one is now free) */ + msg = lapd_msgb_alloc(0, "LAPD SABM"); + msg->l3h = msg->data; + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_U; + nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; + nctx.p_f = 1; + nctx.length = 0; + nctx.more = 0; + + msg_to_tx_hist0(dl, msg); + + /* set Vs to 0, because it is used as index when resending SABM */ + dl->v_send = 0; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + dl->send_ph_data_req(&nctx, msg); + lapd_start_t200(dl); + + return 0; +} + +/* requesst release of link */ +static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + struct lapd_msg_ctx nctx; + + /* local release */ + if (dp->u.rel_req.mode) { + LOGDL(dl, LOGL_INFO, "perform local release\n"); + msgb_free(msg); + /* stop Timer T200 */ + lapd_stop_t200(dl); + /* enter idle state, T203 is stopped here, if running */ + lapd_dl_newstate(dl, LAPD_STATE_IDLE); + /* flush buffers */ + lapd_dl_flush_tx(dl); + lapd_dl_flush_send(dl); + /* send notification to L3 */ + return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); + } + + /* in case we are already disconnecting */ + if (dl->state == LAPD_STATE_DISC_SENT) + return -EBUSY; + + /* flush tx_hist */ + lapd_dl_flush_hist(dl); + + LOGDL(dl, LOGL_INFO, "perform normal release (DISC)\n"); + + /* Push LAPD header on msgb */ + /* assemble message */ + memcpy(&nctx, &dl->lctx, sizeof(nctx)); + /* keep nctx.ldp */ + /* keep nctx.sapi */ + /* keep nctx.tei */ + nctx.cr = dl->cr.loc2rem.cmd; + nctx.format = LAPD_FORM_U; + nctx.s_u = LAPD_U_DISC; + nctx.p_f = 1; + nctx.length = 0; + nctx.more = 0; + + msg_to_tx_hist0(dl, msg); + + /* set Vs to 0, because it is used as index when resending DISC */ + dl->v_send = 0; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapd_dl_newstate(dl, LAPD_STATE_DISC_SENT); + + /* Tramsmit and start T200 */ + dl->send_ph_data_req(&nctx, msg); + lapd_start_t200(dl); + + return 0; +} + +/* request release of link in idle state */ +static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp, + struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + struct msgb *msg = dp->oph.msg; + + msgb_free(msg); + + /* send notification to L3 */ + return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); +} + +/* statefull handling for DL SAP messages from L3 */ +static const struct l2downstate { + uint32_t states; + int prim, op; + const char *name; + int (*rout) (struct osmo_dlsap_prim *dp, + struct lapd_msg_ctx *lctx); +} l2downstatelist = { + /* create and send UI command */ + {ALL_STATES, + PRIM_DL_UNIT_DATA, PRIM_OP_REQUEST, + "DL-UNIT-DATA-REQUEST", lapd_udata_req}, + + /* create and send SABM command */ + {SBIT(LAPD_STATE_IDLE), + PRIM_DL_EST, PRIM_OP_REQUEST, + "DL-ESTABLISH-REQUEST", lapd_est_req}, + + /* create and send I command */ + {SBIT(LAPD_STATE_MF_EST) | + SBIT(LAPD_STATE_TIMER_RECOV), + PRIM_DL_DATA, PRIM_OP_REQUEST, + "DL-DATA-REQUEST", lapd_data_req}, + + /* suspend datalink */ + {SBIT(LAPD_STATE_MF_EST) | + SBIT(LAPD_STATE_TIMER_RECOV), + PRIM_DL_SUSP, PRIM_OP_REQUEST, + "DL-SUSPEND-REQUEST", lapd_susp_req}, + + /* create and send SABM command (resume) */ + {SBIT(LAPD_STATE_MF_EST) | + SBIT(LAPD_STATE_TIMER_RECOV), + PRIM_DL_RES, PRIM_OP_REQUEST, + "DL-RESUME-REQUEST", lapd_res_req}, + + /* create and send SABM command (reconnect) */ + {SBIT(LAPD_STATE_IDLE) | + SBIT(LAPD_STATE_MF_EST) | + SBIT(LAPD_STATE_TIMER_RECOV), + PRIM_DL_RECON, PRIM_OP_REQUEST, + "DL-RECONNECT-REQUEST", lapd_res_req}, + + /* create and send DISC command */ + {SBIT(LAPD_STATE_SABM_SENT) | + SBIT(LAPD_STATE_MF_EST) | + SBIT(LAPD_STATE_TIMER_RECOV) | + SBIT(LAPD_STATE_DISC_SENT), + PRIM_DL_REL, PRIM_OP_REQUEST, + "DL-RELEASE-REQUEST", lapd_rel_req}, + + /* release in idle state */ + {SBIT(LAPD_STATE_IDLE), + PRIM_DL_REL, PRIM_OP_REQUEST, + "DL-RELEASE-REQUEST", lapd_rel_req_idle}, +}; + +#define L2DOWNSLLEN \ + (sizeof(l2downstatelist) / sizeof(struct l2downstate)) + +int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) +{ + struct lapd_datalink *dl = lctx->dl; + int i, supported = 0; + struct msgb *msg = dp->oph.msg; + int rc; + + /* find function for current state and message */ + for (i = 0; i < L2DOWNSLLEN; i++) { + if (dp->oph.primitive == l2downstatelisti.prim + && dp->oph.operation == l2downstatelisti.op) { + supported = 1; + if ((SBIT(dl->state) & l2downstatelisti.states)) + break; + } + } + if (!supported) { + LOGDL(dl, LOGL_NOTICE, "Message %u/%u unsupported\n", + dp->oph.primitive, dp->oph.operation); + msgb_free(msg); + return 0; + } + if (i == L2DOWNSLLEN) { + LOGDL(dl, LOGL_NOTICE, "Message %u/%u unhandled at this state %s\n", + dp->oph.primitive, dp->oph.operation, lapd_state_name(dl->state)); + msgb_free(msg); + return 0; + } + + LOGDL(dl, LOGL_INFO, "Message %s received in state %s\n", + l2downstatelisti.name, lapd_state_name(dl->state)); + + rc = l2downstatelisti.rout(dp, lctx); + + return rc; +} + +/*! @} */
View file
libosmocore_1.8.0.tar.xz/src/isdn/libosmoisdn.map
Added
@@ -0,0 +1,24 @@ +LIBOSMOISDN_1.0 { +global: + +tall_lapd_ctx; +lapd_dl_exit; +lapd_dl_init; +lapd_dl_init2; +lapd_dl_set_name; +lapd_dl_reset; +lapd_msgb_alloc; +lapd_ph_data_ind; +lapd_recv_dlsap; +lapd_set_mode; +lapd_state_names; + +osmo_i460_demux_in; +osmo_i460_mux_enqueue; +osmo_i460_mux_out; +osmo_i460_subchan_add; +osmo_i460_subchan_del; +osmo_i460_ts_init; + +local: *; +};
View file
libosmocore_1.7.0.tar.xz/src/pseudotalloc/Makefile.am -> libosmocore_1.8.0.tar.xz/src/pseudotalloc/Makefile.am
Changed
@@ -1,4 +1,5 @@ -AM_CFLAGS = -Wall -I. $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CPPFLAGS = -I. -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall if ENABLE_PSEUDOTALLOC
View file
libosmocore_1.7.0.tar.xz/src/sim/Makefile.am -> libosmocore_1.8.0.tar.xz/src/sim/Makefile.am
Changed
@@ -1,9 +1,9 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=3:1:1 +LIBVERSION=3:2:1 -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) AM_CFLAGS = -fPIC -Wall $(TALLOC_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) @@ -15,9 +15,12 @@ libosmosim_la_SOURCES = core.c reader.c class_tables.c \ card_fs_sim.c card_fs_usim.c card_fs_uicc.c \ card_fs_isim.c card_fs_hpsim.c card_fs_tetra.c -libosmosim_la_LDFLAGS = -version-info $(LIBVERSION) +libosmosim_la_LDFLAGS = \ + -version-info $(LIBVERSION) \ + -no-undefined \ + $(NULL) libosmosim_la_LIBADD = \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ $(TALLOC_LIBS) if ENABLE_PCSC
View file
libosmocore_1.7.0.tar.xz/src/sim/class_tables.c -> libosmocore_1.8.0.tar.xz/src/sim/class_tables.c
Changed
@@ -113,7 +113,7 @@ 0xD6 = 3, /* UPDATE BINARY */ 0xB2 = 2, /* READ RECORD */ 0xDC = 3, /* UPDATE RECORD */ - 0xA2 = 4, /* SEEK */ + 0xA2 = 4, /* SEARCH RECORD */ 0x20 = 3, /* VERIFY PIN */ 0x24 = 3, /* CHANGE PIN */ 0x26 = 3, /* DISABLE PIN */ @@ -198,6 +198,7 @@ 0xCB = 4, /* RETRIEVE DATA */ 0xDB = 3, /* SET DATA */ 0xAA = 3, /* TERMINAL CAPABILITY */ + 0x78 = 4, /* GET IDENTITY */ }; /* ETSI TS 102 221, Table 10.5, CLA = 0x80 */ @@ -206,6 +207,8 @@ 0xC2 = 4, /* ENVELOPE */ 0x12 = 2, /* FETCH */ 0x14 = 3, /* TERMINAL RESPONSE */ + 0x76 = 4, /* SUSPEND UICC */ + 0x7A = 4, /* EXCHANGE CAPABILITIES */ }; /* Card Specification v2.3.1*/
View file
libosmocore_1.7.0.tar.xz/src/usb/Makefile.am -> libosmocore_1.8.0.tar.xz/src/usb/Makefile.am
Changed
@@ -3,7 +3,7 @@ # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html LIBVERSION=0:1:0 -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) AM_CFLAGS = -fPIC -Wall $(LIBUSB_CFLAGS) $(TALLOC_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) @@ -12,9 +12,12 @@ lib_LTLIBRARIES = libosmousb.la libosmousb_la_SOURCES = osmo_libusb.c -libosmousb_la_LDFLAGS = -version-info $(LIBVERSION) +libosmousb_la_LDFLAGS = \ + -version-info $(LIBVERSION) \ + -no-undefined \ + $(NULL) libosmousb_la_LIBADD = \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(TALLOC_LIBS) \ $(LIBUSB_LIBS)
View file
libosmocore_1.7.0.tar.xz/src/vty/Makefile.am -> libosmocore_1.8.0.tar.xz/src/vty/Makefile.am
Changed
@@ -1,9 +1,9 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=11:0:2 +LIBVERSION=12:0:3 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) if ENABLE_VTY @@ -14,5 +14,5 @@ fsm_vty.c talloc_ctx_vty.c \ cpu_sched_vty.c tdef_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS) +libosmovty_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS) endif
View file
libosmocore_1.7.0.tar.xz/src/vty/command.c -> libosmocore_1.8.0.tar.xz/src/vty/command.c
Changed
@@ -1480,19 +1480,52 @@ #error "LONG_MAX not defined!" #endif +/* This function is aimed at quickly guessing & filtering the numeric base a + * string can contain, by no means validates the entire value. + * Returns 16 if string follows pattern "({+,-}0xdigits)" + * Returns 10 if string follows pattern "({+,-}digits)" + * Returns a negative value if something other is detected (error) +*/ +static int check_base(const char *str) +{ + const char *ptr = str; + /* Skip any space */ + while (isspace(*ptr)) ptr++; + + /* Skip optional sign: */ + if (*ptr == '+' || *ptr == '-') + ptr++; + if (*ptr == '0') { + ptr++; + if (*ptr == '\0' || isdigit(*ptr)) + return 10; + if (!(*ptr == 'x' || *ptr == 'X')) + return -1; + ptr++; + if (isxdigit(*ptr)) + return 16; + return -1; + } + return 10; +} + int vty_cmd_range_match(const char *range, const char *str) { char *p; char bufDECIMAL_STRLEN_MAX_UNSIGNED + 1; char *endptr = NULL; + int min_base, max_base, val_base; if (str == NULL) return 1; + if ((val_base = check_base(str)) < 0) + return 0; + if (range1 == '-') { signed long min = 0, max = 0, val; - val = strtol(str, &endptr, 10); + val = strtol(str, &endptr, val_base); if (*endptr != '\0') return 0; @@ -1504,7 +1537,9 @@ return 0; strncpy(buf, range, p - range); bufp - range = '\0'; - min = -strtol(buf, &endptr, 10); + if ((min_base = check_base(buf)) < 0) + return 0; + min = -strtol(buf, &endptr, min_base); if (*endptr != '\0') return 0; @@ -1516,7 +1551,9 @@ return 0; strncpy(buf, range, p - range); bufp - range = '\0'; - max = strtol(buf, &endptr, 10); + if ((max_base = check_base(buf)) < 0) + return 0; + max = strtol(buf, &endptr, max_base); if (*endptr != '\0') return 0; @@ -1528,7 +1565,7 @@ if (str0 == '-') return 0; - val = strtoul(str, &endptr, 10); + val = strtoul(str, &endptr, val_base); if (*endptr != '\0') return 0; @@ -1540,7 +1577,9 @@ return 0; strncpy(buf, range, p - range); bufp - range = '\0'; - min = strtoul(buf, &endptr, 10); + if ((min_base = check_base(buf)) < 0) + return 0; + min = strtoul(buf, &endptr, min_base); if (*endptr != '\0') return 0; @@ -1552,7 +1591,9 @@ return 0; strncpy(buf, range, p - range); bufp - range = '\0'; - max = strtoul(buf, &endptr, 10); + if ((max_base = check_base(buf)) < 0) + return 0; + max = strtoul(buf, &endptr, max_base); if (*endptr != '\0') return 0; @@ -1560,6 +1601,14 @@ return 0; } + /* Don't allow ranges specified by min and max with different bases */ + if (min_base != max_base) + return 0; + /* arg value passed must match the base of the range */ + if (min_base != val_base) + return 0; + + /* Everything's fine */ return 1; }
View file
libosmocore_1.7.0.tar.xz/src/vty/cpu_sched_vty.c -> libosmocore_1.8.0.tar.xz/src/vty/cpu_sched_vty.c
Changed
@@ -25,7 +25,7 @@ #define _GNU_SOURCE -#include "../../config.h" +#include "config.h" #include <string.h> #include <stdlib.h> @@ -89,7 +89,7 @@ }; /* returns number of configured CPUs in the system, or negative otherwise */ -static int get_num_cpus() { +static int get_num_cpus(void) { static unsigned int num_cpus = 0; long ln;
View file
libosmocore_1.7.0.tar.xz/src/vty/fsm_vty.c -> libosmocore_1.8.0.tar.xz/src/vty/fsm_vty.c
Changed
@@ -19,7 +19,7 @@ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h>
View file
libosmocore_1.7.0.tar.xz/src/vty/logging_vty.c -> libosmocore_1.8.0.tar.xz/src/vty/logging_vty.c
Changed
@@ -20,7 +20,7 @@ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h> @@ -1196,7 +1196,7 @@ /*! Register logging related commands to the VTY. Call this once from * your application if you want to support those commands. */ -void logging_vty_add_cmds() +void logging_vty_add_cmds(void) { install_lib_element_ve(&enable_logging_cmd); install_lib_element_ve(&disable_logging_cmd);
View file
libosmocore_1.7.0.tar.xz/src/vty/stats_vty.c -> libosmocore_1.8.0.tar.xz/src/vty/stats_vty.c
Changed
@@ -21,7 +21,7 @@ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h> @@ -742,7 +742,7 @@ * Call this once during your application initialization if you would * like to have stats VTY commands enabled. */ -void osmo_stats_vty_add_cmds() +void osmo_stats_vty_add_cmds(void) { install_lib_element_ve(&show_stats_cmd); install_lib_element_ve(&show_stats_level_cmd);
View file
libosmocore_1.7.0.tar.xz/src/vty/telnet_interface.c -> libosmocore_1.8.0.tar.xz/src/vty/telnet_interface.c
Changed
@@ -42,7 +42,7 @@ * process in order to enable interactive command-line introspection, * interaction and configuration. * - * You typically call \ref telnet_init or \ref telnet_init_dynif once + * You typically call telnet_init_default once * from your application code to enable this. */ @@ -60,26 +60,14 @@ .priv_nr = 0, }; -/*! Initialize telnet based VTY interface listening to 127.0.0.1 - * \paramin tall_ctx \ref talloc context - * \paramin priv private data to be passed to callback - * \paramin port TCP port number to bind to - */ -int telnet_init(void *tall_ctx, void *priv, int port) -{ - return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); -} - -/*! Initialize telnet based VTY interface - * \paramin tall_ctx \ref talloc context - * \paramin priv private data to be passed to callback - * \paramin ip IP to listen to ('::1' for localhost, '::0' for all, ...) - * \paramin port TCP port number to bind to - */ -int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) +/* Helper for deprecating telnet_init_dynif(), which previously held this code */ +static int _telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) { int rc; + if (port < 0) + return -EINVAL; + tall_telnet_ctx = talloc_named_const(tall_ctx, 1, "telnet_connection"); @@ -94,22 +82,45 @@ if (rc < 0) { LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n", ip, port); - return -1; + return rc; } LOGP(DLGLOBAL, LOGL_NOTICE, "Available via telnet %s %d\n", ip, port); return 0; } +/*! Initialize telnet based VTY interface listening to 127.0.0.1 + * \paramin tall_ctx \ref talloc context + * \paramin priv private data to be passed to callback + * \paramin port TCP port number to bind to + * \deprecated use telnet_init_default() instead + */ +int telnet_init(void *tall_ctx, void *priv, int port) +{ + return _telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); +} + +/*! Initialize telnet based VTY interface + * \paramin tall_ctx \ref talloc context + * \paramin priv private data to be passed to callback + * \paramin ip IP to listen to ('::1' for localhost, '::0' for all, ...) + * \paramin port TCP port number to bind to + * \deprecated use telnet_init_default() instead + */ +int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) +{ + return _telnet_init_dynif(tall_ctx, priv, ip, port); +} + /*! Initializes telnet based VTY interface using the configured bind addr/port. * \paramin tall_ctx \ref talloc context * \paramin priv private data to be passed to callback - * \paramin default_port TCP port number to bind to if not explicitely configured + * \paramin default_port TCP port number to bind to if not explicitly configured */ int telnet_init_default(void *tall_ctx, void *priv, int default_port) { - return telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(), - vty_get_bind_port(default_port)); + return _telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(), + vty_get_bind_port(default_port)); }
View file
libosmocore_1.7.0.tar.xz/tests/Makefile.am -> libosmocore_1.8.0.tar.xz/tests/Makefile.am
Changed
@@ -1,7 +1,7 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) AM_LDFLAGS = -no-install -LDADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS) +LDADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS) if ENABLE_SERCOM_STUB noinst_LIBRARIES = libsercomstub.a @@ -9,13 +9,13 @@ endif check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ - smscb/smscb_test bits/bitrev_test a5/a5_test \ + bits/bitrev_test a5/a5_test \ conv/conv_test auth/milenage_test lapd/lapd_test \ gsm0808/gsm0808_test gsm0408/gsm0408_test \ gprs/gprs_test kasumi/kasumi_test gea/gea_test \ logging/logging_test codec/codec_test \ loggingrb/loggingrb_test strrb/strrb_test \ - comp128/comp128_test smscb/gsm0341_test \ + comp128/comp128_test \ bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test \ bits/bitfield_test \ tlv/tlv_test gsup/gsup_test oap/oap_test \ @@ -46,6 +46,9 @@ gsm48/rest_octets_test \ base64/base64_test \ iuup/iuup_test \ + smscb/smscb_test \ + smscb/gsm0341_test \ + smscb/cbsp_test \ $(NULL) if ENABLE_MSGFILE @@ -90,7 +93,7 @@ stats_stats_test_SOURCES = stats/stats_test.c stats_stats_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la -stats_stats_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src +stats_stats_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/core stats_stats_vty_test_SOURCES = stats/stats_vty_test.c stats_stats_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la @@ -157,7 +160,7 @@ gprs_gprs_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la lapd_lapd_test_SOURCES = lapd/lapd_test.c -lapd_lapd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la +lapd_lapd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/isdn/libosmoisdn.la msgb_msgb_test_SOURCES = msgb/msgb_test.c @@ -169,6 +172,9 @@ smscb_gsm0341_test_SOURCES = smscb/gsm0341_test.c smscb_gsm0341_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la +smscb_cbsp_test_SOURCES = smscb/cbsp_test.c +smscb_cbsp_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la + sms_sms_test_SOURCES = sms/sms_test.c sms_sms_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la @@ -203,11 +209,8 @@ gb_gprs_ns2_test_LDADD = $(LDADD) $(LIBRARY_DLSYM) \ $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la \ - $(top_builddir)/src/libosmocore.la \ + $(top_builddir)/src/core/libosmocore.la \ $(top_builddir)/src/gb/libosmogb-test.la -if ENABLE_LIBMNL -gb_gprs_ns2_test_LDADD += $(LIBMNL_LIBS) -endif logging_logging_test_SOURCES = logging/logging_test.c @@ -309,7 +312,7 @@ exec_exec_test_LDADD = $(LDADD) i460_mux_i460_mux_test_SOURCES = i460_mux/i460_mux_test.c -i460_mux_i460_mux_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la +i460_mux_i460_mux_test_LDADD = $(LDADD) $(top_builddir)/src/isdn/libosmoisdn.la bitgen_bitgen_test_SOURCES = bitgen/bitgen_test.c bitgen_bitgen_test_LDADD = $(LDADD) @@ -352,9 +355,10 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ timer/timer_test.ok sms/sms_test.ok ussd/ussd_test.ok \ - smscb/smscb_test.ok bits/bitrev_test.ok a5/a5_test.ok \ + bits/bitrev_test.ok a5/a5_test.ok \ conv/conv_test.ok auth/milenage_test.ok ctrl/ctrl_test.ok \ - lapd/lapd_test.ok gsm0408/gsm0408_test.ok \ + lapd/lapd_test.ok \ + gsm0408/gsm0408_test.ok gsm0408/gsm0408_test.err \ gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err \ gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh \ gb/gprs_bssgp_test.ok gb/gprs_ns_test.ok gea/gea_test.ok \ @@ -425,6 +429,9 @@ gsm48/rest_octets_test.ok \ base64/base64_test.ok \ iuup/iuup_test.ok \ + smscb/smscb_test.ok \ + smscb/gsm0341_test.ok \ + smscb/cbsp_test.ok \ $(NULL) if ENABLE_LIBSCTP @@ -479,6 +486,10 @@ >$(srcdir)/sms/sms_test.ok smscb/smscb_test \ >$(srcdir)/smscb/smscb_test.ok + smscb/gsm0341_test \ + >$(srcdir)/smscb/gsm0341_test.ok + smscb/cbsp_test \ + >$(srcdir)/smscb/cbsp_test.ok ussd/ussd_test \ >$(srcdir)/ussd/ussd_test.ok auth/milenage_test \ @@ -496,7 +507,8 @@ gsm29205/gsm29205_test \ >$(srcdir)/gsm29205/gsm29205_test.ok gsm0408/gsm0408_test \ - >$(srcdir)/gsm0408/gsm0408_test.ok + 2>$(srcdir)/gsm0408/gsm0408_test.err \ + 1>$(srcdir)/gsm0408/gsm0408_test.ok gsm48/rest_octets_test \ >$(srcdir)/gsm48/rest_octets_test.ok gprs/gprs_test \
View file
libosmocore_1.7.0.tar.xz/tests/abis/abis_test.c -> libosmocore_1.8.0.tar.xz/tests/abis/abis_test.c
Changed
@@ -167,7 +167,7 @@ } } -static void test_sw_descr() +static void test_sw_descr(void) { const char *f_id = "TEST.L0L", *f_ver = "0.1.666~deadbeeffacefeed-dirty"; uint8_t chain = { 0x42, 0x12, 0x00, 0x03, 0x01, 0x02, 0x03, 0x13, 0x00, 0x03, 0x03, 0x04, 0x05, 0x42, 0x12, @@ -194,7 +194,7 @@ } /* Test decode IPAC_DLCX_IND obtained from SYS#5915 */ -static void test_dec_ipac_dlc_indx() +static void test_dec_ipac_dlc_indx(void) { /* Radio Signalling Link (RSL) 0111 111. = Message discriminator: ip.access Vendor Specific messages (63)
View file
libosmocore_1.7.0.tar.xz/tests/bitgen/bitgen_test.c -> libosmocore_1.8.0.tar.xz/tests/bitgen/bitgen_test.c
Changed
@@ -14,17 +14,17 @@ for (at_idx = 0; at_idx < len; at_idx++) { \ uint##SIZE##_t read_val = 0; \ memset(buf, 0, sizeof(buf)); \ - osmo_store##SIZE####BE_LE##_ext(val, &bufat_idx, len); \ + osmo_store##SIZE##BE_LE##_ext(val, &bufat_idx, len); \ printf("osmo_store" #SIZE #BE_LE "_ext(0x%" PRIx##SIZE ", &buf%d, %d) = %s\n", \ val, \ at_idx, len, osmo_hexdump(buf, sizeof(buf))); \ \ - read_val = osmo_load##SIZE####BE_LE##_ext(&bufat_idx, len); \ + read_val = osmo_load##SIZE##BE_LE##_ext(&bufat_idx, len); \ printf("osmo_load" #SIZE #BE_LE "_ext(&buf%d, %d) = 0x%" PRIx##SIZE "\n", \ at_idx, len, read_val); \ \ if (!strcmp(#BE_LE, "be")) { \ - read_val = osmo_load##SIZE####BE_LE##_ext_2(&bufat_idx, len); \ + read_val = osmo_load##SIZE##BE_LE##_ext_2(&bufat_idx, len); \ printf("osmo_load" #SIZE #BE_LE "_ext_2(&buf%d, %d) = 0x%" PRIx##SIZE "\n", \ at_idx, len, read_val); \ } \
View file
libosmocore_1.7.0.tar.xz/tests/bitvec/bitvec_test.c -> libosmocore_1.8.0.tar.xz/tests/bitvec/bitvec_test.c
Changed
@@ -69,7 +69,7 @@ printf(" %s %d\n\n", lol, bv->cur_bit); } -static void test_byte_ops() +static void test_byte_ops(void) { struct bitvec bv; const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -162,7 +162,7 @@ OSMO_ASSERT(num == result); } -static void test_array() +static void test_array(void) { struct bitvec b; uint8_t d4096; @@ -181,7 +181,7 @@ test_array_item(17, &b, n, array, n * 3); } -static void test_used_bytes() +static void test_used_bytes(void) { struct bitvec b; uint8_t d32; @@ -204,7 +204,7 @@ } } -static void test_tailroom() +static void test_tailroom(void) { struct bitvec b; uint8_t d32;
View file
libosmocore_1.7.0.tar.xz/tests/bsslap/bsslap_test.c -> libosmocore_1.8.0.tar.xz/tests/bsslap/bsslap_test.c
Changed
@@ -48,7 +48,7 @@ }, }; -void test_bsslap_enc_dec() +void test_bsslap_enc_dec(void) { struct bsslap_pdu *pdu; printf("--- %s\n", __func__); @@ -96,7 +96,7 @@ } } -int main() +int main(int argc, char **argv) { test_bsslap_enc_dec(); return 0;
View file
libosmocore_1.7.0.tar.xz/tests/bssmap_le/bssmap_le_test.c -> libosmocore_1.8.0.tar.xz/tests/bssmap_le/bssmap_le_test.c
Changed
@@ -150,7 +150,7 @@ }, }; -void test_bssmap_le_enc_dec() +void test_bssmap_le_enc_dec(void) { struct bssmap_le_pdu *pdu; printf("--- %s\n", __func__); @@ -162,7 +162,7 @@ .bssmap_le = *pdu, }; struct bssap_le_pdu dec_pdu; - struct osmo_bssap_le_err *err; + struct osmo_bssap_le_err *err = NULL; void *loop_ctx; int rc; @@ -200,7 +200,7 @@ } } -int main() +int main(int argc, char **argv) { test_bssmap_le_enc_dec(); return 0;
View file
libosmocore_1.7.0.tar.xz/tests/codec/codec_ecu_fr_test.c -> libosmocore_1.8.0.tar.xz/tests/codec/codec_ecu_fr_test.c
Changed
@@ -183,7 +183,7 @@ } /* Simulate a real life situation: voice frames with a few dropouts */ -void test_fr_concealment_realistic() +void test_fr_concealment_realistic(void) { struct osmo_ecu_fr_state state; uint8_t frameGSM_FR_BYTES; @@ -219,7 +219,7 @@ } /* Simulate a real life situation: voice frames with a few dropouts, using generic core */ -void test_fr_concealment_realistic_core() +void test_fr_concealment_realistic_core(void) { struct osmo_ecu_state *state = osmo_ecu_init(NULL, OSMO_ECU_CODEC_FR); uint8_t frameGSM_FR_BYTES;
View file
libosmocore_1.7.0.tar.xz/tests/ctrl/ctrl_test.c -> libosmocore_1.8.0.tar.xz/tests/ctrl/ctrl_test.c
Changed
@@ -334,7 +334,7 @@ }, }; -static void test_messages() +static void test_messages(void) { struct ctrl_handle *ctrl; struct ctrl_connection *ccon; @@ -390,7 +390,7 @@ return 0; } -static void test_deferred_cmd() +static void test_deferred_cmd(void) { struct ctrl_handle *ctrl; struct ctrl_connection *ccon;
View file
libosmocore_1.7.0.tar.xz/tests/fr/fr_test.c -> libosmocore_1.8.0.tar.xz/tests/fr/fr_test.c
Changed
@@ -52,7 +52,7 @@ return fd; } -void bssgp_prim_cb() +void bssgp_prim_cb(void) { }
View file
libosmocore_1.7.0.tar.xz/tests/fsm/fsm_dealloc_test.c -> libosmocore_1.8.0.tar.xz/tests/fsm/fsm_dealloc_test.c
Changed
@@ -290,7 +290,7 @@ obj_add_other(b, a); } -static struct scene *scene_alloc() +static struct scene *scene_alloc(void) { struct scene *s = talloc_zero(ctx, struct scene); s->use_count.talloc_object = s; @@ -419,7 +419,7 @@ } } -void test_osmo_fsm_term_safely() +void test_osmo_fsm_term_safely(void) { fprintf(stderr, "\n\n%s()\n", __func__); osmo_fsm_term_safely(true); @@ -428,7 +428,7 @@ fprintf(stderr, "\n\n%s() done\n", __func__); } -void test_osmo_fsm_set_dealloc_ctx() +void test_osmo_fsm_set_dealloc_ctx(void) { fprintf(stderr, "\n\n%s()\n", __func__); void *dealloc_ctx = talloc_named_const(ctx, 0, "fsm_dealloc");
View file
libosmocore_1.7.0.tar.xz/tests/fsm/fsm_test.c -> libosmocore_1.8.0.tar.xz/tests/fsm/fsm_test.c
Changed
@@ -172,7 +172,7 @@ return fi; } -static void test_id_api() +static void test_id_api(void) { struct osmo_fsm_inst *fi; @@ -280,7 +280,7 @@ osmo_timers_update(); \ } while (0) -void fake_time_start() +void fake_time_start(void) { struct timespec *clock_override; @@ -301,7 +301,7 @@ return 0; } -static void test_state_chg_keep_timer() +static void test_state_chg_keep_timer(void) { struct osmo_fsm_inst *fi; @@ -349,7 +349,7 @@ fprintf(stderr, "--- %s() done\n", __func__); } -static void test_state_chg_T() +static void test_state_chg_T(void) { struct osmo_fsm_inst *fi; @@ -386,6 +386,74 @@ fprintf(stderr, "--- %s() done\n", __func__); } +/* Test setting a state timeout with second granularity */ +static void test_state_chg_Ts(void) +{ + struct osmo_fsm_inst *fi; + + fprintf(stderr, "\n--- %s()\n", __func__); + + fsm.timer_cb = &timer_cb; + timeout_fired = -1; + fake_time_start(); + + fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL); + OSMO_ASSERT(fi); + + osmo_fsm_inst_state_chg(fi, ST_ONE, 8, 4242); + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(3, 0); /* +3s */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(2, 500000); /* +2.5s */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(2, 500000); /* +2.5s */ + OSMO_ASSERT(timeout_fired == 4242); + + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL); + + fprintf(stderr, "--- %s() done\n", __func__); +} + +/* Test setting a state timeout with millisecond granularity */ +static void test_state_chg_Tms(void) +{ + struct osmo_fsm_inst *fi; + + fprintf(stderr, "\n--- %s()\n", __func__); + + fsm.timer_cb = &timer_cb; + timeout_fired = -1; + fake_time_start(); + + fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL); + OSMO_ASSERT(fi); + + osmo_fsm_inst_state_chg_ms(fi, ST_ONE, 1337, 4242); /* 1s 337ms */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(0, 500000); /* +500ms, 500ms total */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(0, 250000); /* +250ms, 750ms total */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(0, 350000); /* +350ms, 1s 100ms total */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(0, 200000); /* +200ms, 1s 300ms total */ + OSMO_ASSERT(timeout_fired == -1); + + fake_time_passes(0, 37000); /* +37ms, 1s 337ms total */ + OSMO_ASSERT(timeout_fired == 4242); + + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL); + + fprintf(stderr, "--- %s() done\n", __func__); +} + static const struct log_info_cat default_categories = { DMAIN = { .name = "DMAIN", @@ -434,6 +502,8 @@ test_id_api(); test_state_chg_keep_timer(); test_state_chg_T(); + test_state_chg_Ts(); + test_state_chg_Tms(); osmo_fsm_unregister(&fsm); exit(0);
View file
libosmocore_1.7.0.tar.xz/tests/fsm/fsm_test.err -> libosmocore_1.8.0.tar.xz/tests/fsm/fsm_test.err
Changed
@@ -116,3 +116,31 @@ Test_FSM{TWO}: Freeing instance Test_FSM{TWO}: Deallocated --- test_state_chg_T() done + +--- test_state_chg_Ts() +Total time passed: 0.000000 s +Test_FSM{NULL}: Allocated +Test_FSM{NULL}: State change to ONE (T4242, 8s) +Total time passed: 3.000000 s +Total time passed: 5.500000 s +Total time passed: 8.000000 s +Test_FSM{ONE}: Timeout of T4242 +Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST) +Test_FSM{ONE}: Freeing instance +Test_FSM{ONE}: Deallocated +--- test_state_chg_Ts() done + +--- test_state_chg_Tms() +Total time passed: 0.000000 s +Test_FSM{NULL}: Allocated +Test_FSM{NULL}: State change to ONE (T4242, 1337ms) +Total time passed: 0.500000 s +Total time passed: 0.750000 s +Total time passed: 1.100000 s +Total time passed: 1.300000 s +Total time passed: 1.337000 s +Test_FSM{ONE}: Timeout of T4242 +Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST) +Test_FSM{ONE}: Freeing instance +Test_FSM{ONE}: Deallocated +--- test_state_chg_Tms() done
View file
libosmocore_1.7.0.tar.xz/tests/gad/gad_test.c -> libosmocore_1.8.0.tar.xz/tests/gad/gad_test.c
Changed
@@ -4,7 +4,7 @@ #include <osmocom/core/msgb.h> #include <osmocom/gsm/gad.h> -void test_gad_lat_lon_dec_enc_stability() +void test_gad_lat_lon_dec_enc_stability(void) { uint32_t lat_enc; uint32_t lon_enc; @@ -49,7 +49,7 @@ }, }; -void test_gad_enc_dec() +void test_gad_enc_dec(void) { int i; printf("--- %s\n", __func__); @@ -115,7 +115,7 @@ } } -void test_gad_to_str() +void test_gad_to_str(void) { int i; printf("--- %s\n", __func__); @@ -134,7 +134,7 @@ } } -int main() +int main(int argc, char **argv) { test_gad_lat_lon_dec_enc_stability(); test_gad_enc_dec();
View file
libosmocore_1.7.0.tar.xz/tests/gb/gprs_bssgp_rim_test.c -> libosmocore_1.8.0.tar.xz/tests/gb/gprs_bssgp_rim_test.c
Changed
@@ -60,7 +60,7 @@ } } -static void test_bssgp_parse_rim_ri() +static void test_bssgp_parse_rim_ri(void) { int rc; struct bssgp_rim_routing_info result; @@ -94,7 +94,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_create_rim_ri() +static void test_bssgp_create_rim_ri(void) { int rc; struct bssgp_rim_routing_info ri; @@ -194,7 +194,7 @@ } } -static void test_bssgp_dec_ran_inf_req_rim_cont_nacc() +static void test_bssgp_dec_ran_inf_req_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_req_rim_cont rim_cont_dec; @@ -212,7 +212,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_req_rim_cont_nacc() +static void test_bssgp_enc_ran_inf_req_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_req_rim_cont rim_cont = { }; @@ -321,7 +321,7 @@ } } -static void test_bssgp_dec_ran_inf_rim_cont_nacc() +static void test_bssgp_dec_ran_inf_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_rim_cont rim_cont_dec; @@ -346,7 +346,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_dec_ran_inf_rim_cont_err_nacc() +static void test_bssgp_dec_ran_inf_rim_cont_err_nacc(void) { int rc; struct bssgp_ran_inf_rim_cont rim_cont_dec; @@ -364,7 +364,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_rim_cont_nacc() +static void test_bssgp_enc_ran_inf_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_rim_cont rim_cont = { }; @@ -415,7 +415,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_rim_cont_err_nacc() +static void test_bssgp_enc_ran_inf_rim_cont_err_nacc(void) { int rc; struct bssgp_ran_inf_rim_cont rim_cont = { }; @@ -458,7 +458,7 @@ } } -static void test_bssgp_dec_ran_inf_ack_rim_cont() +static void test_bssgp_dec_ran_inf_ack_rim_cont(void) { int rc; struct bssgp_ran_inf_ack_rim_cont rim_cont_dec; @@ -474,7 +474,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_ack_rim_cont() +static void test_bssgp_enc_ran_inf_ack_rim_cont(void) { int rc; struct bssgp_ran_inf_ack_rim_cont rim_cont = { }; @@ -513,7 +513,7 @@ } } -static void test_bssgp_dec_ran_inf_err_rim_cont() +static void test_bssgp_dec_ran_inf_err_rim_cont(void) { int rc; struct bssgp_ran_inf_err_rim_cont rim_cont_dec; @@ -530,7 +530,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_err_rim_cont() +static void test_bssgp_enc_ran_inf_err_rim_cont(void) { int rc; struct bssgp_ran_inf_err_rim_cont rim_cont = { }; @@ -578,7 +578,7 @@ } } -static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc() +static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_app_err_rim_cont rim_cont_dec; @@ -597,7 +597,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc() +static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc(void) { int rc; struct bssgp_ran_inf_app_err_rim_cont rim_cont = { }; @@ -623,7 +623,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_dec_ran_inf_req_app_cont_nacc() +static void test_bssgp_dec_ran_inf_req_app_cont_nacc(void) { int rc; struct bssgp_ran_inf_req_app_cont_nacc app_cont_dec; @@ -639,7 +639,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_req_app_cont_nacc() +static void test_bssgp_enc_ran_inf_req_app_cont_nacc(void) { int rc; struct bssgp_ran_inf_req_app_cont_nacc app_cont = { }; @@ -662,7 +662,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_dec_ran_inf_app_cont_nacc() +static void test_bssgp_dec_ran_inf_app_cont_nacc(void) { int rc; struct bssgp_ran_inf_app_cont_nacc app_cont_dec; @@ -682,7 +682,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_ran_inf_app_cont_nacc() +static void test_bssgp_enc_ran_inf_app_cont_nacc(void) { int rc; struct bssgp_ran_inf_app_cont_nacc app_cont = { }; @@ -721,7 +721,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_dec_app_err_cont_nacc() +static void test_bssgp_dec_app_err_cont_nacc(void) { int rc; struct bssgp_app_err_cont_nacc app_cont_dec; @@ -737,7 +737,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_enc_app_err_cont_nacc() +static void test_bssgp_enc_app_err_cont_nacc(void) { int rc; struct bssgp_app_err_cont_nacc app_cont = { };
View file
libosmocore_1.7.0.tar.xz/tests/gb/gprs_bssgp_test.c -> libosmocore_1.8.0.tar.xz/tests/gb/gprs_bssgp_test.c
Changed
@@ -173,7 +173,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_bad_reset() +static void test_bssgp_bad_reset(void) { struct msgb *msg; uint16_t bvci_be = htons(2); @@ -256,7 +256,7 @@ printf("----- %s END\n", __func__); } -static void test_bssgp_msgb_copy() +static void test_bssgp_msgb_copy(void) { struct msgb *msg, *msg2; uint16_t bvci_be = htons(2);
View file
libosmocore_1.7.0.tar.xz/tests/gb/gprs_ns2_vty.vty -> libosmocore_1.8.0.tar.xz/tests/gb/gprs_ns2_vty.vty
Changed
@@ -40,11 +40,11 @@ OsmoNSdummy# show ns NSEI 01234: UDP, DEAD since 0d 0h 0m 0s 1 NS-VC: - RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD (cause: remote) since 0d 0h 0m 0s UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0 IP-SNS signalling weight: 1 data weight: 1 1 NS-VC: - RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD (cause: remote) since 0d 0h 0m 0s OsmoNSdummy# configure terminal OsmoNSdummy(config)# ns OsmoNSdummy(config-ns)# nse 1234 @@ -54,15 +54,15 @@ OsmoNSdummy# show ns NSEI 01234: UDP, DEAD since 0d 0h 0m 0s 3 NS-VC: - RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD since 0d 0h 0m 0s - RECOVERING PERSIST sig_weight=0 data_weight=9 udp)127.0.0.14:42999<>127.0.0.16:9496 DEAD since 0d 0h 0m 0s - RECOVERING PERSIST sig_weight=0 data_weight=0 udp)127.0.0.14:42999<>127.0.0.17:9496 DEAD since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD (cause: remote) since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=0 data_weight=9 udp)127.0.0.14:42999<>127.0.0.16:9496 DEAD (cause: remote) since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=0 data_weight=0 udp)127.0.0.14:42999<>127.0.0.17:9496 DEAD (cause: remote) since 0d 0h 0m 0s UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0 IP-SNS signalling weight: 1 data weight: 1 3 NS-VC: - RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD since 0d 0h 0m 0s - RECOVERING PERSIST sig_weight=0 data_weight=9 udp)127.0.0.14:42999<>127.0.0.16:9496 DEAD since 0d 0h 0m 0s - RECOVERING PERSIST sig_weight=0 data_weight=0 udp)127.0.0.14:42999<>127.0.0.17:9496 DEAD since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=1 data_weight=1 udp)127.0.0.14:42999<>127.0.0.15:9496 DEAD (cause: remote) since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=0 data_weight=9 udp)127.0.0.14:42999<>127.0.0.16:9496 DEAD (cause: remote) since 0d 0h 0m 0s + RECOVERING PERSIST sig_weight=0 data_weight=0 udp)127.0.0.14:42999<>127.0.0.17:9496 DEAD (cause: remote) since 0d 0h 0m 0s OsmoNSdummy# configure terminal OsmoNSdummy(config)# ns OsmoNSdummy(config-ns)# nse 1234
View file
libosmocore_1.7.0.tar.xz/tests/gb/gprs_ns_test.c -> libosmocore_1.8.0.tar.xz/tests/gb/gprs_ns_test.c
Changed
@@ -420,7 +420,7 @@ return rc; } -static void test_nsvc() +static void test_nsvc(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer1 = {{0},}; @@ -459,7 +459,7 @@ alarm(0); } -static void test_ignored_messages() +static void test_ignored_messages(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer1 = {{0},}; @@ -486,7 +486,7 @@ nsi = NULL; } -static void test_bss_port_changes() +static void test_bss_port_changes(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer4 = {{0},}; @@ -540,7 +540,7 @@ nsi = NULL; } -static void test_bss_reset_ack() +static void test_bss_reset_ack(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer4 = {{0},}; @@ -687,7 +687,7 @@ } -static void test_sgsn_reset() +static void test_sgsn_reset(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0}; @@ -765,7 +765,7 @@ nsi = NULL; } -static void test_sgsn_reset_invalid_state() +static void test_sgsn_reset_invalid_state(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0}; @@ -833,7 +833,7 @@ nsi = NULL; } -static void test_sgsn_output() +static void test_sgsn_output(void) { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0};
View file
libosmocore_1.7.0.tar.xz/tests/gsm0408/gsm0408_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm0408/gsm0408_test.c
Changed
@@ -65,6 +65,18 @@ }, }; +static const uint8_t speech_no3a_lv = { 0x01, 0xa0 }; + +static const struct gsm_mncc_bearer_cap bcap_speech_no3a = { + .transfer = GSM48_BCAP_ITCAP_SPEECH, + .mode = GSM48_BCAP_TMOD_CIRCUIT, + .coding = GSM48_BCAP_CODING_GSM_STD, + .radio = GSM48_BCAP_RRQ_FR_ONLY, + .speech_ver = { + 0, -1, + }, +}; + struct bcap_test { const uint8_t *lv; @@ -75,15 +87,17 @@ static const struct bcap_test bcap_tests = { { csd_9600_v110_lv, &bcap_csd_9600_v110, "CSD 9600/V.110/transparent" }, { speech_all_lv, &bcap_speech_all, "Speech, all codecs" }, + { speech_no3a_lv, &bcap_speech_no3a, "Speech, without octet 3a" }, }; -static int test_bearer_cap() +static int test_bearer_cap(void) { struct gsm_mncc_bearer_cap bc; int i, rc; for (i = 0; i < ARRAY_SIZE(bcap_tests); i++) { struct msgb *msg = msgb_alloc(100, "test"); + bool pass = false; int lv_len; memset(&bc, 0, sizeof(bc)); @@ -93,7 +107,7 @@ if (rc < 0) { fprintf(stderr, "Error decoding %s\n", bcap_testsi.name); - return rc; + goto verdict; } if (memcmp(&bc, bcap_testsi.bc, sizeof(bc))) { fprintf(stderr, "Incorrect decoded result of %s:\n", @@ -102,7 +116,7 @@ osmo_hexdump((uint8_t *) bcap_testsi.bc, sizeof(bc))); fprintf(stderr, " is: %s\n", osmo_hexdump((uint8_t *) &bc, sizeof(bc))); - return -1; + goto verdict; } /* also test re-encode? */ @@ -110,7 +124,7 @@ if (rc < 0) { fprintf(stderr, "Error encoding %s\n", bcap_testsi.name); - return rc; + goto verdict; } lv_len = bcap_testsi.lv0+1; if (memcmp(msg->data, bcap_testsi.lv, lv_len)) { @@ -120,10 +134,14 @@ osmo_hexdump(bcap_testsi.lv, lv_len)); fprintf(stderr, " is: %s\n", osmo_hexdump(msg->data, msg->len)); - return -1; + goto verdict; } - printf("Test `%s' passed\n", bcap_testsi.name); + /* all checks passed */ + pass = true; + +verdict: + printf("Test `%s' %sed\n", bcap_testsi.name, pass ? "pass" : "fail"); msgb_free(msg); } @@ -1136,7 +1154,7 @@ }, }; -void test_struct_mobile_identity() +void test_struct_mobile_identity(void) { struct mobile_identity_tc *t; printf("%s()\n", __func__); @@ -1150,7 +1168,7 @@ rc = osmo_mobile_identity_decode_from_l3(&mi, msg, false); msgb_free(msg); - printf("%s: rc = %d", t->label, rc); + printf("%s: %s", t->label, rc ? "rc != 0" : "rc == 0"); if (!rc) { printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &mi)); } @@ -1159,7 +1177,7 @@ && ((rc != 0) || !osmo_mobile_identity_cmp(&mi, &t->expect_mi))) { printf(" ok"); } else { - printf(" ERROR: Expected rc = %d", t->expect_rc); + printf(" ERROR: Got rc = %d, expected rc = %d", rc, t->expect_rc); if (!t->expect_rc) printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &t->expect_mi)); } @@ -1298,7 +1316,7 @@ }, }; -static void test_bcd_number_encode_decode() +static void test_bcd_number_encode_decode(void) { const struct bcd_number_test *test; uint8_t buf_enc0xff = { 0xff }; @@ -1532,7 +1550,7 @@ } } -static void test_range_encoding() +static void test_range_encoding(void) { int *arfcns; int arfcns_num = 0; @@ -1593,7 +1611,7 @@ __FILE__, __LINE__, (int) res, # cmp, (int) wanted); \ } -static void test_arfcn_filter() +static void test_arfcn_filter(void) { int arfcns50, i, res, f0_included; for (i = 0; i < ARRAY_SIZE(arfcns); ++i) @@ -1626,7 +1644,7 @@ VERIFY(arfcnsi, ==, ((i + 1) * 2) - 1); } -static void test_print_encoding() +static void test_print_encoding(void) { int rc; int w17; @@ -1651,7 +1669,7 @@ printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); } -static void test_si_range_helpers() +static void test_si_range_helpers(void) { int ws(sizeof(freqs1)/sizeof(freqs10)); int i, f0 = 0xFFFFFF; @@ -1690,7 +1708,7 @@ VERIFY(f0, ==, 1); } -static void test_power_ctrl() +static void test_power_ctrl(void) { int8_t rc8; int rc; @@ -1732,7 +1750,7 @@ VERIFY(rc, <, 0); } -static void test_rach_tx_integer_raw2val() +static void test_rach_tx_integer_raw2val(void) { unsigned int raw; for (raw = 0; raw <= 0x0f; raw++) { @@ -1742,6 +1760,27 @@ } } +static void test_gsm_gsmtime2fn(void) +{ + struct gsm_time gsm_time; + uint32_t fn; + uint32_t fn_recovered; + + for (fn = 0; fn < 42432; fn++) { + gsm_time.t1 = (fn / 1326) % 32; + gsm_time.t2 = fn % 26; + gsm_time.t3 = fn % 51; + + fn_recovered = gsm_gsmtime2fn(&gsm_time); + + if (fn_recovered != fn) { + printf(" Wrong frame number computed! t1=%d, t2=%d, t3=%d ==> fn=%d, expected fn=%d\n", + gsm_time.t1, gsm_time.t2, gsm_time.t3, fn_recovered, fn); + OSMO_ASSERT(false); + } + } +} + int main(int argc, char **argv) { test_bearer_cap(); @@ -1761,6 +1800,7 @@ test_range_encoding(); test_power_ctrl(); test_rach_tx_integer_raw2val(); + test_gsm_gsmtime2fn(); return EXIT_SUCCESS; }
View file
libosmocore_1.8.0.tar.xz/tests/gsm0408/gsm0408_test.err
Added
@@ -0,0 +1,3 @@ +Incorrect encoded result of Speech, without octet 3a: + should: 01 a0 + is: 02 20 80
View file
libosmocore_1.7.0.tar.xz/tests/gsm0408/gsm0408_test.ok -> libosmocore_1.8.0.tar.xz/tests/gsm0408/gsm0408_test.ok
Changed
@@ -1,5 +1,6 @@ Test `CSD 9600/V.110/transparent' passed Test `Speech, all codecs' passed +Test `Speech, without octet 3a' failed Simple TMSI encoding test....passed Simple IMSI encoding test....passed: 10 17 08 99 10 07 00 00 00 64 02 @@ -140,55 +141,55 @@ returned empty string test_struct_mobile_identity() -LU with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok -LU with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok -LU with invalid MI type: rc = -22 ok -LU with truncated IMSI MI: rc = -74 ok -LU with too short IMSI MI (12345): rc = -74 ok -LU with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok -LU with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok -LU with just too long IMSI MI 1234567890123456: rc = -74 ok -LU with truncated TMSI MI: rc = -74 ok -LU with odd length TMSI: rc = -74 ok -LU with too long TMSI MI: rc = -74 ok -LU with too short TMSI: rc = -74 ok -CM Service Request with IMSI 123456: rc = 0, mi = IMSI-123456 ok -CM Service Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 ok -CM Service Request with shorter CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok -CM Service Request with longer CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok -CM Service Request with shorter CM2, with TMSI 0x00000000: rc = 0, mi = TMSI-0x00000000 ok -CM Service Request with invalid MI type: rc = -22 ok -CM Service Request with truncated IMSI MI: rc = -74 ok -CM Service Request with truncated TMSI MI: rc = -74 ok -CM Service Request with odd length TMSI: rc = -74 ok -CM Service Request with too long TMSI MI: rc = -74 ok -CM Service Request with too short TMSI: rc = -74 ok -CM Service Reestablish Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 ok -Paging Response with IMSI 1234567: rc = 0, mi = IMSI-1234567 ok -Paging Response with TMSI 0xb48883de: rc = 0, mi = TMSI-0xB48883DE ok -Paging Response with TMSI, with unused nibble not 0xf: rc = -74 ok -Paging Response with too short IMEI (1234567): rc = -74 ok -Paging Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok -Paging Response with IMEI 12345678901234 (no Luhn checksum): rc = 0, mi = IMEI-12345678901234 ok -Paging Response with IMEISV 1234567890123456: rc = 0, mi = IMEI-SV-1234567890123456 ok -Paging Response with too short IMEISV 123456789012345: rc = -74 ok -Paging Response with too long IMEISV 12345678901234567: rc = -74 ok -Paging Response with IMSI 123456789012345 and flipped ODD bit: rc = -74 ok -IMSI-Detach with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok -IMSI-Detach with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok -IMSI-Detach with invalid MI type: rc = -22 ok -IMSI-Detach with truncated IMSI MI: rc = -74 ok -IMSI-Detach with too short IMSI MI (12345): rc = -74 ok -IMSI-Detach with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok -IMSI-Detach with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok -IMSI-Detach with just too long IMSI MI 1234567890123456: rc = -74 ok -IMSI-Detach with truncated TMSI MI: rc = -74 ok -IMSI-Detach with odd length TMSI: rc = -74 ok -IMSI-Detach with too long TMSI MI: rc = -74 ok -IMSI-Detach with too short TMSI: rc = -74 ok -Identity Response with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok -Identity Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok -Identity Response with IMEISV 9876543210987654: rc = 0, mi = IMEI-SV-9876543210987654 ok +LU with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok +LU with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok +LU with invalid MI type: rc != 0 ok +LU with truncated IMSI MI: rc != 0 ok +LU with too short IMSI MI (12345): rc != 0 ok +LU with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok +LU with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok +LU with just too long IMSI MI 1234567890123456: rc != 0 ok +LU with truncated TMSI MI: rc != 0 ok +LU with odd length TMSI: rc != 0 ok +LU with too long TMSI MI: rc != 0 ok +LU with too short TMSI: rc != 0 ok +CM Service Request with IMSI 123456: rc == 0, mi = IMSI-123456 ok +CM Service Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok +CM Service Request with shorter CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok +CM Service Request with longer CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok +CM Service Request with shorter CM2, with TMSI 0x00000000: rc == 0, mi = TMSI-0x00000000 ok +CM Service Request with invalid MI type: rc != 0 ok +CM Service Request with truncated IMSI MI: rc != 0 ok +CM Service Request with truncated TMSI MI: rc != 0 ok +CM Service Request with odd length TMSI: rc != 0 ok +CM Service Request with too long TMSI MI: rc != 0 ok +CM Service Request with too short TMSI: rc != 0 ok +CM Service Reestablish Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok +Paging Response with IMSI 1234567: rc == 0, mi = IMSI-1234567 ok +Paging Response with TMSI 0xb48883de: rc == 0, mi = TMSI-0xB48883DE ok +Paging Response with TMSI, with unused nibble not 0xf: rc != 0 ok +Paging Response with too short IMEI (1234567): rc != 0 ok +Paging Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok +Paging Response with IMEI 12345678901234 (no Luhn checksum): rc == 0, mi = IMEI-12345678901234 ok +Paging Response with IMEISV 1234567890123456: rc == 0, mi = IMEI-SV-1234567890123456 ok +Paging Response with too short IMEISV 123456789012345: rc != 0 ok +Paging Response with too long IMEISV 12345678901234567: rc != 0 ok +Paging Response with IMSI 123456789012345 and flipped ODD bit: rc != 0 ok +IMSI-Detach with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok +IMSI-Detach with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok +IMSI-Detach with invalid MI type: rc != 0 ok +IMSI-Detach with truncated IMSI MI: rc != 0 ok +IMSI-Detach with too short IMSI MI (12345): rc != 0 ok +IMSI-Detach with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok +IMSI-Detach with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok +IMSI-Detach with just too long IMSI MI 1234567890123456: rc != 0 ok +IMSI-Detach with truncated TMSI MI: rc != 0 ok +IMSI-Detach with odd length TMSI: rc != 0 ok +IMSI-Detach with too long TMSI MI: rc != 0 ok +IMSI-Detach with too short TMSI: rc != 0 ok +Identity Response with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok +Identity Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok +Identity Response with IMEISV 9876543210987654: rc == 0, mi = IMEI-SV-9876543210987654 ok BSD number encoding / decoding test - Running test: regular 9-digit MSISDN
View file
libosmocore_1.7.0.tar.xz/tests/gsm0502/gsm0502_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm0502/gsm0502_test.c
Changed
@@ -86,7 +86,7 @@ 502782, 502825, 502869, 502903, 502955, 502999 }; -static void test_gsm0502_fn_remap() +static void test_gsm0502_fn_remap(void) { unsigned int i; uint32_t fn_begin;
View file
libosmocore_1.7.0.tar.xz/tests/gsm0808/gsm0808_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm0808/gsm0808_test.c
Changed
@@ -111,7 +111,7 @@ msgb_free(in_msg); } -static void test_create_layer3_aoip() +static void test_create_layer3_aoip(void) { static const uint8_t res = { 0x00, 0x17, 0x57, 0x05, 0x08, 0x00, 0x77, 0x62, @@ -148,7 +148,7 @@ msgb_free(in_msg); } -static void test_create_reset() +static void test_create_reset(void) { static const uint8_t res = { 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 }; struct msgb *msg; @@ -159,7 +159,7 @@ msgb_free(msg); } -static void test_create_reset_ack() +static void test_create_reset_ack(void) { static const uint8_t res = { 0x00, 0x01, 0x31 }; struct msgb *msg; @@ -171,7 +171,7 @@ } -static void test_create_clear_command() +static void test_create_clear_command(void) { static const uint8_t res = { 0x20, 0x04, 0x01, 0x23 }; struct msgb *msg; @@ -182,7 +182,7 @@ msgb_free(msg); } -static void test_create_clear_command2() +static void test_create_clear_command2(void) { static const uint8_t res = { 0x00, 0x04, 0x20, 0x04, 0x01, 0x23 }; struct msgb *msg; @@ -193,7 +193,7 @@ msgb_free(msg); } -static void test_create_clear_command2_csfb() +static void test_create_clear_command2_csfb(void) { static const uint8_t res = { 0x00, 0x05, 0x20, 0x04, 0x01, 0x23, 0x8F }; struct msgb *msg; @@ -204,7 +204,7 @@ msgb_free(msg); } -static void test_create_clear_complete() +static void test_create_clear_complete(void) { static const uint8_t res = { 0x00, 0x01, 0x21 }; struct msgb *msg; @@ -215,7 +215,7 @@ msgb_free(msg); } -static void test_create_cipher() +static void test_create_cipher(void) { static const uint8_t res = { 0x00, 0x0c, 0x53, 0x0a, 0x09, 0x03, 0xaa, @@ -255,7 +255,7 @@ msgb_free(msg); } -static void test_create_cipher_complete() +static void test_create_cipher_complete(void) { static const uint8_t res1 = { 0x00, 0x08, 0x55, 0x20, 0x03, 0x23, 0x42, 0x21, 0x2c, 0x04 }; @@ -312,7 +312,7 @@ rc, exp, OSMO_BIT_PRINT(exp), msgb_hexdump(msg)); } -static void test_create_cipher_reject() +static void test_create_cipher_reject(void) { static const uint8_t res = { 0x00, 0x04, 0x59, 0x04, 0x01, 0x23 }; enum gsm0808_cause cause = GSM0808_CAUSE_CCCH_OVERLOAD; @@ -327,7 +327,7 @@ msgb_free(msg); } -static void test_create_cipher_reject_ext() +static void test_create_cipher_reject_ext(void) { static const uint8_t res = { 0x00, 0x05, 0x59, 0x04, 0x02, 0xd0, 0xFA }; uint8_t cause = 0xFA; @@ -342,7 +342,7 @@ msgb_free(msg); } -static void test_create_cm_u() +static void test_create_cm_u(void) { static const uint8_t res = { 0x00, 0x07, 0x54, 0x12, 0x01, 0x23, 0x13, 0x01, 0x42 }; @@ -364,7 +364,7 @@ msgb_free(msg); } -static void test_create_sapi_reject() +static void test_create_sapi_reject(void) { static const uint8_t res = { 0x00, 0x06, 0x25, 0x18, 0x03, 0x04, 0x01, 0x25 }; struct msgb *msg; @@ -375,7 +375,7 @@ msgb_free(msg); } -static void test_dec_confusion() +static void test_dec_confusion(void) { static const uint8_t hex = { 0x26, 0x04, 0x01, 0x52, 0x1f, 0x07, 0x00, 0xff, 0x00, 0x03, 0x25, 0x03, 0x25 }; @@ -425,7 +425,7 @@ } /* Test Perform Location Report SYS#5891 */ -static void test_dec_perform_location_report_sys5891() +static void test_dec_perform_location_report_sys5891(void) { /* Message Type Perform Location Request Location Type @@ -477,7 +477,7 @@ OSMO_ASSERT(rc == 5); } -static void test_create_ass() +static void test_create_ass(void) { static const uint8_t res1 = { 0x00, 0x0a, 0x01, 0x0b, 0x04, 0x01, 0x0b, 0xa1, 0x25, 0x01, 0x00, @@ -527,7 +527,7 @@ msgb_free(msg); } -static void test_create_ass2() +static void test_create_ass2(void) { static const uint8_t res = { BSSAP_MSG_BSS_MANAGEMENT, @@ -604,7 +604,7 @@ msgb_free(msg); } -static void test_create_ass_compl() +static void test_create_ass_compl(void) { static const uint8_t res1 = { 0x00, 0x09, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c, @@ -623,7 +623,7 @@ msgb_free(msg); } -static void test_create_ass_compl_aoip() +static void test_create_ass_compl_aoip(void) { struct sockaddr_storage ss; struct sockaddr_in sin; @@ -660,7 +660,7 @@ msgb_free(msg); } -static void test_create_ass_fail() +static void test_create_ass_fail(void) { static const uint8_t res1 = { 0x00, 0x04, 0x03, 0x04, 0x01, 0x23 }; static const uint8_t res2 = { @@ -678,7 +678,7 @@ msgb_free(msg); } -static void test_create_ass_fail_aoip() +static void test_create_ass_fail_aoip(void) { static const uint8_t res1 = { 0x00, 0x0d, 0x03, 0x04, 0x01, 0x23, GSM0808_IE_SPEECH_CODEC_LIST, @@ -704,7 +704,7 @@ msgb_free(msg); } -static void test_create_clear_rqst() +static void test_create_clear_rqst(void) { static const uint8_t res = { 0x00, 0x04, 0x22, 0x04, 0x01, 0x23 }; struct msgb *msg; @@ -715,7 +715,7 @@ msgb_free(msg); } -static void test_create_paging() +static void test_create_paging(void) { static const uint8_t res = { 0x00, 0x10, 0x52, 0x08, 0x08, 0x09, 0x10, 0x10, 0x00, 0x00, 0x00, @@ -755,7 +755,7 @@ msgb_free(msg); } -static void test_create_dtap() +static void test_create_dtap(void) { static const uint8_t res = { 0x01, 0x03, 0x02, 0x23, 0x42 }; struct msgb *msg, *l3; @@ -772,7 +772,7 @@ msgb_free(l3); } -static void test_prepend_dtap() +static void test_prepend_dtap(void) { static const uint8_t res = { 0x01, 0x03, 0x02, 0x23, 0x42 }; struct msgb *in_msg; @@ -789,7 +789,7 @@ msgb_free(in_msg); } -static void test_enc_dec_lcls() +static void test_enc_dec_lcls(void) { static const uint8_t res = { GSM0808_IE_GLOBAL_CALL_REF, @@ -867,7 +867,7 @@ msgb_free(msg); } -static void test_enc_dec_aoip_trasp_addr_v4() +static void test_enc_dec_aoip_trasp_addr_v4(void) { struct sockaddr_storage enc_addr; struct sockaddr_storage dec_addr; @@ -895,7 +895,7 @@ msgb_free(msg); } -static void test_enc_dec_aoip_trasp_addr_v6() +static void test_enc_dec_aoip_trasp_addr_v6(void) { struct sockaddr_storage enc_addr; struct sockaddr_storage dec_addr; @@ -924,7 +924,29 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_speech_codec() +static void test_enc_aoip_trasp_addr_msg_too_small(void) +{ + struct msgb *msg; + struct sockaddr_storage enc_addr; + struct sockaddr_in enc_addr_in; + uint8_t rc_enc; + + memset(&enc_addr_in, 0, sizeof(enc_addr_in)); + enc_addr_in.sin_family = AF_INET; + enc_addr_in.sin_port = htons(1234); + inet_aton("255.0.255.255", &enc_addr_in.sin_addr); + + memset(&enc_addr, 0, sizeof(enc_addr)); + memcpy(&enc_addr, &enc_addr_in, sizeof(enc_addr_in)); + + msg = msgb_alloc(7, "output buffer"); + rc_enc = gsm0808_enc_aoip_trasp_addr(msg, &enc_addr); + OSMO_ASSERT(rc_enc == 0); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_speech_codec(void) { struct gsm0808_speech_codec enc_sc = { .pi = true, @@ -937,7 +959,7 @@ int rc_dec; msg = msgb_alloc(1024, "output buffer"); - rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc); + rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc); OSMO_ASSERT(rc_enc == 3); rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2); @@ -949,7 +971,7 @@ } -static void test_gsm0808_enc_dec_speech_codec_with_cfg() +static void test_gsm0808_enc_dec_speech_codec_with_cfg(void) { struct gsm0808_speech_codec enc_sc = { .pi = true, @@ -963,7 +985,7 @@ int rc_dec; msg = msgb_alloc(1024, "output buffer"); - rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc); + rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc); OSMO_ASSERT(rc_enc == 5); rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2); @@ -974,7 +996,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg() +static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg(void) { struct gsm0808_speech_codec enc_sc = { .pi = true, @@ -988,7 +1010,7 @@ int rc_dec; msg = msgb_alloc(1024, "output buffer"); - rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc); + rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc); OSMO_ASSERT(rc_enc == 5); rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2); @@ -999,7 +1021,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_speech_codec_list() +static void test_gsm0808_enc_dec_speech_codec_list(void) { struct gsm0808_speech_codec_list enc_scl = { .codec = { @@ -1031,7 +1053,7 @@ int rc_dec; msg = msgb_alloc(1024, "output buffer"); - rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl); + rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl); OSMO_ASSERT(rc_enc == 9); rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2); @@ -1042,7 +1064,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_empty_speech_codec_list() +static void test_gsm0808_enc_dec_empty_speech_codec_list(void) { struct gsm0808_speech_codec_list enc_scl = { .len = 0, @@ -1053,7 +1075,7 @@ int rc_dec; msg = msgb_alloc(1024, "output buffer"); - rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl); + rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl); OSMO_ASSERT(rc_enc == 2); rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2); @@ -1064,7 +1086,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_channel_type() +static void test_gsm0808_enc_dec_channel_type(void) { struct gsm0808_channel_type enc_ct = { .ch_indctr = GSM0808_CHAN_SPEECH, @@ -1095,7 +1117,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_encrypt_info() +static void test_gsm0808_enc_dec_encrypt_info(void) { struct gsm0808_encrypt_info enc_ei = { .perm_algo = { GSM0808_ALG_ID_A5_0, GSM0808_ALG_ID_A5_1 }, @@ -1125,7 +1147,7 @@ msgb_free(msg); } -static void test_gsm0808_dec_cell_id_list_srvcc() +static void test_gsm0808_dec_cell_id_list_srvcc(void) { /* taken from a pcap file of a real-world 3rd party MSC (SYS#5838) */ const uint8_t enc_cil = { 0x0b, 0x2, 0xf2, 0x10, 0x4e, 0x20, 0x15, 0xbe}; @@ -1138,7 +1160,7 @@ OSMO_ASSERT(dec_cil.id_list_len = 1); } -static void test_gsm0808_enc_dec_cell_id_list_lac() +static void test_gsm0808_enc_dec_cell_id_list_lac(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1165,7 +1187,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_single_lac() +static void test_gsm0808_enc_dec_cell_id_list_single_lac(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1194,7 +1216,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_multi_lac() +static void test_gsm0808_enc_dec_cell_id_list_multi_lac(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1230,7 +1252,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_bss() +static void test_gsm0808_enc_dec_cell_id_list_bss(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1253,7 +1275,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac() +static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1309,7 +1331,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_multi_ci() +static void test_gsm0808_enc_dec_cell_id_list_multi_ci(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1343,7 +1365,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci() +static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1384,7 +1406,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_list_multi_global() +static void test_gsm0808_enc_dec_cell_id_list_multi_global(void) { struct gsm0808_cell_id_list2 enc_cil; struct gsm0808_cell_id_list2 dec_cil; @@ -1457,7 +1479,7 @@ printf(" cell_id_list == %s\n", gsm0808_cell_id_list_name(cil)); } -void test_cell_id_list_add() { +void test_cell_id_list_add(void) { size_t zu; const struct gsm0808_cell_id_list2 cgi1 = { @@ -1639,7 +1661,7 @@ printf("------- %s done\n", __func__); } -static void test_gsm0808_enc_dec_cell_id_lac() +static void test_gsm0808_enc_dec_cell_id_lac(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_LAC, @@ -1665,7 +1687,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_bss() +static void test_gsm0808_enc_dec_cell_id_bss(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_BSS, @@ -1687,7 +1709,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_no_cell() +static void test_gsm0808_enc_dec_cell_id_no_cell(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_NO_CELL, @@ -1709,7 +1731,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_lai_and_lac() +static void test_gsm0808_enc_dec_cell_id_lai_and_lac(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_LAI_AND_LAC, @@ -1740,7 +1762,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_ci() +static void test_gsm0808_enc_dec_cell_id_ci(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_CI, @@ -1763,7 +1785,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_lac_and_ci() +static void test_gsm0808_enc_dec_cell_id_lac_and_ci(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_LAC_AND_CI, @@ -1790,7 +1812,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_global() +static void test_gsm0808_enc_dec_cell_id_global(void) { struct gsm0808_cell_id enc_ci = { .id_discr = CELL_IDENT_WHOLE_GLOBAL, @@ -1822,7 +1844,7 @@ msgb_free(msg); } -static void test_gsm0808_enc_dec_cell_id_global_ps() +static void test_gsm0808_enc_dec_cell_id_global_ps(void) { struct gsm0808_cell_id enc_cgi = { .id_discr = CELL_IDENT_WHOLE_GLOBAL, @@ -2108,7 +2130,7 @@ printf("\n"); } -void test_gsm48_mr_cfg_from_gsm0808_sc_cfg() +void test_gsm48_mr_cfg_from_gsm0808_sc_cfg(void) { printf("Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():\n"); @@ -2293,7 +2315,7 @@ { .id = cgi_23_042_23_5, .match_id = cgi_23_99_23_5, .expect_match = false, .expect_exact_match = false }, }; -static void test_cell_id_matching() +static void test_cell_id_matching(void) { int i; bool ok = true; @@ -2456,7 +2478,7 @@ { .id_discr = 423 }, }; -static void test_gsm0808_cell_id_to_from_cgi() +static void test_gsm0808_cell_id_to_from_cgi(void) { int i; int j; @@ -2540,6 +2562,7 @@ test_enc_dec_aoip_trasp_addr_v4(); test_enc_dec_aoip_trasp_addr_v6(); + test_enc_aoip_trasp_addr_msg_too_small(); test_gsm0808_enc_dec_speech_codec(); test_gsm0808_enc_dec_speech_codec_ext_with_cfg(); test_gsm0808_enc_dec_speech_codec_with_cfg();
View file
libosmocore_1.7.0.tar.xz/tests/gsm23003/gsm23003_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm23003/gsm23003_test.c
Changed
@@ -54,7 +54,7 @@ { NULL, false }, }; -bool test_valid_imsi() +bool test_valid_imsi(void) { int i; bool pass = true; @@ -97,7 +97,7 @@ { NULL, false }, }; -bool test_valid_msisdn() +bool test_valid_msisdn(void) { int i; bool pass = true; @@ -138,7 +138,7 @@ { NULL, false, false }, }; -bool test_valid_imei() +bool test_valid_imei(void) { int i; bool pass = true; @@ -185,7 +185,7 @@ { "023 ", { -EINVAL, 0, false } }, }; -static bool test_mnc_from_str() +static bool test_mnc_from_str(void) { int i; bool pass = true; @@ -209,7 +209,7 @@ return pass; } -static bool test_gummei_name() +static bool test_gummei_name(void) { static const struct osmo_gummei gummei = { .plmn = { .mcc = 901, .mnc = 70 }, @@ -226,7 +226,7 @@ return pass; } -static bool test_domain_gen() +static bool test_domain_gen(void) { static const struct osmo_gummei gummei = { .plmn = { .mcc = 901, .mnc = 70 }, @@ -252,7 +252,7 @@ } -static bool test_domain_parse() +static bool test_domain_parse(void) { static const char *mme_dom_valid = "mmec01.mmegiA001.mme.epc.mnc070.mcc901.3gppnetwork.org"; static const char *home_dom_valid = "epc.mnc070.mcc901.3gppnetwork.org";
View file
libosmocore_1.7.0.tar.xz/tests/gsm23236/gsm23236_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm23236/gsm23236_test.c
Changed
@@ -138,7 +138,7 @@ }, }; -void test_nri_v_get_set() +void test_nri_v_get_set(void) { struct nri_v_get_set_test *t; @@ -222,7 +222,7 @@ { .nri = INT16_MAX, .nri_bitlen = 0, .expect_rc = 1 }, }; -void test_nri_validate() +void test_nri_validate(void) { struct nri_validate_tc *t; printf("\n%s()\n", __func__); @@ -327,7 +327,7 @@ }; -void test_nri_range_validate() +void test_nri_range_validate(void) { struct nri_range_validate_tc *t; printf("\n%s()\n", __func__); @@ -357,7 +357,7 @@ printf("};\n"); } -void test_nri_list() +void test_nri_list(void) { struct osmo_nri_ranges *nri_ranges = osmo_nri_ranges_alloc(ctx); printf("\n%s()\n", __func__); @@ -545,7 +545,7 @@ DEL(100, 1); } -void test_nri_limit_by_ranges() +void test_nri_limit_by_ranges(void) { const uint8_t nri_bitlen = 8; const int16_t expect_nri_vals = { 10, 20, 21, 30, 31, 32 }; @@ -594,7 +594,7 @@ } } -int main() +int main(int argc, char **argv) { ctx = talloc_named_const(NULL, 0, "nri_test");
View file
libosmocore_1.7.0.tar.xz/tests/gsm29205/gsm29205_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm29205/gsm29205_test.c
Changed
@@ -28,7 +28,7 @@ #include <string.h> #include <errno.h> -static void test_gcr() +static void test_gcr(void) { static const uint8_t res = { 0x03, /* .net_len */
View file
libosmocore_1.7.0.tar.xz/tests/gsm48/rest_octets_test.c -> libosmocore_1.8.0.tar.xz/tests/gsm48/rest_octets_test.c
Changed
@@ -77,7 +77,7 @@ }, }; -static void test_si13() +static void test_si13(void) { int i, rc; uint8_t dataGSM_MACBLOCK_LEN;
View file
libosmocore_1.7.0.tar.xz/tests/i460_mux/i460_mux_test.c -> libosmocore_1.8.0.tar.xz/tests/i460_mux/i460_mux_test.c
Changed
@@ -1,7 +1,7 @@ #include <osmocom/core/utils.h> -#include <osmocom/gsm/i460_mux.h> +#include <osmocom/isdn/i460_mux.h> static void bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits, unsigned int num_bits)
View file
libosmocore_1.7.0.tar.xz/tests/lapd/lapd_test.c -> libosmocore_1.8.0.tar.xz/tests/lapd/lapd_test.c
Changed
@@ -376,7 +376,7 @@ return 0; } -static void test_lapdm_polling() +static void test_lapdm_polling(void) { printf("I do some very simple LAPDm test.\n"); @@ -470,7 +470,7 @@ lapdm_channel_exit(&ms_to_bts_channel); } -static void test_lapdm_contention_resolution() +static void test_lapdm_contention_resolution(void) { printf("I test contention resultion by having two mobiles collide and " "first mobile repeating SABM.\n"); @@ -523,7 +523,7 @@ lapdm_channel_exit(&bts_to_ms_channel); } -static void test_lapdm_early_release() +static void test_lapdm_early_release(void) { printf("I test RF channel release of an unestablished channel.\n"); @@ -603,7 +603,7 @@ msgb_free(msg); } -static void test_lapdm_establishment() +static void test_lapdm_establishment(void) { printf("I test RF channel establishment.\n"); printf("Testing SAPI3/SDCCH\n"); @@ -677,7 +677,7 @@ printf("\n"); } -static void test_lapdm_desync() +static void test_lapdm_desync(void) { printf("I test if desync problems exist in LAPDm\n");
View file
libosmocore_1.7.0.tar.xz/tests/logging/logging_vty_test.c -> libosmocore_1.8.0.tar.xz/tests/logging/logging_vty_test.c
Changed
@@ -78,7 +78,7 @@ return CMD_SUCCESS; } -static void vty_commands_init() +static void vty_commands_init(void) { install_element_ve(&log_sweep_cmd); } @@ -121,7 +121,7 @@ .num_cat = ARRAY_SIZE(default_categories), }; -static void print_help() +static void print_help(void) { printf( "options:\n" " -h --help this text\n" @@ -261,7 +261,7 @@ } } - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/tests/logging/logging_vty_test.vty -> libosmocore_1.8.0.tar.xz/tests/logging/logging_vty_test.vty
Changed
@@ -54,7 +54,7 @@ logging print level (0|1) logging print file (0|1|basename) last logging set-log-mask MASK - logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp) (debug|info|notice|error|fatal) + logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1) (debug|info|notice|error|fatal) logging level set-all (debug|info|notice|error|fatal) logging level force-all (debug|info|notice|error|fatal) no logging level force-all @@ -472,7 +472,7 @@ logging_vty_test# list ... - logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp) (debug|info|notice|error|fatal) .LOGMESSAGE + logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1) (debug|info|notice|error|fatal) .LOGMESSAGE ... logging_vty_test# logp? @@ -509,6 +509,7 @@ lnssignal GPRS NS layer signal PDU liuup Iu UP layer lpfcp libosmo-pfcp Packet Forwarding Control Protocol + lcsn1 libosmo-csn1 Concrete Syntax Notation 1 codec logging_vty_test# logp lglobal ? debug Log debug messages and higher levels
View file
libosmocore_1.7.0.tar.xz/tests/msgb/msgb_test.c -> libosmocore_1.8.0.tar.xz/tests/msgb/msgb_test.c
Changed
@@ -61,7 +61,7 @@ return *exception == 0; } -static void test_msgb_api() +static void test_msgb_api(void) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "data"); unsigned char *cptr = NULL; @@ -117,7 +117,7 @@ msgb_free(msg); } -static void test_msgb_api_errors() +static void test_msgb_api_errors(void) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "data"); volatile int e = 0; @@ -138,7 +138,7 @@ osmo_set_panic_handler(NULL); } -static void test_msgb_copy() +static void test_msgb_copy(void) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "data"); struct msgb *msg2; @@ -161,19 +161,31 @@ OSMO_ASSERT(msgb_l1len(msg) == msgb_l1len(msg2)); OSMO_ASSERT(msgb_l2len(msg) == msgb_l2len(msg2)); OSMO_ASSERT(msgb_l3len(msg) == msgb_l3len(msg2)); - OSMO_ASSERT(msg->tail - msg->l4h == msg2->tail - msg2->l4h); + OSMO_ASSERT(msgb_l4len(msg) == msgb_l4len(msg2)); - for (i = 0; i < msgb_length(msg2); i++) - OSMO_ASSERT(msg2->datai == (uint8_t)i); + if (!msgb_eq_data_print(msg2, msg->data, msgb_length(msg))) + printf("copy test failed!\n"); + + if (!msgb_eq_l1_data_print(msg2, msgb_l1(msg), msgb_l1len(msg))) + printf("copy test failed at L1!\n"); + if (!msgb_eq_l2_data_print(msg2, msgb_l2(msg), msgb_l2len(msg))) + printf("copy test failed at L2!\n"); + if (!msgb_eq_l3_data_print(msg2, msgb_l3(msg), msgb_l3len(msg))) + printf("copy test failed at L3!\n"); + if (!msgb_eq_l4_data_print(msg2, msgb_l4(msg), msgb_l4len(msg))) + printf("copy test failed at L4!\n"); printf("Src: %s\n", msgb_hexdump(msg)); - printf("Dst: %s\n", msgb_hexdump(msg)); + printf("Dst: %s\n", msgb_hexdump(msg2)); + + OSMO_ASSERT(msgb_test_invariant(msg)); + OSMO_ASSERT(msgb_test_invariant(msg2)); msgb_free(msg); msgb_free(msg2); } -static void test_msgb_resize_area() +static void test_msgb_resize_area(void) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "data"); int rc; @@ -273,7 +285,7 @@ osmo_set_panic_handler(NULL); } -static void test_msgb_printf() +static void test_msgb_printf(void) { struct msgb *msg; struct msgb *msg_ref;
View file
libosmocore_1.7.0.tar.xz/tests/sms/sms_test.c -> libosmocore_1.8.0.tar.xz/tests/sms/sms_test.c
Changed
@@ -215,7 +215,7 @@ }, }; -static void test_octet_return() +static void test_octet_return(void) { char out256; int oct, septets;
View file
libosmocore_1.8.0.tar.xz/tests/smscb/cbsp_test.c
Added
@@ -0,0 +1,108 @@ +/* + * (C) 2022 by sysmocom - s.f.m.c. GmbH + * 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. + * + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gsm/protocol/gsm_48_049.h> +#include <osmocom/gsm/cbsp.h> + +/* +CBSP WRITE-REPLACE FAILURE + Message Type: WRITE-REPLACE FAILURE (3) + Message Length: 44 + IE: Message Identifier: 0x0031 + Information Element Identifier: Message Identifier (14) + Message Identifier: 0x0031 + IE: New Serial Number: 0x4170 + Information Element Identifier: New Serial Number (3) + New Serial Number: 0x4170 + IE: Failure List: 2 items + Information Element Identifier: Failure List (9) + Information Element Length: 15 + Failure List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x0030: Cause Cell-identity-not-valid + Cell ID Discriminator: CGI (0) + Mobile Country Code (MCC): International Mobile, shared code (901) + Mobile Network Code (MNC): Clementvale Baltic OÜ (70) + Location Area Code (LAC): 0x0018 + Cell Identifier (CI): 0x0030 + Cause: Cell-identity-not-valid (0x03) + Failure List Item: LAC 02711, CI 0xc351: Cause LAI-or-LAC-not-valid + Cell ID Discriminator: LAC+CI (1) + Location Area Code (LAC): 0x2711 + Cell Identifier (CI): 0xc351 + Cause: LAI-or-LAC-not-valid (0x0f) + IE: Cell List (CGI): 2 items + Information Element Identifier: Cell List (4) + Information Element Length: 15 + Cell ID Discriminator: CGI (0) + Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0017, CI 0x002a + Mobile Country Code (MCC): International Mobile, shared code (901) + Mobile Network Code (MNC): Clementvale Baltic OÜ (70) + Location Area Code (LAC): 0x0017 + Cell Identifier (CI): 0x002a + Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x002a + Mobile Country Code (MCC): International Mobile, shared code (901) + Mobile Network Code (MNC): Clementvale Baltic OÜ (70) + Location Area Code (LAC): 0x0018 + Cell Identifier (CI): 0x002a + IE: Channel Indicator: basic channel + Information Element Identifier: Channel Indicator (18) + Channel Indicator: basic channel (0x00) +*/ +static const char write_repl_fail_with_failure_list = + "0300002c0e003103417009000f0009f1070018003003012711c3510f04000f0009f1070017002a09f1070018002a1200"; + +static struct msgb *msgb_from_hex(unsigned int size, const char *hex) +{ + struct msgb *msg = msgb_alloc(size, "test_cbsp"); + OSMO_ASSERT(msg); + msg->l1h = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg))); + msg->l2h = msg->l1h + sizeof(struct cbsp_header); + return msg; +} + +static void test_decode(void) +{ + struct msgb *msg; + struct osmo_cbsp_decoded *cbsp_dec; + + printf("=== %s start ===\n", __func__); + + msg = msgb_from_hex(sizeof(write_repl_fail_with_failure_list), + write_repl_fail_with_failure_list); + + cbsp_dec = osmo_cbsp_decode(NULL, msg); + OSMO_ASSERT(cbsp_dec); + + talloc_free(cbsp_dec); + msgb_free(msg); + + printf("=== %s end ===\n", __func__); +} + +int main(int argc, char **argv) +{ + test_decode(); + + return EXIT_SUCCESS; +}
View file
libosmocore_1.8.0.tar.xz/tests/smscb/cbsp_test.ok
Added
@@ -0,0 +1,2 @@ +=== test_decode start === +=== test_decode end ===
View file
libosmocore_1.7.0.tar.xz/tests/smscb/gsm0341_test.c -> libosmocore_1.8.0.tar.xz/tests/smscb/gsm0341_test.c
Changed
@@ -25,7 +25,7 @@ #include <osmocom/core/utils.h> #include <osmocom/core/msgb.h> -struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text) +struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, uint8_t msg_code, const char *text) { struct gsm341_ms_message *cbmsg; int text_len = strlen(text); @@ -34,11 +34,11 @@ uint8_t payloadtext_len; int payload_octets; - srand(time(NULL)); + //srand(time(NULL)); gsm_7bit_encode_n(payload, sizeof(payload), text, &payload_octets); //cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x0f, 1, 1, payload, payload_octets); - cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x00, 1, 1, payload, payload_octets); + cbmsg = gsm0341_build_msg(NULL, 0, msg_code, 0, msg_id, 0x00, 1, 1, payload, payload_octets); printf("%s\n", osmo_hexdump_nospc((uint8_t *)cbmsg, sizeof(*cbmsg)+payload_octets)); @@ -50,6 +50,7 @@ uint16_t msg_id = GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST; char *text = "Mahlzeit!"; char tbufGSM341_MAX_CHARS+1; + struct gsm341_ms_message *cbmsg; if (argc > 1) msg_id = atoi(argv1); @@ -63,7 +64,8 @@ sizeof(tbuf)-strlen(text)); tbufGSM341_MAX_CHARS = 0; - gen_msg_from_text(msg_id, tbuf); + cbmsg = gen_msg_from_text(msg_id, 1, tbuf); + talloc_free(cbmsg); return EXIT_SUCCESS; }
View file
libosmocore_1.8.0.tar.xz/tests/smscb/gsm0341_test.ok
Added
@@ -0,0 +1 @@ +0010111c0011cd309aad2fa7e9a146a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d100
View file
libosmocore_1.7.0.tar.xz/tests/sockaddr_str/sockaddr_str_test.c -> libosmocore_1.8.0.tar.xz/tests/sockaddr_str/sockaddr_str_test.c
Changed
@@ -90,7 +90,7 @@ printf("{ .af = %s, .ip = %s, .port = %u }\n", af_name(oip->af), osmo_quote_str(oip->ip, -1), oip->port); } -void sockaddr_str_test_conversions() +void sockaddr_str_test_conversions(void) { int i; char buf1024; @@ -235,7 +235,7 @@ } -static void test_osmo_sockaddr_str_cmp() +static void test_osmo_sockaddr_str_cmp(void) { int i; printf("\n\n%s\n", __func__);
View file
libosmocore_1.7.0.tar.xz/tests/socket/socket_sctp_test.c -> libosmocore_1.8.0.tar.xz/tests/socket/socket_sctp_test.c
Changed
@@ -31,7 +31,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/bits.h> -#include "../config.h" +#include "config.h" void *ctx = NULL;
View file
libosmocore_1.7.0.tar.xz/tests/socket/socket_test.c -> libosmocore_1.8.0.tar.xz/tests/socket/socket_test.c
Changed
@@ -32,7 +32,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/bits.h> -#include "../config.h" +#include "config.h" void *ctx = NULL; @@ -150,7 +150,7 @@ return 0; } -static int test_get_ip_and_port() +static int test_get_ip_and_port(void) { int fd, rc; char ipINET6_ADDRSTRLEN = { }; @@ -385,6 +385,63 @@ OSMO_ASSERT(!strncmp("2003:1234:5678:90ab:cdef:1234:4321:4321:23420", result, sizeof(buf))); } +static void test_osa_netmask_prefixlen(void) +{ + struct osmo_sockaddr ipv4; + struct osmo_sockaddr ipv6; + int rc; + + ipv4.u.sin = (struct sockaddr_in){ + .sin_family = AF_INET, + }; + + ipv4.u.sin.sin_addr.s_addr = inet_addr("0.0.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 0); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.0.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 8); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 16); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 24); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.255"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + OSMO_ASSERT(rc == 32); + + ipv4.u.sin.sin_addr.s_addr = inet_addr("0.255.0.0"); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4); + /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */ + OSMO_ASSERT(rc == 8); + + ipv6.u.sin6 = (struct sockaddr_in6){ + .sin6_family = AF_INET6, + }; + + inet_pton(AF_INET6, "fe::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 7); + + inet_pton(AF_INET6, "ff::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 8); + + inet_pton(AF_INET6, "ff:ff::", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + OSMO_ASSERT(rc == 16); + + inet_pton(AF_INET6, "ff:ff::ff", &ipv6.u.sin6.sin6_addr); + rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6); + /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */ + OSMO_ASSERT(rc == 24); +} + const struct log_info_cat default_categories = { }; @@ -407,6 +464,7 @@ test_get_ip_and_port(); test_sockinit_osa(); test_osa_str(); + test_osa_netmask_prefixlen(); return EXIT_SUCCESS; }
View file
libosmocore_1.7.0.tar.xz/tests/stats/stats_test.c -> libosmocore_1.8.0.tar.xz/tests/stats/stats_test.c
Changed
@@ -340,7 +340,7 @@ #define do_report(A, B) _do_report(A, B, __LINE__) -static void test_reporting() +static void test_reporting(void) { struct osmo_stats_reporter *srep1, *srep2, *srep; struct osmo_stat_item_group *statg1, *statg2;
View file
libosmocore_1.7.0.tar.xz/tests/stats/stats_vty_test.c -> libosmocore_1.8.0.tar.xz/tests/stats/stats_vty_test.c
Changed
@@ -70,7 +70,7 @@ osmo_stats_vty_add_cmds(); - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/tests/tdef/tdef_test.c -> libosmocore_1.8.0.tar.xz/tests/tdef/tdef_test.c
Changed
@@ -125,7 +125,7 @@ } } -static void test_tdef_get_nonexisting() +static void test_tdef_get_nonexisting(void) { printf("\n%s()\n", __func__); @@ -136,13 +136,14 @@ print_tdef_get(tdefs, 5, OSMO_TDEF_US); } -static void test_tdef_set_and_get() +static void test_tdef_set_and_get(void) { struct osmo_tdef *t; printf("\n%s()\n", __func__); printf("setting 7 = 42\n"); t = osmo_tdef_get_entry(tdefs, 7); + OSMO_ASSERT(t != NULL); OSMO_ASSERT(osmo_tdef_val_in_range(t, 42)); t->val = 42; print_tdef_info(7); @@ -316,7 +317,7 @@ osmo_timers_update(); \ } while (0) -void fake_time_start() +void fake_time_start(void) { struct timespec *clock_override;
View file
libosmocore_1.7.0.tar.xz/tests/tdef/tdef_vty_config_root_test.c -> libosmocore_1.8.0.tar.xz/tests/tdef/tdef_vty_config_root_test.c
Changed
@@ -111,7 +111,7 @@ return CMD_SUCCESS; } -static void timer_init_vty() +static void timer_init_vty(void) { /* Again, this is merely to get a vty write hook, see above. */ install_node(&timer_node, config_write_timer); @@ -123,7 +123,7 @@ void *root_ctx = NULL; -static void print_help() +static void print_help(void) { printf( "options:\n" " -h --help this text\n" @@ -260,7 +260,7 @@ } } - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/tests/tdef/tdef_vty_config_subnode_test.c -> libosmocore_1.8.0.tar.xz/tests/tdef/tdef_vty_config_subnode_test.c
Changed
@@ -102,7 +102,7 @@ return CMD_SUCCESS; } -static void gsmnet_init_vty() +static void gsmnet_init_vty(void) { install_node(&gsmnet_node, config_write_gsmnet); install_element(CONFIG_NODE, &cfg_net_cmd); @@ -116,7 +116,7 @@ void *root_ctx = NULL; -static void print_help() +static void print_help(void) { printf( "options:\n" " -h --help this text\n" @@ -253,7 +253,7 @@ } } - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/tests/tdef/tdef_vty_dynamic_test.c -> libosmocore_1.8.0.tar.xz/tests/tdef/tdef_vty_dynamic_test.c
Changed
@@ -179,7 +179,7 @@ return CMD_SUCCESS; } -static void member_init_vty() +static void member_init_vty(void) { install_node(&member_node, config_write_member); install_element(CONFIG_NODE, &cfg_member_cmd); @@ -190,7 +190,7 @@ /* ------------------- THE REST is just boilerplate osmo main() ------------------- */ -static void print_help() +static void print_help(void) { printf( "options:\n" " -h --help this text\n" @@ -327,7 +327,7 @@ } } - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/tests/testsuite.at -> libosmocore_1.8.0.tar.xz/tests/testsuite.at
Changed
@@ -102,6 +102,18 @@ AT_CHECK($abs_top_builddir/tests/smscb/smscb_test, 0, expout) AT_CLEANUP +AT_SETUP(smscb_gsm0341) +AT_KEYWORDS(smscb_gsm0341) +cat $abs_srcdir/smscb/gsm0341_test.ok > expout +AT_CHECK($abs_top_builddir/tests/smscb/gsm0341_test, 0, expout) +AT_CLEANUP + +AT_SETUP(smscb_cbsp) +AT_KEYWORDS(smscb_cbsp) +cat $abs_srcdir/smscb/cbsp_test.ok > expout +AT_CHECK($abs_top_builddir/tests/smscb/cbsp_test, 0, expout) +AT_CLEANUP + AT_SETUP(ussd) AT_KEYWORDS(ussd) cat $abs_srcdir/ussd/ussd_test.ok > expout @@ -153,7 +165,8 @@ AT_SETUP(gsm0408) AT_KEYWORDS(gsm0408) cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout -AT_CHECK($abs_top_builddir/tests/gsm0408/gsm0408_test, 0, expout, ignore) +cat $abs_srcdir/gsm0408/gsm0408_test.err > experr +AT_CHECK($abs_top_builddir/tests/gsm0408/gsm0408_test, 0, expout, experr) AT_CLEANUP AT_SETUP(gsm48_rest_octets)
View file
libosmocore_1.7.0.tar.xz/tests/time_cc/time_cc_test.c -> libosmocore_1.8.0.tar.xz/tests/time_cc/time_cc_test.c
Changed
@@ -125,7 +125,7 @@ .num_cat = ARRAY_SIZE(log_categories), }; -int main() +int main(int argc, char **argv) { void *ctx = talloc_named_const(NULL, 0, "time_cc_test"); struct timespec *now;
View file
libosmocore_1.7.0.tar.xz/tests/timer/timer_test.c -> libosmocore_1.8.0.tar.xz/tests/timer/timer_test.c
Changed
@@ -29,7 +29,7 @@ #include <osmocom/core/select.h> #include <osmocom/core/linuxlist.h> -#include "../config.h" +#include "config.h" static void main_timer_fired(void *data); static void secondary_timer_fired(void *data);
View file
libosmocore_1.7.0.tar.xz/tests/tlv/tlv_test.c -> libosmocore_1.8.0.tar.xz/tests/tlv/tlv_test.c
Changed
@@ -188,7 +188,7 @@ } } -static void test_tlv_shift_functions() +static void test_tlv_shift_functions(void) { uint8_t test_data1024; uint8_t buf1024; @@ -250,7 +250,7 @@ /* Most GSM related protocols clearly indicate that in case of duplicate * IEs, only the first occurrence shall be used, while any further occurrences * shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3 */ -static void test_tlv_repeated_ie() +static void test_tlv_repeated_ie(void) { uint8_t test_data768; int i, rc; @@ -288,7 +288,7 @@ OSMO_ASSERT(dec32.lvtag.val == &test_data2 + 3 + 3); } -static void test_tlv_encoder() +static void test_tlv_encoder(void) { const uint8_t enc_ies = { 0x17, 0x14, 0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40, @@ -332,7 +332,7 @@ msgb_free(msg); } -static void test_tlv_parser_bounds() +static void test_tlv_parser_bounds(void) { struct tlv_definition tdef; struct tlv_parsed dec; @@ -423,7 +423,7 @@ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL); } -static void test_tlv_lens() +static void test_tlv_lens(void) { uint16_t buf_len; uint8_t buf512;
View file
libosmocore_1.7.0.tar.xz/tests/use_count/use_count_test.c -> libosmocore_1.8.0.tar.xz/tests/use_count/use_count_test.c
Changed
@@ -192,7 +192,7 @@ return foo; } -void print_foos() +void print_foos(void) { int count = 0; struct foo *foo; @@ -204,7 +204,7 @@ fprintf(stderr, "%d foos\n\n", count); } -static void test_use_count_fsm() +static void test_use_count_fsm(void) { struct foo *a, *b, *c; log("\n%s()\n", __func__);
View file
libosmocore_1.7.0.tar.xz/tests/utils/utils_test.c -> libosmocore_1.8.0.tar.xz/tests/utils/utils_test.c
Changed
@@ -342,7 +342,7 @@ { "DeafBeddedBabeAcceededFadedDecaff", 32, 32, false, false }, }; -bool test_is_hexstr() +bool test_is_hexstr(void) { int i; bool pass = true; @@ -766,6 +766,65 @@ } } +static void mod_test_mod(int x, int y, int expected_result) +{ + int result; + result = x % y; + printf(" %d mod %d = %d = %d\n", x, y, result, expected_result); + OSMO_ASSERT(result == expected_result); +} + +static void mod_test_mod_flr(int x, int y, int expected_result) +{ + int result; + result = OSMO_MOD_FLR(x, y); + printf(" %d mod_flr %d = %d = %d\n", x, y, result, expected_result); + OSMO_ASSERT(result == expected_result); +} + +static void mod_test_mod_euc(int x, int y, int expected_result) +{ + int result; + result = OSMO_MOD_EUC(x, y); + printf(" %d mod_euc %d = %d = %d\n", x, y, result, expected_result); + OSMO_ASSERT(result == expected_result); +} + +static void mod_test(void) +{ + /* See also: Daan Leijen, Division and Modulus for Computer + * Scientists, section 1.3 */ + + printf("\nTesting built in truncated modulo for comparison:\n"); + mod_test_mod(8, 3, 2); + mod_test_mod(8, -3, 2); + mod_test_mod(-8, 3, -2); + mod_test_mod(-8, -3, -2); + mod_test_mod(1, 2, 1); + mod_test_mod(1, -2, 1); + mod_test_mod(-1, 2, -1); + mod_test_mod(-1, -2, -1); + + printf("\nTesting OSMO_MOD_FLR():\n"); + mod_test_mod_flr(8, 3, 2); + mod_test_mod_flr(8, -3, -1); + mod_test_mod_flr(-8, 3, 1); + mod_test_mod_flr(-8, -3, -2); + mod_test_mod_flr(1, 2, 1); + mod_test_mod_flr(1, -2, -1); + mod_test_mod_flr(-1, 2, 1); + mod_test_mod_flr(-1, -2, -1); + + printf("\nTesting OSMO_MOD_EUC():\n"); + mod_test_mod_euc(8, 3, 2); + mod_test_mod_euc(8, -3, 2); + mod_test_mod_euc(-8, 3, 1); + mod_test_mod_euc(-8, -3, 1); + mod_test_mod_euc(1, 2, 1); + mod_test_mod_euc(1, -2, 1); + mod_test_mod_euc(-1, 2, 1); + mod_test_mod_euc(-1, -2, 1); +} struct osmo_sockaddr_to_str_and_uint_test_case { uint16_t port; @@ -1023,7 +1082,7 @@ }; -static void osmo_str_tolowupper_test() +static void osmo_str_tolowupper_test(void) { int i; char buf128; @@ -1197,7 +1256,7 @@ return sb.chars_needed; } -void strbuf_test() +void strbuf_test(void) { char buf256; int rc; @@ -1230,7 +1289,7 @@ printf("(need %d chars, had size=63) %s\n", rc, buf); } -void strbuf_test_nolen() +void strbuf_test_nolen(void) { char buf20; struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) }; @@ -1256,7 +1315,7 @@ printf(" ERROR: EXPECTED %s\n", expect_rc ? "true" : "false"); } -static void startswith_test() +static void startswith_test(void) { printf("\n%s()\n", __func__); startswith_test_str(NULL, NULL, true); @@ -1301,7 +1360,7 @@ OSMO_NAME_C_IMPL(ctx, 0, NULL, foo_name_buf, arg) } -static void name_c_impl_test() +static void name_c_impl_test(void) { char *test_strs = { "test", @@ -1703,7 +1762,7 @@ return ""; } } -void test_float_str_to_int() +void test_float_str_to_int(void) { const struct float_str_to_int_test *t; printf("--- %s\n", __func__); @@ -1865,7 +1924,7 @@ { 23, -9223372036854775807, "-0.00009223372036854775807" }, { 23, INT64_MIN, "-ERR" }, }; -void test_int_to_float_str() +void test_int_to_float_str(void) { const struct int_to_float_str_test *t; printf("--- %s\n", __func__); @@ -1956,7 +2015,7 @@ { "123 ", 10, -1000, 1000, -E2BIG, 123 }, { "123.4", 10, -1000, 1000, -E2BIG, 123 }, }; -void test_str_to_int() +void test_str_to_int(void) { const struct str_to_int_test *t; printf("--- %s\n", __func__); @@ -2053,7 +2112,7 @@ { "-9223372036854775809", 10, -1000, 1000, -EOVERFLOW, INT64_MIN }, { "9223372036854775808", 10, -1000, 1000, -EOVERFLOW, INT64_MAX }, }; -void test_str_to_int64() +void test_str_to_int64(void) { const struct str_to_int64_test *t; printf("--- %s\n", __func__); @@ -2088,6 +2147,7 @@ str_escape3_test(); str_quote3_test(); isqrt_test(); + mod_test(); osmo_sockaddr_to_str_and_uint_test(); osmo_str_tolowupper_test(); strbuf_test();
View file
libosmocore_1.7.0.tar.xz/tests/utils/utils_test.ok -> libosmocore_1.8.0.tar.xz/tests/utils/utils_test.ok
Changed
@@ -354,6 +354,36 @@ Testing integer square-root +Testing built in truncated modulo for comparison: + 8 mod 3 = 2 = 2 + 8 mod -3 = 2 = 2 + -8 mod 3 = -2 = -2 + -8 mod -3 = -2 = -2 + 1 mod 2 = 1 = 1 + 1 mod -2 = 1 = 1 + -1 mod 2 = -1 = -1 + -1 mod -2 = -1 = -1 + +Testing OSMO_MOD_FLR(): + 8 mod_flr 3 = 2 = 2 + 8 mod_flr -3 = -1 = -1 + -8 mod_flr 3 = 1 = 1 + -8 mod_flr -3 = -2 = -2 + 1 mod_flr 2 = 1 = 1 + 1 mod_flr -2 = -1 = -1 + -1 mod_flr 2 = 1 = 1 + -1 mod_flr -2 = -1 = -1 + +Testing OSMO_MOD_EUC(): + 8 mod_euc 3 = 2 = 2 + 8 mod_euc -3 = 2 = 2 + -8 mod_euc 3 = 1 = 1 + -8 mod_euc -3 = 1 = 1 + 1 mod_euc 2 = 1 = 1 + 1 mod_euc -2 = 1 = 1 + -1 mod_euc 2 = 1 = 1 + -1 mod_euc -2 = 1 = 1 + osmo_sockaddr_to_str_and_uint_test 0 0.0.0.0:0 addr_len=20 --> 0.0.0.0:0 rc=7 1 255.255.255.255:65535 addr_len=20 --> 255.255.255.255:65535 rc=15
View file
libosmocore_1.7.0.tar.xz/tests/vty/vty_test.c -> libosmocore_1.8.0.tar.xz/tests/vty/vty_test.c
Changed
@@ -448,7 +448,35 @@ return CMD_SUCCESS; } -void test_vty_add_cmds() +DEFUN(cfg_range_base10, cfg_range_base10_cmd, + "range-base10 <0-999999>", + "testing decimal range\n" + "the decimal range\n") +{ + printf("Called: 'return-success'\n"); + return CMD_SUCCESS; +} + +DEFUN(cfg_range_base16, cfg_range_base16_cmd, + "range-base16 <0x0-0x8888>", + "testing hexadecimal range\n" + "the hexadecimal range\n") +{ + printf("Called: 'return-success'\n"); + return CMD_SUCCESS; +} + +DEFUN(cfg_range_baseboth, cfg_range_baseboth_cmd, + "range-baseboth (<0-999999>|<0x0-0x8888>)", + "testing both ranges\n" + "the decimal range\n" + "the hexadecimal range\n") +{ + printf("Called: 'return-success'\n"); + return CMD_SUCCESS; +} + +void test_vty_add_cmds(void) { install_element(CONFIG_NODE, &cfg_ret_warning_cmd); install_element(CONFIG_NODE, &cfg_ret_success_cmd); @@ -473,9 +501,13 @@ install_element_ve(&cfg_ambiguous_str_2_cmd); install_element_ve(&cfg_numeric_range_cmd); + + install_element_ve(&cfg_range_base10_cmd); + install_element_ve(&cfg_range_base16_cmd); + install_element_ve(&cfg_range_baseboth_cmd); } -void test_is_cmd_ambiguous() +void test_is_cmd_ambiguous(void) { struct vty *vty; struct vty_test test; @@ -494,7 +526,7 @@ destroy_test_vty(&test, vty); } -void test_numeric_range() +void test_numeric_range(void) { struct vty *vty; struct vty_test test; @@ -509,6 +541,40 @@ destroy_test_vty(&test, vty); } +void test_ranges(void) +{ + struct vty *vty; + struct vty_test test; + + printf("Going to test test_ranges()\n"); + vty = create_test_vty(&test); + + printf("test range-base10\n"); + OSMO_ASSERT(do_vty_command(vty, "range-base10 0") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-base10 40000") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-base10 -400000") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base10 0x0") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base10 0x343") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base10 -0x343") == CMD_ERR_NO_MATCH); + + printf("test range-base16\n"); + OSMO_ASSERT(do_vty_command(vty, "range-base16 0") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base16 40000") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base16 -400000") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-base16 0x0") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-base16 0x343") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-base16 -0x343") == CMD_ERR_NO_MATCH); + + printf("test range-baseboth\n"); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth 40000") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth -400000") == CMD_ERR_NO_MATCH); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x0") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x343") == CMD_SUCCESS); + OSMO_ASSERT(do_vty_command(vty, "range-baseboth -0x343") == CMD_ERR_NO_MATCH); + + destroy_test_vty(&test, vty); +} /* Application specific attributes */ enum vty_test_attr { VTY_TEST_ATTR_FOO = 0, @@ -591,6 +657,7 @@ test_is_cmd_ambiguous(); test_numeric_range(); + test_ranges(); /* Leak check */ OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
View file
libosmocore_1.7.0.tar.xz/tests/vty/vty_test.err -> libosmocore_1.8.0.tar.xz/tests/vty/vty_test.err
Changed
@@ -65,3 +65,13 @@ Got VTY event: 2 Got VTY event: 1 Got VTY event: 3 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 2 +Got VTY event: 1 +Got VTY event: 3
View file
libosmocore_1.7.0.tar.xz/tests/vty/vty_test.ok -> libosmocore_1.8.0.tar.xz/tests/vty/vty_test.ok
Changed
@@ -320,4 +320,52 @@ Returned: 0, Current node: 1 '%s> ' Going to execute 'numeric-range -400000' Returned: 2, Current node: 1 '%s> ' +Going to test test_ranges() +test range-base10 +Going to execute 'range-base10 0' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-base10 40000' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-base10 -400000' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base10 0x0' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base10 0x343' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base10 -0x343' +Returned: 2, Current node: 1 '%s> ' +test range-base16 +Going to execute 'range-base16 0' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base16 40000' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base16 -400000' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-base16 0x0' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-base16 0x343' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-base16 -0x343' +Returned: 2, Current node: 1 '%s> ' +test range-baseboth +Going to execute 'range-baseboth 0' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-baseboth 40000' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-baseboth -400000' +Returned: 2, Current node: 1 '%s> ' +Going to execute 'range-baseboth 0x0' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-baseboth 0x343' +Called: 'return-success' +Returned: 0, Current node: 1 '%s> ' +Going to execute 'range-baseboth -0x343' +Returned: 2, Current node: 1 '%s> ' All tests passed
View file
libosmocore_1.7.0.tar.xz/tests/vty/vty_transcript_test.c -> libosmocore_1.8.0.tar.xz/tests/vty/vty_transcript_test.c
Changed
@@ -37,7 +37,7 @@ void *root_ctx = NULL; -static void print_help() +static void print_help(void) { printf( "options:\n" " -h --help this text\n" @@ -315,7 +315,7 @@ return CMD_SUCCESS; } -static void init_vty_cmds() +static void init_vty_cmds(void) { install_element_ve(&single0_cmd); install_element_ve(&multi0_cmd); @@ -361,7 +361,7 @@ } } - rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + rc = telnet_init_default(root_ctx, NULL, 42042); if (rc < 0) return 2;
View file
libosmocore_1.7.0.tar.xz/utils/Makefile.am -> libosmocore_1.8.0.tar.xz/utils/Makefile.am
Changed
@@ -1,9 +1,9 @@ bin_PROGRAMS = noinst_PROGRAMS = -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS) -AM_CFLAGS = -Wall $(PTHREAD_CFLAGS) -LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS) +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) +LDADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS) if ENABLE_UTILITIES EXTRA_DIST = conv_gen.py conv_codes_gsm.py @@ -29,12 +29,17 @@ endif if ENABLE_EXT_TESTS +SUBDIRS = \ + osmo-stat-dummy \ + $(NULL) + if ENABLE_GB noinst_PROGRAMS += osmo-ns-dummy osmo_ns_dummy_SOURCES = osmo-ns-dummy.c osmo-ns-dummy-vty.c osmo_ns_dummy_LDADD = $(LDADD) $(TALLOC_LIBS) \ $(top_builddir)/src/gb/libosmogb.la \ $(top_builddir)/src/vty/libosmovty.la \ + $(top_builddir)/src/ctrl/libosmoctrl.la \ $(top_builddir)/src/gsm/libosmogsm.la osmo_ns_dummy_CFLAGS = $(TALLOC_CFLAGS) endif
View file
libosmocore_1.7.0.tar.xz/utils/osmo-aka-verify.c -> libosmocore_1.8.0.tar.xz/utils/osmo-aka-verify.c
Changed
@@ -88,7 +88,7 @@ } -static void help() +static void help(void) { printf( "Static SIM card parameters:\n" "-k --key\tSpecify Ki / K\n"
View file
libosmocore_1.7.0.tar.xz/utils/osmo-auc-gen.c -> libosmocore_1.8.0.tar.xz/utils/osmo-auc-gen.c
Changed
@@ -85,7 +85,7 @@ .algo = OSMO_AUTH_ALG_NONE, }; -static void help() +static void help(void) { int alg; printf( "-2 --2g\tUse 2G (GSM) authentication\n"
View file
libosmocore_1.7.0.tar.xz/utils/osmo-ns-dummy.c -> libosmocore_1.8.0.tar.xz/utils/osmo-ns-dummy.c
Changed
@@ -8,7 +8,8 @@ #include <osmocom/core/select.h> #include <osmocom/core/application.h> #include <osmocom/core/stats.h> - +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_vty.h> #include <osmocom/gprs/gprs_ns2.h> #include <osmocom/vty/vty.h> #include <osmocom/vty/telnet_interface.h> @@ -27,6 +28,7 @@ static bool config_given = false; static bool daemonize = false; static int vty_port = 0; +static int ctrl_port = 0; static char *config_file = NULL; struct gprs_ns2_inst *g_nsi; @@ -43,7 +45,7 @@ .copyright = vty_copyright, }; -static void print_help() +static void print_help(void) { printf( "Some useful options:\n" " -h --help This text\n" @@ -51,6 +53,7 @@ " -V --version Print version\n" " -D --daemonize Fork the process into a background daemon\n" " -p --vty-port PORT Set the vty port to listen on.\n" + " -r --ctrl-port PORT Set the ctrl port to listen on.\n" "\nVTY reference generation:\n" " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n" " --vty-ref-xml Generate the VTY reference XML output and exit.\n" @@ -93,12 +96,13 @@ { "version", 0, 0, 'V' }, { "daemonize", 0, 0, 'D' }, { "vty-port", 1, 0, 'p' }, + { "ctrl-port", 1, 0, 'r' }, { "vty-ref-mode", 1, &long_option, 1 }, { "vty-ref-xml", 0, &long_option, 2 }, { 0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "hc:p:VD", + c = getopt_long(argc, argv, "hc:p:r:VD", long_options, &option_idx); if (c == -1) break; @@ -120,7 +124,14 @@ case 'p': vty_port = atoi(optarg); if (vty_port < 0 || vty_port > 65535) { - fprintf(stderr, "Invalid port %d given!\n", vty_port); + fprintf(stderr, "Invalid VTY port %d given!\n", vty_port); + exit(1); + } + break; + case 'r': + ctrl_port = atoi(optarg); + if (ctrl_port < 0 || ctrl_port > 65535) { + fprintf(stderr, "Invalid CTRL port %d given!\n", ctrl_port); exit(1); } break; @@ -223,6 +234,7 @@ int main (int argc, char *argv) { void *ctx = tall_nsdummy_ctx = talloc_named_const(NULL, 0, "osmo-ns-dummy"); + struct ctrl_handle *ctrl; int rc = 0; osmo_init_logging2(ctx, &log_info); @@ -235,6 +247,7 @@ vty_info.tall_ctx = ctx; vty_init(&vty_info); + ctrl_vty_init(ctx); logging_vty_add_cmds(); osmo_stats_vty_add_cmds(); osmo_talloc_vty_add_cmds(); @@ -259,13 +272,20 @@ fprintf(stderr, "No config file: '%s' Using default config.\n", config_file); - rc = telnet_init_dynif(ctx, NULL, vty_get_bind_addr(), - vty_port); + rc = telnet_init_default(ctx, NULL, vty_port); if (rc < 0) { fprintf(stderr, "Error initializing telnet\n"); exit(1); } + if (ctrl_port > 0) { + ctrl = ctrl_interface_setup(NULL, ctrl_port, NULL); + if (!ctrl) { + fprintf(stderr, "Failed to initialize control interface. Exiting.\n"); + exit(1); + } + } + signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGPIPE, sighandler);
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy
Added
+(directory)
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy/Makefile.am
Added
@@ -0,0 +1,10 @@ +if ENABLE_UTILITIES +noinst_PROGRAMS = osmo-stat-dummy +osmo_stat_dummy_SOURCES = osmo-stat-dummy.c +osmo_stat_dummy_LDADD = $(LDADD) $(TALLOC_LIBS) \ + $(top_builddir)/src/vty/libosmovty.la \ + $(top_builddir)/src/ctrl/libosmoctrl.la \ + $(top_builddir)/src/core/libosmocore.la +osmo_stat_dummy_CFLAGS = -Wall $(TALLOC_CFLAGS) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOCTRL_CFLAGS) +osmo_stat_dummy_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +endif
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy/README.md
Added
@@ -0,0 +1,12 @@ +# Osmocom utilities + +* osmo-stat-dummy: utility for rate counter and statsd testing + +It has 2 rate counters: one ticks twice a seconds, another one can be manually updated with 'update-rate-ctr' command via vty. + +The raw value is sent via statsd protocol. If you install "netdata" monitoring tool than you can open http://localhost:19999 in browser +and observe live counters monitoring under "StatsD dummy" without any additional setup. + +Opening osmo-stat-dummy.html in browser while both netdata and osmo-stat-dummy are running will show dimensioned (per min/hour/day) rate counters as well as raw data. + +The latter is handy for troubleshooting and comparing libosmocore's internal rate counter computation.
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy/osmo-stat-dummy.c
Added
@@ -0,0 +1,335 @@ +/* Rate counter and statsd test application */ +/* (C) 2022 by by sysmocom - s.f.m.c. GmbH + * All Rights Reserved + * + * 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 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 General Public License for more details. + * + */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <getopt.h> +#include <signal.h> +#include <unistd.h> +#include <inttypes.h> + +#include <osmocom/core/select.h> +#include <osmocom/core/application.h> +#include <osmocom/core/stats.h> +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_vty.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/ports.h> +#include <osmocom/vty/tdef_vty.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/stats.h> +#include <osmocom/vty/misc.h> + +#include "config.h" + +void *tall_statdummy_ctx = NULL; +static bool quit = false; +static bool config_given = false; +struct rate_ctr_group *g_ctrg; + +enum dummy_rate_ctr_idx { + DUMMY_VTY = 0, + DUMMY_AUTO, +}; + +static void print_help(void) +{ + printf("Some useful options:\n" + " -h --help This text\n" + " -c --config-file Specify the filename of the config file\n" + " -V --version Print version\n" + "\nVTY reference generation:\n" + " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n" + " --vty-ref-xml Generate the VTY reference XML output and exit.\n" + ); +} + +static void handle_long_options(const char *prog_name, const int long_option) +{ + static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT; + + switch (long_option) { + case 1: + vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg); + if (vty_ref_mode < 0) { + fprintf(stderr, "%s: Unknown VTY reference generation mode '%s'\n", prog_name, optarg); + exit(2); + } + break; + case 2: + fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n", + get_value_string(vty_ref_gen_mode_names, vty_ref_mode), + get_value_string(vty_ref_gen_mode_desc, vty_ref_mode)); + vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode); + exit(0); + default: + fprintf(stderr, "%s: error parsing cmdline options\n", prog_name); + exit(2); + } +} + +static char *handle_options(int argc, char **argv) +{ + char *config_file = NULL; + + while (1) { + int option_idx = 0, c; + static int long_option = 0; + static const struct option long_options = { + { "help", 0, 0, 'h' }, + { "config-file", 1, 0, 'c' }, + { "version", 0, 0, 'V' }, + { "vty-ref-mode", 1, &long_option, 1 }, + { "vty-ref-xml", 0, &long_option, 2 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hc:V", long_options, &option_idx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + break; + case 0: + handle_long_options(argv0, long_option); + break; + case 'c': + if (config_file) + free(config_file); + config_file = optarg; + config_given = true; + break; + case 'V': + print_version(1); + exit(0); + break; + default: + fprintf(stderr, "Unknown option '%c'\n", c); + exit(0); + break; + } + } + + if (!config_file) + return "osmo-stat-dummy.cfg"; + + return config_file; +} + +void sighandler(int sigset) +{ + if (sigset == SIGPIPE) + return; + + fprintf(stderr, "Signal %d received.\n", sigset); + + switch (sigset) { + case SIGINT: + case SIGTERM: + /* If another signal is received afterwards, the program + * is terminated without finishing shutdown process. + */ + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + + quit = 1; + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report and + * then run default SIGABRT handler, who will generate coredump + * and abort the process. abort() should do this for us after we + * return, but program wouldn't exit if an external SIGABRT is + * received. + */ + talloc_report_full(tall_statdummy_ctx, stderr); + signal(SIGABRT, SIG_DFL); + raise(SIGABRT); + break; + case SIGUSR1: + case SIGUSR2: + talloc_report_full(tall_statdummy_ctx, stderr); + break; + } +} + +static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what) +{ + uint64_t expire_count; + int rc; + + /* check that the timer has actually expired */ + if (!(what & OSMO_FD_READ)) + return 0; + + /* read from timerfd: number of expirations of periodic timer */ + rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count)); + if (rc < 0 && errno == EAGAIN) + return 0; + + OSMO_ASSERT(rc == sizeof(expire_count)); + + if (expire_count > 1) + LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n", + expire_count, expire_count-1); + + /* Increment the counter value */ + rate_ctr_inc(rate_ctr_group_get_ctr(g_ctrg, DUMMY_AUTO)); + + return 0; +} + +DEFUN(update_rate_ctr, update_rate_ctr_cmd, + "update-rate-ctr <0-100000>", + "Update dummy rate counter\n" + "Value to add to rate counter\n") +{ + rate_ctr_add(rate_ctr_group_get_ctr(g_ctrg, DUMMY_VTY), atoi(argv0)); + + return CMD_SUCCESS; +} + +static int statdummy_vty_init(void) +{ + install_element_ve(&update_rate_ctr_cmd); + + return 0; +} + +int main(int argc, char *argv) +{ + struct log_info log_info = {}; + char *config_file; + void *ctx = tall_statdummy_ctx = talloc_named_const(NULL, 0, "osmo-stat-dummy"); + struct ctrl_handle *ctrl; + struct osmo_fd rate_ctr_timer = { .fd = -1 }; + struct timespec ts_interval = { .tv_sec = 0, .tv_nsec = 500000000 }; /* 0.5 seconds */ + int rc = 0; + + const char vty_copyright = + "Copyright (C) 2022 by by sysmocom - s.f.m.c. GmbH\r\n" + "Author: Max Suraev <msuraev@sysmocom.de>\r\n" + "License GNU GPL version 3 or later\r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + + struct vty_app_info vty_info = { + .name = "OsmoSTATdummy", + .version = PACKAGE_VERSION, + .copyright = vty_copyright, + .tall_ctx = ctx + }; + + const struct rate_ctr_desc dummy_ctr_desc = { + DUMMY_VTY = { "dummy:vty", "Dummy counter updated via VTY" }, + DUMMY_AUTO = { "dummy:auto", "Dummy counter autoupdated via timer" }, + }; + + const struct rate_ctr_group_desc dummy_ctrg_desc = { + "dummy", + "dummy stat tester", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(dummy_ctr_desc), + dummy_ctr_desc, + }; + + osmo_init_logging2(ctx, &log_info); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); + log_set_log_level(osmo_stderr_target, LOGL_INFO); + + msgb_talloc_ctx_init(ctx, 0); + + vty_init(&vty_info); + ctrl_vty_init(ctx); + logging_vty_add_cmds(); + osmo_stats_vty_add_cmds(); + osmo_talloc_vty_add_cmds(); + + config_file = handle_options(argc, argv); + + statdummy_vty_init(); + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + if (config_given) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + exit(1); + } + fprintf(stderr, "No config file: '%s' Using default config.\n", config_file); + } + + rc = telnet_init_default(ctx, NULL, -1); + if (rc < 0) { + fprintf(stderr, "Error initializing telnet\n"); + exit(1); + } + + ctrl = ctrl_interface_setup(NULL, 1234, NULL); + if (!ctrl) { + fprintf(stderr, "Failed to initialize control interface. Exiting.\n"); + exit(1); + } + + g_ctrg = rate_ctr_group_alloc(ctx, &dummy_ctrg_desc, 0); + if (!g_ctrg) { + fprintf(stderr, "Failed to initialize rate counters. Exiting.\n"); + return -1; + } + + osmo_stats_init(ctx); + rate_ctr_init(ctx); + + rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n", + rc, rate_ctr_timer.fd); + return rc; + } + + rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n", + rc, rate_ctr_timer.fd); + } + + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + signal(SIGABRT, sighandler); + signal(SIGUSR1, sighandler); + signal(SIGUSR2, sighandler); + osmo_init_ignore_signals(); + + while (!quit) { + osmo_select_main(0); + } + + telnet_exit(); + + talloc_report_full(tall_statdummy_ctx, stderr); + talloc_free(tall_statdummy_ctx); + + return 0; +}
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy/osmo-stat-dummy.cfg
Added
@@ -0,0 +1,40 @@ +log stderr + logging filter all 1 + logging color 0 + logging timestamp 0 + logging print extended-timestamp 0 + logging print category-hex 0 + logging print category 1 + !logging print file basename + ! log-levels defined by libosmocore and hence available everywhere, can be overridden by inidividual per-app configs below + logging level lstats notice + logging level lglobal notice + logging level llapd notice + logging level linp notice + logging level lmux notice + logging level lmi notice + logging level lmib notice + logging level lsms notice + logging level lctrl notice + logging level lgtp notice + logging level lgsup notice + logging level loap notice + logging level lss7 error + logging level lsccp notice + logging level lsua notice + logging level lm3ua notice + logging level lmgcp notice + logging level ljibuf notice + logging level lrspro notice +stats reporter statsd + !use default https://learn.netdata.cloud/ statsd plugin: + remote-ip 127.0.0.1 + remote-port 8125 + level global + no prefix + enable +stats interval 1 +line vty + bind 127.0.0.2 6969 +ctrl + bind 127.0.0.11 1234
View file
libosmocore_1.8.0.tar.xz/utils/osmo-stat-dummy/osmo-stat-dummy.html
Added
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="apple-mobile-web-app-capable" content="yes"> + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> +</head> +<body> + <div data-netdata="statsd_dummy.0.dummy.auto_counter" + data-chart-library="dygraph" + data-title="raw data (timerfd ticks twice per second)" + data-width="600" + data-height="200" + data-after="-600" + ></div> + <div data-netdata="statsd_dummy.0.dummy.auto_counter" + data-chart-library="dygraph" + data-title="aggregated per minute (timerfd ticks twice per second)" + data-width="600" + data-height="200" + data-after="-600" + data-method="average" + data-gtime="60" + data-units="events/min" + ></div> + <div data-netdata="statsd_dummy.0.dummy.auto_counter" + data-chart-library="dygraph" + data-title="aggregated per hour (timerfd ticks twice per second)" + data-width="600" + data-height="200" + data-after="-600" + data-method="average" + data-gtime="3600" + data-units="events/hour" + ></div> + <div data-netdata="statsd_dummy.0.dummy.auto_counter" + data-chart-library="dygraph" + data-title="aggregated per day (timerfd ticks twice per second)" + data-width="600" + data-height="200" + data-after="-600" + data-method="average" + data-gtime="86400" + data-units="events/day" + ></div> + + <div data-netdata="statsd_dummy.0.dummy.vty_counter" + data-chart-library="dygraph" + data-title="raw data (updated manually via vty command)" + data-width="600" + data-height="200" + data-after="-600" + ></div> +</body> +<script type="text/javascript" src="http://localhost:19999/dashboard.js"></script> +</html>
View file
rpmlintrc
Added
@@ -0,0 +1,5 @@ +# Don't abort the build when finding a library that depends on a package with +# a specific version. This is intentional for nightly builds, we don't want +# libraries from different build dates to be mixed as they might have ABI +# incompatibilities. +setBadness('shlib-fixed-dependency', 0)
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.