]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: Add function to resolve Generic Netlink family
authorCarmine Scarpitta <carmine.scarpitta@uniroma2.it>
Thu, 3 Nov 2022 15:16:23 +0000 (16:16 +0100)
committerCarmine Scarpitta <cscarpit@cisco.com>
Thu, 14 Dec 2023 13:56:44 +0000 (14:56 +0100)
Generic Netlink is an extension of Netlink meant for kernel-user space
communications. It supports the dynamic allocation of communication
channels. Kernel and user space applications register their services
with a Generic Netlink controller. The Generic Netlink controller is
responsible for assigning a unique channel number with each service.
Clients who want to use a service query the controller to see if
the service exists and to determine the correct channel number. The
channel number is used to access the requested service.

This commit adds the base functionality to get the channel number
assigned to a specific service. More precisely, this commit adds a
function `genl_resolve_family()` that takes the service name (called
family in the Generic Netlink terminology) as an input parameter and
queries the Generic Netlink controller to get the channel number
assigned with the requested service.

Signed-off-by: Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
zebra/ge_netlink.c [new file with mode: 0644]
zebra/ge_netlink.h [new file with mode: 0644]
zebra/kernel_netlink.c
zebra/subdir.am

diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c
new file mode 100644 (file)
index 0000000..6fce1f9
--- /dev/null
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic Netlink functions.
+ * Copyright (C) 2022, Carmine Scarpitta
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_NETLINK
+
+/* The following definition is to workaround an issue in the Linux kernel
+ * header files with redefinition of 'struct in6_addr' in both
+ * netinet/in.h and linux/in6.h.
+ * Reference - https://sourceware.org/ml/libc-alpha/2013-01/msg00599.html
+ */
+#define _LINUX_IN6_H
+
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/seg6_genl.h>
+
+#include "lib/ns.h"
+#include "zebra/ge_netlink.h"
+#include "zebra/debug.h"
+#include "zebra/kernel_netlink.h"
+
+
+/*
+ * Numeric family identifier used to configure SRv6 internal parameters through Generic Netlink.
+ */
+static int16_t seg6_genl_family = -1;
+
+static int genl_parse_getfamily(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+       int len;
+       struct rtattr *tb[CTRL_ATTR_MAX + 1];
+       struct genlmsghdr *ghdr = NLMSG_DATA(h);
+       struct rtattr *attrs;
+       const char *family;
+
+       if (h->nlmsg_type != GENL_ID_CTRL) {
+               zlog_err(
+                       "Not a controller message, nlmsg_len=%d nlmsg_type=0x%x",
+                       h->nlmsg_len, h->nlmsg_type);
+               return 0;
+       }
+
+       len = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+       if (len < 0) {
+               zlog_err(
+                       "Message received from netlink is of a broken size %d %zu",
+                       h->nlmsg_len, (size_t)NLMSG_LENGTH(GENL_HDRLEN));
+               return -1;
+       }
+
+       if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
+               zlog_err("Unknown controller command %d", ghdr->cmd);
+               return -1;
+       }
+
+       attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+       netlink_parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+       if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
+               zlog_err("Missing family id TLV");
+               return -1;
+       }
+
+       if (tb[CTRL_ATTR_FAMILY_NAME] == NULL) {
+               zlog_err("Missing family name TLV");
+               return -1;
+       }
+
+       family = (char *)RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
+
+       if (strmatch(family, "SEG6"))
+               seg6_genl_family =
+                       *(int16_t *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+       else {
+               if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_err("Unsupported Generic Netlink family '%s'",
+                                family);
+               return -1;
+       }
+
+       return 0;
+}
+
+int genl_resolve_family(const char *family)
+{
+       struct zebra_ns *zns;
+       struct genl_request req;
+
+       memset(&req, 0, sizeof(req));
+
+       zns = zebra_ns_lookup(NS_DEFAULT);
+
+       req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = GENL_ID_CTRL;
+
+       req.n.nlmsg_pid = zns->ge_netlink_cmd.snl.nl_pid;
+
+       req.g.cmd = CTRL_CMD_GETFAMILY;
+       req.g.version = 0;
+
+       if (!nl_attr_put(&req.n, sizeof(req), CTRL_ATTR_FAMILY_NAME, family,
+                        strlen(family) + 1))
+               return -1;
+
+       return ge_netlink_talk(genl_parse_getfamily, &req.n, zns, false);
+}
+
+void ge_netlink_init(struct zebra_ns *zns)
+{
+       if (zns->ge_netlink_cmd.sock < 0)
+               return;
+
+       /*
+        * Resolves the 'seg6' Generic Netlink family name to the corresponding numeric family identifier.
+        * This will give us the numeric family identifier required to send 'seg6' commands to the kernel
+        * over the Generic Netlink socket. 'seg6' commands are used to configure SRv6 internal parameters
+        * such as the address to use as source for encapsulated packets.
+        */
+       if (genl_resolve_family("SEG6"))
+               zlog_warn(
+                       "Kernel does not support 'SEG6' Generic Netlink family. Any attempt to set the encapsulation parameters under the SRv6 configuration will fail");
+}
+
+#endif /* HAVE_NETLINK */
diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h
new file mode 100644 (file)
index 0000000..402a29b
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Header file exported by ge_netlink.c to zebra.
+ * Copyright (C) 2022, Carmine Scarpitta
+ */
+
+#ifndef _ZEBRA_GE_NETLINK_H
+#define _ZEBRA_GE_NETLINK_H
+
+#include "zebra_dplane.h"
+
+#ifdef HAVE_NETLINK
+
+#include <linux/genetlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Generic Netlink request message */
+struct genl_request {
+       struct nlmsghdr n;
+       struct genlmsghdr g;
+       char buf[1024];
+};
+
+extern int genl_resolve_family(const char *family);
+extern void ge_netlink_init(struct zebra_ns *zns);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAVE_NETLINK */
+
+#endif /* _ZEBRA_GE_NETLINK_H */
index 424794135de6e0fcba339ce55163f3cf2b891f3a..5aa509d116ec66bfd1e422d8cd842d4703d2e098 100644 (file)
@@ -38,6 +38,7 @@
 #include "zebra/tc_netlink.h"
 #include "zebra/netconf_netlink.h"
 #include "zebra/zebra_errors.h"
+#include "zebra/ge_netlink.h"
 
 #ifndef SO_RCVBUFFORCE
 #define SO_RCVBUFFORCE  (33)
@@ -1958,6 +1959,8 @@ void kernel_init(struct zebra_ns *zns)
                       &zns->t_netlink);
 
        rt_netlink_init();
+
+       ge_netlink_init();
 }
 
 /* Helper to clean up an nlsock */
index b3bd9be9c274ef1abbde4fba69a988e2c2c8e0fe..a59515d3a858c667e9f312771be2f60eca910d52 100644 (file)
@@ -52,6 +52,7 @@ zebra_zebra_SOURCES = \
        zebra/redistribute.c \
        zebra/router-id.c \
        zebra/rt_netlink.c \
+       zebra/ge_netlink.c \
        zebra/rt_socket.c \
        zebra/rtadv.c \
        zebra/rtread_netlink.c \
@@ -144,6 +145,7 @@ noinst_HEADERS += \
        zebra/router-id.h \
        zebra/rt.h \
        zebra/rt_netlink.h \
+       zebra/ge_netlink.h \
        zebra/rtadv.h \
        zebra/rule_netlink.h \
        zebra/table_manager.h \