]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
utils: Add generic socket helpers
authorParav Pandit <parav@nvidia.com>
Wed, 10 Feb 2021 18:34:43 +0000 (20:34 +0200)
committerDavid Ahern <dsahern@kernel.org>
Thu, 11 Feb 2021 16:09:07 +0000 (09:09 -0700)
Subsequent patch needs to
(a) query and use socket family
(b) send/receive messages using this family

Hence add helper routines to open, close, query family and to perform
send receive operations.

Signed-off-by: Parav Pandit <parav@nvidia.com>
Signed-off-by: David Ahern <dsahern@kernel.org>
include/mnl_utils.h
lib/mnl_utils.c

index fa826ef1f8fe3c36cea8e7752b52ac4a3d67ce6f..9e7d6879009c01e6336cc7d568a090f125395880 100644 (file)
@@ -2,6 +2,22 @@
 #ifndef __MNL_UTILS_H__
 #define __MNL_UTILS_H__ 1
 
+struct mnlu_gen_socket {
+       struct mnl_socket *nl;
+       char *buf;
+       uint32_t family;
+       unsigned int seq;
+       uint8_t version;
+};
+
+int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
+                        uint8_t version);
+void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
+struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
+                                            uint8_t cmd, uint16_t flags);
+int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
+                          mnl_cb_t data_cb, void *data);
+
 struct mnl_socket *mnlu_socket_open(int bus);
 struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags,
                                  void *extra_header, size_t extra_header_size);
index 46384ff81cf184b439b54f3bfabbb268317b54bd..4f69945541778936f143a309377c102bcebfe987 100644 (file)
@@ -7,6 +7,7 @@
 #include <string.h>
 #include <time.h>
 #include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
 
 #include "libnetlink.h"
 #include "mnl_utils.h"
@@ -108,3 +109,123 @@ int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, siz
 
        return err;
 }
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
+{
+       int type = mnl_attr_get_type(attr);
+       const struct nlattr **tb = data;
+
+       if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+               return MNL_CB_ERROR;
+
+       if (type == CTRL_ATTR_FAMILY_ID &&
+           mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+               return MNL_CB_ERROR;
+       tb[type] = attr;
+       return MNL_CB_OK;
+}
+
+static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+       struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+       struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+       uint32_t *p_id = data;
+
+       mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
+       if (!tb[CTRL_ATTR_FAMILY_ID])
+               return MNL_CB_ERROR;
+       *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+       return MNL_CB_OK;
+}
+
+static int family_get(struct mnlu_gen_socket *nlg, const char *family_name)
+{
+       struct genlmsghdr hdr = {};
+       struct nlmsghdr *nlh;
+       int err;
+
+       hdr.cmd = CTRL_CMD_GETFAMILY;
+       hdr.version = 0x1;
+
+       nlh = mnlu_msg_prepare(nlg->buf, GENL_ID_CTRL,
+                              NLM_F_REQUEST | NLM_F_ACK,
+                              &hdr, sizeof(hdr));
+
+       mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
+
+       err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+       if (err < 0)
+               return err;
+
+       err = mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
+                                  MNL_SOCKET_BUFFER_SIZE,
+                                  get_family_id_cb, &nlg->family);
+       return err;
+}
+
+int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
+                        uint8_t version)
+{
+       int err;
+
+       nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
+       if (!nlg->buf)
+               goto err_buf_alloc;
+
+       nlg->nl = mnlu_socket_open(NETLINK_GENERIC);
+       if (!nlg->nl)
+               goto err_socket_open;
+
+       err = family_get(nlg, family_name);
+       if (err)
+               goto err_socket;
+
+       return 0;
+
+err_socket:
+       mnl_socket_close(nlg->nl);
+err_socket_open:
+       free(nlg->buf);
+err_buf_alloc:
+       return -1;
+}
+
+void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg)
+{
+       mnl_socket_close(nlg->nl);
+       free(nlg->buf);
+}
+
+struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
+                                            uint8_t cmd, uint16_t flags)
+{
+       struct genlmsghdr hdr = {};
+       struct nlmsghdr *nlh;
+
+       hdr.cmd = cmd;
+       hdr.version = nlg->version;
+       nlh = mnlu_msg_prepare(nlg->buf, nlg->family, flags, &hdr, sizeof(hdr));
+       nlg->seq = nlh->nlmsg_seq;
+       return nlh;
+}
+
+int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
+                          mnl_cb_t data_cb, void *data)
+{
+       int err;
+
+       err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+       if (err < 0) {
+               perror("Failed to send data");
+               return -errno;
+       }
+
+       err = mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
+                                  MNL_SOCKET_BUFFER_SIZE,
+                                  data_cb, data);
+       if (err < 0) {
+               fprintf(stderr, "kernel answers: %s\n", strerror(errno));
+               return -errno;
+       }
+       return 0;
+}