diff options
Diffstat (limited to 'us/genltest.c')
-rw-r--r-- | us/genltest.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/us/genltest.c b/us/genltest.c new file mode 100644 index 0000000..caf44f5 --- /dev/null +++ b/us/genltest.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Generic Netlink libnl example program + * + * An example on how to use the libnl library for communicating over Generic + * Netlink with kernel modules, with unicast and multicast group messages. + * + * Copyright (c) 2022 Yaroslav de la Peña Smirnov <yps@yaroslavps.com> + */ +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <stdbool.h> +#include <netlink/socket.h> +#include <netlink/netlink.h> +#include <netlink/genl/ctrl.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> + +#include "../ks/genltest.h" + +#define prerr(...) fprintf(stderr, "error: " __VA_ARGS__) + +/* + * libnl docs and API: https://www.infradead.org/~tgr/libnl/ + * Current libnl repo: https://github.com/thom311/libnl + */ + +/* + * Handler for all received messages from our Generic Netlink family, both + * unicast and multicast. + */ +static int echo_reply_handler(struct nl_msg *msg, void *arg) +{ + int err = 0; + struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[GENLTEST_A_MAX + 1]; + + /* Parse the attributes */ + err = nla_parse(tb, GENLTEST_A_MAX, genlmsg_attrdata(genlhdr, 0), + genlmsg_attrlen(genlhdr, 0), NULL); + if (err) { + prerr("unable to parse message: %s\n", strerror(-err)); + return NL_SKIP; + } + /* Check that there's actually a payload */ + if (!tb[GENLTEST_A_MSG]) { + prerr("msg attribute missing from message\n"); + return NL_SKIP; + } + + /* Print it! */ + printf("message received: %s\n", nla_get_string(tb[GENLTEST_A_MSG])); + + return NL_OK; +} + +/* Send (unicast) GENLTEST_CMD_ECHO request message */ +static int send_echo_msg(struct nl_sock *sk, int fam) +{ + int err = 0; + struct nl_msg *msg = nlmsg_alloc(); + if (!msg) { + return -ENOMEM; + } + + /* Put the genl header inside message buffer */ + void *hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, fam, 0, 0, + GENLTEST_CMD_ECHO, GENLTEST_GENL_VERSION); + if (!hdr) { + return -EMSGSIZE; + } + + /* Put the string inside the message. */ + err = nla_put_string(msg, GENLTEST_A_MSG, + "Hello from User Space, Netlink!"); + if (err < 0) { + return -err; + } + printf("message sent\n"); + + /* Send the message. */ + err = nl_send_auto(sk, msg); + err = err >= 0 ? 0 : err; + + nlmsg_free(msg); + + return err; +} + +/* Allocate netlink socket and connect to generic netlink */ +static int conn(struct nl_sock **sk) +{ + *sk = nl_socket_alloc(); + if (!sk) { + return -ENOMEM; + } + + return genl_connect(*sk); +} + +/* Disconnect and release socket */ +static void disconn(struct nl_sock *sk) +{ + nl_close(sk); + nl_socket_free(sk); +} + +/* Modify the callback for replies to handle all received messages */ +static inline int set_cb(struct nl_sock *sk) +{ + return nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, + echo_reply_handler, NULL); +} + +int main(void) +{ + int ret = 1; + struct nl_sock *ucsk, *mcsk; + + /* + * We use one socket to receive asynchronous "notifications" over + * multicast group, and another for ops. We do this so that we don't mix + * up responses from ops with notifications to make handling easier. + */ + if ((ret = conn(&ucsk)) || (ret = conn(&mcsk))) { + prerr("failed to connect to generic netlink\n"); + goto out; + } + + /* Resolve the genl family. One family for both unicast and multicast. */ + int fam = genl_ctrl_resolve(ucsk, GENLTEST_GENL_NAME); + if (fam < 0) { + prerr("failed to resolve generic netlink family: %s\n", + strerror(-fam)); + goto out; + } + + /* Disable sequence checks for asynchronous multicast messages. */ + nl_socket_disable_seq_check(mcsk); + + /* Resolve the multicast group. */ + int mcgrp = genl_ctrl_resolve_grp(mcsk, GENLTEST_GENL_NAME, + GENLTEST_MC_GRP_NAME); + if (mcgrp < 0) { + prerr("failed to resolve generic netlink multicast group: %s\n", + strerror(-mcgrp)); + goto out; + } + /* Join the multicast group. */ + if ((ret = nl_socket_add_membership(mcsk, mcgrp) < 0)) { + prerr("failed to join multicast group: %s\n", strerror(-ret)); + goto out; + } + + if ((ret = set_cb(ucsk)) || (ret = set_cb(mcsk))) { + prerr("failed to set callback: %s\n", strerror(-ret)); + goto out; + } + + /* Send unicast message and listen for response. */ + if ((ret = send_echo_msg(ucsk, fam))) { + prerr("failed to send message: %s\n", strerror(-ret)); + } + printf("listening for messages\n"); + nl_recvmsgs_default(ucsk); + + /* Listen for "notifications". */ + while (1) { + nl_recvmsgs_default(mcsk); + } + + ret = 0; +out: + disconn(ucsk); + disconn(mcsk); + return ret; +} |