Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libosmo-netif for openSUSE:Factory checked in at 2026-08-26 19:52:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libosmo-netif (Old) and /work/SRC/openSUSE:Factory/.libosmo-netif.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libosmo-netif" Wed Aug 26 19:52:52 2026 rev:18 rq:1373647 version:1.8.0 Changes: -------- --- /work/SRC/openSUSE:Factory/libosmo-netif/libosmo-netif.changes 2026-03-15 14:33:20.809808230 +0100 +++ /work/SRC/openSUSE:Factory/.libosmo-netif.new.1258/libosmo-netif.changes 2026-08-26 19:53:13.186674423 +0200 @@ -1,0 +2,7 @@ +Tue Aug 25 15:26:57 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 1.8.0 + * Introduce ip_checksum API + * Introduce API osmo_icmpv6 + +------------------------------------------------------------------- Old: ---- libosmo-netif-1.7.0.tar.bz2 New: ---- libosmo-netif-1.8.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libosmo-netif.spec ++++++ --- /var/tmp/diff_new_pack.yBTWoQ/_old 2026-08-26 19:53:14.842732912 +0200 +++ /var/tmp/diff_new_pack.yBTWoQ/_new 2026-08-26 19:53:14.849733160 +0200 @@ -17,7 +17,7 @@ Name: libosmo-netif -Version: 1.7.0 +Version: 1.8.0 Release: 0 Summary: Osmocom library for muxed audio License: GPL-2.0-or-later ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.yBTWoQ/_old 2026-08-26 19:53:14.960737080 +0200 +++ /var/tmp/diff_new_pack.yBTWoQ/_new 2026-08-26 19:53:14.971737469 +0200 @@ -1,5 +1,5 @@ -mtime: 1773519991 -commit: d0734224061e26762f55c24a9ec42ff12bd44a1dea981429372eb5e7c3e377f6 +mtime: 1787671649 +commit: 9a1bc5f819f28bbfe8041f031086b70a882895ac9435e2e04c473e9f5fee0dbf url: https://src.opensuse.org/jengelh/libosmo-netif revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-08-25 17:27:29.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ libosmo-netif-1.7.0.tar.bz2 -> libosmo-netif-1.8.0.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/.tarball-version new/libosmo-netif-1.8.0/.tarball-version --- old/libosmo-netif-1.7.0/.tarball-version 2025-12-04 06:19:50.000000000 +0100 +++ new/libosmo-netif-1.8.0/.tarball-version 2026-08-21 07:19:56.000000000 +0200 @@ -1 +1 @@ -1.7.0 +1.8.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/.version new/libosmo-netif-1.8.0/.version --- old/libosmo-netif-1.7.0/.version 2025-12-04 06:19:49.000000000 +0100 +++ new/libosmo-netif-1.8.0/.version 2026-08-21 07:19:55.000000000 +0200 @@ -1 +1 @@ -1.7.0 +1.8.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/configure new/libosmo-netif-1.8.0/configure --- old/libosmo-netif-1.7.0/configure 2025-12-04 06:19:45.000000000 +0100 +++ new/libosmo-netif-1.8.0/configure 2026-08-21 07:19:49.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libosmo-netif 1.7.0. +# Generated by GNU Autoconf 2.71 for libosmo-netif 1.8.0. # # Report bugs to <[email protected]>. # @@ -621,8 +621,8 @@ # Identity of this package. PACKAGE_NAME='libosmo-netif' PACKAGE_TARNAME='libosmo-netif' -PACKAGE_VERSION='1.7.0' -PACKAGE_STRING='libosmo-netif 1.7.0' +PACKAGE_VERSION='1.8.0' +PACKAGE_STRING='libosmo-netif 1.8.0' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -1388,7 +1388,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libosmo-netif 1.7.0 to adapt to many kinds of systems. +\`configure' configures libosmo-netif 1.8.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1459,7 +1459,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libosmo-netif 1.7.0:";; + short | recursive ) echo "Configuration of libosmo-netif 1.8.0:";; esac cat <<\_ACEOF @@ -1599,7 +1599,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libosmo-netif configure 1.7.0 +libosmo-netif configure 1.8.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1817,7 +1817,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libosmo-netif $as_me 1.7.0, which was +It was created by libosmo-netif $as_me 1.8.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3089,7 +3089,7 @@ # Define the identity of the package. PACKAGE='libosmo-netif' - VERSION='1.7.0' + VERSION='1.8.0' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -14215,7 +14215,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libosmo-netif $as_me 1.7.0, which was +This file was extended by libosmo-netif $as_me 1.8.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14283,7 +14283,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libosmo-netif config.status 1.7.0 +libosmo-netif config.status 1.8.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/debian/changelog new/libosmo-netif-1.8.0/debian/changelog --- old/libosmo-netif-1.7.0/debian/changelog 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/debian/changelog 2026-08-21 07:19:39.000000000 +0200 @@ -1,3 +1,36 @@ +libosmo-netif (1.8.0) unstable; urgency=medium + + [ Pau Espin Pedrol ] + * cosmetic: stream_cli: Fix typos in API documentation + * stream: Log failure to apply NODELAY sockopt + * cosmetic: stream_srv: srv_link: Document no need for OSMO_SOCK_F_NONBLOCK + * stream_cli: Log state in stream_sctp_recvmsg_wrapper() + * stream_srv: keep sockname in iofd name if a stream name is explicitly set + * stream_cli: Add stream sockname to iofd name + * stream: Improve error handling and logging in write_cb + * Introduce ip_checksum API + * Introduce API osmo_icmpv6 + * stream: srv_link: Fix DSCP not set on already created listen socket + + [ Timur Davydov ] + * stream: Fix build with --disable-libsctp + * gitignore: ignore generated .wasm files + + [ Mychaela N. Falconia ] + * twjit: add underrun extension feature + + [ Harald Welte ] + * icmpv6: Re-license code under GPL-v2-or-later + * ip_checksum: Add SPDX license identifier + + [ Vadim Yanitskiy ] + * .gitignore: add missing osmux_{input,output}_test + + [ Andreas Eversberg ] + * Also apply DSCP settings on listening socket + + -- Pau Espin Pedrol <[email protected]> Thu, 20 Aug 2026 10:53:30 +0200 + libosmo-netif (1.7.0) unstable; urgency=medium [ Mychaela N. Falconia ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/Makefile.am new/libosmo-netif-1.8.0/include/osmocom/netif/Makefile.am --- old/libosmo-netif-1.7.0/include/osmocom/netif/Makefile.am 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/Makefile.am 2026-08-21 07:19:39.000000000 +0200 @@ -20,6 +20,8 @@ osmonetif_HEADERS = \ amr.h \ datagram.h \ + icmpv6.h \ + ip_checksum.h \ ipa.h \ ipa_unit.h \ jibuf.h \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/Makefile.in new/libosmo-netif-1.8.0/include/osmocom/netif/Makefile.in --- old/libosmo-netif-1.7.0/include/osmocom/netif/Makefile.in 2025-12-04 06:19:45.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/Makefile.in 2026-08-21 07:19:50.000000000 +0200 @@ -122,9 +122,9 @@ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac -am__osmonetif_HEADERS_DIST = amr.h datagram.h ipa.h ipa_unit.h jibuf.h \ - osmux.h prim.h rs232.h rtp.h stream.h twjit.h twrtp.h \ - version.h sctp.h +am__osmonetif_HEADERS_DIST = amr.h datagram.h icmpv6.h ip_checksum.h \ + ipa.h ipa_unit.h jibuf.h osmux.h prim.h rs232.h rtp.h stream.h \ + twjit.h twrtp.h version.h sctp.h am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -319,9 +319,9 @@ version.h.tpl \ $(NULL) -osmonetif_HEADERS = amr.h datagram.h ipa.h ipa_unit.h jibuf.h osmux.h \ - prim.h rs232.h rtp.h stream.h twjit.h twrtp.h version.h \ - $(NULL) $(am__append_1) +osmonetif_HEADERS = amr.h datagram.h icmpv6.h ip_checksum.h ipa.h \ + ipa_unit.h jibuf.h osmux.h prim.h rs232.h rtp.h stream.h \ + twjit.h twrtp.h version.h $(NULL) $(am__append_1) osmonetifdir = $(includedir)/osmocom/netif all: all-am diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/icmpv6.h new/libosmo-netif-1.8.0/include/osmocom/netif/icmpv6.h --- old/libosmo-netif-1.7.0/include/osmocom/netif/icmpv6.h 1970-01-01 01:00:00.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/icmpv6.h 2026-08-21 07:19:39.000000000 +0200 @@ -0,0 +1,105 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include <netinet/in.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/endian.h> + +#define ICMPv6_OPT_TYPE_PREFIX_INFO 0x03 + +struct osmo_icmpv6_hdr { + uint8_t type; + uint8_t code; + uint16_t csum; +} __attribute__ ((packed)); + +struct osmo_icmpv6_echo_hdr { + struct osmo_icmpv6_hdr hdr; + uint16_t ident; /* Identifier */ + uint16_t seq; /* Sequence number */ + uint8_t data[0]; /* Data */ +} __attribute__ ((packed)); + +/* RFC4861 Section 4.1 */ +struct osmo_icmpv6_rsol_hdr { + struct osmo_icmpv6_hdr hdr; + uint32_t reserved; + uint8_t options[0]; +} __attribute__ ((packed)); + +/* RFC4861 Section 4.2 */ +struct osmo_icmpv6_radv_hdr { + struct osmo_icmpv6_hdr hdr; + uint8_t cur_ho_limit; +#if OSMO_IS_LITTLE_ENDIAN + uint8_t res:6, + m:1, + o:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t o:1, m:1, res:6; +#endif + uint16_t router_lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; + uint8_t options[0]; +} __attribute__ ((packed)); + + +/* RFC4861 Section 4.6 */ +struct osmo_icmpv6_opt_hdr { + uint8_t type; + /* length in units of 8 octets, including type+len! */ + uint8_t len; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* RFC4861 Section 4.6.2 */ +struct osmo_icmpv6_opt_prefix { + struct osmo_icmpv6_opt_hdr hdr; + uint8_t prefix_len; +#if OSMO_IS_LITTLE_ENDIAN + uint8_t res:6, + a:1, + l:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t l:1, a:1, res:6; +#endif + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + uint32_t res2; + uint8_t prefix[16]; +} __attribute__ ((packed)); + +/* RFC4861 Section 4.6.4 */ +struct osmo_icmpv6_opt_mtu { + struct osmo_icmpv6_opt_hdr hdr; + uint16_t reserved; + uint32_t mtu; +} __attribute__ ((packed)); + +uint16_t osmo_icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr, + const struct in6_addr *daddr); + +struct msgb *osmo_icmpv6_construct_rs(const struct in6_addr *saddr); +struct msgb *osmo_icmpv6_construct_ra(const struct in6_addr *saddr, + const struct in6_addr *daddr, + const struct in6_addr *prefix, + uint32_t mtu); + +bool osmo_icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len); +struct osmo_icmpv6_radv_hdr *osmo_icmpv6_validate_router_adv(const uint8_t *pack, unsigned len); + +#define foreach_icmpv6_opt(icmpv6_pkt, icmpv6_len, opt_hdr) \ + for (opt_hdr = (struct osmo_icmpv6_opt_hdr *)(icmpv6_pkt)->options; \ + (uint8_t *)(opt_hdr) + sizeof(struct osmo_icmpv6_opt_hdr) <= (((uint8_t *)(icmpv6_pkt)) + (icmpv6_len)); \ + opt_hdr = (struct osmo_icmpv6_opt_hdr *)((uint8_t *)(opt_hdr) + (opt_hdr)->len) \ + ) + +/* RFC3307 link-local scope multicast address */ +extern const struct in6_addr osmo_icmpv6_all_router_mcast_addr; + +extern const uint8_t osmo_icmpv6_solicited_node_mcast_addr_prefix[13]; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/ip_checksum.h new/libosmo-netif-1.8.0/include/osmocom/netif/ip_checksum.h --- old/libosmo-netif-1.7.0/include/osmocom/netif/ip_checksum.h 1970-01-01 01:00:00.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/ip_checksum.h 2026-08-21 07:19:39.000000000 +0200 @@ -0,0 +1,11 @@ +#pragma once +#include <stdint.h> +#include <netinet/in.h> + +uint16_t osmo_ip_checksum_fast_csum(const void *iph, unsigned int ihl); +uint32_t osmo_ip_checksum_csum_partial(const void *buff, int len, uint32_t wsum); +uint16_t osmo_ip_checksum_compute_csum(const void *buff, int len); + +uint16_t osmo_ip_checksum_csum_ipv6_magic(const struct in6_addr *saddr, + const struct in6_addr *daddr, + uint32_t len, uint8_t proto, uint32_t csum); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/twjit.h new/libosmo-netif-1.8.0/include/osmocom/netif/twjit.h --- old/libosmo-netif-1.7.0/include/osmocom/netif/twjit.h 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/twjit.h 2026-08-21 07:19:39.000000000 +0200 @@ -83,6 +83,9 @@ uint32_t intentional_gaps; uint32_t ts_resets; uint32_t jitter_max; + /* new counters added after first osmo_twjit ABI freeze */ + uint32_t soft_underruns; + /* New fields may be added here at the end; once added, they become * permanent like the initially defined ones. */ }; @@ -134,6 +137,8 @@ uint16_t delta_ms); int osmo_twjit_config_set_start_max_delta(struct osmo_twjit_config *conf, uint16_t delta_ms); +int osmo_twjit_config_set_underrun_ext(struct osmo_twjit_config *conf, + uint16_t underrun_ext); int osmo_twjit_config_set_handover_on_marker(struct osmo_twjit_config *conf, bool hom); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/twjit_private.h new/libosmo-netif-1.8.0/include/osmocom/netif/twjit_private.h --- old/libosmo-netif-1.7.0/include/osmocom/netif/twjit_private.h 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/twjit_private.h 2026-08-21 07:19:39.000000000 +0200 @@ -49,6 +49,12 @@ /*! max time delta in starting state, 1 ms units, 0 means not set; * document section: 2.3.3.2. */ uint16_t start_max_delta; + /*! Optional extended underrun tolerance: maximum gap in RTP stream + * that does not result in full reset and reacquisition, minimum of 1. + * If this field is set to 0, the feature is disabled and the original + * twjit algorithm (no intentional gaps allowed) applies. + * Document section: 2.3.5.1. */ + uint16_t underrun_ext; /*! Osmocom addition, not in ThemWi original: should RTP packets * with M bit set cause a handover or HUNT state reset just like * an SSRC change? With this option enabled, M bit is treated diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/include/osmocom/netif/version.h new/libosmo-netif-1.8.0/include/osmocom/netif/version.h --- old/libosmo-netif-1.7.0/include/osmocom/netif/version.h 2025-12-04 06:19:49.000000000 +0100 +++ new/libosmo-netif-1.8.0/include/osmocom/netif/version.h 2026-08-21 07:19:55.000000000 +0200 @@ -1,10 +1,10 @@ #pragma once -#define LIBOSMO_NETIF_VERSION 1.7.0 -#define LIBOSMO_NETIF_VERSION_STR "1.7.0" +#define LIBOSMO_NETIF_VERSION 1.8.0 +#define LIBOSMO_NETIF_VERSION_STR "1.8.0" #define LIBOSMO_NETIF_VERSION_MAJOR 1 -#define LIBOSMO_NETIF_VERSION_MINOR 7 +#define LIBOSMO_NETIF_VERSION_MINOR 8 #define LIBOSMO_NETIF_VERSION_PATCH 0 #define LIBOSMO_NETIF_VERSION_GREATER_EQUAL(major, minor, patch) \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/Makefile.am new/libosmo-netif-1.8.0/src/Makefile.am --- old/libosmo-netif-1.7.0/src/Makefile.am 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/Makefile.am 2026-08-21 07:19:39.000000000 +0200 @@ -1,6 +1,6 @@ # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -LIBVERSION=15:0:4 +LIBVERSION=16:0:5 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS= -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ @@ -15,6 +15,8 @@ libosmonetif_la_SOURCES = amr.c \ datagram.c \ + icmpv6.c \ + ip_checksum.c \ ipa.c \ ipa_keepalive.c \ ipa_unit.c \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/Makefile.in new/libosmo-netif-1.8.0/src/Makefile.in --- old/libosmo-netif-1.7.0/src/Makefile.in 2025-12-04 06:19:45.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/Makefile.in 2026-08-21 07:19:50.000000000 +0200 @@ -135,16 +135,16 @@ libosmonetif_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) -am__libosmonetif_la_SOURCES_DIST = amr.c datagram.c ipa.c \ - ipa_keepalive.c ipa_unit.c jibuf.c osmux.c osmux_input.c \ - osmux_output.c prim.c rs232.c rtp.c stream.c stream_cli.c \ - stream_srv.c twjit.c twjit_conf.c twrtp.c sctp.c +am__libosmonetif_la_SOURCES_DIST = amr.c datagram.c icmpv6.c \ + ip_checksum.c ipa.c ipa_keepalive.c ipa_unit.c jibuf.c osmux.c \ + osmux_input.c osmux_output.c prim.c rs232.c rtp.c stream.c \ + stream_cli.c stream_srv.c twjit.c twjit_conf.c twrtp.c sctp.c @ENABLE_LIBSCTP_TRUE@am__objects_1 = sctp.lo -am_libosmonetif_la_OBJECTS = amr.lo datagram.lo ipa.lo \ - ipa_keepalive.lo ipa_unit.lo jibuf.lo osmux.lo osmux_input.lo \ - osmux_output.lo prim.lo rs232.lo rtp.lo stream.lo \ - stream_cli.lo stream_srv.lo twjit.lo twjit_conf.lo twrtp.lo \ - $(am__objects_1) +am_libosmonetif_la_OBJECTS = amr.lo datagram.lo icmpv6.lo \ + ip_checksum.lo ipa.lo ipa_keepalive.lo ipa_unit.lo jibuf.lo \ + osmux.lo osmux_input.lo osmux_output.lo prim.lo rs232.lo \ + rtp.lo stream.lo stream_cli.lo stream_srv.lo twjit.lo \ + twjit_conf.lo twrtp.lo $(am__objects_1) libosmonetif_la_OBJECTS = $(am_libosmonetif_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -170,6 +170,7 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/amr.Plo ./$(DEPDIR)/datagram.Plo \ + ./$(DEPDIR)/icmpv6.Plo ./$(DEPDIR)/ip_checksum.Plo \ ./$(DEPDIR)/ipa.Plo ./$(DEPDIR)/ipa_keepalive.Plo \ ./$(DEPDIR)/ipa_unit.Plo ./$(DEPDIR)/jibuf.Plo \ ./$(DEPDIR)/osmux.Plo ./$(DEPDIR)/osmux_input.Plo \ @@ -362,7 +363,7 @@ # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -LIBVERSION = 15:0:4 +LIBVERSION = 16:0:5 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS = -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) $(LIBSCTP_CFLAGS) @@ -373,10 +374,11 @@ $(LIBOSMOVTY_LIBS) $(LIBSCTP_LIBS) libosmonetif_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBVERSION) -no-undefined -libosmonetif_la_SOURCES = amr.c datagram.c ipa.c ipa_keepalive.c \ - ipa_unit.c jibuf.c osmux.c osmux_input.c osmux_output.c prim.c \ - rs232.c rtp.c stream.c stream_cli.c stream_srv.c twjit.c \ - twjit_conf.c twrtp.c $(NULL) $(am__append_1) +libosmonetif_la_SOURCES = amr.c datagram.c icmpv6.c ip_checksum.c \ + ipa.c ipa_keepalive.c ipa_unit.c jibuf.c osmux.c osmux_input.c \ + osmux_output.c prim.c rs232.c rtp.c stream.c stream_cli.c \ + stream_srv.c twjit.c twjit_conf.c twrtp.c $(NULL) \ + $(am__append_1) all: all-am .SUFFIXES: @@ -457,6 +459,8 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/amr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datagram.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icmpv6.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_checksum.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipa.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipa_keepalive.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipa_unit.Plo@am__quote@ # am--include-marker @@ -640,6 +644,8 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/amr.Plo -rm -f ./$(DEPDIR)/datagram.Plo + -rm -f ./$(DEPDIR)/icmpv6.Plo + -rm -f ./$(DEPDIR)/ip_checksum.Plo -rm -f ./$(DEPDIR)/ipa.Plo -rm -f ./$(DEPDIR)/ipa_keepalive.Plo -rm -f ./$(DEPDIR)/ipa_unit.Plo @@ -704,6 +710,8 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/amr.Plo -rm -f ./$(DEPDIR)/datagram.Plo + -rm -f ./$(DEPDIR)/icmpv6.Plo + -rm -f ./$(DEPDIR)/ip_checksum.Plo -rm -f ./$(DEPDIR)/ipa.Plo -rm -f ./$(DEPDIR)/ipa_keepalive.Plo -rm -f ./$(DEPDIR)/ipa_unit.Plo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/icmpv6.c new/libosmo-netif-1.8.0/src/icmpv6.c --- old/libosmo-netif-1.7.0/src/icmpv6.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/icmpv6.c 2026-08-21 07:19:39.000000000 +0200 @@ -0,0 +1,226 @@ +/* Minimal ICMPv6 code for generating router advertisements as required by + * relevant 3GPP specs for a GGSN with IPv6 PDP contexts */ + +/* (C) 2017 by Harald Welte <[email protected]> + * 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. + * + * 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 <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <netinet/in.h> +#if defined(__FreeBSD__) +#include <sys/types.h> /* FreeBSD 10.x needs this before ip6.h */ +#include <sys/endian.h> +#endif +#include <netinet/ip6.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> + +#include <osmocom/netif/ip_checksum.h> +#include <osmocom/netif/icmpv6.h> + +/* 29.061 11.2.1.3.4 IPv6 Router Configuration Variables in GGSN */ +#define GGSN_MaxRtrAdvInterval 21600 /* 6 hours */ +#define GGSN_MinRtrAdvInterval 16200 /* 4.5 hours */ +#define GGSN_AdvValidLifetime 0xffffffff /* infinite */ +#define GGSN_AdvPreferredLifetime 0xffffffff /* infinite */ + +/* RFC3307 link-local scope multicast address */ +const struct in6_addr osmo_icmpv6_all_router_mcast_addr = { + .s6_addr = { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } +}; + + +/* RFC4291 link-local solicited-node multicast address, FF02:0:0:0:0:1:FF, 104 bits = 13 bytes */ +const uint8_t osmo_icmpv6_solicited_node_mcast_addr_prefix[13] = { + 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0xFF +}; + +/* Prepends the ipv6 header and returns checksum content */ +uint16_t osmo_icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr, + const struct in6_addr *daddr) +{ + uint32_t len; + uint16_t skb_csum; + struct ip6_hdr *i6h; + + /* checksum */ + skb_csum = osmo_ip_checksum_csum_partial(msgb_data(msg), msgb_length(msg), 0); + len = msgb_length(msg); + skb_csum = osmo_ip_checksum_csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, skb_csum); + + /* Push IPv6 header in front of ICMPv6 packet */ + i6h = (struct ip6_hdr *) msgb_push(msg, sizeof(*i6h)); + /* 4 bits version, 8 bits TC, 20 bits flow-ID */ + i6h->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000); + i6h->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len); + i6h->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6; + i6h->ip6_ctlun.ip6_un1.ip6_un1_hlim = 255; + i6h->ip6_src = *saddr; + i6h->ip6_dst = *daddr; + return skb_csum; +} + +/*! construct a RFC4861 compliant ICMPv6 router soliciation + * \param[in] saddr Source IPv6 address for router advertisement + * \param[in] daddr Destination IPv6 address for router advertisement IPv6 header + * \param[in] prefix The single prefix to be advertised (/64 implied!) + * \returns callee-allocated message buffer containing router advertisement */ +struct msgb *osmo_icmpv6_construct_rs(const struct in6_addr *saddr) +{ + struct msgb *msg = msgb_alloc_headroom(512, 128, "IPv6 RS"); + struct osmo_icmpv6_rsol_hdr *rs; + OSMO_ASSERT(msg); + rs = (struct osmo_icmpv6_rsol_hdr *) msgb_put(msg, sizeof(*rs)); + rs->hdr.type = 133; /* see RFC4861 4.1 */ + rs->hdr.code = 0; /* see RFC4861 4.1 */ + rs->hdr.csum = 0; /* updated below */ + rs->reserved = 0; /* see RFC4861 4.1 */ + + rs->hdr.csum = osmo_icmpv6_prepend_ip6hdr(msg, saddr, &osmo_icmpv6_all_router_mcast_addr); + + return msg; +} +/*! construct a 3GPP 29.061 compliant router advertisement for a given prefix + * \param[in] saddr Source IPv6 address for router advertisement + * \param[in] daddr Destination IPv6 address for router advertisement IPv6 header + * \param[in] prefix The single prefix to be advertised (/64 implied!) + * \returns callee-allocated message buffer containing router advertisement */ +struct msgb *osmo_icmpv6_construct_ra(const struct in6_addr *saddr, + const struct in6_addr *daddr, + const struct in6_addr *prefix, + uint32_t mtu) +{ + struct msgb *msg = msgb_alloc_headroom(512, 128, "IPv6 RA"); + struct osmo_icmpv6_radv_hdr *ra; + struct osmo_icmpv6_opt_prefix *ra_opt_pref; + struct osmo_icmpv6_opt_mtu *ra_opt_mtu; + + OSMO_ASSERT(msg); + + ra = (struct osmo_icmpv6_radv_hdr *) msgb_put(msg, sizeof(*ra)); + ra->hdr.type = 134; /* see RFC4861 4.2 */ + ra->hdr.code = 0; /* see RFC4861 4.2 */ + ra->hdr.csum = 0; /* updated below */ + ra->cur_ho_limit = 64; /* seems reasonable? */ + /* the GGSN shall leave the M-flag cleared in the Router + * Advertisement messages */ + ra->m = 0; + /* The GGSN may set the O-flag if there are additional + * configuration parameters that need to be fetched by the MS */ + ra->o = 0; /* no DHCPv6 */ + ra->res = 0; + /* RFC4861 Default: 3 * MaxRtrAdvInterval */ + ra->router_lifetime = htons(3*GGSN_MaxRtrAdvInterval); + ra->reachable_time = 0; /* Unspecified */ + + /* RFC4861 Section 4.6.2 */ + ra_opt_pref = (struct osmo_icmpv6_opt_prefix *) msgb_put(msg, sizeof(*ra_opt_pref)); + ra_opt_pref->hdr.type = 3; /* RFC4861 4.6.2 */ + ra_opt_pref->hdr.len = 4; /* RFC4861 4.6.2 */ + ra_opt_pref->prefix_len = 64; /* only prefix length as per 3GPP */ + /* The Prefix is contained in the Prefix Information Option of + * the Router Advertisements and shall have the A-flag set + * and the L-flag cleared */ + ra_opt_pref->a = 1; + ra_opt_pref->l = 0; + ra_opt_pref->res = 0; + /* The lifetime of the prefix shall be set to infinity */ + ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime); + ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime); + ra_opt_pref->res2 = 0; + memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix)); + + /* RFC4861 Section 4.6.4, MTU */ + ra_opt_mtu = (struct osmo_icmpv6_opt_mtu *) msgb_put(msg, sizeof(*ra_opt_mtu)); + ra_opt_mtu->hdr.type = 5; /* RFC4861 4.6.4 */ + ra_opt_mtu->hdr.len = 1; /* RFC4861 4.6.4 */ + ra_opt_mtu->reserved = 0; + ra_opt_mtu->mtu = htonl(mtu); + + /* The Prefix is contained in the Prefix Information Option of + * the Router Advertisements and shall have the A-flag set + * and the L-flag cleared */ + ra_opt_pref->a = 1; + ra_opt_pref->l = 0; + ra_opt_pref->res = 0; + /* The lifetime of the prefix shall be set to infinity */ + ra_opt_pref->valid_lifetime = htonl(GGSN_AdvValidLifetime); + ra_opt_pref->preferred_lifetime = htonl(GGSN_AdvPreferredLifetime); + ra_opt_pref->res2 = 0; + memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix)); + + /* checksum */ + ra->hdr.csum = osmo_icmpv6_prepend_ip6hdr(msg, saddr, daddr); + + return msg; +} + +/* Validate an ICMPv6 router solicitation according to RFC4861 6.1.1 */ +bool osmo_icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len) +{ + const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; + //const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h)); + + /* Hop limit field must have 255 */ + if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255) + return false; + /* FIXME: ICMP checksum is valid */ + /* ICMP length (derived from IP length) is 8 or more octets */ + if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 8) + return false; + /* FIXME: All included options have a length > 0 */ + /* FIXME: If IP source is unspecified, no source link-layer addr option */ + return true; +} + +/* Validate an ICMPv6 router advertisement according to RFC4861 6.1.2. + Returns pointer packet header on success, NULL otherwise. */ +struct osmo_icmpv6_radv_hdr *osmo_icmpv6_validate_router_adv(const uint8_t *pack, unsigned len) +{ + const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; + const struct osmo_icmpv6_hdr *ic6h = (struct osmo_icmpv6_hdr *) (pack + sizeof(*ip6h)); + + /* ICMP length (derived from IP length) is 16 or more octets */ + if (len < sizeof(*ip6h) + 16) + return NULL; + + if (ic6h->type != 134) /* router advertismenet type */ + return NULL; + + /*Routers must use their link-local address */ + if (!IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src)) + return NULL; + /* Hop limit field must have 255 */ + if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255) + return NULL; + /* ICMP Code is 0 */ + if (ic6h->code != 0) + return NULL; + /* ICMP length (derived from IP length) is 16 or more octets */ + if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 16) + return NULL; + /* FIXME: All included options have a length > 0 */ + /* FIXME: If IP source is unspecified, no source link-layer addr option */ + return (struct osmo_icmpv6_radv_hdr *)ic6h; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/ip_checksum.c new/libosmo-netif-1.8.0/src/ip_checksum.c --- old/libosmo-netif-1.7.0/src/ip_checksum.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/ip_checksum.c 2026-08-21 07:19:39.000000000 +0200 @@ -0,0 +1,214 @@ +/* + * + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <[email protected]> + * Arnt Gulbrandsen, <[email protected]> + * Tom May, <[email protected]> + * Andreas Schwab, <[email protected]> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek: + * Fixed some nasty bugs, causing some horrible crashes. + * A: At some points, the sum (%0) was used as + * length-counter instead of the length counter + * (%1). Thanks to Roman Hodek for pointing this out. + * B: GCC seems to mess up if one uses too many + * data-registers to hold input values and one tries to + * specify d0 and d1 as scratch registers. Letting gcc + * choose these registers itself solves the problem. + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access + kills, so most of the assembly has to go. */ + +#if defined(__FreeBSD__) +#define _KERNEL /* needed on FreeBSD 10.x for s6_addr32 */ +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/endian.h> +#endif + +#include <arpa/inet.h> + +#include <osmocom/netif/ip_checksum.h> + +static inline unsigned short from32to16(unsigned int x) +{ + /* add up 16-bit and 16-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +static unsigned int do_csum(const unsigned char *buff, int len) +{ + int odd; + unsigned int result = 0; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { +#if BYTE_ORDER == LITTLE_ENDIAN + result += (*buff << 8); +#else + result = *buff; +#endif + len--; + buff++; + } + if (len >= 2) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + len -= 2; + buff += 2; + } + if (len >= 4) { + const unsigned char *end = buff + ((unsigned)len & ~3); + unsigned int carry = 0; + do { + unsigned int w = *(unsigned int *) buff; + buff += 4; + result += carry; + result += w; + carry = (w > result); + } while (buff < end); + result += carry; + result = (result & 0xffff) + (result >> 16); + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) +#if BYTE_ORDER == LITTLE_ENDIAN + result += *buff; +#else + result += (*buff << 8); +#endif + result = from32to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +/* fold a partial checksum */ +static uint16_t csum_fold(uint32_t csum) +{ + uint32_t sum = (uint32_t)csum; + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (uint16_t)~sum; +} + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + */ +uint16_t osmo_ip_checksum_fast_csum(const void *iph, unsigned int ihl) +{ + return (uint16_t)~do_csum(iph, ihl*4); +} + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +uint32_t osmo_ip_checksum_csum_partial(const void *buff, int len, uint32_t wsum) +{ + unsigned int sum = (unsigned int)wsum; + unsigned int result = do_csum(buff, len); + + /* add in old sum, and carry.. */ + result += sum; + if (sum > result) + result += 1; + return (uint32_t)result; +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +uint16_t osmo_ip_checksum_compute_csum(const void *buff, int len) +{ + return (uint16_t)~do_csum(buff, len); +} + +uint16_t osmo_ip_checksum_csum_ipv6_magic(const struct in6_addr *saddr, + const struct in6_addr *daddr, + uint32_t len, uint8_t proto, uint32_t csum) +{ + int carry; + uint32_t ulen; + uint32_t uproto; + uint32_t sum = (uint32_t)csum; + + sum += (uint32_t)saddr->s6_addr32[0]; + carry = (sum < (uint32_t)saddr->s6_addr32[0]); + sum += carry; + + sum += (uint32_t)saddr->s6_addr32[1]; + carry = (sum < (uint32_t)saddr->s6_addr32[1]); + sum += carry; + + sum += (uint32_t)saddr->s6_addr32[2]; + carry = (sum < (uint32_t)saddr->s6_addr32[2]); + sum += carry; + + sum += (uint32_t)saddr->s6_addr32[3]; + carry = (sum < (uint32_t)saddr->s6_addr32[3]); + sum += carry; + + sum += (uint32_t)daddr->s6_addr32[0]; + carry = (sum < (uint32_t)daddr->s6_addr32[0]); + sum += carry; + + sum += (uint32_t)daddr->s6_addr32[1]; + carry = (sum < (uint32_t)daddr->s6_addr32[1]); + sum += carry; + + sum += (uint32_t)daddr->s6_addr32[2]; + carry = (sum < (uint32_t)daddr->s6_addr32[2]); + sum += carry; + + sum += (uint32_t)daddr->s6_addr32[3]; + carry = (sum < (uint32_t)daddr->s6_addr32[3]); + sum += carry; + + ulen = (uint32_t)htonl((uint32_t) len); + sum += ulen; + carry = (sum < ulen); + sum += carry; + + uproto = (uint32_t)htonl(proto); + sum += uproto; + carry = (sum < uproto); + sum += carry; + + return csum_fold((uint32_t)sum); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/stream.c new/libosmo-netif-1.8.0/src/stream.c --- old/libosmo-netif-1.7.0/src/stream.c 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/stream.c 2026-08-21 07:19:39.000000000 +0200 @@ -49,12 +49,12 @@ #include "config.h" -#include <osmocom/netif/sctp.h> - /*! \cond private */ #ifdef HAVE_LIBSCTP +#include <osmocom/netif/sctp.h> + /* is any of the bytes from offset .. u8_size in 'u8' non-zero? return offset or -1 if all zero */ static int byte_nonzero(const uint8_t *u8, unsigned int offset, unsigned int u8_size) { @@ -182,10 +182,22 @@ #ifdef HAVE_LIBSCTP case IPPROTO_SCTP: rc = setsockopt(fd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)); + if (rc < 0) { + char errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + LOGP(DLINP, LOGL_ERROR, "setsockopt(IPPROTO_SCTP, SCTP_NODELAY, %u) failed: %d (%s)\n", + on, errno, errbuf); + } break; #endif case IPPROTO_TCP: rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (rc < 0) { + char errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + LOGP(DLINP, LOGL_ERROR, "setsockopt(IPPROTO_TCP, TCP_NODELAY, %u) failed: %d (%s)\n", + on, errno, errbuf); + } break; default: rc = -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/stream_cli.c new/libosmo-netif-1.8.0/src/stream_cli.c --- old/libosmo-netif-1.7.0/src/stream_cli.c 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/stream_cli.c 2026-08-21 07:19:39.000000000 +0200 @@ -49,7 +49,9 @@ #include "config.h" +#ifdef HAVE_LIBSCTP #include <osmocom/netif/sctp.h> +#endif /*! \file stream_cli.c */ @@ -411,6 +413,26 @@ #endif } +/* Update cli->sockname based on socket info: */ +static void stream_cli_update_iofd_name(struct osmo_stream_cli *cli) +{ + if (!(cli->mode == OSMO_STREAM_MODE_OSMO_IO && cli->iofd)) + return; + + char *tmp = talloc_asprintf(cli, "%s,%s", cli->name, cli->sockname); + osmo_iofd_set_name(cli->iofd, tmp); + talloc_free(tmp); +} + +/* Update cli->sockname based on socket info: */ +static void stream_cli_update_sockname(struct osmo_stream_cli *cli) +{ + osmo_sock_get_name_buf(cli->sockname, sizeof(cli->sockname), osmo_stream_cli_get_fd(cli)); + /* Update stream iofd with the new cli->sockname too: */ + stream_cli_update_iofd_name(cli); + +} + /* returns true if cli is freed */ static bool stream_cli_handle_connecting(struct osmo_stream_cli *cli, int res) { @@ -435,8 +457,7 @@ if (cli->mode == OSMO_STREAM_MODE_OSMO_FD && llist_empty(&cli->tx_queue)) osmo_fd_write_disable(&cli->ofd); - /* Update sockname based on socket info: */ - osmo_sock_get_name_buf(cli->sockname, sizeof(cli->sockname), osmo_stream_cli_get_fd(cli)); + stream_cli_update_sockname(cli); LOGSCLI(cli, LOGL_INFO, "connection established\n"); cli->state = STREAM_CLI_STATE_CONNECTED; @@ -583,8 +604,10 @@ (void)stream_cli_handle_connecting(cli, res); break; case STREAM_CLI_STATE_CONNECTED: - if (msg && res <= 0) { - LOGSCLI(cli, LOGL_ERROR, "received error %d in response to send\n", res); + if (msg && res < 0) { + char errbuf[64]; + strerror_r(-res, errbuf, sizeof(errbuf)); + LOGSCLI(cli, LOGL_ERROR, "error to send: %d (%s)\n", res, errbuf); (void)stream_cli_reconnect(cli); } /* res=0 && msgb=NULL: "connected notify", but we already received before a read_cb @@ -691,8 +714,8 @@ talloc_free((void *)cli->name); cli->name = name; - if (cli->mode == OSMO_STREAM_MODE_OSMO_IO && cli->iofd) - osmo_iofd_set_name(cli->iofd, cli->name); + /* Update stream iofd with the new cli->name too: */ + stream_cli_update_iofd_name(cli); } /*! Retrieve name previously set on the cli object (see osmo_stream_cli_set_name()). @@ -876,7 +899,7 @@ configure_cli_segmentation_cb(cli); } -/*! Set the socket type for the stream server link. +/*! Set the socket type for the stream client. * Any changes to this setting will only become active upon next (re)connect. * \param[in] cli Stream Client to modify * \param[in] type Socket Type (like SOCK_STREAM (default), SOCK_SEQPACKET, ...) @@ -896,7 +919,7 @@ return 0; } -/*! Set the socket domain for the stream client link. +/*! Set the socket domain for the stream client. * Any changes to this setting will only become active upon next (re)connect. * \param[in] cli Stream Client to modify * \param[in] domain Socket Domain (like AF_UNSPEC (default for IP), AF_UNIX, AF_INET, ...) @@ -1224,9 +1247,8 @@ * \return negative on error, 0 on success */ int osmo_stream_cli_open(struct osmo_stream_cli *cli) { - int ret, flags; + int ret; int fd = -1; - unsigned int local_addrcnt; /* we are reconfiguring this socket, close existing first. */ if ((cli->flags & OSMO_STREAM_CLI_F_RECONF) && osmo_stream_cli_get_fd(cli) >= 0) { @@ -1246,8 +1268,9 @@ switch (cli->proto) { #ifdef HAVE_LIBSCTP case IPPROTO_SCTP: - local_addrcnt = cli->local_addrcnt; - flags = OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_NONBLOCK | + { + unsigned int local_addrcnt = cli->local_addrcnt; + int flags = OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_NONBLOCK | OSMO_SOCK_F_DSCP(cli->ip_dscp) | OSMO_SOCK_F_PRIO(cli->sk_prio); if (cli->local_addrcnt > 0 || cli->local_port > 0) { /* explicit bind required? */ flags |= OSMO_SOCK_F_BIND; @@ -1260,6 +1283,7 @@ (const char **)cli->addr, cli->addrcnt, cli->port, flags, &cli->ma_pars); break; + } #endif default: ret = osmo_sock_init2(cli->sk_domain, cli->sk_type, cli->proto, @@ -1383,9 +1407,11 @@ case OSMO_STREAM_MODE_OSMO_IO: /* whenever osmo_stream_cli_is_connected() [see above check], we should have an iofd */ OSMO_ASSERT(cli->iofd); +#ifdef HAVE_LIBSCTP if (cli->proto == IPPROTO_SCTP) rc = stream_iofd_sctp_send_msgb(cli->iofd, msg, MSG_NOSIGNAL); else +#endif rc = osmo_iofd_write_msgb(cli->iofd, msg); if (rc < 0) msgb_free(msg); @@ -1433,7 +1459,9 @@ case IPPROTO_SCTP: { char log_pfx[128]; - snprintf(log_pfx, sizeof(log_pfx), "CLICONN(%s,%s)", cli->name ? : "", cli->sockname); + snprintf(log_pfx, sizeof(log_pfx), "CLICONN(%s,%s){%s}", + cli->name ? : "", cli->sockname, + get_value_string(stream_cli_state_names, cli->state)); ret = stream_sctp_recvmsg_wrapper(cli->ofd.fd, msg, log_pfx); break; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/stream_srv.c new/libosmo-netif-1.8.0/src/stream_srv.c --- old/libosmo-netif-1.7.0/src/stream_srv.c 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/stream_srv.c 2026-08-21 07:19:39.000000000 +0200 @@ -50,7 +50,9 @@ #include "config.h" +#ifdef HAVE_LIBSCTP #include <osmocom/netif/sctp.h> +#endif /*! \file stream_srv.c */ @@ -172,8 +174,11 @@ if (link->flags & OSMO_STREAM_SRV_F_NODELAY) { ret = stream_setsockopt_nodelay(sock_fd, link->proto, 1); - if (ret < 0) + if (ret < 0) { + LOGSLNK(link, LOGL_ERROR, "stream_setsockopt_nodelay: failed setsockopt err=%d\n", + errno); goto error_close_socket; + } } if (link->ip_dscp > 0) { @@ -294,7 +299,19 @@ */ int osmo_stream_srv_link_set_ip_dscp(struct osmo_stream_srv_link *link, uint8_t ip_dscp) { + int rc; + link->ip_dscp = ip_dscp; + + if (!osmo_stream_srv_link_is_opened(link)) + return 0; + + rc = osmo_sock_set_dscp(link->ofd.fd, link->ip_dscp); + if (rc < 0) { + LOGSLNK(link, LOGL_ERROR, "set_ip_dscp(%u): failed setsockopt err=%d\n", + link->ip_dscp, errno); + return -EIO; + } return 0; } @@ -568,6 +585,10 @@ int osmo_stream_srv_link_open(struct osmo_stream_srv_link *link) { int ret; + /* No need to set OSMO_SOCK_F_NONBLOCK since non-blocking behavior is only + * required later on, during accept() call, and and osmo_fd_register() + * below already takes care of setting the socket as non-blocking. */ + const int sk_flags = OSMO_SOCK_F_BIND; if (link->ofd.fd >= 0) { /* No reconfigure needed for existing socket, we are fine */ @@ -581,7 +602,7 @@ switch (link->sk_domain) { case AF_UNIX: - ret = osmo_sock_unix_init(link->sk_type, 0, link->addr[0], OSMO_SOCK_F_BIND); + ret = osmo_sock_unix_init(link->sk_type, 0, link->addr[0], sk_flags); break; case AF_UNSPEC: case AF_INET: @@ -591,12 +612,12 @@ case IPPROTO_SCTP: ret = osmo_sock_init2_multiaddr2(link->sk_domain, link->sk_type, link->proto, (const char **)link->addr, link->addrcnt, link->port, - NULL, 0, 0, OSMO_SOCK_F_BIND, &link->ma_pars); + NULL, 0, 0, sk_flags, &link->ma_pars); break; #endif default: ret = osmo_sock_init(link->sk_domain, link->sk_type, link->proto, - link->addr[0], link->port, OSMO_SOCK_F_BIND); + link->addr[0], link->port, sk_flags); } break; default: @@ -605,6 +626,18 @@ if (ret < 0) return ret; + if (link->ip_dscp > 0) { + int rc; + rc = osmo_sock_set_dscp(ret, link->ip_dscp); + if (rc < 0) { + LOGSLNK(link, LOGL_ERROR, "set_ip_dscp(%u): failed setsockopt err=%d\n", + link->ip_dscp, errno); + close(ret); + link->ofd.fd = -1; + return -EIO; + } + } + link->ofd.fd = ret; if (osmo_fd_register(&link->ofd) < 0) { close(ret); @@ -792,8 +825,11 @@ struct osmo_stream_srv *conn = osmo_iofd_get_data(iofd); LOGSSRV(conn, LOGL_DEBUG, "connected write\n"); - if (res < 0) - LOGSSRV(conn, LOGL_ERROR, "error to send: %s\n", strerror(errno)); + if (res < 0) { + char errbuf[64]; + strerror_r(-res, errbuf, sizeof(errbuf)); + LOGSSRV(conn, LOGL_ERROR, "error to send: %d (%s)\n", res, errbuf); + } if (osmo_iofd_txqueue_len(iofd) == 0) if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) @@ -1038,12 +1074,15 @@ osmo_sock_get_name_buf(conn->sockname, sizeof(conn->sockname), fd); +#ifdef HAVE_LIBSCTP if (link->proto == IPPROTO_SCTP) { conn->iofd = osmo_iofd_setup(conn, fd, conn->sockname, OSMO_IO_FD_MODE_RECVMSG_SENDMSG, &srv_ioops_sctp, conn); if (conn->iofd) osmo_iofd_set_cmsg_size(conn->iofd, CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))); - } else { + } else +#endif + { conn->iofd = osmo_iofd_setup(conn, fd, conn->sockname, OSMO_IO_FD_MODE_READ_WRITE, &srv_ioops, conn); } @@ -1096,8 +1135,11 @@ talloc_free((void *)conn->name); conn->name = name; - if (conn->mode == OSMO_STREAM_MODE_OSMO_IO && conn->iofd) - osmo_iofd_set_name(conn->iofd, conn->name); + if (conn->mode == OSMO_STREAM_MODE_OSMO_IO && conn->iofd) { + char *tmp = talloc_asprintf(conn, "%s,%s", conn->name, conn->sockname); + osmo_iofd_set_name(conn->iofd, tmp); + talloc_free(tmp); + } } /*! Retrieve name previously set on the srv object (see osmo_stream_srv_set_name()). @@ -1369,9 +1411,11 @@ osmo_fd_write_enable(&conn->ofd); break; case OSMO_STREAM_MODE_OSMO_IO: +#ifdef HAVE_LIBSCTP if (conn->srv->proto == IPPROTO_SCTP) rc = stream_iofd_sctp_send_msgb(conn->iofd, msg, MSG_NOSIGNAL); else +#endif rc = osmo_iofd_write_msgb(conn->iofd, msg); if (rc < 0) msgb_free(msg); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/twjit.c new/libosmo-netif-1.8.0/src/twjit.c --- old/libosmo-netif-1.7.0/src/twjit.c 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/twjit.c 2026-08-21 07:19:39.000000000 +0200 @@ -51,7 +51,7 @@ uint16_t drop_int_count; }; -/*! Each twjit instance is in one of 4 fundamental states at any moment, +/*! Each twjit instance is in one of 5 fundamental states at any moment, * as enumerated here. See document section 2.3.1 for state transition * diagram. */ @@ -62,6 +62,8 @@ TWJIT_STATE_HUNT, /*! one subbuf is both flowing out and accepting new packets */ TWJIT_STATE_FLOWING, + /*! limited continuation of FLOWING state past underrun */ + TWJIT_STATE_UNDERRUN, /*! one subbuf is flowing out while another receives new packets */ TWJIT_STATE_HANDOVER, }; @@ -89,6 +91,8 @@ uint8_t read_sb; /*! current subbuf being written, 0 or 1 */ uint8_t write_sb; + /*! UNDERRUN state: remaining number of ticks before we give up */ + uint16_t underrun_countdown; /*! RTP timestamp of the most recently received packet */ uint32_t last_ts; /*! RTP sequence number of the most recently received packet */ @@ -533,6 +537,34 @@ } insert_pkt_write_sb(twjit, msg, rx_ts); return; + case TWJIT_STATE_UNDERRUN: + id = check_input_for_subbuf(twjit, false, rx_ssrc, rx_ts, + rtph->marker); + if (id == INPUT_CONTINUE) { + /* Underrun extension mechanism did its intended + * job: continue flow without reset. */ + twjit->state = TWJIT_STATE_FLOWING; + insert_pkt_write_sb(twjit, msg, rx_ts); + return; + } + /* For either INPUT_TOO_OLD or INPUT_RESET, we take the same + * path that existed prior to addition of UNDERRUN state: + * the original underrun was a "hard" one, and we initiate + * HUNT with this newly received packet. + * + * Please note that we don't increment too_old stats counter + * in the case of INPUT_TOO_OLD: that counter counts packets + * that were discarded for being too old in FLOWING state. + * In the present case, however, the packet is not discarded: + * instead it is fed into HUNT state to begin acquisition of + * a new flow. This behavior is necessary in order to + * reacquire flow if the original underrun was caused by a + * sudden increase in IP path latency. + */ + twjit->stats.underruns++; + twjit->state = TWJIT_STATE_HUNT; + init_subbuf_first_packet(twjit, msg, rx_ssrc, rx_ts); + return; default: OSMO_ASSERT(0); } @@ -598,6 +630,30 @@ sb->drop_int_count = twjit->config.thinning_int - 2; } +static void underrun_advance_ts(struct osmo_twjit *twjit) +{ + struct twjit_subbuf *sb = &twjit->sb[twjit->read_sb]; + + sb->head_ts += twjit->ts_quantum; +} + +/* This static function is called only from osmo_twjit_output() in the + * case of underrun in FLOWING state. It has been factored out in order + * to reduce the level of indentation and thus make it easier to keep + * line length below the 80 character limit which the author regards as + * an unviolable Universal constant. */ +static void handle_flowing_underrun(struct osmo_twjit *twjit) +{ + if (twjit->config.underrun_ext) { + twjit->state = TWJIT_STATE_UNDERRUN; + twjit->underrun_countdown = twjit->config.underrun_ext - 1; + underrun_advance_ts(twjit); + twjit->stats.soft_underruns++; + } else { + twjit->state = TWJIT_STATE_EMPTY; + } +} + static void toss_read_queue(struct osmo_twjit *twjit) { struct twjit_subbuf *sb = &twjit->sb[twjit->read_sb]; @@ -628,11 +684,19 @@ return pull_from_read_sb(twjit); case TWJIT_STATE_FLOWING: if (read_sb_is_empty(twjit)) { - twjit->state = TWJIT_STATE_EMPTY; + handle_flowing_underrun(twjit); return NULL; } read_sb_thinning(twjit); return pull_from_read_sb(twjit); + case TWJIT_STATE_UNDERRUN: + if (twjit->underrun_countdown) { + twjit->underrun_countdown--; + underrun_advance_ts(twjit); + } else { + twjit->state = TWJIT_STATE_EMPTY; + } + return NULL; case TWJIT_STATE_HANDOVER: if (starting_sb_is_ready(twjit)) { toss_read_queue(twjit); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/src/twjit_conf.c new/libosmo-netif-1.8.0/src/twjit_conf.c --- old/libosmo-netif-1.7.0/src/twjit_conf.c 2025-12-04 06:19:35.000000000 +0100 +++ new/libosmo-netif-1.8.0/src/twjit_conf.c 2026-08-21 07:19:39.000000000 +0200 @@ -136,6 +136,10 @@ conf->start_max_delta, VTY_NEWLINE); } + if (conf->underrun_ext) { + vty_out(vty, "%s underrun-extension %u%s", prefix, + conf->underrun_ext, VTY_NEWLINE); + } vty_out(vty, "%s marker-handling %s%s", prefix, conf->handover_on_marker ? "handover" : "ignore", VTY_NEWLINE); @@ -222,6 +226,25 @@ return CMD_SUCCESS; } +DEFUN(cfg_underrun_ext, cfg_underrun_ext_cmd, + "underrun-extension <1-65535>", + "Underrun extension for intentional gaps (DTX)\n" + "Maximum number of consecutive omitted packets\n") +{ + struct osmo_twjit_config *conf = vty->index; + conf->underrun_ext = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_no_underrun_ext, cfg_no_underrun_ext_cmd, + "no underrun-extension", + NO_STR "Underrun extension for intentional gaps\n") +{ + struct osmo_twjit_config *conf = vty->index; + conf->underrun_ext = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_marker_handling, cfg_marker_handling_cmd, "marker-handling (handover|ignore)", "How to handle RTP packets with marker bit set\n" @@ -246,6 +269,8 @@ install_lib_element(twjit_node, &cfg_no_start_min_delta_cmd); install_lib_element(twjit_node, &cfg_start_max_delta_cmd); install_lib_element(twjit_node, &cfg_no_start_max_delta_cmd); + install_lib_element(twjit_node, &cfg_underrun_ext_cmd); + install_lib_element(twjit_node, &cfg_no_underrun_ext_cmd); install_lib_element(twjit_node, &cfg_marker_handling_cmd); } @@ -329,6 +354,20 @@ return 0; } +/*! Non-vty function for underrun-extension setting + * + * \param[in] conf twjit config instance to operate on. + * \param[in] underrun_ext Maximum permissible intentional gap, in packets, + * or 0 to disable this mechanism. + * \returns 0 if successful, negative on errors. + */ +int osmo_twjit_config_set_underrun_ext(struct osmo_twjit_config *conf, + uint16_t underrun_ext) +{ + conf->underrun_ext = underrun_ext; + return 0; +} + /*! Non-vty function for marker-handling setting * * \param[in] conf twjit config instance to operate on. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/tests/package.m4 new/libosmo-netif-1.8.0/tests/package.m4 --- old/libosmo-netif-1.7.0/tests/package.m4 2025-12-04 06:19:50.000000000 +0100 +++ new/libosmo-netif-1.8.0/tests/package.m4 2026-08-21 07:19:56.000000000 +0200 @@ -4,9 +4,9 @@ m4_define([AT_PACKAGE_TARNAME], [libosmo-netif]) m4_define([AT_PACKAGE_VERSION], - [1.7.0]) + [1.8.0]) m4_define([AT_PACKAGE_STRING], - [libosmo-netif 1.7.0]) + [libosmo-netif 1.8.0]) m4_define([AT_PACKAGE_BUGREPORT], [[email protected]]) m4_define([AT_PACKAGE_URL], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libosmo-netif-1.7.0/tests/testsuite new/libosmo-netif-1.8.0/tests/testsuite --- old/libosmo-netif-1.7.0/tests/testsuite 2025-12-04 06:19:50.000000000 +0100 +++ new/libosmo-netif-1.8.0/tests/testsuite 2026-08-21 07:19:56.000000000 +0200 @@ -926,7 +926,7 @@ # List of tests. if $at_list_p; then cat <<_ATEOF || at_write_fail=1 -libosmo-netif 1.7.0 test suite test groups: +libosmo-netif 1.8.0 test suite test groups: NUM: FILE-NAME:LINE TEST-GROUP-NAME KEYWORDS @@ -967,7 +967,7 @@ exit $at_write_fail fi if $at_version_p; then - printf "%s\n" "$as_me (libosmo-netif 1.7.0)" && + printf "%s\n" "$as_me (libosmo-netif 1.8.0)" && cat <<\_ATEOF || at_write_fail=1 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1155,11 +1155,11 @@ # Banners and logs. printf "%s\n" "## ------------------------------- ## -## libosmo-netif 1.7.0 test suite. ## +## libosmo-netif 1.8.0 test suite. ## ## ------------------------------- ##" { printf "%s\n" "## ------------------------------- ## -## libosmo-netif 1.7.0 test suite. ## +## libosmo-netif 1.8.0 test suite. ## ## ------------------------------- ##" echo @@ -1976,7 +1976,7 @@ printf "%s\n" "Please send $at_msg and all information you think might help: To: <[email protected]> - Subject: [libosmo-netif 1.7.0] $as_me: $at_msg1$at_msg2 + Subject: [libosmo-netif 1.8.0] $as_me: $at_msg1$at_msg2 You may investigate any problem if you feel able to do so, in which case the test suite provides a good starting point. Its output may
