summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zebra/ge_netlink.c139
-rw-r--r--zebra/ge_netlink.h4
-rw-r--r--zebra/kernel_netlink.c2
-rw-r--r--zebra/zebra_srv6.c1
4 files changed, 145 insertions, 1 deletions
diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c
index 6df3ea0f28..e7d2e6b12a 100644
--- a/zebra/ge_netlink.c
+++ b/zebra/ge_netlink.c
@@ -23,6 +23,27 @@
#include "zebra/ge_netlink.h"
#include "zebra/debug.h"
#include "zebra/kernel_netlink.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_srv6.h"
+
+
+/**
+ * This file provides an implementation of the functionality exposed by the
+ * kernel through the Generic Netlink mechanism.
+ *
+ * Supported features include the ability to configure the source address used
+ * for SRv6 encapsulation ('sr tunsrc' in kernel terminology).
+ *
+ * At the time of writing this code, the kernel does not send us any asynchronous
+ * notifications when someone changes the 'sr tunsrc' under us. As a result, we
+ * are currently unable to detect when the source address changes and update the
+ * SRv6 encapsulation source address configured in zebra.
+ *
+ * In the future, when the kernel supports async notifications, the implementation
+ * can be improved by listening on the Generic Netlink socket and adding a handler
+ * to process/parse incoming 'sr tunsrc' change messages and update the SRv6 zebra
+ * configuration with the new encap source address.
+ */
/*
@@ -211,6 +232,117 @@ netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx
return ge_netlink_talk(netlink_talk_filter, &req.n, zns, false);
}
+/**
+ * netlink_sr_tunsrc_reply_read() - Read in SR tunsrc reply from the kernel
+ *
+ * @h: Netlink message header
+ * @ns_id: Namspace id
+ * @startup: Are we reading under startup conditions?
+ *
+ * Return: Result status
+ */
+int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+ int len;
+ struct genlmsghdr *ghdr;
+ struct rtattr *tb[SEG6_ATTR_MAX + 1] = {};
+ struct rtattr *attrs;
+
+ if (h->nlmsg_type != seg6_genl_family)
+ return 0;
+
+ len = h->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+ if (len < 0) {
+ zlog_warn("%s: Message received from netlink is of a broken size %d %zu",
+ __func__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(GENL_HDRLEN));
+ return -1;
+ }
+
+ ghdr = NLMSG_DATA(h);
+
+ if (ghdr->cmd != SEG6_CMD_GET_TUNSRC)
+ return 0;
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ netlink_parse_rtattr(tb, SEG6_ATTR_MAX, attrs, len);
+
+ if (tb[SEG6_ATTR_DST] == NULL) {
+ zlog_err("Missing tunsrc addr");
+ return -1;
+ }
+
+ zebra_srv6_encap_src_addr_set(
+ (struct in6_addr *)RTA_DATA(tb[SEG6_ATTR_DST]));
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: SRv6 encap source address received from kernel: '%pI6'",
+ __func__,
+ (struct in6_addr *)RTA_DATA(tb[SEG6_ATTR_DST]));
+
+ return 0;
+}
+
+/**
+ * netlink_request_sr_tunsrc() - Request SR tunsrc from the kernel
+ * @zns: Zebra namespace
+ *
+ * Return: Result status
+ */
+static int netlink_request_sr_tunsrc(struct zebra_ns *zns)
+{
+ struct genl_request req;
+
+ if (zns->ge_netlink_cmd.sock < 0)
+ return -1;
+
+ if (seg6_genl_family < 0) {
+ zlog_err(
+ "Failed to get SRv6 encap source address: kernel does not support 'SEG6' Generic Netlink family.");
+ return -1;
+ }
+
+ /* Form the request, specifying filter (rtattr) if needed. */
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = seg6_genl_family;
+ req.g.cmd = SEG6_CMD_GET_TUNSRC;
+ req.g.version = SEG6_GENL_VERSION;
+
+ return netlink_request(&zns->ge_netlink_cmd, &req);
+}
+
+/**
+ * SR tunsrc read function using netlink interface. Only called
+ * on bootstrap time.
+ */
+int netlink_sr_tunsrc_read(struct zebra_ns *zns)
+{
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ if (zns->ge_netlink_cmd.sock < 0)
+ return -1;
+
+ /* Capture info in intermediate info struct */
+ dp_info.ns_id = zns->ns_id;
+ dp_info.is_cmd = true;
+ dp_info.sock = zns->ge_netlink_cmd.sock;
+ dp_info.seq = zns->ge_netlink_cmd.seq;
+
+ /* Get SR tunsrc. */
+ ret = netlink_request_sr_tunsrc(zns);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info(netlink_sr_tunsrc_reply_read,
+ &zns->ge_netlink_cmd, &dp_info, 0, true);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
void ge_netlink_init(struct zebra_ns *zns)
{
if (zns->ge_netlink_cmd.sock < 0)
@@ -225,6 +357,13 @@ void ge_netlink_init(struct zebra_ns *zns)
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");
+
+ /**
+ * Retrieve the actual SRv6 encap source address from the kernel
+ * (default namespace) and save it to zebra SRv6 config
+ */
+ if (zns->ns_id == NS_DEFAULT)
+ netlink_sr_tunsrc_read(zns);
}
#endif /* HAVE_NETLINK */
diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h
index c1a10dfa39..20d09116c0 100644
--- a/zebra/ge_netlink.h
+++ b/zebra/ge_netlink.h
@@ -34,6 +34,10 @@ struct nl_batch;
extern enum netlink_msg_status
netlink_put_sr_tunsrc_set_msg(struct nl_batch *bth,
struct zebra_dplane_ctx *ctx);
+
+int netlink_sr_tunsrc_reply_read(struct nlmsghdr *h, ns_id_t ns_id, int startup);
+int netlink_sr_tunsrc_read(struct zebra_ns *zns);
+
extern void ge_netlink_init(struct zebra_ns *zns);
#ifdef __cplusplus
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 0ef2a57409..a05f2d3edc 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -1963,7 +1963,7 @@ void kernel_init(struct zebra_ns *zns)
rt_netlink_init();
- ge_netlink_init();
+ ge_netlink_init(zns);
}
/* Helper to clean up an nlsock */
diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c
index adc7d32aae..bb872ef91c 100644
--- a/zebra/zebra_srv6.c
+++ b/zebra/zebra_srv6.c
@@ -17,6 +17,7 @@
#include "zebra/zebra_router.h"
#include "zebra/zebra_srv6.h"
#include "zebra/zebra_errors.h"
+#include "zebra/ge_netlink.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>