/*! \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 * 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 #include #include #include #include #include #include /* 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_open(bus); if (!olm->mnls) { LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n", bus, strerror(errno)); goto out_free; } if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) { LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n", bus, groups, strerror(errno)); goto out_close; } osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0); if (osmo_fd_register(&olm->ofd)) { LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n"); goto out_close; } return olm; out_close: mnl_socket_close(olm->mnls); out_free: talloc_free(olm); return NULL; } /*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free. * \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); }