blob: a7d3511ed66738c5591e01d9ba236462f7826fb6 [file] [log] [blame]
Harald Welte53a2fde2020-12-01 22:21:14 +01001/*! \file mnl.c
2 *
3 * This code integrates libmnl (minimal netlink library) into the osmocom select
4 * loop abstraction. It allows other osmocom libraries or application code to
5 * create netlink sockets and subscribe to netlink events via libmnl. The completion
6 * handler / callbacks are dispatched via libosmocore select loop handling.
7 */
8
9/*
10 * (C) 2020 by Harald Welte <laforge@gnumonks.org>
11 * All Rights Reserverd.
12 *
13 * SPDX-License-Identifier: GPL-2.0+
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
28 * MA 02110-1301, USA.
29 */
30
31#include <osmocom/core/select.h>
32#include <osmocom/core/talloc.h>
33#include <osmocom/core/logging.h>
34#include <osmocom/core/mnl.h>
35
36#include <libmnl/libmnl.h>
37
38#include <errno.h>
39#include <string.h>
40
41/* osmo_fd call-back for when RTNL socket is readable */
42static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what)
43{
44 uint8_t buf[MNL_SOCKET_BUFFER_SIZE];
45 struct osmo_mnl *omnl = ofd->data;
46 int rc;
47
48 if (!(what & OSMO_FD_READ))
49 return 0;
50
51 rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf));
52 if (rc <= 0) {
53 LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n",
54 strerror(errno));
55 return -EIO;
56 }
57
58 return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl);
59}
60
61/*! create an osmocom-wrapped limnl netlink socket.
62 * \parma[in] ctx talloc context from which to allocate
63 * \param[in] bus netlink socket bus ID (see NETLINK_* constants)
64 * \param[in] groups groups of messages to bind/subscribe to
65 * \param[in] mnl_cb callback function called for each incoming message
66 * \param[in] priv opaque private user data
67 * \returns newly-allocated osmo_mnl or NULL in case of error. */
68struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv)
69{
70 struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl);
71
72 if (!olm)
73 return NULL;
74
75 olm->priv = priv;
76 olm->mnl_cb = mnl_cb;
Harald Welte26fc9132020-12-04 10:27:41 +010077 olm->mnls = mnl_socket_open(bus);
Harald Welte53a2fde2020-12-01 22:21:14 +010078 if (!olm->mnls) {
79 LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n",
80 bus, strerror(errno));
81 goto out_free;
82 }
83
84 if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) {
85 LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n",
86 bus, groups, strerror(errno));
87 goto out_close;
88 }
89
90 osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0);
91
92 if (osmo_fd_register(&olm->ofd)) {
93 LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n");
94 goto out_close;
95 }
96
97 return olm;
98
99out_close:
100 mnl_socket_close(olm->mnls);
101out_free:
102 talloc_free(olm);
103 return NULL;
104}
105
106/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free.
107 * \param[in] omnl osmo_mnl socket previously returned by osmo_mnl_init() */
108void osmo_mnl_destroy(struct osmo_mnl *omnl)
109{
110 if (!omnl)
111 return;
112
113 osmo_fd_unregister(&omnl->ofd);
114 mnl_socket_close(omnl->mnls);
115 talloc_free(omnl);
116}