From 223d4534cde3ec260b2f563e51b57ea1a6aff979 Mon Sep 17 00:00:00 2001 From: Carmine Scarpitta Date: Thu, 7 Dec 2023 18:31:37 +0100 Subject: [PATCH] zebra: Fetch actual SRv6 encap src addr on startup When zebra is started, someone may have configured an SRv6 encap source address different from the default address ( :: ) in the kernel. On startup, zebra should not assume that the actual SRv6 encap source address is the default address ( :: ), but should retrieve the actual source address from the kernel and put it in zebra configuration. In other words, on startup we expect the actual SRv6 encap source address and the configured one to be the same. This commit makes the necessary changes to support the above. Signed-off-by: Carmine Scarpitta --- zebra/ge_netlink.c | 139 +++++++++++++++++++++++++++++++++++++++++ zebra/ge_netlink.h | 4 ++ zebra/kernel_netlink.c | 2 +- zebra/zebra_srv6.c | 1 + 4 files changed, 145 insertions(+), 1 deletion(-) 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 #include #include