From 4b6ce34083e62c5f50e0b665011c668aa041ecc4 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Tue, 30 Nov 2021 13:06:32 +0100 Subject: [PATCH] Introduce osmo_prim_srv APIs This new module allows easy exchange of osmo_prim based data types over IPC communication (UD socket supported only so far), by replacing the osmo_prim_hdr struct with a serialized header when submitting/receiving it from the IPC socket. This patch introduces the server side of the UD socket, but the client side can easily be introduced in the same file whenever needed. Related: SYS#5516 Change-Id: I7cab15ac092e45a256c4f0bab11b3962df861044 --- TODO-RELEASE | 1 + include/osmocom/netif/Makefile.am | 1 + include/osmocom/netif/prim.h | 54 +++++ src/Makefile.am | 1 + src/prim.c | 352 ++++++++++++++++++++++++++++++ 5 files changed, 409 insertions(+) create mode 100644 include/osmocom/netif/prim.h create mode 100644 src/prim.c diff --git a/TODO-RELEASE b/TODO-RELEASE index 1c9a2a6..b146231 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -8,3 +8,4 @@ # If any interfaces have been removed or changed since the last public release: c:r:0. #library what description / commit summary line sctp.h new APIs +prim.h new data type, APIs diff --git a/include/osmocom/netif/Makefile.am b/include/osmocom/netif/Makefile.am index d9d030b..92712a9 100644 --- a/include/osmocom/netif/Makefile.am +++ b/include/osmocom/netif/Makefile.am @@ -4,6 +4,7 @@ osmonetif_HEADERS = amr.h \ osmux.h \ ipa.h \ ipa_unit.h \ + prim.h \ rs232.h \ rtp.h \ stream.h diff --git a/include/osmocom/netif/prim.h b/include/osmocom/netif/prim.h new file mode 100644 index 0000000..4f17622 --- /dev/null +++ b/include/osmocom/netif/prim.h @@ -0,0 +1,54 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include + +#include +#include +#include + +struct osmo_prim_srv_link; +struct osmo_prim_srv; + +typedef int (*osmo_prim_srv_conn_cb)(struct osmo_prim_srv *prim_srv); +/*! oph and related msgb is owned by srv and wll be freed after the callback returns. */ +typedef int (*osmo_prim_srv_rx_cb)(struct osmo_prim_srv *prim_srv, struct osmo_prim_hdr *oph); + +struct osmo_prim_hdr *osmo_prim_msgb_alloc(unsigned int sap, unsigned int primitive, + enum osmo_prim_operation operation, size_t alloc_len); + +struct osmo_prim_srv_link *osmo_prim_srv_link_alloc(void *ctx); +void osmo_prim_srv_link_free(struct osmo_prim_srv_link *prim_link); +int osmo_prim_srv_link_set_addr(struct osmo_prim_srv_link *prim_link, const char *path); +const char *osmo_prim_srv_link_get_addr(struct osmo_prim_srv_link *prim_link); +void osmo_prim_srv_link_set_priv(struct osmo_prim_srv_link *prim_link, void *priv); +void *osmo_prim_srv_link_get_priv(const struct osmo_prim_srv_link *prim_link); +void osmo_prim_srv_link_set_log_category(struct osmo_prim_srv_link *prim_link, int log_cat); +void osmo_prim_srv_link_set_opened_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb opened_conn_cb); +void osmo_prim_srv_link_set_closed_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb closed_conn_cb); +void osmo_prim_srv_link_set_rx_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_rx_cb rx_cb); +void osmo_prim_srv_link_set_rx_msgb_alloc_len(struct osmo_prim_srv_link *prim_link, size_t alloc_len); +int osmo_prim_srv_link_open(struct osmo_prim_srv_link *prim_link); + +int osmo_prim_srv_send(struct osmo_prim_srv *prim_srv, struct msgb *msg); +struct osmo_prim_srv_link *osmo_prim_srv_get_link(struct osmo_prim_srv *prims_srv); +void osmo_prim_srv_set_priv(struct osmo_prim_srv *prim_srv, void *priv); +void *osmo_prim_srv_get_priv(const struct osmo_prim_srv *prim_srv); +void osmo_prim_srv_close(struct osmo_prim_srv *prim_srv); diff --git a/src/Makefile.am b/src/Makefile.am index 438b58e..5c23896 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,7 @@ libosmonetif_la_SOURCES = amr.c \ ipa_unit.c \ jibuf.c \ osmux.c \ + prim.c \ rs232.c \ rtp.c \ stream.c diff --git a/src/prim.c b/src/prim.c new file mode 100644 index 0000000..6c6da6e --- /dev/null +++ b/src/prim.c @@ -0,0 +1,352 @@ +/* (C) 2021 by sysmocom - s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +struct osmo_prim_pkt_hdr { + uint32_t sap; /*!< Service Access Point Identifier */ + uint16_t primitive; /*!< Primitive number */ + uint16_t operation; /*! Primitive Operation (enum osmo_prim_operation) */ +} __attribute__ ((packed)); + +/* Here we take advantage of the fact that sizeof(struct + * osmo_prim_pkt_hdr) <= sizeof(struct osmo_prim_hdr), so we don't need + * to allocate headroom when serializing later. + */ +osmo_static_assert(sizeof(struct osmo_prim_pkt_hdr) <= sizeof(struct osmo_prim_hdr), + osmo_prim_msgb_alloc_validate_headroom); + +/*! Allocate a primitive of given type and its associated msgb. +* \param[in] sap Service Access Point +* \param[in] primitive Primitive Number +* \param[in] operation Primitive Operation (REQ/RESP/IND/CONF) +* \param[in] alloc_len Total length (including struct osmo_prim_hdr) to allocate for the primitive +* \returns Pointer to allocated prim_hdr inisde its own msgb. The osmo_prim_hdr +* is pre-alocated & pre-filled. +*/ +struct osmo_prim_hdr *osmo_prim_msgb_alloc(unsigned int sap, unsigned int primitive, + enum osmo_prim_operation operation, size_t alloc_len) +{ + struct msgb *msg; + struct osmo_prim_hdr *oph; + + if (alloc_len < sizeof(*oph)) + return NULL; + + msg = msgb_alloc(alloc_len, "osmo_prim_msgb_alloc"); + oph = (struct osmo_prim_hdr *)msgb_put(msg, sizeof(*oph)); + osmo_prim_init(oph, sap, primitive, operation, msg); + msg->l2h = msg->tail; + + return oph; +} + +struct osmo_prim_srv_link { + void *priv; + char *addr; + int log_cat; /* Defaults to DLGLOBAL */ + struct osmo_stream_srv_link *stream; + osmo_prim_srv_conn_cb opened_conn_cb; + osmo_prim_srv_conn_cb closed_conn_cb; + osmo_prim_srv_rx_cb rx_cb; + size_t rx_msgb_alloc_len; +}; + +struct osmo_prim_srv { + void *priv; + struct osmo_prim_srv_link *link; /* backpointer */ + struct osmo_stream_srv *stream; +}; + +/****************************** + * osmo_prim_srv + ******************************/ +#define LOGSRV(srv, lvl, fmt, args...) LOGP((srv)->link->log_cat, lvl, fmt, ## args) + +static int _osmo_prim_srv_read_cb(struct osmo_stream_srv *srv) +{ + struct osmo_prim_srv *prim_srv = osmo_stream_srv_get_data(srv); + struct osmo_prim_pkt_hdr *pkth; + struct msgb *msg; + struct osmo_prim_hdr oph; + int rc; + + msg = msgb_alloc_c(prim_srv, sizeof(*pkth) + prim_srv->link->rx_msgb_alloc_len, + "osmo_prim_srv_link_rx"); + if (!msg) + return -ENOMEM; + rc = osmo_stream_srv_recv(srv, msg); + if (rc == 0) + goto close; + + if (rc < 0) { + if (errno == EAGAIN) { + msgb_free(msg); + return 0; + } + goto close; + } + + if (rc < sizeof(*pkth)) { + LOGSRV(prim_srv, LOGL_ERROR, "Received %d bytes on UD Socket, but primitive hdr size " + "is %zu, discarding\n", rc, sizeof(*pkth)); + msgb_free(msg); + return 0; + } + pkth = (struct osmo_prim_pkt_hdr *)msgb_data(msg); + + /* De-serialize message: */ + osmo_prim_init(&oph, pkth->sap, pkth->primitive, pkth->operation, msg); + msgb_pull(msg, sizeof(*pkth)); + + if (prim_srv->link->rx_cb) + rc = prim_srv->link->rx_cb(prim_srv, &oph); + + /* as we always synchronously process the message in _osmo_prim_srv_link_rx() and + * its callbacks, we can free the message here. */ + msgb_free(msg); + + return rc; + +close: + msgb_free(msg); + osmo_prim_srv_close(prim_srv); + return -1; +} + +static void osmo_prim_srv_free(struct osmo_prim_srv *prim_srv); +static int _osmo_prim_srv_closed_cb(struct osmo_stream_srv *srv) +{ + struct osmo_prim_srv *prim_srv = osmo_stream_srv_get_data(srv); + struct osmo_prim_srv_link *prim_link = prim_srv->link; + if (prim_link->closed_conn_cb) + return prim_link->closed_conn_cb(prim_srv); + osmo_prim_srv_free(prim_srv); + return 0; +} + +/*! Allocate a primitive of given type and its associated msgb. +* \param[in] srv The osmo_prim_srv_link instance where message is to be sent through +* \param[in] msg msgb containing osmo_prim_hdr plus extra content, allocated through \ref osmo_prim_msgb_alloc() +* \returns zero on success, negative on error */ +int osmo_prim_srv_send(struct osmo_prim_srv *prim_srv, struct msgb *msg) +{ + struct osmo_prim_hdr *oph; + struct osmo_prim_pkt_hdr *pkth; + unsigned int sap; + unsigned int primitive; + enum osmo_prim_operation operation; + + /* Serialize the oph: */ + oph = (struct osmo_prim_hdr *)msgb_data(msg); + OSMO_ASSERT(oph && msgb_length(msg) >= sizeof(*oph)); + sap = oph->sap; + primitive = oph->primitive; + operation = oph->operation; + msgb_pull(msg, sizeof(*oph)); + pkth = (struct osmo_prim_pkt_hdr *)msgb_push(msg, sizeof(*pkth)); + pkth->sap = sap; + pkth->primitive = primitive; + pkth->operation = operation; + + /* Finally enqueue the msg */ + osmo_stream_srv_send(prim_srv->stream, msg); + + return 0; +} + +static struct osmo_prim_srv *osmo_prim_srv_alloc(struct osmo_prim_srv_link *prim_link, int fd) +{ + struct osmo_prim_srv *prim_srv; + prim_srv = talloc_zero(prim_link, struct osmo_prim_srv); + if (!prim_srv) + return NULL; + prim_srv->link = prim_link; + prim_srv->stream = osmo_stream_srv_create(prim_link, prim_link->stream, fd, + _osmo_prim_srv_read_cb, + _osmo_prim_srv_closed_cb, + prim_srv); + if (!prim_srv->stream) { + talloc_free(prim_srv); + return NULL; + } + /* Inherit link priv pointer by default, user can later set it through API: */ + prim_srv->priv = prim_link->priv; + return prim_srv; +} + +static void osmo_prim_srv_free(struct osmo_prim_srv *prim_srv) +{ + talloc_free(prim_srv); +} + +struct osmo_prim_srv_link *osmo_prim_srv_get_link(struct osmo_prim_srv *prim_srv) +{ + return prim_srv->link; +} + +void osmo_prim_srv_set_priv(struct osmo_prim_srv *prim_srv, void *priv) +{ + prim_srv->priv = priv; +} + +void *osmo_prim_srv_get_priv(const struct osmo_prim_srv *prim_srv) +{ + return prim_srv->priv; +} + +void osmo_prim_srv_close(struct osmo_prim_srv *prim_srv) +{ + osmo_stream_srv_destroy(prim_srv->stream); + /* we free prim_srv in _osmo_prim_srv_closed_cb() */ +} + +/****************************** + * osmo_prim_srv_link + ******************************/ + +#define LOGSRVLINK(srv, lvl, fmt, args...) LOGP((srv)->log_cat, lvl, fmt, ## args) + +/* accept connection coming from PCU */ +static int _osmo_prim_srv_link_accept(struct osmo_stream_srv_link *link, int fd) +{ + struct osmo_prim_srv *prim_srv; + struct osmo_prim_srv_link *prim_link = osmo_stream_srv_link_get_data(link); + + prim_srv = osmo_prim_srv_alloc(prim_link, fd); + + if (prim_link->opened_conn_cb) + return prim_link->opened_conn_cb(prim_srv); + + return 0; +} + +struct osmo_prim_srv_link *osmo_prim_srv_link_alloc(void *ctx) +{ + struct osmo_prim_srv_link *prim_link; + prim_link = talloc_zero(ctx, struct osmo_prim_srv_link); + if (!prim_link) + return NULL; + prim_link->stream = osmo_stream_srv_link_create(prim_link); + if (!prim_link->stream) { + talloc_free(prim_link); + return NULL; + } + osmo_stream_srv_link_set_data(prim_link->stream, prim_link); + osmo_stream_srv_link_set_domain(prim_link->stream, AF_UNIX); + osmo_stream_srv_link_set_type(prim_link->stream, SOCK_SEQPACKET); + osmo_stream_srv_link_set_accept_cb(prim_link->stream, _osmo_prim_srv_link_accept); + + prim_link->log_cat = DLGLOBAL; + prim_link->rx_msgb_alloc_len = 1600 - sizeof(struct osmo_prim_pkt_hdr); + return prim_link; +} + +void osmo_prim_srv_link_free(struct osmo_prim_srv_link *prim_link) +{ + if (!prim_link) + return; + + if (prim_link->stream) { + osmo_stream_srv_link_close(prim_link->stream); + osmo_stream_srv_link_destroy(prim_link->stream); + prim_link->stream = NULL; + } + talloc_free(prim_link); +} + +int osmo_prim_srv_link_set_addr(struct osmo_prim_srv_link *prim_link, const char *path) +{ + osmo_talloc_replace_string(prim_link, &prim_link->addr, path); + osmo_stream_srv_link_set_addr(prim_link->stream, path); + return 0; +} + +const char *osmo_prim_srv_link_get_addr(struct osmo_prim_srv_link *prim_link) +{ + return prim_link->addr; +} + +void osmo_prim_srv_link_set_priv(struct osmo_prim_srv_link *prim_link, void *priv) +{ + prim_link->priv = priv; +} + +void *osmo_prim_srv_link_get_priv(const struct osmo_prim_srv_link *prim_link) +{ + return prim_link->priv; +} + +void osmo_prim_srv_link_set_log_category(struct osmo_prim_srv_link *prim_link, int log_cat) +{ + prim_link->log_cat = log_cat; +} + +void osmo_prim_srv_link_set_opened_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb opened_conn_cb) +{ + prim_link->opened_conn_cb = opened_conn_cb; +} +void osmo_prim_srv_link_set_closed_conn_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_conn_cb closed_conn_cb) +{ + prim_link->closed_conn_cb = closed_conn_cb; +} + +void osmo_prim_srv_link_set_rx_cb(struct osmo_prim_srv_link *prim_link, osmo_prim_srv_rx_cb rx_cb) +{ + prim_link->rx_cb = rx_cb; +} + +void osmo_prim_srv_link_set_rx_msgb_alloc_len(struct osmo_prim_srv_link *prim_link, size_t alloc_len) +{ + prim_link->rx_msgb_alloc_len = alloc_len; +} + +int osmo_prim_srv_link_open(struct osmo_prim_srv_link *prim_link) +{ + int rc; + + if (!prim_link->addr) { + LOGSRVLINK(prim_link, LOGL_ERROR, "Cannot open, Address not configured\n"); + return -1; + } + + rc = osmo_stream_srv_link_open(prim_link->stream); + + LOGSRVLINK(prim_link, LOGL_INFO, "Started listening on Lower Layer Unix Domain Socket: %s\n", prim_link->addr); + + return rc; +}