Projects
osmocom:latest
libosmo-netif
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 5
View file
libosmo-netif.spec
Changed
@@ -14,21 +14,21 @@ Name: libosmo-netif Requires: osmocom-latest -Version: 1.3.0 +Version: 1.4.0 Release: 0 Summary: Osmocom library for muxed audio License: GPL-2.0-or-later Group: Productivity/Telephony/Utilities URL: https://osmocom.org/projects/libosmo-netif -Source: libosmo-netif_1.3.0.tar.xz +Source: libosmo-netif_1.4.0.tar.xz Source1: rpmlintrc BuildRequires: automake BuildRequires: libtool >= 2 BuildRequires: lksctp-tools-devel BuildRequires: pkgconfig >= 0.20 -BuildRequires: pkgconfig(libosmocore) >= 1.8.0 -BuildRequires: pkgconfig(libosmogsm) >= 1.8.0 -BuildRequires: pkgconfig(libosmocodec) >= 1.8.0 +BuildRequires: pkgconfig(libosmocore) >= 1.9.0 +BuildRequires: pkgconfig(libosmogsm) >= 1.9.0 +BuildRequires: pkgconfig(libosmocodec) >= 1.9.0 %description Network interface demuxer library for OsmoCom projects.
View file
libosmo-netif_1.3.0.dsc -> libosmo-netif_1.4.0.dsc
Changed
@@ -2,21 +2,21 @@ Source: libosmo-netif Binary: libosmonetif11, libosmo-netif-dev, libosmo-netif-doc, libosmo-netif-dbg Architecture: any all -Version: 1.3.0 +Version: 1.4.0 Maintainer: Osmocom team <openbsc@lists.osmocom.org> Homepage: https://projects.osmocom.org/projects/libosmo-netif Standards-Version: 3.9.6 Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmo-netif Vcs-Git: https://gitea.osmocom.org/osmocom/libosmo-netif -Build-Depends: debhelper (>= 9), autotools-dev, autoconf, automake, libtool, dh-autoreconf, libdpkg-perl, git, doxygen, libosmocore-dev (>= 1.8.0), pkg-config, libpcap0.8-dev, libsctp-dev +Build-Depends: debhelper (>= 10), autotools-dev, autoconf, automake, libtool, dh-autoreconf, libdpkg-perl, git, doxygen, libosmocore-dev (>= 1.9.0), pkg-config, libpcap0.8-dev, libsctp-dev Package-List: libosmo-netif-dbg deb debug extra arch=any libosmo-netif-dev deb libdevel optional arch=any libosmo-netif-doc deb doc optional arch=all libosmonetif11 deb libs optional arch=any Checksums-Sha1: - 3ccd5889d74e846abe11113926027a8b1eaf5fc6 180288 libosmo-netif_1.3.0.tar.xz + 6000513b1c6ecc605ffb1741089a304ceb7f406a 187720 libosmo-netif_1.4.0.tar.xz Checksums-Sha256: - 603f3318ec0ca35cbe1c1301f015b22e29205449a8d82a630751e5e5da7adc81 180288 libosmo-netif_1.3.0.tar.xz + 24eed47dc650822cc84fae35ace4b18b837c8e82427e788b271e96469cdfe4d9 187720 libosmo-netif_1.4.0.tar.xz Files: - b738aca6613ba1c392f5844833c9b43e 180288 libosmo-netif_1.3.0.tar.xz + 53c29e93fce3fd8cddefe8c745988dfd 187720 libosmo-netif_1.4.0.tar.xz
View file
libosmo-netif_1.3.0.tar.xz/.tarball-version -> libosmo-netif_1.4.0.tar.xz/.tarball-version
Changed
@@ -1 +1 @@ -1.3.0 +1.4.0
View file
libosmo-netif_1.3.0.tar.xz/configure.ac -> libosmo-netif_1.4.0.tar.xz/configure.ac
Changed
@@ -89,9 +89,9 @@ dnl Generate the output AM_CONFIG_HEADER(config.h) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0) -PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0) -PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.8.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0) +PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.9.0) AC_ARG_ENABLE(lapd_examples, AS_HELP_STRING( @@ -100,7 +100,7 @@ ), lapd_examples=$enableval, lapd_examples="no") AS_IF(test "x$lapd_examples" = "xyes", - PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.4.0) + PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0) AC_DEFINE(ENABLE_LAPD, 1, Enable LAPD examples) ) AM_CONDITIONAL(ENABLE_LAPD, test "x$lapd_examples" = "xyes")
View file
libosmo-netif_1.3.0.tar.xz/contrib/libosmo-netif.spec.in -> libosmo-netif_1.4.0.tar.xz/contrib/libosmo-netif.spec.in
Changed
@@ -25,9 +25,9 @@ BuildRequires: libtool >= 2 BuildRequires: lksctp-tools-devel BuildRequires: pkgconfig >= 0.20 -BuildRequires: pkgconfig(libosmocore) >= 1.8.0 -BuildRequires: pkgconfig(libosmogsm) >= 1.8.0 -BuildRequires: pkgconfig(libosmocodec) >= 1.8.0 +BuildRequires: pkgconfig(libosmocore) >= 1.9.0 +BuildRequires: pkgconfig(libosmogsm) >= 1.9.0 +BuildRequires: pkgconfig(libosmocodec) >= 1.9.0 %description Network interface demuxer library for OsmoCom projects.
View file
libosmo-netif_1.3.0.tar.xz/debian/changelog -> libosmo-netif_1.4.0.tar.xz/debian/changelog
Changed
@@ -1,3 +1,77 @@ +libosmo-netif (1.4.0) unstable; urgency=medium + + Max + * osmo_stream_srv_link_close(): properly handle NULL input + * Add osmo_stream_srv_link_is_opened() + + Oliver Smith + * Run struct_endianness.py + * rtp.h: add RTP_PT_CSDATA + * debian: set compat level to 10 + + Neels Hofmeyr + * fix msgb leak on OSMO_STREAM_SRV_F_FLUSH_DESTROY + + Vadim Yanitskiy + * {utils,tests}/Makefile.am: reorder libraries in LDADD + * tests/Makefile.am: clean up AM_* variables + * src/Makefile.am: do not overwrite AM_LDFLAGS + + Daniel Willmann + * stream: Introduce and use osmo_stream_cli_fd() to get the fd + * stream: (typo) Change callback param name of struct osmo_stream_cli from srv to cli + * ipa-stream-server: Return -EBADF in read_cb after osmo_stream_srv_destroy() + * stream: Properly name osmo_stream_srv read callback + * cosmetic: Change name of osmo_stream_src_fd_cb() + * stream: Update log messages + * stream: Factor out reconnection handling + * stream: Use cli->state to check if cli is already closed + * stream: Correctly close osmo_stream_cli when in state WAIT_RECONNECT + * Add osmo_io support to osmo_stream_cli and osmo_stream_srv + * examples: Use new stream API in {ipa-,}stream-{client,server} + * stream: Setup ofd in osmo_stream_cli_open + * stream: Document osmo_stream_cli_create2() + * stream: Remove duplicated code in osmo_stream_cli_create() + * examples: Set logging level to DEBUG in {,ipa-}stream-{client,server} + * stream: Assert that fd is valid in stream_cli_handle_connecting() + * stream: Set state to closed before calling disconnect_cb() + * stream: Notify stream_cli on connect() + + Pau Espin Pedrol + * stream: Allow setting name printed during logging + * stream: Print socket info as part of the logging context + * stream: Drop recently added API osmo_stream_cli_create2 + * stream: Drop name param from recently added API osmo_stream_srv_create2() + * stream: srv cb: Use osmo_sockaddr and improve logging when cli connects + * stream: Rename static function to have correct prefix + * stream: Split cli/srv specific code out of stream.c + * stream_cli: Increase log level of established conn to INFO + * stream_srv: Use LOGSLNK() to print log line + * stream_srv: call setsockopt(SO_NOSIGPIPE) also in srv sockets + * stream_srv: Improve logging lines accepting new connections + * stream: Append data to current tail of message upon recv() + * stream_srv: Handle ESHUTDOWN and other write() errors destroying the socket + * stream_cli: Proper handling of send() socket errors + * sctp: Document relevant RFC specs + * stream_srv: sctp: Log error cause of COMM_LOST event + * stream_srv: Log SCTP REMOTE_ERROR events + * stream: Refactor sctp_recvmsg_wrapper() logging + * stream_cli: Forward SCTP MSG_NOTIFICATION to upper layers + * stream: Use new flag OSMO_SOCK_F_SCTP_ASCONF_SUPPORTED for SCTP sockets + * stream_test: Avoid leaking osmo_stream_srv_link + * stream: Use new libosmocore API osmo_sock_init2_multiaddr2() + * stream: Introduce API to set several transport parameters + * stream: Add new stream_{cli,srv_link} parameters to set SCTP_INITMSG sockopt values + + arehbein + * stream: Fix osmo_panic log fmts + * examples: Add extension header octet to example + * ipa: Add segmentation callback + * stream: Add server-side (segmentation) support for IPA + * stream test: Fix test output check + + -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 13:50:25 +0200 + libosmo-netif (1.3.0) unstable; urgency=medium Pau Espin Pedrol
View file
libosmo-netif_1.3.0.tar.xz/debian/compat -> libosmo-netif_1.4.0.tar.xz/debian/compat
Changed
@@ -1 +1 @@ -9 +10
View file
libosmo-netif_1.3.0.tar.xz/debian/control -> libosmo-netif_1.4.0.tar.xz/debian/control
Changed
@@ -2,7 +2,7 @@ Section: libs Priority: optional Maintainer: Osmocom team <openbsc@lists.osmocom.org> -Build-Depends: debhelper (>= 9), +Build-Depends: debhelper (>= 10), autotools-dev, autoconf, automake, @@ -11,7 +11,7 @@ libdpkg-perl, git, doxygen, - libosmocore-dev (>= 1.8.0), + libosmocore-dev (>= 1.9.0), pkg-config, libpcap0.8-dev, libsctp-dev
View file
libosmo-netif_1.3.0.tar.xz/examples/ipa-stream-client.c -> libosmo-netif_1.4.0.tar.xz/examples/ipa-stream-client.c
Changed
@@ -74,7 +74,7 @@ char *ptr; int x; - msg = osmo_ipa_msg_alloc(0); + msg = osmo_ipa_ext_msg_alloc(0); if (msg == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot alloc msg\n"); return -1; @@ -93,6 +93,7 @@ msg_sent->num = i; llist_add(&msg_sent->head, &msg_sent_list); + ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_MGCP); osmo_ipa_msg_push_header(msg, IPAC_PROTO_OSMO); osmo_stream_cli_send(conn, msg); @@ -103,21 +104,10 @@ return 0; } -static int read_cb(struct osmo_stream_cli *conn) +static int read_cb(struct osmo_stream_cli *conn, struct msgb *msg) { - struct msgb *msg; + LOGP(DIPATEST, LOGL_DEBUG, "received message from stream (len=%d)\n", msgb_length(msg)); - LOGP(DIPATEST, LOGL_DEBUG, "received message from stream\n"); - - msg = osmo_ipa_msg_alloc(0); - if (msg == NULL) { - LOGP(DIPATEST, LOGL_ERROR, "cannot allocate message\n"); - return 0; - } - if (osmo_stream_cli_recv(conn, msg) <= 0) { - LOGP(DIPATEST, LOGL_ERROR, "cannot receive message\n"); - return 0; - } if (osmo_ipa_process_msg(msg) < 0) { LOGP(DIPATEST, LOGL_ERROR, "bad IPA message\n"); return 0; @@ -126,7 +116,7 @@ int num; struct msg_sent *cur, *tmp, *found = NULL; - num = ntohl(*((int *)(msg->data + sizeof(struct ipa_head)))); + num = ntohl(*((int *)(msg->data + sizeof(struct ipa_head) + sizeof(struct ipa_head_ext)))); LOGP(DLINP, LOGL_DEBUG, "received msg number %d\n", num); llist_for_each_entry_safe(cur, tmp, &msg_sent_list, head) { @@ -169,7 +159,7 @@ tall_test = talloc_named_const(NULL, 1, "osmo_stream_client_test"); msgb_talloc_ctx_init(tall_test, 0); osmo_init_logging2(tall_test, &osmo_stream_client_test_log_info); - log_set_log_level(osmo_stderr_target, LOGL_NOTICE); + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); /* * initialize stream client. @@ -180,11 +170,12 @@ fprintf(stderr, "cannot create client\n"); exit(EXIT_FAILURE); } + osmo_stream_cli_set_name(conn, "ipa_test_client"); osmo_stream_cli_set_addr(conn, "127.0.0.1"); osmo_stream_cli_set_port(conn, 10000); osmo_stream_cli_set_connect_cb(conn, connect_cb); osmo_stream_cli_set_disconnect_cb(conn, disconnect_cb); - osmo_stream_cli_set_read_cb(conn, read_cb); + osmo_stream_cli_set_read_cb2(conn, read_cb); osmo_stream_cli_set_data(conn, &num_msgs); osmo_stream_cli_set_nodelay(conn, true);
View file
libosmo-netif_1.3.0.tar.xz/examples/ipa-stream-server.c -> libosmo-netif_1.4.0.tar.xz/examples/ipa-stream-server.c
Changed
@@ -1,4 +1,5 @@ /* IPA stream srv example */ +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -46,28 +47,9 @@ exit(EXIT_SUCCESS); } -int read_cb(struct osmo_stream_srv *conn) +int read_cb(struct osmo_stream_srv *conn, struct msgb *msg) { - struct msgb *msg; - - LOGP(DSTREAMTEST, LOGL_DEBUG, "received message from stream\n"); - - msg = osmo_ipa_msg_alloc(0); - if (msg == NULL) { - LOGP(DSTREAMTEST, LOGL_ERROR, "cannot allocate message\n"); - return 0; - } - if (osmo_stream_srv_recv(conn, msg) <= 0) { - LOGP(DSTREAMTEST, LOGL_ERROR, "cannot receive message\n"); - osmo_stream_srv_destroy(conn); - msgb_free(msg); - return 0; - } - if (osmo_ipa_process_msg(msg) < 0) { - LOGP(DSTREAMTEST, LOGL_ERROR, "Bad IPA message\n"); - msgb_free(msg); - return 0; - } + LOGP(DSTREAMTEST, LOGL_DEBUG, "received message from stream (len=%d)\n", msgb_length(msg)); osmo_stream_srv_send(conn, msg); return 0; @@ -83,17 +65,20 @@ { if (conn != NULL) { LOGP(DSTREAMTEST, LOGL_ERROR, "Sorry, this example only " - "support one client simultaneously\n"); + "supports one client simultaneously\n"); return -1; } - conn = osmo_stream_srv_create(tall_test, srv, fd, - read_cb, close_cb, NULL); + conn = osmo_stream_srv_create2(tall_test, srv, fd, NULL); if (conn == NULL) { LOGP(DSTREAMTEST, LOGL_ERROR, "error while creating connection\n"); return -1; } + osmo_stream_srv_set_name(conn, "ipa_srv"); + osmo_stream_srv_set_read_cb(conn, read_cb); + osmo_stream_srv_set_closed_cb(conn, close_cb); + osmo_stream_srv_set_segmentation_cb(conn, osmo_ipa_segmentation_cb); return 0; } @@ -103,7 +88,7 @@ tall_test = talloc_named_const(NULL, 1, "osmo_stream_srv_test"); msgb_talloc_ctx_init(tall_test, 0); osmo_init_logging2(tall_test, &osmo_stream_srv_test_log_info); - log_set_log_level(osmo_stderr_target, LOGL_NOTICE); + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); /* * initialize stream srv.
View file
libosmo-netif_1.3.0.tar.xz/examples/stream-client.c -> libosmo-netif_1.4.0.tar.xz/examples/stream-client.c
Changed
@@ -50,27 +50,11 @@ return 0; } -static int read_cb(struct osmo_stream_cli *conn) +static int read_cb(struct osmo_stream_cli *conn, struct msgb *msg) { - int bytes; - struct msgb *msg; - LOGP(DSTREAMTEST, LOGL_NOTICE, "receiving message from stream... "); - msg = msgb_alloc(1024, "STREAMCLIENT/test"); - if (msg == NULL) { - LOGPC(DSTREAMTEST, LOGL_ERROR, "cannot allocate message\n"); - return 0; - } - - bytes = osmo_stream_cli_recv(conn, msg); - - if (bytes < 0) { - LOGPC(DSTREAMTEST, LOGL_ERROR, "cannot receive message\n"); - return 0; - } - - LOGPC(DSTREAMTEST, LOGL_NOTICE, "got %d (%d) bytes: %s\n", bytes, msg->len, msgb_hexdump(msg)); + LOGPC(DSTREAMTEST, LOGL_NOTICE, "got %d bytes: %s\n", msg->len, msgb_hexdump(msg)); msgb_free(msg); return 0; @@ -113,8 +97,7 @@ tall_test = talloc_named_const(NULL, 1, "osmo_stream_cli_test"); msgb_talloc_ctx_init(tall_test, 0); osmo_init_logging2(tall_test, &osmo_stream_cli_test_log_info); - log_set_log_level(osmo_stderr_target, 1); - log_set_category_filter(osmo_stderr_target, DLINP, 0, LOGL_INFO); + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); /* * initialize stream cli. @@ -125,11 +108,12 @@ fprintf(stderr, "cannot create cli\n"); exit(EXIT_FAILURE); } + osmo_stream_cli_set_name(conn, "stream_client"); osmo_stream_cli_set_addr(conn, "127.0.0.1"); osmo_stream_cli_set_port(conn, 10000); osmo_stream_cli_set_connect_cb(conn, connect_cb); osmo_stream_cli_set_disconnect_cb(conn, disconnect_cb); - osmo_stream_cli_set_read_cb(conn, read_cb); + osmo_stream_cli_set_read_cb2(conn, read_cb); if (osmo_stream_cli_open(conn) < 0) { fprintf(stderr, "cannot open cli\n");
View file
libosmo-netif_1.3.0.tar.xz/examples/stream-server.c -> libosmo-netif_1.4.0.tar.xz/examples/stream-server.c
Changed
@@ -42,29 +42,11 @@ exit(EXIT_SUCCESS); } -int read_cb(struct osmo_stream_srv *conn) +int read_cb(struct osmo_stream_srv *conn, struct msgb *msg) { - int bytes; - struct msgb *msg; - LOGP(DSTREAMTEST, LOGL_NOTICE, "receiving message from stream... "); - msg = msgb_alloc(1024, "STREAMSERVER/test"); - if (msg == NULL) { - LOGPC(DSTREAMTEST, LOGL_ERROR, "cannot allocate message\n"); - return 0; - } - - bytes = osmo_stream_srv_recv(conn, msg); - - if (bytes <= 0) { - if (bytes < 0) - LOGPC(DSTREAMTEST, LOGL_ERROR, "cannot receive message: %s\n", strerror(-bytes)); - else - LOGPC(DSTREAMTEST, LOGL_ERROR, "client closed connection\n"); - osmo_stream_srv_destroy(conn); - } else - LOGPC(DSTREAMTEST, LOGL_NOTICE, "got %d (%d) bytes: %s\n", bytes, msg->len, msgb_hexdump(msg)); + LOGPC(DSTREAMTEST, LOGL_NOTICE, "got %d bytes: %s\n", msg->len, msgb_hexdump(msg)); msgb_free(msg); return 0; @@ -72,6 +54,7 @@ static int close_cb(struct osmo_stream_srv *dummy) { + LOGPC(DSTREAMTEST, LOGL_ERROR, "client closed connection\n"); conn = NULL; return 0; } @@ -86,13 +69,15 @@ return -1; } - conn = osmo_stream_srv_create(tall_test, srv, fd, read_cb, - close_cb, NULL); + conn = osmo_stream_srv_create2(tall_test, srv, fd, NULL); if (conn == NULL) { LOGP(DSTREAMTEST, LOGL_ERROR, "error while creating connection\n"); return -1; } + osmo_stream_srv_set_name(conn, "stream_server"); + osmo_stream_srv_set_read_cb(conn, read_cb); + osmo_stream_srv_set_closed_cb(conn, close_cb); osmo_sock_get_name_buf(buf, OSMO_SOCK_NAME_MAXLEN, fd); LOGP(DSTREAMTEST, LOGL_NOTICE, "accepted client: %s\n", buf); @@ -139,8 +124,7 @@ tall_test = talloc_named_const(NULL, 1, "osmo_stream_srv_test"); msgb_talloc_ctx_init(tall_test, 0); osmo_init_logging2(tall_test, &osmo_stream_srv_test_log_info); - log_set_log_level(osmo_stderr_target, 1); - log_set_category_filter(osmo_stderr_target, DLINP, 0, LOGL_INFO); + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); /* * initialize stream srv.
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/Makefile.am -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/Makefile.am
Changed
@@ -1,3 +1,7 @@ +noinst_HEADERS = \ + stream_private.h \ + $(NULL) + osmonetif_HEADERS = amr.h \ datagram.h \ jibuf.h \
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/amr.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/amr.h
Changed
@@ -36,7 +36,7 @@ q:1, /* OK (not damaged) at origin? */ ft_lo:1; /* coding mode lowest bit */ #elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ uint8_t cmr:4, f:1, ft_hi:3; uint8_t ft_lo:1, q:1, data_start:6; #endif @@ -88,7 +88,7 @@ ft:4, /* coding mode */ f:1; /* followed by another speech frame? */ #elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ uint8_t cmr:4, pad1:4; uint8_t f:1, ft:4, q:1, pad2:2; #endif
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/ipa.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/ipa.h
Changed
@@ -18,7 +18,18 @@ uint8_t data0; } __attribute__ ((packed)); +struct osmo_ipa_msgb_cb { + uint8_t proto; + uint8_t proto_ext; +} __attribute__ ((packed)); + +#define OSMO_IPA_MSGB_CB(__msg) ((struct osmo_ipa_msgb_cb *)&((__msg)->cb0)) +#define osmo_ipa_msgb_cb_proto(__x) OSMO_IPA_MSGB_CB(__x)->proto +#define osmo_ipa_msgb_cb_proto_ext(__x) OSMO_IPA_MSGB_CB(__x)->proto_ext + struct msgb *osmo_ipa_msg_alloc(int headroom); +struct msgb *osmo_ipa_ext_msg_alloc(size_t headroom); + void osmo_ipa_msg_push_header(struct msgb *msg, uint8_t proto); int osmo_ipa_process_msg(struct msgb *msg); @@ -40,4 +51,6 @@ int osmo_ipa_parse_msg_id_resp(struct msgb *msg, struct ipaccess_unit *unit_data); +int osmo_ipa_segmentation_cb(struct msgb *msg); + #endif
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/osmux.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/osmux.h
Changed
@@ -40,7 +40,7 @@ ft:2, rtp_m:1; #elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ uint8_t rtp_m:1, ft:2, ctr:3, amr_f:1, amr_q:1; #endif uint8_t seq; @@ -50,7 +50,7 @@ uint8_t amr_cmr:4, amr_ft:4; #elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ uint8_t amr_ft:4, amr_cmr:4; #endif uint8_t data0;
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/rtp.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/rtp.h
Changed
@@ -13,7 +13,7 @@ uint8_t payload_type:7, marker:1; #elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */ +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ uint8_t version:2, padding:1, extension:1, csrc_count:4; uint8_t marker:1, payload_type:7; #endif @@ -82,4 +82,6 @@ #define RTP_PT_AMR 98 +#define RTP_PT_CSDATA 120 /* 3GPP TS 48.103 table 5.4.2.2.1 */ + #endif
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/sctp.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/sctp.h
Changed
@@ -2,6 +2,12 @@ #include <osmocom/core/utils.h> +/* Relevant SCTP RFCs: + * rfc9260 (obsoletes rfc4960): SCTP protocol + * rfc5061: SCTP Dynamic Address Reconfiguration + * rfc6458: SCTP Sockets API Extensions + */ + enum sctp_sac_state; extern const struct value_string osmo_sctp_assoc_chg_strs; static inline const char *osmo_sctp_assoc_chg_str(enum sctp_sac_state val) @@ -21,3 +27,24 @@ extern const struct value_string osmo_sctp_sn_error_strs; static inline const char *osmo_sctp_sn_error_str(enum sctp_sn_error val) { return get_value_string(osmo_sctp_sn_error_strs, val); } + +enum osmo_sctp_op_error { + OSMO_SCTP_OP_ERR_INVALID_STREAM_ID = 1, + OSMO_SCTP_OP_ERR_MISS_MAND_PARAM = 2, + OSMO_SCTP_OP_ERR_STALE_COOKIE = 3, + OSMO_SCTP_OP_ERR_NO_RESOURCES = 4, + OSMO_SCTP_OP_ERR_UNRESOLV_ADDR = 5, + OSMO_SCTP_OP_ERR_UNKN_CHUNK_TYPE = 6, + OSMO_SCTP_OP_ERR_INVALID_MAND_PARAM = 7, + OSMO_SCTP_OP_ERR_UNKN_PARAM = 8, + OSMO_SCTP_OP_ERR_NO_USER_DATA = 9, + OSMO_SCTP_OP_ERR_COOKIE_RX_WHILE_SHUTDOWN = 10, + OSMO_SCTP_OP_ERR_RESTART_ASSC_NEW_ADDR = 11, + OSMO_SCTP_OP_ERR_UNER_INITED_ABORT = 12, + OSMO_SCTP_OP_ERR_PROTO_VERSION = 13, +}; + + +extern const struct value_string osmo_sctp_op_error_strs; +static inline const char *osmo_sctp_op_error_str(enum osmo_sctp_op_error val) +{ return get_value_string(osmo_sctp_op_error_strs, val); }
View file
libosmo-netif_1.3.0.tar.xz/include/osmocom/netif/stream.h -> libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/stream.h
Changed
@@ -2,6 +2,7 @@ #include <stdbool.h> #include <stdint.h> +#include <unistd.h> #include <osmocom/core/msgb.h> @@ -24,6 +25,7 @@ struct osmo_stream_srv_link *osmo_stream_srv_link_create(void *ctx); void osmo_stream_srv_link_destroy(struct osmo_stream_srv_link *link); +void osmo_stream_srv_link_set_name(struct osmo_stream_srv_link *link, const char *name); void osmo_stream_srv_link_set_nodelay(struct osmo_stream_srv_link *link, bool nodelay); void osmo_stream_srv_link_set_addr(struct osmo_stream_srv_link *link, const char *addr); int osmo_stream_srv_link_set_addrs(struct osmo_stream_srv_link *link, const char **addr, size_t addrcnt); @@ -36,15 +38,29 @@ void *osmo_stream_srv_link_get_data(struct osmo_stream_srv_link *link); char *osmo_stream_srv_link_get_sockname(const struct osmo_stream_srv_link *link); struct osmo_fd *osmo_stream_srv_link_get_ofd(struct osmo_stream_srv_link *link); - +bool osmo_stream_srv_link_is_opened(const struct osmo_stream_srv_link *link); int osmo_stream_srv_link_open(struct osmo_stream_srv_link *link); void osmo_stream_srv_link_close(struct osmo_stream_srv_link *link); +enum osmo_stream_srv_link_param { + OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */ + OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */ + OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS, /* uint16_t: amount of streams */ + OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */ +}; + +int osmo_stream_srv_link_set_param(struct osmo_stream_srv_link *link, enum osmo_stream_srv_link_param par, + void *val, size_t val_len); + /*! \brief Osmocom Stream Server: Single connection accept()ed via \ref * osmo_stream_srv_link */ struct osmo_stream_srv; -struct osmo_stream_srv *osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, int fd, int (*cb)(struct osmo_stream_srv *conn), int (*closed_cb)(struct osmo_stream_srv *conn), void *data); +struct osmo_stream_srv *osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, int fd, int (*read_cb)(struct osmo_stream_srv *conn), int (*closed_cb)(struct osmo_stream_srv *conn), void *data); +struct osmo_stream_srv *osmo_stream_srv_create2(void *ctx, struct osmo_stream_srv_link *link, int fd, void *data); +void osmo_stream_srv_set_name(struct osmo_stream_srv *conn, const char *name); +void osmo_stream_srv_set_read_cb(struct osmo_stream_srv *conn, int (*read_cb)(struct osmo_stream_srv *conn, struct msgb *msg)); +void osmo_stream_srv_set_closed_cb(struct osmo_stream_srv *conn, int (*closed_cb)(struct osmo_stream_srv *conn)); void *osmo_stream_srv_get_data(struct osmo_stream_srv *conn); struct osmo_stream_srv_link *osmo_stream_srv_get_master(struct osmo_stream_srv *conn); struct osmo_fd *osmo_stream_srv_get_ofd(struct osmo_stream_srv *srv); @@ -53,6 +69,9 @@ void osmo_stream_srv_set_flush_and_destroy(struct osmo_stream_srv *conn); void osmo_stream_srv_set_data(struct osmo_stream_srv *conn, void *data); +void osmo_stream_srv_set_segmentation_cb(struct osmo_stream_srv *conn, + int (*segmentation_cb)(struct msgb *msg)); + void osmo_stream_srv_send(struct osmo_stream_srv *conn, struct msgb *msg); int osmo_stream_srv_recv(struct osmo_stream_srv *conn, struct msgb *msg); @@ -61,6 +80,7 @@ /*! \brief Osmocom Stream Client: Single client connection */ struct osmo_stream_cli; +void osmo_stream_cli_set_name(struct osmo_stream_cli *cli, const char *name); void osmo_stream_cli_set_nodelay(struct osmo_stream_cli *cli, bool nodelay); void osmo_stream_cli_set_addr(struct osmo_stream_cli *cli, const char *addr); int osmo_stream_cli_set_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt); @@ -79,6 +99,7 @@ void osmo_stream_cli_set_connect_cb(struct osmo_stream_cli *cli, int (*connect_cb)(struct osmo_stream_cli *cli)); void osmo_stream_cli_set_disconnect_cb(struct osmo_stream_cli *cli, int (*disconnect_cb)(struct osmo_stream_cli *cli)); void osmo_stream_cli_set_read_cb(struct osmo_stream_cli *cli, int (*read_cb)(struct osmo_stream_cli *cli)); +void osmo_stream_cli_set_read_cb2(struct osmo_stream_cli *cli, int (*read_cb)(struct osmo_stream_cli *cli, struct msgb *msg)); void osmo_stream_cli_reconnect(struct osmo_stream_cli *cli); bool osmo_stream_cli_is_connected(struct osmo_stream_cli *cli); @@ -95,4 +116,16 @@ void osmo_stream_cli_clear_tx_queue(struct osmo_stream_cli *cli); +enum osmo_stream_cli_param { + OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */ + OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, /* uint8_t: 0 disable, 1 enable, 2 force disable, 3 force enable */ + OSMO_STREAM_CLI_PAR_SCTP_INIT_NUM_OSTREAMS, /* uint16_t: amount of streams */ + OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */ + OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_ATTEMPTS, /* uint16_t: amount of attempts */ + OSMO_STREAM_CLI_PAR_SCTP_INIT_TIMEOUT, /* uint16_t: milliseconds */ +}; + +int osmo_stream_cli_set_param(struct osmo_stream_cli *cli, enum osmo_stream_cli_param par, + void *val, size_t val_len); + /*! @} */
View file
libosmo-netif_1.4.0.tar.xz/include/osmocom/netif/stream_private.h
Added
@@ -0,0 +1,38 @@ +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include <osmocom/core/socket.h> + +#include "config.h" + +#ifdef HAVE_LIBSCTP +#include <netinet/sctp.h> + #define OSMO_STREAM_MAX_ADDRS OSMO_SOCK_MAX_ADDRS + /* + * Platforms that don't have MSG_NOSIGNAL (which disables SIGPIPE) + * usually have SO_NOSIGPIPE (set via setsockopt). + */ + #ifndef MSG_NOSIGNAL + #define MSG_NOSIGNAL 0 + #endif +#else + #define OSMO_STREAM_MAX_ADDRS 1 +#endif + +/*! \addtogroup stream + * @{ + */ + +enum osmo_stream_mode { + OSMO_STREAM_MODE_UNKNOWN, + OSMO_STREAM_MODE_OSMO_FD, + OSMO_STREAM_MODE_OSMO_IO, +}; + +int stream_sctp_sock_activate_events(int fd); +int stream_setsockopt_nodelay(int fd, int proto, int on); +int stream_sctp_recvmsg_wrapper(int fd, struct msgb *msg, const char *log_pfx); + +/*! @} */
View file
libosmo-netif_1.3.0.tar.xz/libosmo-netif.pc.in -> libosmo-netif_1.4.0.tar.xz/libosmo-netif.pc.in
Changed
@@ -7,5 +7,5 @@ Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmonetif -Libs.private: @LIBSCTP_LIBS@ +Libs.private: @LIBSCTP_LIBS@ @LIBOSMOCORE_LIBS@ Cflags: -I${includedir}/
View file
libosmo-netif_1.3.0.tar.xz/src/Makefile.am -> libosmo-netif_1.4.0.tar.xz/src/Makefile.am
Changed
@@ -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=11:0:0 +LIBVERSION=12:0:1 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS= -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) $(LIBSCTP_CFLAGS) @@ -9,7 +9,7 @@ lib_LTLIBRARIES = libosmonetif.la libosmonetif_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBSCTP_LIBS) -libosmonetif_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined +libosmonetif_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBVERSION) -no-undefined libosmonetif_la_SOURCES = amr.c \ datagram.c \ @@ -22,6 +22,8 @@ prim.c \ rs232.c \ rtp.c \ + stream_cli.c \ + stream_srv.c \ stream.c if ENABLE_LIBSCTP
View file
libosmo-netif_1.3.0.tar.xz/src/ipa.c -> libosmo-netif_1.4.0.tar.xz/src/ipa.c
Changed
@@ -97,6 +97,11 @@ return msg; } +struct msgb *osmo_ipa_ext_msg_alloc(size_t headroom) +{ + return osmo_ipa_msg_alloc(sizeof(struct ipa_head_ext) + headroom); +} + void osmo_ipa_msg_push_header(struct msgb *msg, uint8_t proto) { struct ipa_head *hh; @@ -366,3 +371,68 @@ return 0; } + +#define MSG_CB_IPA_INFO_OFFSET 0 + +/* Check and remove headers (in case of p == IPAC_PROTO_OSMO, also the IPA extension header). + * Returns a negative number on error, otherwise the number of octets removed */ +static inline int ipa_check_pull_headers(struct msgb *msg) +{ + int ret; + size_t octets_removed = 0; + msg->l1h = msg->data; + struct ipa_head *ih = (struct ipa_head *)msg->data; + osmo_ipa_msgb_cb_proto(msg) = ih->proto; + + if ((ret = osmo_ipa_process_msg(msg)) < 0) { + LOGP(DLINP, LOGL_ERROR, "Error processing IPA message\n"); + return -EIO; + } + msgb_pull(msg, sizeof(struct ipa_head)); + octets_removed += sizeof(struct ipa_head); + msg->l2h = msg->data; + if (ih->proto != IPAC_PROTO_OSMO) + return octets_removed; + + osmo_ipa_msgb_cb_proto_ext(msg) = msg->data0; + msgb_pull(msg, sizeof(struct ipa_head_ext)); + octets_removed += sizeof(struct ipa_head_ext); + return octets_removed; +} + +/*! Segmentation callback used by libosmo-netif streaming backend + * See definition of `struct osmo_io_ops` for callback semantics + * \paramout msg Original `struct msgb` received via osmo_io + * \returns The total packet length indicated by the first header, + * otherwise negative number on error. Constants: + * -EAGAIN, if the header has not been read yet, + * -ENOBUFS, if the header declares a payload too large + */ +int osmo_ipa_segmentation_cb(struct msgb *msg) +{ + const struct ipa_head *hh = (const struct ipa_head *) msg->data; + size_t payload_len, total_len; + size_t available = msgb_length(msg) + msgb_tailroom(msg); + int removed_octets = 0; + + if (msgb_length(msg) < sizeof(*hh)) { + /* Haven't even read the entire header */ + return -EAGAIN; + } + payload_len = osmo_ntohs(hh->len); + total_len = sizeof(*hh) + payload_len; + if (OSMO_UNLIKELY(available < total_len)) { + LOGP(DLINP, LOGL_ERROR, "Not enough space left in message buffer. " + "Have %zu octets, but need %zu\n", + available, total_len); + return -ENOBUFS; + } + if (total_len <= msgb_length(msg)) { + removed_octets = ipa_check_pull_headers(msg); + if (removed_octets < 0) { + LOGP(DLINP, LOGL_ERROR, "Error pulling IPA headers\n"); + return removed_octets; + } + } + return total_len; +}
View file
libosmo-netif_1.3.0.tar.xz/src/sctp.c -> libosmo-netif_1.4.0.tar.xz/src/sctp.c
Changed
@@ -51,3 +51,21 @@ { SCTP_PEER_FAULTY, "PEER_FAULTY" }, { 0, NULL } }; + +/* rfc4960 section 3.3.10 "Operation Error", in host byte order */ +const struct value_string osmo_sctp_op_error_strs = { + { OSMO_SCTP_OP_ERR_INVALID_STREAM_ID, "Invalid Stream Identifier" }, + { OSMO_SCTP_OP_ERR_MISS_MAND_PARAM, "Missing Mandatory Parameter" }, + { OSMO_SCTP_OP_ERR_STALE_COOKIE, "Stale Cookie Error" }, + { OSMO_SCTP_OP_ERR_NO_RESOURCES, "Out of Resource" }, + { OSMO_SCTP_OP_ERR_UNRESOLV_ADDR, "Unresolvable Address" }, + { OSMO_SCTP_OP_ERR_UNKN_CHUNK_TYPE, "Unrecognized Chunk Type" }, + { OSMO_SCTP_OP_ERR_INVALID_MAND_PARAM, "Invalid Mandatory Parameter" }, + { OSMO_SCTP_OP_ERR_UNKN_PARAM, "Unrecognized Parameters" }, + { OSMO_SCTP_OP_ERR_NO_USER_DATA, "No User Data" }, + { OSMO_SCTP_OP_ERR_COOKIE_RX_WHILE_SHUTDOWN, "Cookie Received While Shutting Down" }, + { OSMO_SCTP_OP_ERR_RESTART_ASSC_NEW_ADDR, "Restart of an Association with New Addresses" }, + { OSMO_SCTP_OP_ERR_UNER_INITED_ABORT, "User Initiated Abort" }, + { OSMO_SCTP_OP_ERR_PROTO_VERSION, "Protocol Violation" }, + { 0, NULL } +};
View file
libosmo-netif_1.3.0.tar.xz/src/stream.c -> libosmo-netif_1.4.0.tar.xz/src/stream.c
Changed
@@ -1,5 +1,6 @@ /* (C) 2011 by Pablo Neira Ayuso <pablo@gnumonks.org> * (C) 2015-2016 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * All Rights Reserved. * * SPDX-License-Identifier: GPL-2.0+ @@ -37,22 +38,19 @@ #include <osmocom/core/utils.h> #include <osmocom/gsm/tlv.h> #include <osmocom/core/msgb.h> +#include <osmocom/core/osmo_io.h> +#include <osmocom/core/panic.h> #include <osmocom/core/logging.h> #include <osmocom/core/talloc.h> #include <osmocom/core/socket.h> #include <osmocom/netif/stream.h> +#include <osmocom/netif/stream_private.h> #include "config.h" -#ifdef HAVE_LIBSCTP -#include <netinet/sctp.h> -#endif - #include <osmocom/netif/sctp.h> -#define LOGSCLI(cli, level, fmt, args...) \ - LOGP(DLINP, level, "%s %s(): " fmt, get_value_string(stream_cli_state_names, (cli)->state), __func__, ## args) /*! \addtogroup stream Osmocom Stream Socket * @{ @@ -68,13 +66,6 @@ */ #ifdef HAVE_LIBSCTP -/* - * Platforms that don't have MSG_NOSIGNAL (which disables SIGPIPE) - * usually have SO_NOSIGPIPE (set via setsockopt). - */ -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 -#endif /* 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) @@ -169,7 +160,7 @@ } #endif // HAVE_LIBSCTP -static int sctp_sock_activate_events(int fd) +int stream_sctp_sock_activate_events(int fd) { #ifdef HAVE_LIBSCTP struct sctp_event_subscribe event; @@ -195,7 +186,7 @@ #endif } -static int setsockopt_nodelay(int fd, int proto, int on) +int stream_setsockopt_nodelay(int fd, int proto, int on) { int rc; @@ -217,1330 +208,55 @@ return rc; } - -/* - * Client side. - */ - -enum osmo_stream_cli_state { - STREAM_CLI_STATE_CLOSED, /* No fd associated, no timer active */ - STREAM_CLI_STATE_WAIT_RECONNECT, /* No fd associated, has timer active to try to connect again */ - STREAM_CLI_STATE_CONNECTING, /* Fd associated, but connection not yet confirmed by peer or lower layers */ - STREAM_CLI_STATE_CONNECTED, /* Fd associated and connection is established */ - STREAM_CLI_STATE_MAX -}; - -static const struct value_string stream_cli_state_names = { - { STREAM_CLI_STATE_CLOSED, "CLOSED" }, - { STREAM_CLI_STATE_WAIT_RECONNECT, "WAIT_RECONNECT" }, - { STREAM_CLI_STATE_CONNECTING, "CONNECTING" }, - { STREAM_CLI_STATE_CONNECTED, "CONNECTED" }, - { 0, NULL } -}; - -#define OSMO_STREAM_CLI_F_RECONF (1 << 0) -#define OSMO_STREAM_CLI_F_NODELAY (1 << 1) - -#ifdef HAVE_LIBSCTP -#define OSMO_STREAM_MAX_ADDRS OSMO_SOCK_MAX_ADDRS -#else -#define OSMO_STREAM_MAX_ADDRS 1 -#endif - -struct osmo_stream_cli { - struct osmo_fd ofd; - struct llist_head tx_queue; - struct osmo_timer_list timer; - enum osmo_stream_cli_state state; - char *addrOSMO_STREAM_MAX_ADDRS; - uint8_t addrcnt; - uint16_t port; - char *local_addrOSMO_STREAM_MAX_ADDRS; - uint8_t local_addrcnt; - uint16_t local_port; - int sk_domain; - int sk_type; - uint16_t proto; - int (*connect_cb)(struct osmo_stream_cli *srv); - int (*disconnect_cb)(struct osmo_stream_cli *srv); - int (*read_cb)(struct osmo_stream_cli *srv); - int (*write_cb)(struct osmo_stream_cli *srv); - void *data; - int flags; - int reconnect_timeout; -}; - -void osmo_stream_cli_close(struct osmo_stream_cli *cli); - -/*! \brief Re-connect an Osmocom Stream Client - * If re-connection is enabled for this client - * (which is the case unless negative timeout was explicitly set via osmo_stream_cli_set_reconnect_timeout() call), - * we close any existing connection (if any) and schedule a re-connect timer */ -void osmo_stream_cli_reconnect(struct osmo_stream_cli *cli) -{ - osmo_stream_cli_close(cli); - - if (cli->reconnect_timeout < 0) { - LOGSCLI(cli, LOGL_INFO, "not reconnecting, disabled.\n"); - return; - } - - cli->state = STREAM_CLI_STATE_WAIT_RECONNECT; - LOGSCLI(cli, LOGL_INFO, "retrying in %d seconds...\n", - cli->reconnect_timeout); - osmo_timer_schedule(&cli->timer, cli->reconnect_timeout, 0); -} - -/*! \brief Check if Osmocom Stream Client is in connected state - * \paramin cli Osmocom Stream Client - * \return true if connected, false otherwise - */ -bool osmo_stream_cli_is_connected(struct osmo_stream_cli *cli) -{ - return cli->state == STREAM_CLI_STATE_CONNECTED; -} - -/*! \brief Close an Osmocom Stream Client - * \paramin cli Osmocom Stream Client to be closed - * We unregister the socket fd from the osmocom select() loop - * abstraction and close the socket */ -void osmo_stream_cli_close(struct osmo_stream_cli *cli) -{ - if (cli->ofd.fd == -1) - return; - osmo_fd_unregister(&cli->ofd); - close(cli->ofd.fd); - cli->ofd.fd = -1; - - if (cli->state == STREAM_CLI_STATE_CONNECTED) { - LOGSCLI(cli, LOGL_DEBUG, "connection closed\n"); - if (cli->disconnect_cb) - cli->disconnect_cb(cli); - } - - cli->state = STREAM_CLI_STATE_CLOSED; -} - -static void osmo_stream_cli_read(struct osmo_stream_cli *cli) -{ - LOGSCLI(cli, LOGL_DEBUG, "message received\n"); - - if (cli->read_cb) - cli->read_cb(cli); -} - -static int osmo_stream_cli_write(struct osmo_stream_cli *cli) -{ -#ifdef HAVE_LIBSCTP - struct sctp_sndrcvinfo sinfo; -#endif - struct msgb *msg; - int ret; - - if (llist_empty(&cli->tx_queue)) { - osmo_fd_write_disable(&cli->ofd); - return 0; - } - msg = llist_first_entry(&cli->tx_queue, struct msgb, list); - llist_del(&msg->list); - - if (!osmo_stream_cli_is_connected(cli)) { - LOGSCLI(cli, LOGL_ERROR, "not connected, dropping data!\n"); - return 0; - } - - LOGSCLI(cli, LOGL_DEBUG, "sending %u bytes of data\n", msgb_length(msg)); - - switch (cli->sk_domain) { - case AF_UNIX: - ret = send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), 0); - break; - case AF_UNSPEC: - case AF_INET: - case AF_INET6: - switch (cli->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.sinfo_ppid = htonl(msgb_sctp_ppid(msg)); - sinfo.sinfo_stream = msgb_sctp_stream(msg); - ret = sctp_send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), - &sinfo, MSG_NOSIGNAL); - break; -#endif - case IPPROTO_TCP: - default: - ret = send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), 0); - break; - } - break; - default: - ret = -ENOTSUP; - } - - if (ret >= 0 && ret < msgb_length(msg)) { - LOGP(DLINP, LOGL_ERROR, "short send: %d < exp %u\n", ret, msgb_length(msg)); - /* Update msgb and re-add it at the start of the queue: */ - msgb_pull(msg, ret); - llist_add(&msg->list, &cli->tx_queue); - return 0; - } - - if (ret < 0) { - if (errno == EPIPE || errno == ENOTCONN) { - osmo_stream_cli_reconnect(cli); - } - LOGSCLI(cli, LOGL_ERROR, "error %d to send\n", ret); - } - - msgb_free(msg); - - if (llist_empty(&cli->tx_queue)) - osmo_fd_write_disable(&cli->ofd); - - return 0; -} - -static int _setsockopt_nosigpipe(struct osmo_stream_cli *cli) -{ -#ifdef SO_NOSIGPIPE - int ret; - int val = 1; - ret = setsockopt(cli->ofd.fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&val, sizeof(val)); - if (ret < 0) - LOGSCLI(cli, LOGL_DEBUG, "Failed setting SO_NOSIGPIPE: %s\n", strerror(errno)); - return ret; -#else - return 0; -#endif -} - -static int osmo_stream_cli_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct osmo_stream_cli *cli = ofd->data; - int error, ret; - socklen_t len = sizeof(error); - - switch(cli->state) { - case STREAM_CLI_STATE_CONNECTING: - ret = getsockopt(ofd->fd, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret >= 0 && error > 0) { - osmo_stream_cli_reconnect(cli); - return 0; - } - - /* If messages got enqueued while 'connecting', keep WRITE flag - up to dispatch them upon next main loop step */ - if (llist_empty(&cli->tx_queue)) - osmo_fd_write_disable(&cli->ofd); - - LOGSCLI(cli, LOGL_DEBUG, "connection done.\n"); - cli->state = STREAM_CLI_STATE_CONNECTED; - switch (cli->sk_domain) { - case AF_UNIX: - _setsockopt_nosigpipe(cli); - break; - case AF_UNSPEC: - case AF_INET: - case AF_INET6: - if (cli->proto == IPPROTO_SCTP) { - _setsockopt_nosigpipe(cli); - sctp_sock_activate_events(ofd->fd); - } - break; - default: - break; - } - if (cli->connect_cb) - cli->connect_cb(cli); - break; - case STREAM_CLI_STATE_CONNECTED: - if (what & OSMO_FD_READ) { - LOGSCLI(cli, LOGL_DEBUG, "connected read\n"); - osmo_stream_cli_read(cli); - } - if (what & OSMO_FD_WRITE) { - LOGSCLI(cli, LOGL_DEBUG, "connected write\n"); - osmo_stream_cli_write(cli); - } - break; - default: - /* Only CONNECTING and CONNECTED states are expected, since they are the only states where FD exists: */ - osmo_panic("osmo_stream_cli_fd_cb called with unexpected state %d\n", cli->state); - } - return 0; -} - -static void cli_timer_cb(void *data); - -/*! \brief Create an Osmocom stream client - * \paramin ctx talloc context from which to allocate memory - * This function allocates a new \ref osmo_stream_cli and initializes - * it with default values (5s reconnect timer, TCP protocol) - * \return allocated stream client, or NULL in case of error - */ -struct osmo_stream_cli *osmo_stream_cli_create(void *ctx) -{ - struct osmo_stream_cli *cli; - - cli = talloc_zero(ctx, struct osmo_stream_cli); - if (!cli) - return NULL; - - cli->sk_domain = AF_UNSPEC; - cli->sk_type = SOCK_STREAM; - cli->proto = IPPROTO_TCP; - cli->ofd.fd = -1; - cli->ofd.priv_nr = 0; /* XXX */ - cli->ofd.cb = osmo_stream_cli_fd_cb; - cli->ofd.data = cli; - cli->state = STREAM_CLI_STATE_CLOSED; - osmo_timer_setup(&cli->timer, cli_timer_cb, cli); - cli->reconnect_timeout = 5; /* default is 5 seconds. */ - INIT_LLIST_HEAD(&cli->tx_queue); - - return cli; -} - -/*! \brief Set the remote address to which we connect - * \paramin cli Stream Client to modify - * \paramin addr Remote IP address - */ -void -osmo_stream_cli_set_addr(struct osmo_stream_cli *cli, const char *addr) -{ - osmo_stream_cli_set_addrs(cli, &addr, 1); -} - -/*! \brief Set the remote address set to which we connect. - * Useful for protocols allowing connecting to more than one address (such as SCTP) - * \paramin cli Stream Client to modify - * \paramin addr Remote IP address set - * \return negative on error, 0 on success - */ -int osmo_stream_cli_set_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt) -{ - int i = 0; - - if (addrcnt > OSMO_STREAM_MAX_ADDRS) - return -EINVAL; - - for (; i < addrcnt; i++) - osmo_talloc_replace_string(cli, &cli->addri, addri); - for (; i < cli->addrcnt; i++) { - talloc_free(cli->addri); - cli->addri = NULL; - } - - cli->addrcnt = addrcnt; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; - return 0; -} - -/*! \brief Set the remote port number to which we connect - * \paramin cli Stream Client to modify - * \paramin port Remote port number - */ -void -osmo_stream_cli_set_port(struct osmo_stream_cli *cli, uint16_t port) -{ - cli->port = port; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; -} - -/*! \brief Set the local port number for the socket (to be bound to) - * \paramin cli Stream Client to modify - * \paramin port Local port number - */ -void -osmo_stream_cli_set_local_port(struct osmo_stream_cli *cli, uint16_t port) -{ - cli->local_port = port; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; -} - -/*! \brief Set the local address for the socket (to be bound to) - * \paramin cli Stream Client to modify - * \paramin port Local host name - */ -void -osmo_stream_cli_set_local_addr(struct osmo_stream_cli *cli, const char *addr) -{ - osmo_stream_cli_set_local_addrs(cli, &addr, 1); -} - -/*! \brief Set the local address set to which we connect. - * Useful for protocols allowing bind to more than one address (such as SCTP) - * \paramin cli Stream Client to modify - * \paramin addr Local IP address set - * \return negative on error, 0 on success - */ -int osmo_stream_cli_set_local_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt) -{ - int i = 0; - - if (addrcnt > OSMO_STREAM_MAX_ADDRS) - return -EINVAL; - - for (; i < addrcnt; i++) - osmo_talloc_replace_string(cli, &cli->local_addri, addri); - for (; i < cli->local_addrcnt; i++) { - talloc_free(cli->local_addri); - cli->local_addri = NULL; - } - - cli->local_addrcnt = addrcnt; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; - return 0; -} - -/*! \brief Set the protocol for the stream client socket - * \paramin cli Stream Client to modify - * \paramin proto Protocol (like IPPROTO_TCP (default), IPPROTO_SCTP, ...) - */ -void -osmo_stream_cli_set_proto(struct osmo_stream_cli *cli, uint16_t proto) -{ - cli->proto = proto; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; -} - -/*! \brief Set the socket type for the stream server link - * \paramin cli Stream Client to modify - * \paramin type Socket Type (like SOCK_STREAM (default), SOCK_SEQPACKET, ...) - * \returns zero on success, negative on error. - */ -int osmo_stream_cli_set_type(struct osmo_stream_cli *cli, int type) -{ - switch (type) { - case SOCK_STREAM: - case SOCK_SEQPACKET: - break; - default: - return -ENOTSUP; - } - cli->sk_type = type; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; - return 0; -} - -/*! \brief Set the socket type for the stream server link - * \paramin cli Stream Client to modify - * \paramin type Socket Domain (like AF_UNSPEC (default for IP), AF_UNIX, AF_INET, ...) - * \returns zero on success, negative on error. - */ -int osmo_stream_cli_set_domain(struct osmo_stream_cli *cli, int domain) -{ - switch (domain) { - case AF_UNSPEC: - case AF_INET: - case AF_INET6: - case AF_UNIX: - break; - default: - return -ENOTSUP; - } - cli->sk_domain = domain; - cli->flags |= OSMO_STREAM_CLI_F_RECONF; - return 0; -} - -/*! \brief Set the reconnect time of the stream client socket - * \paramin cli Stream Client to modify - * \paramin timeout Re-connect timeout in seconds or negative value to disable auto-reconnection */ -void -osmo_stream_cli_set_reconnect_timeout(struct osmo_stream_cli *cli, int timeout) -{ - cli->reconnect_timeout = timeout; -} - -/*! \brief Set application private data of the stream client socket - * \paramin cli Stream Client to modify - * \paramin data User-specific data (available in call-back functions) */ -void -osmo_stream_cli_set_data(struct osmo_stream_cli *cli, void *data) -{ - cli->data = data; -} - -/*! \brief Get application private data of the stream client socket - * \paramin cli Stream Client to modify - * \returns Application private data, as set by \ref osmo_stream_cli_set_data() */ -void *osmo_stream_cli_get_data(struct osmo_stream_cli *cli) -{ - return cli->data; -} - -/*! \brief Get the stream client socket description. - * \paramin cli Stream Client to examine - * \returns Socket description or NULL in case of error */ -char *osmo_stream_cli_get_sockname(const struct osmo_stream_cli *cli) -{ - static char bufOSMO_SOCK_NAME_MAXLEN; - - osmo_sock_get_name_buf(buf, OSMO_SOCK_NAME_MAXLEN, cli->ofd.fd); - - return buf; -} - -/*! \brief Get Osmocom File Descriptor of the stream client socket - * \paramin cli Stream Client to modify - * \returns Pointer to \ref osmo_fd */ -struct osmo_fd * -osmo_stream_cli_get_ofd(struct osmo_stream_cli *cli) -{ - return &cli->ofd; -} - -/*! \brief Set the call-back function called on connect of the stream client socket - * \paramin cli Stream Client to modify - * \paramin connect_cb Call-back function to be called upon connect */ -void -osmo_stream_cli_set_connect_cb(struct osmo_stream_cli *cli, - int (*connect_cb)(struct osmo_stream_cli *cli)) -{ - cli->connect_cb = connect_cb; -} - -/*! \brief Set the call-back function called on disconnect of the stream client socket - * \paramin cli Stream Client to modify - * \paramin disconnect_cb Call-back function to be called upon disconnect */ -void osmo_stream_cli_set_disconnect_cb(struct osmo_stream_cli *cli, - int (*disconnect_cb)(struct osmo_stream_cli *cli)) -{ - cli->disconnect_cb = disconnect_cb; -} - -/*! \brief Set the call-back function called to read from the stream client socket - * \paramin cli Stream Client to modify - * \paramin read_cb Call-back function to be called when we want to read */ -void -osmo_stream_cli_set_read_cb(struct osmo_stream_cli *cli, - int (*read_cb)(struct osmo_stream_cli *cli)) -{ - cli->read_cb = read_cb; -} - -/*! \brief Destroy a Osmocom stream client (includes close) - * \paramin cli Stream Client to destroy */ -void osmo_stream_cli_destroy(struct osmo_stream_cli *cli) -{ - osmo_stream_cli_close(cli); - osmo_timer_del(&cli->timer); - msgb_queue_free(&cli->tx_queue); - talloc_free(cli); -} - -/*! \brief DEPRECATED: use osmo_stream_cli_set_reconnect_timeout() or osmo_stream_cli_reconnect() instead! - * Open connection of an Osmocom stream client - * \paramin cli Stream Client to connect - * \paramin reconect 1 if we should not automatically reconnect - * \return negative on error, 0 on success - */ -int osmo_stream_cli_open2(struct osmo_stream_cli *cli, int reconnect) -{ - int ret; - - /* we are reconfiguring this socket, close existing first. */ - if ((cli->flags & OSMO_STREAM_CLI_F_RECONF) && cli->ofd.fd >= 0) - osmo_stream_cli_close(cli); - - cli->flags &= ~OSMO_STREAM_CLI_F_RECONF; - - switch (cli->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - ret = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, cli->proto, - (const char **)cli->local_addr, cli->local_addrcnt, cli->local_port, - (const char **)cli->addr, cli->addrcnt, cli->port, - OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); - break; -#endif - default: - ret = osmo_sock_init2(AF_UNSPEC, SOCK_STREAM, cli->proto, - cli->local_addr0, cli->local_port, - cli->addr0, cli->port, - OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); - } - - if (ret < 0) { - if (reconnect) - osmo_stream_cli_reconnect(cli); - return ret; - } - osmo_fd_setup(&cli->ofd, ret, OSMO_FD_READ | OSMO_FD_WRITE, cli->ofd.cb, cli->ofd.data, cli->ofd.priv_nr); - - if (cli->flags & OSMO_STREAM_CLI_F_NODELAY) { - ret = setsockopt_nodelay(cli->ofd.fd, cli->proto, 1); - if (ret < 0) - goto error_close_socket; - } - - if (osmo_fd_register(&cli->ofd) < 0) - goto error_close_socket; - - cli->state = STREAM_CLI_STATE_CONNECTING; - return 0; - -error_close_socket: - close(cli->ofd.fd); - cli->ofd.fd = -1; - return -EIO; -} - -/*! \brief Set the NODELAY socket option to avoid Nagle-like behavior - * Setting this to nodelay=true will automatically set the NODELAY - * socket option on any socket established via \ref osmo_stream_cli_open - * or any re-connect. You have to set this _before_ opening the - * socket. - * \paramin cli Stream client whose sockets are to be configured - * \paramin nodelay whether to set (true) NODELAY before connect() - */ -void osmo_stream_cli_set_nodelay(struct osmo_stream_cli *cli, bool nodelay) -{ - if (nodelay) - cli->flags |= OSMO_STREAM_CLI_F_NODELAY; - else - cli->flags &= ~OSMO_STREAM_CLI_F_NODELAY; -} - -/*! \brief Open connection of an Osmocom stream client - * By default the client will automatically reconnect after default timeout. - * To disable this, use osmo_stream_cli_set_reconnect_timeout() before calling this function. - * \paramin cli Stream Client to connect - * \return negative on error, 0 on success */ -int osmo_stream_cli_open(struct osmo_stream_cli *cli) -{ - int ret; - - /* we are reconfiguring this socket, close existing first. */ - if ((cli->flags & OSMO_STREAM_CLI_F_RECONF) && cli->ofd.fd >= 0) - osmo_stream_cli_close(cli); - - cli->flags &= ~OSMO_STREAM_CLI_F_RECONF; - - switch (cli->sk_domain) { - case AF_UNIX: - ret = osmo_sock_unix_init(cli->sk_type, 0, cli->addr0, OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); - break; - case AF_INET: - case AF_INET6: - case AF_UNSPEC: - switch (cli->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - ret = osmo_sock_init2_multiaddr(cli->sk_domain, cli->sk_type, cli->proto, - (const char **)cli->local_addr, cli->local_addrcnt, cli->local_port, - (const char **)cli->addr, cli->addrcnt, cli->port, - OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); - break; -#endif - default: - ret = osmo_sock_init2(cli->sk_domain, cli->sk_type, cli->proto, - cli->local_addr0, cli->local_port, - cli->addr0, cli->port, - OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); - } - break; - default: - return -ENOTSUP; - } - - if (ret < 0) { - osmo_stream_cli_reconnect(cli); - return ret; - } - osmo_fd_setup(&cli->ofd, ret, OSMO_FD_READ | OSMO_FD_WRITE, cli->ofd.cb, cli->ofd.data, cli->ofd.priv_nr); - - if (cli->flags & OSMO_STREAM_CLI_F_NODELAY) { - ret = setsockopt_nodelay(cli->ofd.fd, cli->proto, 1); - if (ret < 0) - goto error_close_socket; - } - - if (osmo_fd_register(&cli->ofd) < 0) - goto error_close_socket; - - cli->state = STREAM_CLI_STATE_CONNECTING; - return 0; - -error_close_socket: - cli->state = STREAM_CLI_STATE_CLOSED; - close(cli->ofd.fd); - cli->ofd.fd = -1; - return -EIO; -} - -static void cli_timer_cb(void *data) -{ - struct osmo_stream_cli *cli = data; - - LOGSCLI(cli, LOGL_DEBUG, "reconnecting.\n"); - osmo_stream_cli_open(cli); -} - -/*! \brief Enqueue data to be sent via an Osmocom stream client - * \paramin cli Stream Client through which we want to send - * \paramin msg Message buffer to enqueue in transmit queue */ -void osmo_stream_cli_send(struct osmo_stream_cli *cli, struct msgb *msg) -{ - OSMO_ASSERT(cli); - OSMO_ASSERT(msg); - msgb_enqueue(&cli->tx_queue, msg); - osmo_fd_write_enable(&cli->ofd); -} - -/*! \brief Receive data via an Osmocom stream client - * \paramin cli Stream Client through which we want to send - * \param msg pre-allocate message buffer to which received data is appended - * \returns number of bytes read; <=0 in case of error */ -int osmo_stream_cli_recv(struct osmo_stream_cli *cli, struct msgb *msg) -{ - int ret; - OSMO_ASSERT(cli); - OSMO_ASSERT(msg); - - ret = recv(cli->ofd.fd, msg->data, msg->data_len, 0); - if (ret < 0) { - if (errno == EPIPE || errno == ECONNRESET) { - LOGSCLI(cli, LOGL_ERROR, "lost connection with srv\n"); - } - osmo_stream_cli_reconnect(cli); - return ret; - } else if (ret == 0) { - LOGSCLI(cli, LOGL_ERROR, "connection closed with srv\n"); - osmo_stream_cli_reconnect(cli); - return ret; - } - msgb_put(msg, ret); - LOGSCLI(cli, LOGL_DEBUG, "received %d bytes from srv\n", ret); - return ret; -} - -void osmo_stream_cli_clear_tx_queue(struct osmo_stream_cli *cli) -{ - msgb_queue_free(&cli->tx_queue); - /* If in state 'connecting', keep WRITE flag up to receive - * socket connection signal and then transition to STATE_CONNECTED: */ - if (cli->state == STREAM_CLI_STATE_CONNECTED) - osmo_fd_write_disable(&cli->ofd); -} - -/* - * Server side. - */ - -#define OSMO_STREAM_SRV_F_RECONF (1 << 0) -#define OSMO_STREAM_SRV_F_NODELAY (1 << 1) - -struct osmo_stream_srv_link { - struct osmo_fd ofd; - char *addrOSMO_STREAM_MAX_ADDRS; - uint8_t addrcnt; - uint16_t port; - int sk_domain; - int sk_type; - uint16_t proto; - int (*accept_cb)(struct osmo_stream_srv_link *srv, int fd); - void *data; - int flags; -}; - -static int osmo_stream_srv_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int ret; - int sock_fd; - char addrstr128; - bool is_ipv6 = false; - struct sockaddr_storage sa; - socklen_t sa_len = sizeof(sa); - struct osmo_stream_srv_link *link = ofd->data; - - ret = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "failed to accept from origin " - "peer, reason=`%s'\n", strerror(errno)); - return ret; - } - sock_fd = ret; - - is_ipv6 = false; - switch (((struct sockaddr *)&sa)->sa_family) { - case AF_UNIX: - LOGP(DLINP, LOGL_DEBUG, "accept()ed new link on fd %d\n", - sock_fd); - break; - case AF_INET6: - is_ipv6 = true; - /* fall through */ - case AF_INET: - LOGP(DLINP, LOGL_DEBUG, "accept()ed new link from %s to port %u\n", - inet_ntop(is_ipv6 ? AF_INET6 : AF_INET, - is_ipv6 ? (void *)&(((struct sockaddr_in6 *)&sa)->sin6_addr) : - (void *)&(((struct sockaddr_in *)&sa)->sin_addr), - addrstr, sizeof(addrstr)), - link->port); - - if (link->proto == IPPROTO_SCTP) { - ret = sctp_sock_activate_events(sock_fd); - if (ret < 0) - goto error_close_socket; - } - break; - default: - LOGP(DLINP, LOGL_DEBUG, "accept()ed unexpected address family %d\n", - ((struct sockaddr *)&sa)->sa_family); - goto error_close_socket; - } - - if (link->flags & OSMO_STREAM_SRV_F_NODELAY) { - ret = setsockopt_nodelay(sock_fd, link->proto, 1); - if (ret < 0) - goto error_close_socket; - } - - if (!link->accept_cb) { - ret = -ENOTSUP; - goto error_close_socket; - } - - ret = link->accept_cb(link, sock_fd); - if (ret) - goto error_close_socket; - return 0; - -error_close_socket: - close(sock_fd); - return ret; -} - -/*! \brief Create an Osmocom Stream Server Link - * A Stream Server Link is the listen()+accept() "parent" to individual - * Stream Servers - * \paramin ctx talloc allocation context - * \returns Stream Server Link with default values (TCP) - */ -struct osmo_stream_srv_link *osmo_stream_srv_link_create(void *ctx) -{ - struct osmo_stream_srv_link *link; - - link = talloc_zero(ctx, struct osmo_stream_srv_link); - if (!link) - return NULL; - - link->sk_domain = AF_UNSPEC; - link->sk_type = SOCK_STREAM; - link->proto = IPPROTO_TCP; - osmo_fd_setup(&link->ofd, -1, OSMO_FD_READ | OSMO_FD_WRITE, osmo_stream_srv_fd_cb, link, 0); - - return link; -} - -/*! \brief Set the NODELAY socket option to avoid Nagle-like behavior - * Setting this to nodelay=true will automatically set the NODELAY - * socket option on any socket established via this server link, before - * calling the accept_cb() - * \paramin link server link whose sockets are to be configured - * \paramin nodelay whether to set (true) NODELAY after accept - */ -void osmo_stream_srv_link_set_nodelay(struct osmo_stream_srv_link *link, bool nodelay) -{ - if (nodelay) - link->flags |= OSMO_STREAM_SRV_F_NODELAY; - else - link->flags &= ~OSMO_STREAM_SRV_F_NODELAY; -} - -/*! \brief Set the local address to which we bind - * \paramin link Stream Server Link to modify - * \paramin addr Local IP address - */ -void osmo_stream_srv_link_set_addr(struct osmo_stream_srv_link *link, - const char *addr) -{ - osmo_stream_srv_link_set_addrs(link, &addr, 1); -} - -/*! \brief Set the local address set to which we bind. - * Useful for protocols allowing bind on more than one address (such as SCTP) - * \paramin link Stream Server Link to modify - * \paramin addr Local IP address - * \return negative on error, 0 on success - */ -int osmo_stream_srv_link_set_addrs(struct osmo_stream_srv_link *link, const char **addr, size_t addrcnt) -{ - int i = 0; - - if (addrcnt > OSMO_STREAM_MAX_ADDRS) - return -EINVAL; - - for (; i < addrcnt; i++) - osmo_talloc_replace_string(link, &link->addri, addri); - for (; i < link->addrcnt; i++) { - talloc_free(link->addri); - link->addri = NULL; - } - - link->addrcnt = addrcnt; - link->flags |= OSMO_STREAM_SRV_F_RECONF; - return 0; -} - -/*! \brief Set the local port number to which we bind - * \paramin link Stream Server Link to modify - * \paramin port Local port number - */ -void osmo_stream_srv_link_set_port(struct osmo_stream_srv_link *link, - uint16_t port) -{ - link->port = port; - link->flags |= OSMO_STREAM_SRV_F_RECONF; -} - -/*! \brief Set the protocol for the stream server link - * \paramin link Stream Server Link to modify - * \paramin proto Protocol (like IPPROTO_TCP (default), IPPROTO_SCTP, ...) - */ -void -osmo_stream_srv_link_set_proto(struct osmo_stream_srv_link *link, - uint16_t proto) -{ - link->proto = proto; - link->flags |= OSMO_STREAM_SRV_F_RECONF; -} - - -/*! \brief Set the socket type for the stream server link - * \paramin link Stream Server Link to modify - * \paramin type Socket Type (like SOCK_STREAM (default), SOCK_SEQPACKET, ...) - * \returns zero on success, negative on error. - */ -int osmo_stream_srv_link_set_type(struct osmo_stream_srv_link *link, int type) -{ - switch (type) { - case SOCK_STREAM: - case SOCK_SEQPACKET: - break; - default: - return -ENOTSUP; - } - link->sk_type = type; - link->flags |= OSMO_STREAM_SRV_F_RECONF; - return 0; -} - -/*! \brief Set the socket type for the stream server link - * \paramin link Stream Server Link to modify - * \paramin type Socket Domain (like AF_UNSPEC (default for IP), AF_UNIX, AF_INET, ...) - * \returns zero on success, negative on error. - */ -int osmo_stream_srv_link_set_domain(struct osmo_stream_srv_link *link, int domain) -{ - switch (domain) { - case AF_UNSPEC: - case AF_INET: - case AF_INET6: - case AF_UNIX: - break; - default: - return -ENOTSUP; - } - link->sk_domain = domain; - link->flags |= OSMO_STREAM_SRV_F_RECONF; - return 0; -} - -/*! \brief Set application private data of the stream server link - * \paramin link Stream Server Link to modify - * \paramin data User-specific data (available in call-back functions) */ -void -osmo_stream_srv_link_set_data(struct osmo_stream_srv_link *link, - void *data) -{ - link->data = data; -} - -/*! \brief Get application private data of the stream server link - * \paramin link Stream Server Link to modify - * \returns Application private data, as set by \ref osmo_stream_cli_set_data() */ -void *osmo_stream_srv_link_get_data(struct osmo_stream_srv_link *link) -{ - return link->data; -} - -/*! \brief Get description of the stream server link e. g. 127.0.0.1:1234 - * \paramin link Stream Server Link to examine - * \returns Link description or NULL in case of error */ -char *osmo_stream_srv_link_get_sockname(const struct osmo_stream_srv_link *link) -{ - static char bufINET6_ADDRSTRLEN + 6; - int rc = osmo_sock_get_local_ip(link->ofd.fd, buf, INET6_ADDRSTRLEN); - if (rc < 0) - return NULL; - - bufstrnlen(buf, INET6_ADDRSTRLEN + 6) = ':'; - - rc = osmo_sock_get_local_ip_port(link->ofd.fd, buf + strnlen(buf, INET6_ADDRSTRLEN + 6), 6); - if (rc < 0) - return NULL; - - return buf; -} - -/*! \brief Get Osmocom File Descriptor of the stream server link - * \paramin link Stream Server Link - * \returns Pointer to \ref osmo_fd */ -struct osmo_fd * -osmo_stream_srv_link_get_ofd(struct osmo_stream_srv_link *link) -{ - return &link->ofd; -} - -/*! \brief Set the accept() call-back of the stream server link - * \paramin link Stream Server Link - * \paramin accept_cb Call-back function executed upon accept() */ -void osmo_stream_srv_link_set_accept_cb(struct osmo_stream_srv_link *link, - int (*accept_cb)(struct osmo_stream_srv_link *link, int fd)) - -{ - link->accept_cb = accept_cb; -} - -/*! \brief Destroy the stream server link. Closes + Releases Memory. - * \paramin link Stream Server Link */ -void osmo_stream_srv_link_destroy(struct osmo_stream_srv_link *link) -{ - osmo_stream_srv_link_close(link); - talloc_free(link); -} - -/*! \brief Open the stream server link. This actually initializes the - * underlying socket and binds it to the configured ip/port - * \paramin link Stream Server Link to open - * \return negative on error, 0 on success */ -int osmo_stream_srv_link_open(struct osmo_stream_srv_link *link) -{ - int ret; - - if (link->ofd.fd >= 0) { - /* No reconfigure needed for existing socket, we are fine */ - if (!(link->flags & OSMO_STREAM_SRV_F_RECONF)) - return 0; - /* we are reconfiguring this socket, close existing first. */ - osmo_stream_srv_link_close(link); - } - - link->flags &= ~OSMO_STREAM_SRV_F_RECONF; - - switch (link->sk_domain) { - case AF_UNIX: - ret = osmo_sock_unix_init(link->sk_type, 0, link->addr0, OSMO_SOCK_F_BIND); - break; - case AF_UNSPEC: - case AF_INET: - case AF_INET6: - switch (link->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - ret = osmo_sock_init2_multiaddr(link->sk_domain, link->sk_type, link->proto, - (const char **)link->addr, link->addrcnt, link->port, - NULL, 0, 0, OSMO_SOCK_F_BIND); - break; -#endif - default: - ret = osmo_sock_init(link->sk_domain, link->sk_type, link->proto, - link->addr0, link->port, OSMO_SOCK_F_BIND); - } - break; - default: - ret = -ENOTSUP; - } - if (ret < 0) - return ret; - - link->ofd.fd = ret; - if (osmo_fd_register(&link->ofd) < 0) { - close(ret); - link->ofd.fd = -1; - return -EIO; - } - return 0; -} - -/*! \brief Close the stream server link and unregister from select loop - * Does not destroy the server link, merely closes it! - * \paramin link Stream Server Link to close */ -void osmo_stream_srv_link_close(struct osmo_stream_srv_link *link) -{ - if (link->ofd.fd == -1) - return; - osmo_fd_unregister(&link->ofd); - close(link->ofd.fd); - link->ofd.fd = -1; -} - -#define OSMO_STREAM_SRV_F_FLUSH_DESTROY (1 << 0) - -struct osmo_stream_srv { - struct osmo_stream_srv_link *srv; - struct osmo_fd ofd; - struct llist_head tx_queue; - int (*closed_cb)(struct osmo_stream_srv *peer); - int (*cb)(struct osmo_stream_srv *peer); - void *data; - int flags; -}; - -static int osmo_stream_srv_read(struct osmo_stream_srv *conn) -{ - int rc = 0; - - LOGP(DLINP, LOGL_DEBUG, "message received\n"); - - if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) { - LOGP(DLINP, LOGL_DEBUG, "Connection is being flushed and closed; ignoring received message\n"); - return 0; - } - - if (conn->cb) - rc = conn->cb(conn); - - return rc; -} - -static void osmo_stream_srv_write(struct osmo_stream_srv *conn) -{ -#ifdef HAVE_LIBSCTP - struct sctp_sndrcvinfo sinfo; -#endif - struct msgb *msg; - int ret; - - if (llist_empty(&conn->tx_queue)) { - osmo_fd_write_disable(&conn->ofd); - return; - } - msg = llist_first_entry(&conn->tx_queue, struct msgb, list); - llist_del(&msg->list); - - LOGP(DLINP, LOGL_DEBUG, "sending %u bytes of data\n", msg->len); - - switch (conn->srv->sk_domain) { - case AF_UNIX: - ret = send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), 0); - break; - case AF_INET: - case AF_INET6: - case AF_UNSPEC: - switch (conn->srv->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.sinfo_ppid = htonl(msgb_sctp_ppid(msg)); - sinfo.sinfo_stream = msgb_sctp_stream(msg); - ret = sctp_send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), - &sinfo, MSG_NOSIGNAL); - break; -#endif - case IPPROTO_TCP: - default: - ret = send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), 0); - break; - } - break; - default: - ret = -1; - errno = ENOTSUP; - } - - if (ret >= 0 && ret < msgb_length(msg)) { - LOGP(DLINP, LOGL_ERROR, "short send: %d < exp %u\n", ret, msgb_length(msg)); - /* Update msgb and re-add it at the start of the queue: */ - msgb_pull(msg, ret); - llist_add(&msg->list, &conn->tx_queue); - return; - } - - if (ret == -1) /* send(): On error -1 is returned, and errno is set appropriately */ - LOGP(DLINP, LOGL_ERROR, "error to send: %s\n", strerror(errno)); - - msgb_free(msg); - - if (llist_empty(&conn->tx_queue)) { - osmo_fd_write_disable(&conn->ofd); - if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) - osmo_stream_srv_destroy(conn); - } -} - -static int osmo_stream_srv_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct osmo_stream_srv *conn = ofd->data; - int rc = 0; - - LOGP(DLINP, LOGL_DEBUG, "connected read/write (what=0x%x)\n", what); - if (what & OSMO_FD_READ) - rc = osmo_stream_srv_read(conn); - if (rc != -EBADF && (what & OSMO_FD_WRITE)) - osmo_stream_srv_write(conn); - - return rc; -} - -/*! \brief Create a Stream Server inside the specified link - * \paramin ctx talloc allocation context from which to allocate - * \paramin link Stream Server Link to which we belong - * \returns Stream Server in case of success; NULL on error */ -struct osmo_stream_srv * -osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, - int fd, - int (*cb)(struct osmo_stream_srv *conn), - int (*closed_cb)(struct osmo_stream_srv *conn), void *data) -{ - struct osmo_stream_srv *conn; - - OSMO_ASSERT(link); - - conn = talloc_zero(ctx, struct osmo_stream_srv); - if (conn == NULL) { - LOGP(DLINP, LOGL_ERROR, "cannot allocate new peer in srv, " - "reason=`%s'\n", strerror(errno)); - return NULL; - } - conn->srv = link; - osmo_fd_setup(&conn->ofd, fd, OSMO_FD_READ, osmo_stream_srv_cb, conn, 0); - conn->cb = cb; - conn->closed_cb = closed_cb; - conn->data = data; - INIT_LLIST_HEAD(&conn->tx_queue); - - if (osmo_fd_register(&conn->ofd) < 0) { - LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); - talloc_free(conn); - return NULL; - } - return conn; -} - -/*! \brief Prepare to send out all pending messages on the connection's Tx queue - * and then automatically destroy the stream with osmo_stream_srv_destroy(). - * This function disables queuing of new messages on the connection and also - * disables reception of new messages on the connection. - * \paramin conn Stream Server to modify */ -void osmo_stream_srv_set_flush_and_destroy(struct osmo_stream_srv *conn) -{ - conn->flags |= OSMO_STREAM_SRV_F_FLUSH_DESTROY; -} - -/*! \brief Set application private data of the stream server - * \paramin conn Stream Server to modify - * \paramin data User-specific data (available in call-back functions) */ -void -osmo_stream_srv_set_data(struct osmo_stream_srv *conn, - void *data) -{ - conn->data = data; -} - -/*! \brief Get application private data of the stream server - * \paramin conn Stream Server - * \returns Application private data, as set by \ref osmo_stream_srv_set_data() */ -void *osmo_stream_srv_get_data(struct osmo_stream_srv *conn) -{ - return conn->data; -} - -/*! \brief Get Osmocom File Descriptor of the stream server - * \paramin conn Stream Server - * \returns Pointer to \ref osmo_fd */ -struct osmo_fd * -osmo_stream_srv_get_ofd(struct osmo_stream_srv *conn) -{ - return &conn->ofd; -} - -/*! \brief Get the master (Link) from a Stream Server - * \paramin conn Stream Server of which we want to know the Link - * \returns Link through which the given Stream Server is established */ -struct osmo_stream_srv_link *osmo_stream_srv_get_master(struct osmo_stream_srv *conn) -{ - return conn->srv; -} - -/*! \brief Destroy given Stream Server - * This function closes the Stream Server socket, unregisters from - * select loop, invokes the connection's closed_cb() callback to allow API - * users to clean up any associated state they have for this connection, - * and then de-allocates associated memory. - * \paramin conn Stream Server to be destroyed */ -void osmo_stream_srv_destroy(struct osmo_stream_srv *conn) -{ - osmo_fd_unregister(&conn->ofd); - close(conn->ofd.fd); - conn->ofd.fd = -1; - if (conn->closed_cb) - conn->closed_cb(conn); - msgb_queue_free(&conn->tx_queue); - talloc_free(conn); -} - -/*! \brief Enqueue data to be sent via an Osmocom stream server - * \paramin conn Stream Server through which we want to send - * \paramin msg Message buffer to enqueue in transmit queue */ -void osmo_stream_srv_send(struct osmo_stream_srv *conn, struct msgb *msg) -{ - OSMO_ASSERT(conn); - OSMO_ASSERT(msg); - if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) { - LOGP(DLINP, LOGL_DEBUG, "Connection is being flushed and closed; ignoring new outgoing message\n"); - return; - } - - msgb_enqueue(&conn->tx_queue, msg); - osmo_fd_write_enable(&conn->ofd); -} - #ifdef HAVE_LIBSCTP -static int _sctp_recvmsg_wrapper(int fd, struct msgb *msg) +#define LOGPFX(pfx, level, fmt, args...) \ + LOGP(DLINP, level, "%s " fmt, pfx, ## args) +int stream_sctp_recvmsg_wrapper(int fd, struct msgb *msg, const char *log_pfx) { struct sctp_sndrcvinfo sinfo; int flags = 0; int ret; + uint8_t *data = msg->tail; - ret = sctp_recvmsg(fd, msgb_data(msg), msgb_tailroom(msg), - NULL, NULL, &sinfo, &flags); + ret = sctp_recvmsg(fd, data, msgb_tailroom(msg), NULL, NULL, &sinfo, &flags); msgb_sctp_msg_flags(msg) = 0; msgb_sctp_ppid(msg) = ntohl(sinfo.sinfo_ppid); msgb_sctp_stream(msg) = sinfo.sinfo_stream; + if (flags & MSG_NOTIFICATION) { - union sctp_notification *notif = (union sctp_notification *)msgb_data(msg); - LOGP(DLINP, LOGL_DEBUG, "NOTIFICATION %u flags=0x%x\n", notif->sn_header.sn_type, notif->sn_header.sn_flags); + char buf512; + struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) }; + int logl = LOGL_INFO; + union sctp_notification *notif = (union sctp_notification *)data; + + OSMO_STRBUF_PRINTF(sb, "%s NOTIFICATION %s flags=0x%x", log_pfx, + osmo_sctp_sn_type_str(notif->sn_header.sn_type), notif->sn_header.sn_flags); msgb_put(msg, sizeof(union sctp_notification)); msgb_sctp_msg_flags(msg) = OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION; + ret = -EAGAIN; + switch (notif->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: - LOGP(DLINP, LOGL_DEBUG, "===> ASSOC CHANGE:"); + OSMO_STRBUF_PRINTF(sb, " %s", osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state)); switch (notif->sn_assoc_change.sac_state) { case SCTP_COMM_UP: - LOGPC(DLINP, LOGL_DEBUG, " UP\n"); break; case SCTP_COMM_LOST: - LOGPC(DLINP, LOGL_DEBUG, " LOST\n"); + OSMO_STRBUF_PRINTF(sb, " (err: %s)", + osmo_sctp_sn_error_str(notif->sn_assoc_change.sac_error)); /* Handle this like a regular disconnect */ - return 0; - case SCTP_RESTART: - LOGPC(DLINP, LOGL_DEBUG, " RESTART\n"); + ret = 0; break; + case SCTP_RESTART: case SCTP_SHUTDOWN_COMP: - LOGPC(DLINP, LOGL_DEBUG, " SHUTDOWN COMP\n"); + logl = LOGL_NOTICE; break; case SCTP_CANT_STR_ASSOC: - LOGPC(DLINP, LOGL_DEBUG, " CANT STR ASSOC\n"); break; } break; case SCTP_SEND_FAILED: - LOGP(DLINP, LOGL_DEBUG, "===> SEND FAILED\n"); + logl = LOGL_ERROR; break; case SCTP_PEER_ADDR_CHANGE: { @@ -1548,88 +264,28 @@ struct sockaddr_storage sa = notif->sn_paddr_change.spc_aaddr; osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), (const struct osmo_sockaddr *)&sa); - LOGP(DLINP, LOGL_DEBUG, "===> PEER ADDR CHANGE: %s %s err=%s\n", - addr_str, osmo_sctp_paddr_chg_str(notif->sn_paddr_change.spc_state), - (notif->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) ? - osmo_sctp_sn_error_str(notif->sn_paddr_change.spc_error) : "None"); + OSMO_STRBUF_PRINTF(sb, " %s %s err=%s", + osmo_sctp_paddr_chg_str(notif->sn_paddr_change.spc_state), addr_str, + (notif->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) ? + osmo_sctp_sn_error_str(notif->sn_paddr_change.spc_error) : "None"); } break; case SCTP_SHUTDOWN_EVENT: - LOGP(DLINP, LOGL_DEBUG, "===> SHUTDOWN EVT\n"); - /* Handle this like a regular disconnect */ - return 0; - } - return -EAGAIN; - } - return ret; -} -#endif - -/*! \brief Receive data via Osmocom stream server - * \paramin conn Stream Server from which to receive - * \param msg pre-allocate message buffer to which received data is appended - * \returns number of bytes read, negative on error. - * - * If conn is an SCTP connection, additional specific considerations shall be taken: - * - msg->cb is always filled with SCTP ppid, and SCTP stream values, see msgb_sctp_*() APIs. - * - If an SCTP notification was received when reading from the SCTP socket, - * msgb_sctp_msg_flags(msg) will contain bit flag - * OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION set, and the msgb will - * contain a "union sctp_notification" instead of user data. In this case the - * return code will be either 0 (if conn is considered dead after the - * notification) or -EAGAIN (if conn is considered still alive after the - * notification) resembling the standard recv() API. - */ -int osmo_stream_srv_recv(struct osmo_stream_srv *conn, struct msgb *msg) -{ - int ret; - OSMO_ASSERT(conn); - OSMO_ASSERT(msg); - - switch (conn->srv->sk_domain) { - case AF_UNIX: - ret = recv(conn->ofd.fd, msgb_data(msg), msgb_tailroom(msg), 0); - break; - case AF_INET: - case AF_INET6: - case AF_UNSPEC: - switch (conn->srv->proto) { -#ifdef HAVE_LIBSCTP - case IPPROTO_SCTP: - ret = _sctp_recvmsg_wrapper(conn->ofd.fd, msg); + logl = LOGL_NOTICE; + /* RFC6458 3.1.4: Any attempt to send more data will cause sendmsg() + * to return with an ESHUTDOWN error. */ break; -#endif - case IPPROTO_TCP: - default: - ret = recv(conn->ofd.fd, msgb_data(msg), msgb_tailroom(msg), 0); + case SCTP_REMOTE_ERROR: + logl = LOGL_NOTICE; + OSMO_STRBUF_PRINTF(sb, " %s", osmo_sctp_op_error_str(ntohs(notif->sn_remote_error.sre_error))); break; } - break; - default: - ret = -ENOTSUP; - } - - if (ret < 0) { - if (errno == EPIPE || errno == ECONNRESET) { - LOGP(DLINP, LOGL_ERROR, - "lost connection with client\n"); - } - return ret; - } else if (ret == 0) { - LOGP(DLINP, LOGL_ERROR, "connection closed with client\n"); + LOGP(DLINP, logl, "%s\n", buf); return ret; } - msgb_put(msg, ret); - LOGP(DLINP, LOGL_DEBUG, "received %d bytes from client\n", ret); return ret; } +#endif -void osmo_stream_srv_clear_tx_queue(struct osmo_stream_srv *conn) -{ - msgb_queue_free(&conn->tx_queue); - osmo_fd_write_disable(&conn->ofd); - if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) - osmo_stream_srv_destroy(conn); -} /*! @} */
View file
libosmo-netif_1.4.0.tar.xz/src/stream_cli.c
Added
@@ -0,0 +1,1060 @@ +/* (C) 2011 by Pablo Neira Ayuso <pablo@gnumonks.org> + * (C) 2015-2016 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <osmocom/core/timer.h> +#include <osmocom/core/select.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/osmo_io.h> +#include <osmocom/core/panic.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/socket.h> + +#include <osmocom/netif/stream.h> +#include <osmocom/netif/stream_private.h> + +#include "config.h" + +#include <osmocom/netif/sctp.h> + + +/*! \addtogroup stream Osmocom Stream Socket (client side) + * @{ + * + * This code is intended to abstract any use of stream-type sockets, + * such as TCP and SCTP. It offers client side implementation, + * fully integrated with the libosmocore select loop abstraction. + */ + +/*! \file stream_cli.c + * \brief Osmocom stream socket helpers (client side) + */ + +#define LOGSCLI(cli, level, fmt, args...) \ + LOGP(DLINP, level, "CLICONN(%s,%s){%s} " fmt, \ + cli->name ? : "", \ + cli->sockname, \ + get_value_string(stream_cli_state_names, (cli)->state), \ + ## args) + +/* + * Client side. + */ + +enum osmo_stream_cli_state { + STREAM_CLI_STATE_CLOSED, /* No fd associated, no timer active */ + STREAM_CLI_STATE_WAIT_RECONNECT, /* No fd associated, has timer active to try to connect again */ + STREAM_CLI_STATE_CONNECTING, /* Fd associated, but connection not yet confirmed by peer or lower layers */ + STREAM_CLI_STATE_CONNECTED, /* Fd associated and connection is established */ + STREAM_CLI_STATE_MAX +}; + +static const struct value_string stream_cli_state_names = { + { STREAM_CLI_STATE_CLOSED, "CLOSED" }, + { STREAM_CLI_STATE_WAIT_RECONNECT, "WAIT_RECONNECT" }, + { STREAM_CLI_STATE_CONNECTING, "CONNECTING" }, + { STREAM_CLI_STATE_CONNECTED, "CONNECTED" }, + { 0, NULL } +}; + +#define OSMO_STREAM_CLI_F_RECONF (1 << 0) +#define OSMO_STREAM_CLI_F_NODELAY (1 << 1) + +struct osmo_stream_cli { + char *name; + char socknameOSMO_SOCK_NAME_MAXLEN; + enum osmo_stream_mode mode; + union { + struct osmo_fd ofd; + struct osmo_io_fd *iofd; + }; + struct llist_head tx_queue; + struct osmo_timer_list timer; + enum osmo_stream_cli_state state; + char *addrOSMO_STREAM_MAX_ADDRS; + uint8_t addrcnt; + uint16_t port; + char *local_addrOSMO_STREAM_MAX_ADDRS; + uint8_t local_addrcnt; + uint16_t local_port; + int sk_domain; + int sk_type; + uint16_t proto; + int (*connect_cb)(struct osmo_stream_cli *cli); + int (*disconnect_cb)(struct osmo_stream_cli *cli); + int (*read_cb)(struct osmo_stream_cli *cli); + int (*iofd_read_cb)(struct osmo_stream_cli *cli, struct msgb *msg); + int (*write_cb)(struct osmo_stream_cli *cli); + void *data; + int flags; + int reconnect_timeout; + struct osmo_sock_init2_multiaddr_pars ma_pars; +}; + +void osmo_stream_cli_close(struct osmo_stream_cli *cli); + +/*! \brief Re-connect an Osmocom Stream Client + * If re-connection is enabled for this client + * (which is the case unless negative timeout was explicitly set via osmo_stream_cli_set_reconnect_timeout() call), + * we close any existing connection (if any) and schedule a re-connect timer */ +void osmo_stream_cli_reconnect(struct osmo_stream_cli *cli) +{ + osmo_stream_cli_close(cli); + + if (cli->reconnect_timeout < 0) { + LOGSCLI(cli, LOGL_INFO, "not reconnecting, disabled\n"); + return; + } + + cli->state = STREAM_CLI_STATE_WAIT_RECONNECT; + LOGSCLI(cli, LOGL_INFO, "retrying reconnect in %d seconds...\n", + cli->reconnect_timeout); + osmo_timer_schedule(&cli->timer, cli->reconnect_timeout, 0); +} + +/*! \brief Check if Osmocom Stream Client is in connected state + * \paramin cli Osmocom Stream Client + * \return true if connected, false otherwise + */ +bool osmo_stream_cli_is_connected(struct osmo_stream_cli *cli) +{ + return cli->state == STREAM_CLI_STATE_CONNECTED; +} + +static void osmo_stream_cli_close_iofd(struct osmo_stream_cli *cli) +{ + if (!cli->iofd) + return; + + osmo_iofd_close(cli->iofd); +} + +static void osmo_stream_cli_close_ofd(struct osmo_stream_cli *cli) +{ + if (cli->ofd.fd == -1) + return; + osmo_fd_unregister(&cli->ofd); + close(cli->ofd.fd); + cli->ofd.fd = -1; +} + +/*! \brief Close an Osmocom Stream Client + * \paramin cli Osmocom Stream Client to be closed + * We unregister the socket fd from the osmocom select() loop + * abstraction and close the socket */ +void osmo_stream_cli_close(struct osmo_stream_cli *cli) +{ + int old_state = cli->state; + + if (cli->state == STREAM_CLI_STATE_CLOSED) + return; + if (cli->state == STREAM_CLI_STATE_WAIT_RECONNECT) { + osmo_timer_del(&cli->timer); + cli->state = STREAM_CLI_STATE_CLOSED; + return; + } + + + switch (cli->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + osmo_stream_cli_close_ofd(cli); + break; + case OSMO_STREAM_MODE_OSMO_IO: + osmo_stream_cli_close_iofd(cli); + break; + default: + OSMO_ASSERT(false); + } + + cli->state = STREAM_CLI_STATE_CLOSED; + + if (old_state == STREAM_CLI_STATE_CONNECTED) { + LOGSCLI(cli, LOGL_DEBUG, "connection closed\n"); + if (cli->disconnect_cb) + cli->disconnect_cb(cli); + } +} + +static inline int osmo_stream_cli_fd(const struct osmo_stream_cli *cli) +{ + switch (cli->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + return cli->ofd.fd; + case OSMO_STREAM_MODE_OSMO_IO: + if (cli->iofd) + return osmo_iofd_get_fd(cli->iofd); + default: + break; + } + return -EINVAL; +} + +static void osmo_stream_cli_read(struct osmo_stream_cli *cli) +{ + LOGSCLI(cli, LOGL_DEBUG, "message received\n"); + + if (cli->read_cb) + cli->read_cb(cli); +} + +static int osmo_stream_cli_write(struct osmo_stream_cli *cli) +{ +#ifdef HAVE_LIBSCTP + struct sctp_sndrcvinfo sinfo; +#endif + struct msgb *msg; + int ret; + + if (llist_empty(&cli->tx_queue)) { + osmo_fd_write_disable(&cli->ofd); + return 0; + } + msg = llist_first_entry(&cli->tx_queue, struct msgb, list); + llist_del(&msg->list); + + if (!osmo_stream_cli_is_connected(cli)) { + LOGSCLI(cli, LOGL_ERROR, "send: not connected, dropping data!\n"); + return 0; + } + + LOGSCLI(cli, LOGL_DEBUG, "sending %u bytes of data\n", msgb_length(msg)); + + switch (cli->sk_domain) { + case AF_UNIX: + ret = send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), 0); + break; + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + switch (cli->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.sinfo_ppid = htonl(msgb_sctp_ppid(msg)); + sinfo.sinfo_stream = msgb_sctp_stream(msg); + ret = sctp_send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), + &sinfo, MSG_NOSIGNAL); + break; +#endif + case IPPROTO_TCP: + default: + ret = send(cli->ofd.fd, msgb_data(msg), msgb_length(msg), 0); + break; + } + break; + default: + ret = -ENOTSUP; + } + + if (ret >= 0 && ret < msgb_length(msg)) { + LOGSCLI(cli, LOGL_ERROR, "short send: %d < exp %u\n", ret, msgb_length(msg)); + /* Update msgb and re-add it at the start of the queue: */ + msgb_pull(msg, ret); + llist_add(&msg->list, &cli->tx_queue); + return 0; + } + + if (ret < 0) { + int err = errno; + LOGSCLI(cli, LOGL_ERROR, "send(len=%u) error: %s\n", msgb_length(msg), strerror(err)); + if (err == EAGAIN) { + /* Re-add at the start of the queue to re-attempt: */ + llist_add(&msg->list, &cli->tx_queue); + return 0; + } + msgb_free(msg); + osmo_stream_cli_reconnect(cli); + } + + msgb_free(msg); + + if (llist_empty(&cli->tx_queue)) + osmo_fd_write_disable(&cli->ofd); + + return 0; +} + +static int _setsockopt_nosigpipe(struct osmo_stream_cli *cli) +{ +#ifdef SO_NOSIGPIPE + int ret; + int val = 1; + ret = setsockopt(osmo_stream_cli_fd(cli), SOL_SOCKET, SO_NOSIGPIPE, (void *)&val, sizeof(val)); + if (ret < 0) + LOGSCLI(cli, LOGL_ERROR, "Failed setting SO_NOSIGPIPE: %s\n", strerror(errno)); + return ret; +#else + return 0; +#endif +} + +static void stream_cli_handle_connecting(struct osmo_stream_cli *cli, int res) +{ + int error, ret = res; + socklen_t len = sizeof(error); + + int fd = osmo_stream_cli_fd(cli); + OSMO_ASSERT(fd >= 0); + + if (ret < 0) { + osmo_stream_cli_reconnect(cli); + return; + } + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret >= 0 && error > 0) { + osmo_stream_cli_reconnect(cli); + return; + } + + /* If messages got enqueued while 'connecting', keep WRITE flag + up to dispatch them upon next main loop step */ + 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_fd(cli)); + + LOGSCLI(cli, LOGL_INFO, "connection established\n"); + cli->state = STREAM_CLI_STATE_CONNECTED; + switch (cli->sk_domain) { + case AF_UNIX: + _setsockopt_nosigpipe(cli); + break; + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + if (cli->proto == IPPROTO_SCTP) { + _setsockopt_nosigpipe(cli); + stream_sctp_sock_activate_events(fd); + } + break; + default: + break; + } + if (cli->connect_cb) + cli->connect_cb(cli); +} + +static int osmo_stream_cli_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct osmo_stream_cli *cli = ofd->data; + + switch (cli->state) { + case STREAM_CLI_STATE_CONNECTING: + stream_cli_handle_connecting(cli, 0); + break; + case STREAM_CLI_STATE_CONNECTED: + if (what & OSMO_FD_READ) { + LOGSCLI(cli, LOGL_DEBUG, "connected read\n"); + osmo_stream_cli_read(cli); + } + if (what & OSMO_FD_WRITE) { + LOGSCLI(cli, LOGL_DEBUG, "connected write\n"); + osmo_stream_cli_write(cli); + } + break; + default: + /* Only CONNECTING and CONNECTED states are expected, since they are the only states + * where FD exists: */ + osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); + } + return 0; +} + +static void cli_timer_cb(void *data); + +/*! \brief Create an Osmocom stream client + * \paramin ctx talloc context from which to allocate memory + * This function allocates a new \ref osmo_stream_cli and initializes + * it with default values (5s reconnect timer, TCP protocol) + * \return allocated stream client, or NULL in case of error + */ +struct osmo_stream_cli *osmo_stream_cli_create(void *ctx) +{ + struct osmo_stream_cli *cli; + + cli = talloc_zero(ctx, struct osmo_stream_cli); + if (!cli) + return NULL; + + cli->mode = OSMO_STREAM_MODE_UNKNOWN; + cli->sk_domain = AF_UNSPEC; + cli->sk_type = SOCK_STREAM; + cli->proto = IPPROTO_TCP; + + cli->state = STREAM_CLI_STATE_CLOSED; + osmo_timer_setup(&cli->timer, cli_timer_cb, cli); + cli->reconnect_timeout = 5; /* default is 5 seconds. */ + INIT_LLIST_HEAD(&cli->tx_queue); + + cli->ma_pars.sctp.version = 0; + + return cli; +} + +static void stream_cli_iofd_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg) +{ + struct osmo_stream_cli *cli = osmo_iofd_get_data(iofd); + + switch (cli->state) { + case STREAM_CLI_STATE_CONNECTING: + stream_cli_handle_connecting(cli, res); + break; + case STREAM_CLI_STATE_CONNECTED: + if (res == 0) + osmo_stream_cli_reconnect(cli); + else if (cli->iofd_read_cb) + cli->iofd_read_cb(cli, msg); + break; + default: + osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); + } +} + +static void stream_cli_iofd_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg) +{ + struct osmo_stream_cli *cli = osmo_iofd_get_data(iofd); + + switch (cli->state) { + case STREAM_CLI_STATE_CONNECTING: + stream_cli_handle_connecting(cli, res); + break; + case STREAM_CLI_STATE_CONNECTED: + if (msg && res <= 0) { + osmo_stream_cli_reconnect(cli); + LOGSCLI(cli, LOGL_ERROR, "received error %d in response to send\n", res); + } + break; + default: + osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); + } +} + +static struct osmo_io_ops osmo_stream_cli_ioops = { + .read_cb = stream_cli_iofd_read_cb, + .write_cb = stream_cli_iofd_write_cb, + + .segmentation_cb = NULL, +}; + +/*! \brief Set a name on the cli object (used during logging) + * \paramin cli stream_cli whose name is to be set + * \paramin name the name to be set on cli + */ +void osmo_stream_cli_set_name(struct osmo_stream_cli *cli, const char *name) +{ + osmo_talloc_replace_string(cli, &cli->name, name); + if (cli->mode == OSMO_STREAM_MODE_OSMO_IO && cli->iofd) + osmo_iofd_set_name(cli->iofd, name); +} + +/*! \brief Set the remote address to which we connect + * \paramin cli Stream Client to modify + * \paramin addr Remote IP address + */ +void +osmo_stream_cli_set_addr(struct osmo_stream_cli *cli, const char *addr) +{ + osmo_stream_cli_set_addrs(cli, &addr, 1); +} + +/*! \brief Set the remote address set to which we connect. + * Useful for protocols allowing connecting to more than one address (such as SCTP) + * \paramin cli Stream Client to modify + * \paramin addr Remote IP address set + * \return negative on error, 0 on success + */ +int osmo_stream_cli_set_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt) +{ + int i = 0; + + if (addrcnt > OSMO_STREAM_MAX_ADDRS) + return -EINVAL; + + for (; i < addrcnt; i++) + osmo_talloc_replace_string(cli, &cli->addri, addri); + for (; i < cli->addrcnt; i++) { + talloc_free(cli->addri); + cli->addri = NULL; + } + + cli->addrcnt = addrcnt; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; + return 0; +} + +/*! \brief Set the remote port number to which we connect + * \paramin cli Stream Client to modify + * \paramin port Remote port number + */ +void +osmo_stream_cli_set_port(struct osmo_stream_cli *cli, uint16_t port) +{ + cli->port = port; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; +} + +/*! \brief Set the local port number for the socket (to be bound to) + * \paramin cli Stream Client to modify + * \paramin port Local port number + */ +void +osmo_stream_cli_set_local_port(struct osmo_stream_cli *cli, uint16_t port) +{ + cli->local_port = port; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; +} + +/*! \brief Set the local address for the socket (to be bound to) + * \paramin cli Stream Client to modify + * \paramin port Local host name + */ +void +osmo_stream_cli_set_local_addr(struct osmo_stream_cli *cli, const char *addr) +{ + osmo_stream_cli_set_local_addrs(cli, &addr, 1); +} + +/*! \brief Set the local address set to which we connect. + * Useful for protocols allowing bind to more than one address (such as SCTP) + * \paramin cli Stream Client to modify + * \paramin addr Local IP address set + * \return negative on error, 0 on success + */ +int osmo_stream_cli_set_local_addrs(struct osmo_stream_cli *cli, const char **addr, size_t addrcnt) +{ + int i = 0; + + if (addrcnt > OSMO_STREAM_MAX_ADDRS) + return -EINVAL; + + for (; i < addrcnt; i++) + osmo_talloc_replace_string(cli, &cli->local_addri, addri); + for (; i < cli->local_addrcnt; i++) { + talloc_free(cli->local_addri); + cli->local_addri = NULL; + } + + cli->local_addrcnt = addrcnt; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; + return 0; +} + +/*! \brief Set the protocol for the stream client socket + * \paramin cli Stream Client to modify + * \paramin proto Protocol (like IPPROTO_TCP (default), IPPROTO_SCTP, ...) + */ +void +osmo_stream_cli_set_proto(struct osmo_stream_cli *cli, uint16_t proto) +{ + cli->proto = proto; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; +} + +/*! \brief Set the socket type for the stream server link + * \paramin cli Stream Client to modify + * \paramin type Socket Type (like SOCK_STREAM (default), SOCK_SEQPACKET, ...) + * \returns zero on success, negative on error. + */ +int osmo_stream_cli_set_type(struct osmo_stream_cli *cli, int type) +{ + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + break; + default: + return -ENOTSUP; + } + cli->sk_type = type; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; + return 0; +} + +/*! \brief Set the socket type for the stream server link + * \paramin cli Stream Client to modify + * \paramin type Socket Domain (like AF_UNSPEC (default for IP), AF_UNIX, AF_INET, ...) + * \returns zero on success, negative on error. + */ +int osmo_stream_cli_set_domain(struct osmo_stream_cli *cli, int domain) +{ + switch (domain) { + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + case AF_UNIX: + break; + default: + return -ENOTSUP; + } + cli->sk_domain = domain; + cli->flags |= OSMO_STREAM_CLI_F_RECONF; + return 0; +} + +/*! \brief Set the reconnect time of the stream client socket + * \paramin cli Stream Client to modify + * \paramin timeout Re-connect timeout in seconds or negative value to disable auto-reconnection */ +void +osmo_stream_cli_set_reconnect_timeout(struct osmo_stream_cli *cli, int timeout) +{ + cli->reconnect_timeout = timeout; +} + +/*! \brief Set application private data of the stream client socket + * \paramin cli Stream Client to modify + * \paramin data User-specific data (available in call-back functions) */ +void +osmo_stream_cli_set_data(struct osmo_stream_cli *cli, void *data) +{ + cli->data = data; +} + +/*! \brief Get application private data of the stream client socket + * \paramin cli Stream Client to modify + * \returns Application private data, as set by \ref osmo_stream_cli_set_data() */ +void *osmo_stream_cli_get_data(struct osmo_stream_cli *cli) +{ + return cli->data; +} + +/*! \brief Get the stream client socket description. + * \paramin cli Stream Client to examine + * \returns Socket description or NULL in case of error */ +char *osmo_stream_cli_get_sockname(const struct osmo_stream_cli *cli) +{ + static char bufOSMO_SOCK_NAME_MAXLEN; + + osmo_sock_get_name_buf(buf, OSMO_SOCK_NAME_MAXLEN, osmo_stream_cli_fd(cli)); + + return buf; +} + +/*! \brief Get Osmocom File Descriptor of the stream client socket + * \paramin cli Stream Client to modify + * \returns Pointer to \ref osmo_fd */ +struct osmo_fd * +osmo_stream_cli_get_ofd(struct osmo_stream_cli *cli) +{ + OSMO_ASSERT(cli->mode == OSMO_STREAM_MODE_OSMO_FD); + return &cli->ofd; +} + +/*! \brief Set the call-back function called on connect of the stream client socket + * \paramin cli Stream Client to modify + * \paramin connect_cb Call-back function to be called upon connect */ +void +osmo_stream_cli_set_connect_cb(struct osmo_stream_cli *cli, + int (*connect_cb)(struct osmo_stream_cli *cli)) +{ + cli->connect_cb = connect_cb; +} + +/*! \brief Set the call-back function called on disconnect of the stream client socket + * \paramin cli Stream Client to modify + * \paramin disconnect_cb Call-back function to be called upon disconnect */ +void osmo_stream_cli_set_disconnect_cb(struct osmo_stream_cli *cli, + int (*disconnect_cb)(struct osmo_stream_cli *cli)) +{ + cli->disconnect_cb = disconnect_cb; +} + +/*! \brief Set the call-back function called to read from the stream client socket + * This function will configure osmo_stream_cli to use osmo_ofd internally. + * \paramin cli Stream Client to modify + * \paramin read_cb Call-back function to be called when we want to read */ +void +osmo_stream_cli_set_read_cb(struct osmo_stream_cli *cli, + int (*read_cb)(struct osmo_stream_cli *cli)) +{ + OSMO_ASSERT(cli->mode != OSMO_STREAM_MODE_OSMO_IO); + cli->mode = OSMO_STREAM_MODE_OSMO_FD; + cli->read_cb = read_cb; +} + +/*! \brief Set the call-back function called to read from the stream client socket + * This function will configure osmo_stream_cli to use osmo_iofd internally. + * \paramin cli Stream Client to modify + * \paramin read_cb Call-back function to be called when data was read from the socket */ +void +osmo_stream_cli_set_read_cb2(struct osmo_stream_cli *cli, + int (*read_cb)(struct osmo_stream_cli *cli, struct msgb *msg)) +{ + OSMO_ASSERT(cli->mode != OSMO_STREAM_MODE_OSMO_FD); + cli->mode = OSMO_STREAM_MODE_OSMO_IO; + cli->iofd_read_cb = read_cb; +} + +/*! \brief Destroy a Osmocom stream client (includes close) + * \paramin cli Stream Client to destroy */ +void osmo_stream_cli_destroy(struct osmo_stream_cli *cli) +{ + osmo_stream_cli_close(cli); + osmo_timer_del(&cli->timer); + msgb_queue_free(&cli->tx_queue); + talloc_free(cli); +} + +/*! \brief DEPRECATED: use osmo_stream_cli_set_reconnect_timeout() or osmo_stream_cli_reconnect() instead! + * Open connection of an Osmocom stream client + * \paramin cli Stream Client to connect + * \paramin reconect 1 if we should not automatically reconnect + * \return negative on error, 0 on success + */ +int osmo_stream_cli_open2(struct osmo_stream_cli *cli, int reconnect) +{ + int ret; + + /* we are reconfiguring this socket, close existing first. */ + if ((cli->flags & OSMO_STREAM_CLI_F_RECONF) && cli->ofd.fd >= 0) + osmo_stream_cli_close(cli); + + cli->flags &= ~OSMO_STREAM_CLI_F_RECONF; + + switch (cli->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + ret = osmo_sock_init2_multiaddr2(AF_UNSPEC, SOCK_STREAM, cli->proto, + (const char **)cli->local_addr, cli->local_addrcnt, cli->local_port, + (const char **)cli->addr, cli->addrcnt, cli->port, + OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK, + &cli->ma_pars); + break; +#endif + default: + ret = osmo_sock_init2(AF_UNSPEC, SOCK_STREAM, cli->proto, + cli->local_addr0, cli->local_port, + cli->addr0, cli->port, + OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); + } + + if (ret < 0) { + if (reconnect) + osmo_stream_cli_reconnect(cli); + return ret; + } + osmo_fd_setup(&cli->ofd, ret, OSMO_FD_READ | OSMO_FD_WRITE, osmo_stream_cli_fd_cb, cli, 0); + + if (cli->flags & OSMO_STREAM_CLI_F_NODELAY) { + ret = stream_setsockopt_nodelay(cli->ofd.fd, cli->proto, 1); + if (ret < 0) + goto error_close_socket; + } + + if (osmo_fd_register(&cli->ofd) < 0) + goto error_close_socket; + + cli->state = STREAM_CLI_STATE_CONNECTING; + return 0; + +error_close_socket: + close(cli->ofd.fd); + cli->ofd.fd = -1; + return -EIO; +} + +/*! \brief Set the NODELAY socket option to avoid Nagle-like behavior + * Setting this to nodelay=true will automatically set the NODELAY + * socket option on any socket established via \ref osmo_stream_cli_open + * or any re-connect. You have to set this _before_ opening the + * socket. + * \paramin cli Stream client whose sockets are to be configured + * \paramin nodelay whether to set (true) NODELAY before connect() + */ +void osmo_stream_cli_set_nodelay(struct osmo_stream_cli *cli, bool nodelay) +{ + if (nodelay) + cli->flags |= OSMO_STREAM_CLI_F_NODELAY; + else + cli->flags &= ~OSMO_STREAM_CLI_F_NODELAY; +} + +/*! \brief Open connection of an Osmocom stream client + * By default the client will automatically reconnect after default timeout. + * To disable this, use osmo_stream_cli_set_reconnect_timeout() before calling this function. + * \paramin cli Stream Client to connect + * \return negative on error, 0 on success */ +int osmo_stream_cli_open(struct osmo_stream_cli *cli) +{ + int ret, fd = -1; + + /* we are reconfiguring this socket, close existing first. */ + if ((cli->flags & OSMO_STREAM_CLI_F_RECONF) && osmo_stream_cli_fd(cli) >= 0) + osmo_stream_cli_close(cli); + + cli->flags &= ~OSMO_STREAM_CLI_F_RECONF; + + switch (cli->sk_domain) { + case AF_UNIX: + ret = osmo_sock_unix_init(cli->sk_type, 0, cli->addr0, OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); + break; + case AF_INET: + case AF_INET6: + case AF_UNSPEC: + switch (cli->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + ret = osmo_sock_init2_multiaddr2(cli->sk_domain, cli->sk_type, cli->proto, + (const char **)cli->local_addr, cli->local_addrcnt, cli->local_port, + (const char **)cli->addr, cli->addrcnt, cli->port, + OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK, + &cli->ma_pars); + break; +#endif + default: + ret = osmo_sock_init2(cli->sk_domain, cli->sk_type, cli->proto, + cli->local_addr0, cli->local_port, + cli->addr0, cli->port, + OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); + } + break; + default: + return -ENOTSUP; + } + + if (ret < 0) { + osmo_stream_cli_reconnect(cli); + return ret; + } + + fd = ret; + + if (cli->flags & OSMO_STREAM_CLI_F_NODELAY) { + ret = stream_setsockopt_nodelay(fd, cli->proto, 1); + if (ret < 0) + goto error_close_socket; + } + + switch (cli->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + osmo_fd_setup(&cli->ofd, fd, OSMO_FD_READ | OSMO_FD_WRITE, osmo_stream_cli_fd_cb, cli, 0); + if (osmo_fd_register(&cli->ofd) < 0) + goto error_close_socket; + break; + case OSMO_STREAM_MODE_OSMO_IO: + if (!cli->iofd) + cli->iofd = osmo_iofd_setup(cli, fd, cli->name, OSMO_IO_FD_MODE_READ_WRITE, &osmo_stream_cli_ioops, cli); + if (!cli->iofd) + goto error_close_socket; + + if (osmo_iofd_register(cli->iofd, fd) < 0) + goto error_close_socket; + osmo_iofd_notify_connected(cli->iofd); + break; + default: + OSMO_ASSERT(false); + } + + cli->state = STREAM_CLI_STATE_CONNECTING; + return 0; + +error_close_socket: + cli->state = STREAM_CLI_STATE_CLOSED; + close(fd); + if (cli->mode == OSMO_STREAM_MODE_OSMO_FD) + cli->ofd.fd = -1; + return -EIO; +} + +static void cli_timer_cb(void *data) +{ + struct osmo_stream_cli *cli = data; + + LOGSCLI(cli, LOGL_DEBUG, "reconnecting\n"); + osmo_stream_cli_open(cli); +} + +/*! \brief Enqueue data to be sent via an Osmocom stream client + * \paramin cli Stream Client through which we want to send + * \paramin msg Message buffer to enqueue in transmit queue */ +void osmo_stream_cli_send(struct osmo_stream_cli *cli, struct msgb *msg) +{ + OSMO_ASSERT(cli); + OSMO_ASSERT(msg); + + switch (cli->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + msgb_enqueue(&cli->tx_queue, msg); + osmo_fd_write_enable(&cli->ofd); + break; + case OSMO_STREAM_MODE_OSMO_IO: + osmo_iofd_write_msgb(cli->iofd, msg); + break; + default: + OSMO_ASSERT(false); + } +} + +/*! \brief Receive data via an Osmocom stream client + * \paramin cli Stream Client through which we want to send + * \param msg pre-allocate message buffer to which received data is appended + * \returns number of bytes read; <=0 in case of error + * + * If conn is an SCTP connection, additional specific considerations shall be taken: + * - msg->cb is always filled with SCTP ppid, and SCTP stream values, see msgb_sctp_*() APIs. + * - If an SCTP notification was received when reading from the SCTP socket, + * msgb_sctp_msg_flags(msg) will contain bit flag + * OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION set, and the msgb will + * contain a "union sctp_notification" instead of user data. In this case the + * return code will be either 0 (if conn is considered dead after the + * notification) or -EAGAIN (if conn is considered still alive after the + * notification) resembling the standard recv() API. + */ +int osmo_stream_cli_recv(struct osmo_stream_cli *cli, struct msgb *msg) +{ + int ret; + OSMO_ASSERT(cli); + OSMO_ASSERT(msg); + + switch (cli->sk_domain) { + case AF_UNIX: + ret = recv(cli->ofd.fd, msg->tail, msgb_tailroom(msg), 0); + break; + case AF_INET: + case AF_INET6: + case AF_UNSPEC: + switch (cli->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + { + char log_pfx128; + snprintf(log_pfx, sizeof(log_pfx), "CLICONN(%s,%s)", cli->name ? : "", cli->sockname); + ret = stream_sctp_recvmsg_wrapper(cli->ofd.fd, msg, log_pfx); + break; + } +#endif + case IPPROTO_TCP: + default: + ret = recv(cli->ofd.fd, msg->tail, msgb_tailroom(msg), 0); + break; + } + break; + default: + ret = -ENOTSUP; + } + + if (ret < 0) { + if (ret == -EAGAIN) + return ret; + if (errno == EPIPE || errno == ECONNRESET) + LOGSCLI(cli, LOGL_ERROR, "lost connection with srv\n"); + osmo_stream_cli_reconnect(cli); + return ret; + } else if (ret == 0) { + LOGSCLI(cli, LOGL_ERROR, "connection closed with srv\n"); + osmo_stream_cli_reconnect(cli); + return ret; + } + msgb_put(msg, ret); + LOGSCLI(cli, LOGL_DEBUG, "received %d bytes from srv\n", ret); + return ret; +} + +void osmo_stream_cli_clear_tx_queue(struct osmo_stream_cli *cli) +{ + switch (cli->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + msgb_queue_free(&cli->tx_queue); + /* If in state 'connecting', keep WRITE flag up to receive + * socket connection signal and then transition to STATE_CONNECTED: */ + if (cli->state == STREAM_CLI_STATE_CONNECTED) + osmo_fd_write_disable(&cli->ofd); + break; + case OSMO_STREAM_MODE_OSMO_IO: + osmo_iofd_txqueue_clear(cli->iofd); + break; + default: + OSMO_ASSERT(false); + } +} + +int osmo_stream_cli_set_param(struct osmo_stream_cli *cli, enum osmo_stream_cli_param par, void *val, size_t val_len) +{ + OSMO_ASSERT(cli); + uint8_t val8; + + switch (par) { + case OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED: + if (!val || val_len != sizeof(uint8_t)) + return -EINVAL; + val8 = *(uint8_t *)val; + cli->ma_pars.sctp.sockopt_auth_supported.set = true; + cli->ma_pars.sctp.sockopt_auth_supported.abort_on_failure = val8 > 1; + cli->ma_pars.sctp.sockopt_auth_supported.value = (val8 == 1 || val8 == 3) ? 1 : 0; + break; + case OSMO_STREAM_CLI_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED: + if (!val || val_len != sizeof(uint8_t)) + return -EINVAL; + val8 = *(uint8_t *)val; + cli->ma_pars.sctp.sockopt_asconf_supported.set = true; + cli->ma_pars.sctp.sockopt_asconf_supported.abort_on_failure = val8 > 1; + cli->ma_pars.sctp.sockopt_asconf_supported.value = (val8 == 1 || val8 == 3) ? 1 : 0; + break; + case OSMO_STREAM_CLI_PAR_SCTP_INIT_NUM_OSTREAMS: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + cli->ma_pars.sctp.sockopt_initmsg.set = true; + cli->ma_pars.sctp.sockopt_initmsg.num_ostreams_present = true; + cli->ma_pars.sctp.sockopt_initmsg.num_ostreams_value = *(uint16_t *)val; + break; + case OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_INSTREAMS: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + cli->ma_pars.sctp.sockopt_initmsg.set = true; + cli->ma_pars.sctp.sockopt_initmsg.max_instreams_present = true; + cli->ma_pars.sctp.sockopt_initmsg.max_instreams_value = *(uint16_t *)val; + break; + case OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_ATTEMPTS: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + cli->ma_pars.sctp.sockopt_initmsg.set = true; + cli->ma_pars.sctp.sockopt_initmsg.max_attempts_present = true; + cli->ma_pars.sctp.sockopt_initmsg.max_attempts_value = *(uint16_t *)val; + break; + case OSMO_STREAM_CLI_PAR_SCTP_INIT_TIMEOUT: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + cli->ma_pars.sctp.sockopt_initmsg.set = true; + cli->ma_pars.sctp.sockopt_initmsg.max_init_timeo_present = true; + cli->ma_pars.sctp.sockopt_initmsg.max_init_timeo_value = *(uint16_t *)val; + break; + default: + return -ENOENT; + }; + return 0; +} + +/*! @} */
View file
libosmo-netif_1.4.0.tar.xz/src/stream_srv.c
Added
@@ -0,0 +1,965 @@ +/* (C) 2011 by Pablo Neira Ayuso <pablo@gnumonks.org> + * (C) 2015-2016 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <osmocom/core/timer.h> +#include <osmocom/core/select.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/osmo_io.h> +#include <osmocom/core/panic.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/socket.h> + +#include <osmocom/netif/stream.h> +#include <osmocom/netif/stream_private.h> + +#include "config.h" + +#include <osmocom/netif/sctp.h> + + +/*! \addtogroup stream Osmocom Stream Socket (server side) + * @{ + * + * This code is intended to abstract any use of stream-type sockets, + * such as TCP and SCTP. It offers server side implementation, + * fully integrated with the libosmocore select loop abstraction. + */ + +/*! \file stream_srv.c + * \brief Osmocom stream socket helpers (server side) + */ + +#define LOGSLNK(link, level, fmt, args...) \ + LOGP(DLINP, level, "SRV(%s,%s) " fmt, \ + link->name ? : "", \ + link->sockname, \ + ## args) + +#define LOGSSRV(srv, level, fmt, args...) \ + LOGP(DLINP, level, "SRVCONN(%s,%s) " fmt, \ + srv->name ? : "", \ + srv->sockname, \ + ## args) +/* + * Server side. + */ + +#define OSMO_STREAM_SRV_F_RECONF (1 << 0) +#define OSMO_STREAM_SRV_F_NODELAY (1 << 1) + +struct osmo_stream_srv_link { + struct osmo_fd ofd; + char *name; + char socknameOSMO_SOCK_NAME_MAXLEN; + char *addrOSMO_STREAM_MAX_ADDRS; + uint8_t addrcnt; + uint16_t port; + int sk_domain; + int sk_type; + uint16_t proto; + int (*accept_cb)(struct osmo_stream_srv_link *srv, int fd); + void *data; + int flags; + struct osmo_sock_init2_multiaddr_pars ma_pars; +}; + +static int _setsockopt_nosigpipe(struct osmo_stream_srv_link *link, int new_fd) +{ +#ifdef SO_NOSIGPIPE + int ret; + int val = 1; + ret = setsockopt(new_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&val, sizeof(val)); + if (ret < 0) + LOGSLNK(link, LOGL_ERROR, "Failed setting SO_NOSIGPIPE: %s\n", strerror(errno)); + return ret; +#else + return 0; +#endif +} + +static int osmo_stream_srv_link_ofd_cb(struct osmo_fd *ofd, unsigned int what) +{ + int ret; + int sock_fd; + struct osmo_sockaddr osa; + socklen_t sa_len = sizeof(osa.u.sas); + struct osmo_stream_srv_link *link = ofd->data; + + ret = accept(ofd->fd, &osa.u.sa, &sa_len); + if (ret < 0) { + LOGSLNK(link, LOGL_ERROR, "failed to accept from origin peer, reason=`%s'\n", + strerror(errno)); + return ret; + } + sock_fd = ret; + + switch (osa.u.sa.sa_family) { + case AF_UNIX: + LOGSLNK(link, LOGL_INFO, "accept()ed new link on fd %d\n", + sock_fd); + _setsockopt_nosigpipe(link, sock_fd); + break; + case AF_INET6: + case AF_INET: + LOGSLNK(link, LOGL_INFO, "accept()ed new link from %s\n", + osmo_sockaddr_to_str(&osa)); + + if (link->proto == IPPROTO_SCTP) { + _setsockopt_nosigpipe(link, sock_fd); + ret = stream_sctp_sock_activate_events(sock_fd); + if (ret < 0) + goto error_close_socket; + } + break; + default: + LOGSLNK(link, LOGL_ERROR, "accept()ed unexpected address family %d\n", + osa.u.sa.sa_family); + goto error_close_socket; + } + + if (link->flags & OSMO_STREAM_SRV_F_NODELAY) { + ret = stream_setsockopt_nodelay(sock_fd, link->proto, 1); + if (ret < 0) + goto error_close_socket; + } + + if (!link->accept_cb) { + ret = -ENOTSUP; + goto error_close_socket; + } + + ret = link->accept_cb(link, sock_fd); + if (ret) + goto error_close_socket; + return 0; + +error_close_socket: + close(sock_fd); + return ret; +} + +/*! \brief Create an Osmocom Stream Server Link + * A Stream Server Link is the listen()+accept() "parent" to individual + * Stream Servers + * \paramin ctx talloc allocation context + * \returns Stream Server Link with default values (TCP) + */ +struct osmo_stream_srv_link *osmo_stream_srv_link_create(void *ctx) +{ + struct osmo_stream_srv_link *link; + + link = talloc_zero(ctx, struct osmo_stream_srv_link); + if (!link) + return NULL; + + link->sk_domain = AF_UNSPEC; + link->sk_type = SOCK_STREAM; + link->proto = IPPROTO_TCP; + osmo_fd_setup(&link->ofd, -1, OSMO_FD_READ | OSMO_FD_WRITE, osmo_stream_srv_link_ofd_cb, link, 0); + + link->ma_pars.sctp.version = 0; + + return link; +} + +/*! \brief Set a name on the srv_link object (used during logging) + * \paramin link server link whose name is to be set + * \paramin name the name to be set on link + */ +void osmo_stream_srv_link_set_name(struct osmo_stream_srv_link *link, const char *name) +{ + osmo_talloc_replace_string(link, &link->name, name); +} + +/*! \brief Set the NODELAY socket option to avoid Nagle-like behavior + * Setting this to nodelay=true will automatically set the NODELAY + * socket option on any socket established via this server link, before + * calling the accept_cb() + * \paramin link server link whose sockets are to be configured + * \paramin nodelay whether to set (true) NODELAY after accept + */ +void osmo_stream_srv_link_set_nodelay(struct osmo_stream_srv_link *link, bool nodelay) +{ + if (nodelay) + link->flags |= OSMO_STREAM_SRV_F_NODELAY; + else + link->flags &= ~OSMO_STREAM_SRV_F_NODELAY; +} + +/*! \brief Set the local address to which we bind + * \paramin link Stream Server Link to modify + * \paramin addr Local IP address + */ +void osmo_stream_srv_link_set_addr(struct osmo_stream_srv_link *link, + const char *addr) +{ + osmo_stream_srv_link_set_addrs(link, &addr, 1); +} + +/*! \brief Set the local address set to which we bind. + * Useful for protocols allowing bind on more than one address (such as SCTP) + * \paramin link Stream Server Link to modify + * \paramin addr Local IP address + * \return negative on error, 0 on success + */ +int osmo_stream_srv_link_set_addrs(struct osmo_stream_srv_link *link, const char **addr, size_t addrcnt) +{ + int i = 0; + + if (addrcnt > OSMO_STREAM_MAX_ADDRS) + return -EINVAL; + + for (; i < addrcnt; i++) + osmo_talloc_replace_string(link, &link->addri, addri); + for (; i < link->addrcnt; i++) { + talloc_free(link->addri); + link->addri = NULL; + } + + link->addrcnt = addrcnt; + link->flags |= OSMO_STREAM_SRV_F_RECONF; + return 0; +} + +/*! \brief Set the local port number to which we bind + * \paramin link Stream Server Link to modify + * \paramin port Local port number + */ +void osmo_stream_srv_link_set_port(struct osmo_stream_srv_link *link, + uint16_t port) +{ + link->port = port; + link->flags |= OSMO_STREAM_SRV_F_RECONF; +} + +/*! \brief Set the protocol for the stream server link + * \paramin link Stream Server Link to modify + * \paramin proto Protocol (like IPPROTO_TCP (default), IPPROTO_SCTP, ...) + */ +void +osmo_stream_srv_link_set_proto(struct osmo_stream_srv_link *link, + uint16_t proto) +{ + link->proto = proto; + link->flags |= OSMO_STREAM_SRV_F_RECONF; +} + + +/*! \brief Set the socket type for the stream server link + * \paramin link Stream Server Link to modify + * \paramin type Socket Type (like SOCK_STREAM (default), SOCK_SEQPACKET, ...) + * \returns zero on success, negative on error. + */ +int osmo_stream_srv_link_set_type(struct osmo_stream_srv_link *link, int type) +{ + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + break; + default: + return -ENOTSUP; + } + link->sk_type = type; + link->flags |= OSMO_STREAM_SRV_F_RECONF; + return 0; +} + +/*! \brief Set the socket type for the stream server link + * \paramin link Stream Server Link to modify + * \paramin type Socket Domain (like AF_UNSPEC (default for IP), AF_UNIX, AF_INET, ...) + * \returns zero on success, negative on error. + */ +int osmo_stream_srv_link_set_domain(struct osmo_stream_srv_link *link, int domain) +{ + switch (domain) { + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + case AF_UNIX: + break; + default: + return -ENOTSUP; + } + link->sk_domain = domain; + link->flags |= OSMO_STREAM_SRV_F_RECONF; + return 0; +} + +/*! \brief Set application private data of the stream server link + * \paramin link Stream Server Link to modify + * \paramin data User-specific data (available in call-back functions) */ +void +osmo_stream_srv_link_set_data(struct osmo_stream_srv_link *link, + void *data) +{ + link->data = data; +} + +/*! \brief Get application private data of the stream server link + * \paramin link Stream Server Link to modify + * \returns Application private data, as set by \ref osmo_stream_cli_set_data() */ +void *osmo_stream_srv_link_get_data(struct osmo_stream_srv_link *link) +{ + return link->data; +} + +/*! \brief Get description of the stream server link e. g. 127.0.0.1:1234 + * \paramin link Stream Server Link to examine + * \returns Link description or NULL in case of error */ +char *osmo_stream_srv_link_get_sockname(const struct osmo_stream_srv_link *link) +{ + static char bufINET6_ADDRSTRLEN + 6; + int rc = osmo_sock_get_local_ip(link->ofd.fd, buf, INET6_ADDRSTRLEN); + if (rc < 0) + return NULL; + + bufstrnlen(buf, INET6_ADDRSTRLEN + 6) = ':'; + + rc = osmo_sock_get_local_ip_port(link->ofd.fd, buf + strnlen(buf, INET6_ADDRSTRLEN + 6), 6); + if (rc < 0) + return NULL; + + return buf; +} + +/*! \brief Get Osmocom File Descriptor of the stream server link + * \paramin link Stream Server Link + * \returns Pointer to \ref osmo_fd */ +struct osmo_fd * +osmo_stream_srv_link_get_ofd(struct osmo_stream_srv_link *link) +{ + return &link->ofd; +} + +/*! \brief Set the accept() call-back of the stream server link + * \paramin link Stream Server Link + * \paramin accept_cb Call-back function executed upon accept() */ +void osmo_stream_srv_link_set_accept_cb(struct osmo_stream_srv_link *link, + int (*accept_cb)(struct osmo_stream_srv_link *link, int fd)) + +{ + link->accept_cb = accept_cb; +} + +/*! \brief Destroy the stream server link. Closes + Releases Memory. + * \paramin link Stream Server Link */ +void osmo_stream_srv_link_destroy(struct osmo_stream_srv_link *link) +{ + osmo_stream_srv_link_close(link); + talloc_free(link); +} + +/*! \brief Open the stream server link. This actually initializes the + * underlying socket and binds it to the configured ip/port + * \paramin link Stream Server Link to open + * \return negative on error, 0 on success */ +int osmo_stream_srv_link_open(struct osmo_stream_srv_link *link) +{ + int ret; + + if (link->ofd.fd >= 0) { + /* No reconfigure needed for existing socket, we are fine */ + if (!(link->flags & OSMO_STREAM_SRV_F_RECONF)) + return 0; + /* we are reconfiguring this socket, close existing first. */ + osmo_stream_srv_link_close(link); + } + + link->flags &= ~OSMO_STREAM_SRV_F_RECONF; + + switch (link->sk_domain) { + case AF_UNIX: + ret = osmo_sock_unix_init(link->sk_type, 0, link->addr0, OSMO_SOCK_F_BIND); + break; + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + switch (link->proto) { +#ifdef HAVE_LIBSCTP + 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); + break; +#endif + default: + ret = osmo_sock_init(link->sk_domain, link->sk_type, link->proto, + link->addr0, link->port, OSMO_SOCK_F_BIND); + } + break; + default: + ret = -ENOTSUP; + } + if (ret < 0) + return ret; + + link->ofd.fd = ret; + if (osmo_fd_register(&link->ofd) < 0) { + close(ret); + link->ofd.fd = -1; + return -EIO; + } + + OSMO_STRLCPY_ARRAY(link->sockname, osmo_stream_srv_link_get_sockname(link)); + return 0; +} + +/*! \brief Check whether the stream server link is opened + * \paramin link Stream Server Link to check */ +bool osmo_stream_srv_link_is_opened(const struct osmo_stream_srv_link *link) +{ + if (!link) + return false; + + if (link->ofd.fd == -1) + return false; + + return true; +} + +/*! \brief Close the stream server link and unregister from select loop + * Does not destroy the server link, merely closes it! + * \paramin link Stream Server Link to close */ +void osmo_stream_srv_link_close(struct osmo_stream_srv_link *link) +{ + if (!osmo_stream_srv_link_is_opened(link)) + return; + + osmo_fd_unregister(&link->ofd); + close(link->ofd.fd); + link->ofd.fd = -1; +} + +int osmo_stream_srv_link_set_param(struct osmo_stream_srv_link *link, enum osmo_stream_srv_link_param par, + void *val, size_t val_len) +{ + OSMO_ASSERT(link); + uint8_t val8; + + switch (par) { + case OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED: + if (!val || val_len != sizeof(uint8_t)) + return -EINVAL; + val8 = *(uint8_t *)val; + link->ma_pars.sctp.sockopt_auth_supported.set = true; + link->ma_pars.sctp.sockopt_auth_supported.abort_on_failure = val8 > 1; + link->ma_pars.sctp.sockopt_auth_supported.value = (val8 == 1 || val8 == 3) ? 1 : 0; + break; + case OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED: + if (!val || val_len != sizeof(uint8_t)) + return -EINVAL; + val8 = *(uint8_t *)val; + link->ma_pars.sctp.sockopt_asconf_supported.set = true; + link->ma_pars.sctp.sockopt_asconf_supported.abort_on_failure = val8 > 1; + link->ma_pars.sctp.sockopt_asconf_supported.value = (val8 == 1 || val8 == 3) ? 1 : 0; + break; + case OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + link->ma_pars.sctp.sockopt_initmsg.set = true; + link->ma_pars.sctp.sockopt_initmsg.num_ostreams_present = true; + link->ma_pars.sctp.sockopt_initmsg.num_ostreams_value = *(uint16_t *)val; + break; + case OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS: + if (!val || val_len != sizeof(uint16_t)) + return -EINVAL; + link->ma_pars.sctp.sockopt_initmsg.set = true; + link->ma_pars.sctp.sockopt_initmsg.max_instreams_present = true; + link->ma_pars.sctp.sockopt_initmsg.max_instreams_value = *(uint16_t *)val; + break; + default: + return -ENOENT; + }; + return 0; +} + +#define OSMO_STREAM_SRV_F_FLUSH_DESTROY (1 << 0) + +struct osmo_stream_srv { + struct osmo_stream_srv_link *srv; + char *name; + char socknameOSMO_SOCK_NAME_MAXLEN; + enum osmo_stream_mode mode; + union { + struct osmo_fd ofd; + struct osmo_io_fd *iofd; + }; + struct llist_head tx_queue; + int (*closed_cb)(struct osmo_stream_srv *peer); + int (*read_cb)(struct osmo_stream_srv *peer); + int (*iofd_read_cb)(struct osmo_stream_srv *peer, struct msgb *msg); + void *data; + int flags; +}; + +static void stream_srv_iofd_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg) +{ + struct osmo_stream_srv *conn = osmo_iofd_get_data(iofd); + LOGSSRV(conn, LOGL_DEBUG, "message received (res=%d)\n", res); + + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) { + LOGSSRV(conn, LOGL_INFO, "Connection is being flushed and closed; ignoring received message\n"); + msgb_free(msg); + return; + } + + if (res <= 0) { + osmo_stream_srv_set_flush_and_destroy(conn); + if (osmo_iofd_txqueue_len(iofd) == 0) + osmo_stream_srv_destroy(conn); + } else if (conn->iofd_read_cb) { + conn->iofd_read_cb(conn, msg); + } +} + +static void stream_srv_iofd_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg) +{ + struct osmo_stream_srv *conn = osmo_iofd_get_data(iofd); + LOGSSRV(conn, LOGL_DEBUG, "connected write\n"); + + if (res == -1) + LOGSSRV(conn, LOGL_ERROR, "error to send: %s\n", strerror(errno)); + + if (osmo_iofd_txqueue_len(iofd) == 0) + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) + osmo_stream_srv_destroy(conn); +} + +static struct osmo_io_ops srv_ioops = { + .read_cb = stream_srv_iofd_read_cb, + .write_cb = stream_srv_iofd_write_cb, +}; +static int osmo_stream_srv_read(struct osmo_stream_srv *conn) +{ + int rc = 0; + + LOGSSRV(conn, LOGL_DEBUG, "message received\n"); + + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) { + LOGSSRV(conn, LOGL_INFO, "Connection is being flushed and closed; ignoring received message\n"); + return 0; + } + + if (conn->read_cb) + rc = conn->read_cb(conn); + + return rc; +} + +static void osmo_stream_srv_write(struct osmo_stream_srv *conn) +{ +#ifdef HAVE_LIBSCTP + struct sctp_sndrcvinfo sinfo; +#endif + struct msgb *msg; + int ret; + + if (llist_empty(&conn->tx_queue)) { + osmo_fd_write_disable(&conn->ofd); + return; + } + msg = llist_first_entry(&conn->tx_queue, struct msgb, list); + llist_del(&msg->list); + + LOGSSRV(conn, LOGL_DEBUG, "sending %u bytes of data\n", msg->len); + + switch (conn->srv->sk_domain) { + case AF_UNIX: + ret = send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), 0); + break; + case AF_INET: + case AF_INET6: + case AF_UNSPEC: + switch (conn->srv->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.sinfo_ppid = htonl(msgb_sctp_ppid(msg)); + sinfo.sinfo_stream = msgb_sctp_stream(msg); + ret = sctp_send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), + &sinfo, MSG_NOSIGNAL); + break; +#endif + case IPPROTO_TCP: + default: + ret = send(conn->ofd.fd, msgb_data(msg), msgb_length(msg), 0); + break; + } + break; + default: + ret = -1; + errno = ENOTSUP; + } + + if (ret >= 0 && ret < msgb_length(msg)) { + LOGSSRV(conn, LOGL_ERROR, "short send: %d < exp %u\n", ret, msgb_length(msg)); + /* Update msgb and re-add it at the start of the queue: */ + msgb_pull(msg, ret); + llist_add(&msg->list, &conn->tx_queue); + return; + } + + if (ret == -1) {/* send(): On error -1 is returned, and errno is set appropriately */ + int err = errno; + LOGSSRV(conn, LOGL_ERROR, "send(len=%u) error: %s\n", msgb_length(msg), strerror(err)); + if (err == EAGAIN) { + /* Re-add at the start of the queue to re-attempt: */ + llist_add(&msg->list, &conn->tx_queue); + return; + } + msgb_free(msg); + osmo_stream_srv_destroy(conn); + return; + } + + msgb_free(msg); + + if (llist_empty(&conn->tx_queue)) { + osmo_fd_write_disable(&conn->ofd); + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) + osmo_stream_srv_destroy(conn); + } +} + +static int osmo_stream_srv_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct osmo_stream_srv *conn = ofd->data; + int rc = 0; + + LOGSSRV(conn, LOGL_DEBUG, "connected read/write (what=0x%x)\n", what); + if (what & OSMO_FD_READ) + rc = osmo_stream_srv_read(conn); + if (rc != -EBADF && (what & OSMO_FD_WRITE)) + osmo_stream_srv_write(conn); + + return rc; +} + +/*! \brief Create a Stream Server inside the specified link + * \paramin ctx talloc allocation context from which to allocate + * \paramin link Stream Server Link to which we belong + * \paramin fd system file descriptor of the new connection + * \paramin read_cb Call-back to call when the socket is readable + * \paramin closed_cb Call-back to call when the connection is closed + * \paramin data User data to save in the new Stream Server struct + * \returns Stream Server in case of success; NULL on error */ +struct osmo_stream_srv * +osmo_stream_srv_create(void *ctx, struct osmo_stream_srv_link *link, + int fd, + int (*read_cb)(struct osmo_stream_srv *conn), + int (*closed_cb)(struct osmo_stream_srv *conn), void *data) +{ + struct osmo_stream_srv *conn; + + OSMO_ASSERT(link); + + conn = talloc_zero(ctx, struct osmo_stream_srv); + if (conn == NULL) + return NULL; + + conn->mode = OSMO_STREAM_MODE_OSMO_FD; + conn->srv = link; + osmo_fd_setup(&conn->ofd, fd, OSMO_FD_READ, osmo_stream_srv_cb, conn, 0); + conn->read_cb = read_cb; + conn->closed_cb = closed_cb; + conn->data = data; + INIT_LLIST_HEAD(&conn->tx_queue); + + osmo_sock_get_name_buf(conn->sockname, sizeof(conn->sockname), fd); + + if (osmo_fd_register(&conn->ofd) < 0) { + LOGSSRV(conn, LOGL_ERROR, "could not register FD\n"); + talloc_free(conn); + return NULL; + } + return conn; +} + +/*! \brief Create a Stream Server inside the specified link + * \paramin ctx talloc allocation context from which to allocate + * \paramin link Stream Server Link to which we belong + * \paramin fd system file descriptor of the new connection + * \paramin data User data to save in the new Stream Server struct + * \returns Stream Server in case of success; NULL on error */ +struct osmo_stream_srv * +osmo_stream_srv_create2(void *ctx, struct osmo_stream_srv_link *link, int fd, void *data) +{ + struct osmo_stream_srv *conn; + + OSMO_ASSERT(link); + + conn = talloc_zero(ctx, struct osmo_stream_srv); + if (conn == NULL) + return NULL; + + conn->mode = OSMO_STREAM_MODE_OSMO_IO; + conn->srv = link; + + osmo_sock_get_name_buf(conn->sockname, sizeof(conn->sockname), fd); + + conn->iofd = osmo_iofd_setup(conn, fd, conn->sockname, + OSMO_IO_FD_MODE_READ_WRITE, &srv_ioops, conn); + if (!conn->iofd) { + talloc_free(conn); + return NULL; + } + conn->data = data; + + if (osmo_iofd_register(conn->iofd, fd) < 0) { + LOGSSRV(conn, LOGL_ERROR, "could not register FD %d\n", fd); + talloc_free(conn); + return NULL; + } + + return conn; +} + +/*! \brief Set a name on the srv object (used during logging) + * \paramin conn server whose name is to be set + * \paramin name the name to be set on conn + */ +void osmo_stream_srv_set_name(struct osmo_stream_srv *conn, const char *name) +{ + osmo_talloc_replace_string(conn, &conn->name, name); + if (conn->mode == OSMO_STREAM_MODE_OSMO_IO && conn->iofd) + osmo_iofd_set_name(conn->iofd, name); +} + +/*! \brief Set the call-back function when data was read from the stream server socket + * Only for osmo_stream_srv created with osmo_stream_srv_create2() + * \paramin conn Stream Server to modify + * \paramin read_cb Call-back function to be called when data was read */ +void osmo_stream_srv_set_read_cb(struct osmo_stream_srv *conn, int (*read_cb)(struct osmo_stream_srv *conn, struct msgb *msg)) +{ + OSMO_ASSERT(conn && conn->mode == OSMO_STREAM_MODE_OSMO_IO); + conn->iofd_read_cb = read_cb; +} + +/*! \brief Set the call-back function called when the stream server socket was closed + * \paramin conn Stream Server to modify + * \paramin closed_cb Call-back function to be called when the connection was closed */ +void osmo_stream_srv_set_closed_cb(struct osmo_stream_srv *conn, int (*closed_cb)(struct osmo_stream_srv *conn)) +{ + OSMO_ASSERT(conn); + conn->closed_cb = closed_cb; +} + +/*! \brief Prepare to send out all pending messages on the connection's Tx queue + * and then automatically destroy the stream with osmo_stream_srv_destroy(). + * This function disables queuing of new messages on the connection and also + * disables reception of new messages on the connection. + * \paramin conn Stream Server to modify */ +void osmo_stream_srv_set_flush_and_destroy(struct osmo_stream_srv *conn) +{ + conn->flags |= OSMO_STREAM_SRV_F_FLUSH_DESTROY; +} + +/*! \brief Set application private data of the stream server + * \paramin conn Stream Server to modify + * \paramin data User-specific data (available in call-back functions) */ +void +osmo_stream_srv_set_data(struct osmo_stream_srv *conn, + void *data) +{ + conn->data = data; +} + +/*! \brief Set the segmentation callback for target osmo_stream_srv structure. + * The connection has to have been established prior to calling this function. + * \paramin,out conn Target Stream Server to modify + * \paramin segmentation_cb Segmentation callback to be set */ +void osmo_stream_srv_set_segmentation_cb(struct osmo_stream_srv *conn, + int (*segmentation_cb)(struct msgb *msg)) +{ + /* Note that the following implies that iofd != NULL, since + * osmo_stream_srv_create2() creates the iofd member, too */ + OSMO_ASSERT(conn->mode == OSMO_STREAM_MODE_OSMO_IO); + /* Copy default settings */ + struct osmo_io_ops conn_ops = srv_ioops; + /* Set segmentation cb for this connection */ + conn_ops.segmentation_cb = segmentation_cb; + osmo_iofd_set_ioops(conn->iofd, &conn_ops); +} + +/*! \brief Get application private data of the stream server + * \paramin conn Stream Server + * \returns Application private data, as set by \ref osmo_stream_srv_set_data() */ +void *osmo_stream_srv_get_data(struct osmo_stream_srv *conn) +{ + return conn->data; +} + +/*! \brief Get Osmocom File Descriptor of the stream server + * \paramin conn Stream Server + * \returns Pointer to \ref osmo_fd */ +struct osmo_fd * +osmo_stream_srv_get_ofd(struct osmo_stream_srv *conn) +{ + return &conn->ofd; +} + +/*! \brief Get the master (Link) from a Stream Server + * \paramin conn Stream Server of which we want to know the Link + * \returns Link through which the given Stream Server is established */ +struct osmo_stream_srv_link *osmo_stream_srv_get_master(struct osmo_stream_srv *conn) +{ + return conn->srv; +} + +/*! \brief Destroy given Stream Server + * This function closes the Stream Server socket, unregisters from + * select loop, invokes the connection's closed_cb() callback to allow API + * users to clean up any associated state they have for this connection, + * and then de-allocates associated memory. + * \paramin conn Stream Server to be destroyed */ +void osmo_stream_srv_destroy(struct osmo_stream_srv *conn) +{ + switch (conn->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + osmo_fd_unregister(&conn->ofd); + close(conn->ofd.fd); + msgb_queue_free(&conn->tx_queue); + conn->ofd.fd = -1; + break; + case OSMO_STREAM_MODE_OSMO_IO: + osmo_iofd_free(conn->iofd); + break; + default: + OSMO_ASSERT(false); + } + if (conn->closed_cb) + conn->closed_cb(conn); + talloc_free(conn); +} + +/*! \brief Enqueue data to be sent via an Osmocom stream server + * \paramin conn Stream Server through which we want to send + * \paramin msg Message buffer to enqueue in transmit queue */ +void osmo_stream_srv_send(struct osmo_stream_srv *conn, struct msgb *msg) +{ + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) { + LOGSSRV(conn, LOGL_DEBUG, "Connection is being flushed and closed; ignoring new outgoing message\n"); + msgb_free(msg); + return; + } + + switch (conn->mode) { + case OSMO_STREAM_MODE_OSMO_FD: + msgb_enqueue(&conn->tx_queue, msg); + osmo_fd_write_enable(&conn->ofd); + break; + case OSMO_STREAM_MODE_OSMO_IO: + osmo_iofd_write_msgb(conn->iofd, msg); + break; + default: + OSMO_ASSERT(false); + } +} + +/*! \brief Receive data via Osmocom stream server + * \paramin conn Stream Server from which to receive + * \param msg pre-allocate message buffer to which received data is appended + * \returns number of bytes read, negative on error. + * + * If conn is an SCTP connection, additional specific considerations shall be taken: + * - msg->cb is always filled with SCTP ppid, and SCTP stream values, see msgb_sctp_*() APIs. + * - If an SCTP notification was received when reading from the SCTP socket, + * msgb_sctp_msg_flags(msg) will contain bit flag + * OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION set, and the msgb will + * contain a "union sctp_notification" instead of user data. In this case the + * return code will be either 0 (if conn is considered dead after the + * notification) or -EAGAIN (if conn is considered still alive after the + * notification) resembling the standard recv() API. + */ +int osmo_stream_srv_recv(struct osmo_stream_srv *conn, struct msgb *msg) +{ + int ret; + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + + switch (conn->srv->sk_domain) { + case AF_UNIX: + ret = recv(conn->ofd.fd, msg->tail, msgb_tailroom(msg), 0); + break; + case AF_INET: + case AF_INET6: + case AF_UNSPEC: + switch (conn->srv->proto) { +#ifdef HAVE_LIBSCTP + case IPPROTO_SCTP: + { + char log_pfx128; + snprintf(log_pfx, sizeof(log_pfx), "SRV(%s,%s)", conn->name ? : "", conn->sockname); + ret = stream_sctp_recvmsg_wrapper(conn->ofd.fd, msg, log_pfx); + break; + } +#endif + case IPPROTO_TCP: + default: + ret = recv(conn->ofd.fd, msg->tail, msgb_tailroom(msg), 0); + break; + } + break; + default: + ret = -ENOTSUP; + } + + if (ret < 0) { + if (errno == EPIPE || errno == ECONNRESET) + LOGSSRV(conn, LOGL_ERROR, "lost connection with client\n"); + return ret; + } else if (ret == 0) { + LOGSSRV(conn, LOGL_ERROR, "connection closed with client\n"); + return ret; + } + msgb_put(msg, ret); + LOGSSRV(conn, LOGL_DEBUG, "received %d bytes from client\n", ret); + return ret; +} + +void osmo_stream_srv_clear_tx_queue(struct osmo_stream_srv *conn) +{ + msgb_queue_free(&conn->tx_queue); + osmo_fd_write_disable(&conn->ofd); + if (conn->flags & OSMO_STREAM_SRV_F_FLUSH_DESTROY) + osmo_stream_srv_destroy(conn); +} + +/*! @} */
View file
libosmo-netif_1.3.0.tar.xz/tests/Makefile.am -> libosmo-netif_1.4.0.tar.xz/tests/Makefile.am
Changed
@@ -1,5 +1,12 @@ -AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g -AM_LDFLAGS = $(LIBOSMOCORE_LDFLAGS) -no-install +AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) -g +AM_LDFLAGS = -no-install + +LDADD = \ + $(top_builddir)/src/libosmonetif.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) check_PROGRAMS = \ osmux/osmux_test \ @@ -12,22 +19,11 @@ check_HEADERS = osmux_osmux_test_SOURCES = osmux/osmux_test.c -osmux_osmux_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la - osmux_osmux_output_test_SOURCES = osmux/osmux_output_test.c -osmux_osmux_output_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la - osmux_osmux_input_test_SOURCES = osmux/osmux_input_test.c -osmux_osmux_input_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la - stream_stream_test_SOURCES = stream/stream_test.c -stream_stream_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la - jibuf_jibuf_test_SOURCES = jibuf/jibuf_test.c -jibuf_jibuf_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la - amr_amr_test_SOURCES = amr/amr_test.c -amr_amr_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(top_builddir)/src/libosmonetif.la if HAVE_PCAP check_PROGRAMS += jibuf/jibuf_tool @@ -45,11 +41,7 @@ osmo-pcap-test/l4_udp.c \ osmo-pcap-test/pcap.c -jibuf_jibuf_tool_LDADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(top_builddir)/src/libosmonetif.la \ - -lpcap +jibuf_jibuf_tool_LDADD = $(LDADD) -lpcap endif # The `:;' works around a Bash 3.2 bug when the output is not writeable.
View file
libosmo-netif_1.3.0.tar.xz/tests/osmo-pcap-test/configure.ac -> libosmo-netif_1.4.0.tar.xz/tests/osmo-pcap-test/configure.ac
Changed
@@ -16,7 +16,7 @@ dnl kernel style compile messages m4_ifdef(AM_SILENT_RULES, AM_SILENT_RULES(yes)) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0) AC_PROG_CC AC_DISABLE_STATIC
View file
libosmo-netif_1.3.0.tar.xz/tests/stream/stream_test.c -> libosmo-netif_1.4.0.tar.xz/tests/stream/stream_test.c
Changed
@@ -1,6 +1,7 @@ /* - * (C) 2019 by sysmocom - s.f.m.c. GmbH. - * Author: Max Suraev + * (C) 2023 by sysmocom - s.f.m.c. GmbH. + * Authors: Max Suraev + * Alexander Rehbein * * 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 @@ -8,19 +9,23 @@ * (at your option) any later version. */ +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> +#include <osmocom/core/byteswap.h> #include <osmocom/core/select.h> #include <osmocom/core/talloc.h> #include <osmocom/core/msgb.h> #include <osmocom/core/logging.h> #include <osmocom/core/application.h> #include <osmocom/core/timer.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/netif/ipa.h> #include <osmocom/netif/stream.h> #define RECONNECT_TIMEOUT_SECS 9 @@ -214,6 +219,8 @@ printf("Prepare %s stream client...\n", ASTR(autoreconnect)); + osmo_stream_cli_set_local_port(cli, 8976); + osmo_stream_cli_set_name(cli, "cli_test"); osmo_stream_cli_set_addr(cli, host); osmo_stream_cli_set_port(cli, port); osmo_stream_cli_set_connect_cb(cli, connect_cb_cli); @@ -323,6 +330,7 @@ LOGLNK(lnk, "error while creating connection\n"); return -EINVAL; } + osmo_stream_srv_set_name(srv, "srv_test"); return 0; } @@ -363,9 +371,274 @@ printf("{%lu.%06lu} %s test complete.\n\n", tv.tv_sec, tv.tv_usec, ASTR(autoreconnect)); } +/* Segmentation test code (using IPA) */ +#define IPAC_MSG_PING_LEN 0x01 +static const uint8_t ipac_msg_ping = { + 0x00, IPAC_MSG_PING_LEN, + IPAC_PROTO_IPACCESS, + IPAC_MSGT_PING +}; +#define IPAC_MSG_PONG_LEN 0x01 +static const uint8_t ipac_msg_pong = { + 0x00, IPAC_MSG_PONG_LEN, + IPAC_PROTO_IPACCESS, + IPAC_MSGT_PONG +}; +#define IPAC_MSG_ID_REQ_LEN 0x03 +static const uint8_t ipac_msg_idreq = { + 0x00, IPAC_MSG_ID_REQ_LEN, + IPAC_PROTO_IPACCESS, + IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_UNITNAME +}; +#define ipac_msg_idreq_third (sizeof(ipac_msg_idreq)/3) +#define ipac_msg_idreq_last_third (sizeof(ipac_msg_idreq) - 2 * ipac_msg_idreq_third) +#define IPAC_MSG_ID_RESP_LEN 0x07 +static const uint8_t ipac_msg_idresp = { + 0x00, IPAC_MSG_ID_RESP_LEN, + IPAC_PROTO_IPACCESS, + IPAC_MSGT_ID_RESP, + 0x01, IPAC_IDTAG_UNITNAME, 0xde, 0xad, 0xbe, 0xef +}; + +#define put_ipa_msg(unsigned_char_ptr, struct_msgb_ptr, byte_array) do {\ + (unsigned_char_ptr) = msgb_put(struct_msgb_ptr, sizeof(byte_array));\ + memcpy(unsigned_char_ptr, byte_array, sizeof(byte_array));\ +} while (0) + +/* Array indices correspond to enum values stringified on the right */ +static const char * const IPAC_MSG_TYPES = { + 0 = "IPAC_MSGT_PING", + 1 = "IPAC_MSGT_PONG", + 2 = "UNEXPECTED VALUE", + 3 = "UNEXPECTED VALUE", + 4 = "IPAC_MSGT_ID_GET", + 5 = "IPAC_MSGT_ID_RESP", +}; + +#define IPAC_MSGT_OFFSET 3 +/* Append a message to UCHAR_PTR_DST. SRC_IPAC_MSG_BUF is expected to be a + * buffer containing an IPA message of type IPAC_PROTO_ACCESS that is + * syntactically correct up to offset 3 (IPAC_MSGT_OFFSET). + * Uses a counter so that appended messages can be distinguished easily in the logs */ +#define CLI_APPEND_MSG(OSMO_STREAM_CLI_PTR, UCHAR_PTR_DST, STRUCT_MSGB_PTR, SRC_IPAC_MSG_BUF) do {\ + LOGCLI(OSMO_STREAM_CLI_PTR, "%u-cli Appending msg of type %s into buffer\n",\ + ++test_segm_ipa_stream_srv_msglognum_cli, IPAC_MSG_TYPESSRC_IPAC_MSG_BUFIPAC_MSGT_OFFSET);\ + LOGCLI(OSMO_STREAM_CLI_PTR, "\t(msg dump: %s)\n", osmo_hexdump(SRC_IPAC_MSG_BUF,\ + sizeof(SRC_IPAC_MSG_BUF)));\ + put_ipa_msg(UCHAR_PTR_DST, STRUCT_MSGB_PTR, SRC_IPAC_MSG_BUF);\ +} while (0) + +static unsigned test_segm_ipa_stream_srv_msglognum_cli = 0; +static int test_segm_ipa_stream_srv_cli_connect_cb(struct osmo_stream_cli *cli) +{ + unsigned char *data; + struct msgb *m = msgb_alloc_headroom(128, 0, "IPA messages"); + if (m == NULL) { + fprintf(stderr, "Cannot allocate message\n"); + return -ENOMEM; + } + + /* Send 4 and 1/3 messages */ + /* Append 4 */ + CLI_APPEND_MSG(cli, data, m, ipac_msg_ping); + CLI_APPEND_MSG(cli, data, m, ipac_msg_pong); + CLI_APPEND_MSG(cli, data, m, ipac_msg_ping); + CLI_APPEND_MSG(cli, data, m, ipac_msg_idresp); + /* Append 1/3 */ + LOGCLI(cli, "(0%u + 1/3)-cli Appending 1st third of msg of type %s into buffer\n", + test_segm_ipa_stream_srv_msglognum_cli, IPAC_MSG_TYPESipac_msg_idreq3); + LOGCLI(cli, "\t(dump: %s)\n", osmo_hexdump(ipac_msg_idreq, ipac_msg_idreq_third)); + data = msgb_put(m, ipac_msg_idreq_third); + memcpy(data, ipac_msg_idreq, ipac_msg_idreq_third); + + LOGCLI(cli, "Sending 4 + 1/3 messages as one:\n"); + LOGCLI(cli, "\t(msg dump: %s)\n\n", osmo_hexdump(m->data, m->len)); + osmo_stream_cli_send(cli, m); + return 0; +} + +static bool test_segm_ipa_stream_srv_all_msgs_processed = false; + +static void send_last_third(void *osmo_stream_cli_arg) +{ + struct osmo_stream_cli *osc = osmo_stream_cli_arg; + unsigned char *data; + struct msgb *reply = msgb_alloc_headroom(128, 0, "IPA delayed reply"); + + LOGCLI(osc, "Delay for sending last third of message is over\n"); + if (reply == NULL) { + fprintf(stderr, "Cannot allocate message\n"); + return; + } + LOGCLI(osc, "%u-cli Appending: Last third of IPAC_MSGT_ID_GET\n", + ++test_segm_ipa_stream_srv_msglognum_cli); + data = msgb_put(reply, ipac_msg_idreq_last_third); + memcpy(data, ipac_msg_idreq + 2 * ipac_msg_idreq_third, + ipac_msg_idreq_last_third); + /* Append two entire messages */ + CLI_APPEND_MSG(osc, data, reply, ipac_msg_pong); + CLI_APPEND_MSG(osc, data, reply, ipac_msg_pong); + LOGCLI(osc, "\tSending:" + " Last third of IPAC_MSGT_ID_GET | IPAC_MSGT_PONG | IPAC_MSGT_PONG \n"); + LOGCLI(osc, "\t(msg dump: %s)\n\n", osmo_hexdump(reply->data, reply->len)); + osmo_stream_cli_send(osc, reply); +} + +static struct osmo_timer_list fragmented_send_tl_cli; + +static int test_segm_ipa_stream_srv_cli_read_cb(struct osmo_stream_cli *osc, struct msgb *msg) +{ + unsigned char *data; + struct ipa_head *h = (struct ipa_head *) msg->data; + uint8_t ipac_msg_type = ((uint8_t *)h)sizeof(struct ipa_head); + struct msgb *reply = msgb_alloc_headroom(128, 0, "IPA reply"); + if (reply == NULL) { + fprintf(stderr, "Cannot allocate message\n"); + return -ENOMEM; + } + LOGCLI(osc, "Received message from stream (total len = %" PRIu16 ")\n", msgb_length(msg)); + if (ipac_msg_type < 0 || 5 < ipac_msg_type) { + fprintf(stderr, "Received unexpected IPAC message type %"PRIu8"\n", ipac_msg_type); + return -ENOMSG; + } + LOGCLI(osc, "\tType: %s\n", IPAC_MSG_TYPESipac_msg_type); + if (ipac_msg_type == IPAC_MSGT_ID_GET) { + LOGCLI(osc, "Got IPAC_MSGT_ID_GET from server\n"); + LOGCLI(osc, "(%u + 2/3) -cli Appending: Second third of IPAC_MSGT_ID_GET\n", + test_segm_ipa_stream_srv_msglognum_cli); + data = msgb_put(reply, ipac_msg_idreq_third); + memcpy(data, ipac_msg_idreq + ipac_msg_idreq_third, + ipac_msg_idreq_third); + LOGCLI(osc, "\tSending: Second third of IPAC_MSGT_ID_GET\n"); + LOGCLI(osc, "\t(msg dump: %s)\n", osmo_hexdump(reply->data, reply->len)); + osmo_stream_cli_send(osc, reply); + osmo_timer_setup(&fragmented_send_tl_cli, send_last_third, osc); + osmo_timer_add(&fragmented_send_tl_cli); + osmo_timer_schedule(&fragmented_send_tl_cli, 0, 500000); + } else if (ipac_msg_type == IPAC_MSGT_ID_RESP) { + LOGCLI(osc, "\tresult= %s\n", + osmo_hexdump((const unsigned char *)h, sizeof(*h) + h->len)); + LOGCLI(osc, "\texpected=%s\n", + osmo_hexdump(ipac_msg_idresp, sizeof(ipac_msg_idresp))); + } + printf("\n"); + return 0; +} + +static void *test_segm_ipa_stream_srv_run_client(void) +{ + struct osmo_stream_cli *osc; + void *ctx = talloc_named_const(NULL, 0, __func__); + + (void) msgb_talloc_ctx_init(ctx, 0); + osc = osmo_stream_cli_create(ctx); + if (osc == NULL) { + fprintf(stderr, "osmo_stream_cli_create_iofd()\n"); + return NULL; + } + osmo_stream_cli_set_addr(osc, "127.0.0.11"); + osmo_stream_cli_set_local_port(osc, 8977); + osmo_stream_cli_set_port(osc, 1111); + osmo_stream_cli_set_connect_cb(osc, test_segm_ipa_stream_srv_cli_connect_cb); + osmo_stream_cli_set_data(osc, ctx); + osmo_stream_cli_set_read_cb2(osc, test_segm_ipa_stream_srv_cli_read_cb); + osmo_stream_cli_set_nodelay(osc, true); + if (osmo_stream_cli_open(osc) < 0) { + fprintf(stderr, "Cannot open stream client\n"); + return NULL; + } + + return NULL; +} + +int test_segm_ipa_stream_srv_srv_read_cb(struct osmo_stream_srv *conn, struct msgb *msg) +{ + static unsigned msgnum_srv = 0; + struct ipa_head *ih = (struct ipa_head *)msg->l1h; + unsigned char *data; + struct msgb *m; + uint8_t *msgt = msg->l2h; /* Octet right after IPA header */ + LOGSRV(conn, "%u-srv Received IPA message from stream (payload len = %" PRIu16 ")\n", + ++msgnum_srv, msgb_length(msg)); + LOGSRV(conn, "\tmsg buff data (including stripped headers): %s\n", + osmo_hexdump((unsigned char *)ih, osmo_ntohs(ih->len) + sizeof(*ih))); + LOGSRV(conn, "\tIPA payload: %s\n", osmo_hexdump(ih->data, osmo_ntohs(ih->len))); + LOGSRV(conn, "\tType: %s\n", IPAC_MSG_TYPES*msgt); + LOGSRV(conn, "\t(msg dump: %s)\n", osmo_hexdump(msg->l1h, msg->len + sizeof(struct ipa_head))); + if (*msgt == IPAC_MSGT_ID_RESP) { /* */ + LOGSRV(conn, "Send IPAC_MSGT_ID_GET to trigger client to send next third\n\n"); + m = msgb_alloc_headroom(128, 0, "IPA messages"); + if (m == NULL) { + fprintf(stderr, "Cannot allocate message\n"); + return -ENOMEM; + } + put_ipa_msg(data, m, ipac_msg_idreq); + osmo_stream_srv_send(conn, m); + } else if (msgnum_srv == 7 && *msgt == IPAC_MSGT_PONG) { + test_segm_ipa_stream_srv_all_msgs_processed = true; + } + return 0; +} + +static int test_segm_ipa_stream_srv_srv_close_cb(struct osmo_stream_srv *conn) +{ + osmo_stream_srv_set_segmentation_cb(conn, NULL); + return 0; +} + +static int test_segm_ipa_stream_srv_srv_accept_cb(struct osmo_stream_srv_link *srv, int fd) +{ + void *ctx = talloc_named_const(NULL, 0, __func__); + struct osmo_stream_srv *oss = + osmo_stream_srv_create2(ctx, srv, fd, NULL); + if (oss == NULL) { + fprintf(stderr, "Error while creating connection\n"); + return -1; + } + osmo_stream_srv_set_segmentation_cb(oss, osmo_ipa_segmentation_cb); + osmo_stream_srv_set_read_cb(oss, test_segm_ipa_stream_srv_srv_read_cb); + osmo_stream_srv_set_closed_cb(oss, test_segm_ipa_stream_srv_srv_close_cb); + return 0; +} + +static void test_segm_ipa_stream_srv_run(void *ctx, const char *host, unsigned port, + struct osmo_stream_srv_link *srv) +{ + const char *testname = "test_segm_ipa_stream_srv"; + osmo_stream_srv_link_set_accept_cb(srv, + test_segm_ipa_stream_srv_srv_accept_cb); + if (osmo_stream_srv_link_open(srv) < 0) { + printf("Unable to open server\n"); + exit(1); + } + test_segm_ipa_stream_srv_run_client(); + + printf("______________________________________Running test %s______________________________________\n", testname); + alarm(2); + + while (!test_segm_ipa_stream_srv_all_msgs_processed) { + osmo_gettimeofday_override_add(0, 1); /* small increment to easily spot iterations */ + osmo_select_main(1); + } + alarm(0); + printf("==================================Test %s complete========================================\n\n", testname); +} + +static void sigalarm_handler(int _foo) +{ + printf("FAIL: test did not run successfully\n"); + exit(EXIT_FAILURE); +} int main(void) { + + if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) { + perror("signal"); + exit(EXIT_FAILURE); + } + struct osmo_stream_srv_link *srv; char *host = "127.0.0.11"; unsigned port = 1111; @@ -390,20 +663,24 @@ return EXIT_FAILURE; } + osmo_stream_srv_link_set_name(srv, "srv_link_test"); osmo_stream_srv_link_set_addr(srv, host); osmo_stream_srv_link_set_port(srv, port); osmo_stream_srv_link_set_accept_cb(srv, accept_cb_srv); if (osmo_stream_srv_link_open(srv) < 0) { printf("Unable to open server\n"); + osmo_stream_srv_link_destroy(srv); return EXIT_FAILURE; } test_recon(tall_test, host, port, 12, srv, true); test_recon(tall_test, host, port, 8, srv, false); - osmo_stream_srv_link_destroy(srv); + test_segm_ipa_stream_srv_run(tall_test, host, port, srv); + printf("Stream tests completed\n"); + osmo_stream_srv_link_destroy(srv); return EXIT_SUCCESS; }
View file
libosmo-netif_1.3.0.tar.xz/tests/stream/stream_test.err -> libosmo-netif_1.4.0.tar.xz/tests/stream/stream_test.err
Changed
@@ -1,3 +1,5 @@ +SRV(srv_link_test,127.0.0.11:1111) accept()ed new link from 127.0.0.1:8976 +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CONNECTING} connection established {2.000001} autoreconnecting test step 11 client NA, server OK, FD reg 1 @@ -12,17 +14,21 @@ {2.000006} autoreconnecting test step 6 client OK, server OK, FD reg 1 {2.000007} autoreconnecting test step 5 client OK, server OK, FD reg 1 -CONNECTED osmo_stream_cli_recv(): connection closed with srv -WAIT_RECONNECT osmo_stream_cli_reconnect(): retrying in 9 seconds... +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CONNECTED} connection closed with srv +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){WAIT_RECONNECT} retrying reconnect in 9 seconds... {11.000008} autoreconnecting test step 4 client OK, server OK, FD reg 0 {11.000009} autoreconnecting test step 3 client OK, server OK, FD reg 1 +SRV(srv_link_test,127.0.0.11:1111) accept()ed new link from 127.0.0.1:8976 +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CONNECTING} connection established {11.000010} autoreconnecting test step 2 client OK, server OK, FD reg 0 -connection closed with client +SRVCONN(srv_test,r=127.0.0.1:8976<->l=127.0.0.11:1111) connection closed with client {11.000011} autoreconnecting test step 1 client OK, server NA, FD reg 0 +SRV(srv_link_test,127.0.0.11:1111) accept()ed new link from 127.0.0.1:8976 +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CONNECTING} connection established {11.000012} non-reconnecting test step 7 client NA, server OK, FD reg 1 @@ -37,7 +43,9 @@ {11.000017} non-reconnecting test step 2 client OK, server OK, FD reg 1 {11.000018} non-reconnecting test step 1 client OK, server OK, FD reg 1 -CONNECTED osmo_stream_cli_recv(): connection closed with srv -CLOSED osmo_stream_cli_reconnect(): not reconnecting, disabled. +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CONNECTED} connection closed with srv +CLICONN(cli_test,r=127.0.0.11:1111<->l=127.0.0.1:8976){CLOSED} not reconnecting, disabled {20.000019} non-reconnecting test step 0 client OK, server OK, FD reg 0 +SRV(srv_link_test,127.0.0.11:1111) accept()ed new link from 127.0.0.1:8977 +CLICONN(,r=127.0.0.11:1111<->l=127.0.0.1:8977){CONNECTING} connection established
View file
libosmo-netif_1.3.0.tar.xz/tests/stream/stream_test.ok -> libosmo-netif_1.4.0.tar.xz/tests/stream/stream_test.ok
Changed
@@ -48,4 +48,73 @@ {11.000019} OK Client's read_cb_cli(): 0-byte read, auto-reconnect will be triggered if enabled {20.000019} non-reconnecting test complete. +______________________________________Running test test_segm_ipa_stream_srv______________________________________ +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): 1-cli Appending msg of type IPAC_MSGT_PING into buffer +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (msg dump: 00 01 fe 00 ) +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): 2-cli Appending msg of type IPAC_MSGT_PONG into buffer +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (msg dump: 00 01 fe 01 ) +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): 3-cli Appending msg of type IPAC_MSGT_PING into buffer +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (msg dump: 00 01 fe 00 ) +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): 4-cli Appending msg of type IPAC_MSGT_ID_RESP into buffer +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (msg dump: 00 07 fe 05 01 01 de ad be ef ) +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (04 + 1/3)-cli Appending 1st third of msg of type IPAC_MSGT_ID_GET into buffer +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (dump: 00 03 ) +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): Sending 4 + 1/3 messages as one: +{20.000020} OK Client's test_segm_ipa_stream_srv_cli_connect_cb(): (msg dump: 00 01 fe 00 00 01 fe 01 00 01 fe 00 00 07 fe 05 01 01 de ad be ef 00 03 ) + +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 1-srv Received IPA message from stream (payload len = 1) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 01 fe 00 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 00 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_PING +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 01 fe 00 ) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 2-srv Received IPA message from stream (payload len = 1) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 01 fe 01 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 01 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_PONG +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 01 fe 01 ) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 3-srv Received IPA message from stream (payload len = 1) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 01 fe 00 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 00 +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_PING +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 01 fe 00 ) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 4-srv Received IPA message from stream (payload len = 7) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 07 fe 05 01 01 de ad be ef +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 05 01 01 de ad be ef +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_ID_RESP +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 07 fe 05 01 01 de ad be ef ) +{20.000022} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Send IPAC_MSGT_ID_GET to trigger client to send next third + +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): Received message from stream (total len = 6) +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): Type: IPAC_MSGT_ID_GET +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): Got IPAC_MSGT_ID_GET from server +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): (4 + 2/3) -cli Appending: Second third of IPAC_MSGT_ID_GET +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): Sending: Second third of IPAC_MSGT_ID_GET +{20.000024} OK Client's test_segm_ipa_stream_srv_cli_read_cb(): (msg dump: fe 04 ) + +{20.500024} OK Client's send_last_third(): Delay for sending last third of message is over +{20.500024} OK Client's send_last_third(): 5-cli Appending: Last third of IPAC_MSGT_ID_GET +{20.500024} OK Client's send_last_third(): 6-cli Appending msg of type IPAC_MSGT_PONG into buffer +{20.500024} OK Client's send_last_third(): (msg dump: 00 01 fe 01 ) +{20.500024} OK Client's send_last_third(): 7-cli Appending msg of type IPAC_MSGT_PONG into buffer +{20.500024} OK Client's send_last_third(): (msg dump: 00 01 fe 01 ) +{20.500024} OK Client's send_last_third(): Sending: Last third of IPAC_MSGT_ID_GET | IPAC_MSGT_PONG | IPAC_MSGT_PONG +{20.500024} OK Client's send_last_third(): (msg dump: 01 01 00 01 fe 01 00 01 fe 01 ) + +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 5-srv Received IPA message from stream (payload len = 3) +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 03 fe 04 01 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 04 01 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_ID_GET +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 03 fe 04 01 01 ) +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 6-srv Received IPA message from stream (payload len = 1) +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 01 fe 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_PONG +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 01 fe 01 ) +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): 7-srv Received IPA message from stream (payload len = 1) +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): msg buff data (including stripped headers): 00 01 fe 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): IPA payload: 01 +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): Type: IPAC_MSGT_PONG +{20.500026} NA|OK Server's test_segm_ipa_stream_srv_srv_read_cb(): (msg dump: 00 01 fe 01 ) +==================================Test test_segm_ipa_stream_srv complete======================================== + Stream tests completed
View file
libosmo-netif_1.3.0.tar.xz/utils/Makefile.am -> libosmo-netif_1.4.0.tar.xz/utils/Makefile.am
Changed
@@ -8,10 +8,10 @@ $(NULL) LDADD = \ + $(top_builddir)/src/libosmonetif.la \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOCODEC_LIBS) \ $(LIBOSMOGSM_LIBS) \ - $(top_builddir)/src/libosmonetif.la \ $(NULL) noinst_PROGRAMS = osmo-amr-inspect
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
.