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)