From 4116372312c27a48fb48716701a4cb4c84367f8a 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 | 129 +++++++++++++++++++++++++++++++++++++++++ zebra/ge_netlink.h | 4 ++ zebra/kernel_netlink.c | 2 +- zebra/zebra_srv6.c | 10 ++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/zebra/ge_netlink.c b/zebra/ge_netlink.c index 6df3ea0f2892..d98e624a8cd3 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,114 @@ 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__, 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) diff --git a/zebra/ge_netlink.h b/zebra/ge_netlink.h index c1a10dfa3946..20d09116c00a 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 0ef2a57409e8..a05f2d3edcb1 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 6aa0b3d4ca31..308f9b69a5a4 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -427,11 +427,21 @@ void zebra_srv6_encap_src_addr_unset(void) void zebra_srv6_init(void) { + struct zebra_ns *zns; + hook_register(zserv_client_close, zebra_srv6_cleanup); hook_register(srv6_manager_get_chunk, zebra_srv6_manager_get_locator_chunk); hook_register(srv6_manager_release_chunk, zebra_srv6_manager_release_locator_chunk); + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return; + + /* Retrieve the actual SRv6 encap source address from the kernel + * and save it to zebra SRv6 config */ + netlink_sr_tunsrc_read(zns); } bool zebra_srv6_is_enable(void)