Integrate libmnl (minimal netlink) library with libosmocore select loop

This adds an easy way to listen to netlink events form the Linux kernel
from within libosmocore applications.

The new dependency can be disabled via the "--disable-lbimnl" configure flag.

Change-Id: I4f787ee68f0d6d04f0a5655eb57d55b3b326a42f
This commit is contained in:
Harald Welte 2020-12-01 22:21:14 +01:00 committed by laforge
parent b7fc3a0a6d
commit 53a2fde368
9 changed files with 165 additions and 1 deletions

View File

@ -194,6 +194,19 @@ AS_IF([test "x$systemd_logging" = "xyes"], [
AM_CONDITIONAL(ENABLE_SYSTEMD_LOGGING, test "x$systemd_logging" = "xyes")
AC_SUBST(ENABLE_SYSTEMD_LOGGING)
AC_ARG_ENABLE([libmnl],
[AS_HELP_STRING(
[--disable-libmnl],
[Build without netlink socket support via libmnl]
)],
[mnl=$enableval], [mnl="yes"])
AS_IF([test "x$mnl" = "xyes"], [
PKG_CHECK_MODULES(LIBMNL, libmnl)
AC_DEFINE([ENABLE_LIBMNL], [1], [Enable netlink socket support via libmnl])
])
AM_CONDITIONAL(ENABLE_LIBMNL, test "x$mnl" = "xyes")
AC_SUBST(ENABLE_LIBMNL)
AC_ARG_ENABLE([libsctp], [AS_HELP_STRING([--disable-libsctp], [Do not enable socket multiaddr APIs requiring libsctp])],
[ENABLE_LIBSCTP=$enableval], [ENABLE_LIBSCTP="yes"])
AM_CONDITIONAL(ENABLE_LIBSCTP, test x"$ENABLE_LIBSCTP" = x"yes")

View File

@ -20,6 +20,7 @@ build() {
--disable-shared \
--disable-libsctp \
--disable-libusb \
--disable-libmnl \
--enable-external-tests \
CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS"

View File

@ -30,6 +30,7 @@ BuildRequires: pkgconfig(gnutls) >= 2.12.0
BuildRequires: pkgconfig(libpcsclite)
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(talloc) >= 2.0.1
BuildRequires: pkgconfig(libmnl)
%description
libosmocore is a package with various utility functions that were

1
debian/control vendored
View File

@ -17,6 +17,7 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libsctp-dev,
libusb-1.0-0-dev,
libmnl-dev,
python3:native
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/libosmocore.git

View File

@ -32,6 +32,7 @@ nobase_include_HEADERS = \
osmocom/core/linuxrbtree.h \
osmocom/core/logging.h \
osmocom/core/loggingrb.h \
osmocom/core/mnl.h \
osmocom/core/stats.h \
osmocom/core/macaddr.h \
osmocom/core/msgb.h \

View File

@ -0,0 +1,22 @@
/*! \file select.h
* libmnl integration
*/
#pragma once
#include <osmocom/core/select.h>
#include <libmnl/libmnl.h>
/*! osmocom wrapper around libmnl abstraction of netlink socket */
struct osmo_mnl {
/*! osmo-wrapped netlink file descriptor */
struct osmo_fd ofd;
/*! libmnl socket abstraction */
struct mnl_socket *mnls;
/*! call-back called for received netlink messages */
mnl_cb_t mnl_cb;
/*! opaque data provided by user */
void *priv;
};
struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv);
void osmo_mnl_destroy(struct osmo_mnl *omnl);

View File

@ -4,7 +4,7 @@
LIBVERSION=16:0:0
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@ -76,5 +76,10 @@ libosmocore_la_SOURCES += logging_systemd.c
libosmocore_la_LIBADD += $(SYSTEMD_LIBS)
endif
if ENABLE_LIBMNL
libosmocore_la_SOURCES += mnl.c
libosmocore_la_LIBADD += $(LIBMNL_LIBS)
endif
crc%gen.c: crcXXgen.c.tpl
$(AM_V_GEN)sed -e's/XX/$*/g' $< > $@

View File

@ -95,8 +95,12 @@ struct gprs_ns2_inst {
/*! workaround for rate counter until rate counter accepts char str as index */
uint32_t rate_ctr_idx;
/*! libmnl netlink socket for link state monitoring */
struct osmo_mnl *linkmon_mnl;
};
/*! Structure repesenting a NSE. The BSS/PCU will only have a single NSE, while SGSN has one for each BSS/PCU */
struct gprs_ns2_nse {
uint16_t nsei;

116
src/mnl.c Normal file
View File

@ -0,0 +1,116 @@
/*! \file mnl.c
*
* This code integrates libmnl (minimal netlink library) into the osmocom select
* loop abstraction. It allows other osmocom libraries or application code to
* create netlink sockets and subscribe to netlink events via libmnl. The completion
* handler / callbacks are dispatched via libosmocore select loop handling.
*/
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserverd.
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <osmocom/core/select.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/mnl.h>
#include <libmnl/libmnl.h>
#include <errno.h>
#include <string.h>
/* osmo_fd call-back for when RTNL socket is readable */
static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
uint8_t buf[MNL_SOCKET_BUFFER_SIZE];
struct osmo_mnl *omnl = ofd->data;
int rc;
if (!(what & OSMO_FD_READ))
return 0;
rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf));
if (rc <= 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n",
strerror(errno));
return -EIO;
}
return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl);
}
/*! create an osmocom-wrapped limnl netlink socket.
* \parma[in] ctx talloc context from which to allocate
* \param[in] bus netlink socket bus ID (see NETLINK_* constants)
* \param[in] groups groups of messages to bind/subscribe to
* \param[in] mnl_cb callback function called for each incoming message
* \param[in] priv opaque private user data
* \returns newly-allocated osmo_mnl or NULL in case of error. */
struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv)
{
struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl);
if (!olm)
return NULL;
olm->priv = priv;
olm->mnl_cb = mnl_cb;
olm->mnls = mnl_socket_open2(bus, SOCK_CLOEXEC);
if (!olm->mnls) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n",
bus, strerror(errno));
goto out_free;
}
if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n",
bus, groups, strerror(errno));
goto out_close;
}
osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0);
if (osmo_fd_register(&olm->ofd)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n");
goto out_close;
}
return olm;
out_close:
mnl_socket_close(olm->mnls);
out_free:
talloc_free(olm);
return NULL;
}
/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free.
* \param[in] omnl osmo_mnl socket previously returned by osmo_mnl_init() */
void osmo_mnl_destroy(struct osmo_mnl *omnl)
{
if (!omnl)
return;
osmo_fd_unregister(&omnl->ofd);
mnl_socket_close(omnl->mnls);
talloc_free(omnl);
}