summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--include/linux/netconf.h30
-rw-r--r--include/subdir.am1
-rw-r--r--zebra/debug_nl.c93
-rw-r--r--zebra/dplane_fpm_nl.c25
-rw-r--r--zebra/if_netlink.c2
-rw-r--r--zebra/interface.c55
-rw-r--r--zebra/interface.h4
-rw-r--r--zebra/kernel_netlink.c47
-rw-r--r--zebra/netconf_netlink.c175
-rw-r--r--zebra/netconf_netlink.h48
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/zebra_dplane.c190
-rw-r--r--zebra/zebra_dplane.h34
-rw-r--r--zebra/zebra_nhg.c1
-rw-r--r--zebra/zebra_rib.c4
-rw-r--r--zebra/zebra_script.c2
17 files changed, 668 insertions, 47 deletions
diff --git a/configure.ac b/configure.ac
index 32ee560df9..ddd883e54f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -765,7 +765,7 @@ AC_ARG_ENABLE([scripting],
AS_HELP_STRING([--enable-scripting], [Build with scripting support]))
AC_ARG_ENABLE([netlink-debug],
- AS_HELP_STRING([--disable-netlink-debug], [pretty print netlink debug messages]))
+ AS_HELP_STRING([--disable-netlink-debug], [don't pretty print netlink debug messages]))
if test "$enable_netlink_debug" != "no" ; then
AC_DEFINE([NETLINK_DEBUG], [1], [Netlink extra debugging code])
diff --git a/include/linux/netconf.h b/include/linux/netconf.h
new file mode 100644
index 0000000000..fac4edd553
--- /dev/null
+++ b/include/linux/netconf.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_NETCONF_H_
+#define _UAPI_LINUX_NETCONF_H_
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct netconfmsg {
+ __u8 ncm_family;
+};
+
+enum {
+ NETCONFA_UNSPEC,
+ NETCONFA_IFINDEX,
+ NETCONFA_FORWARDING,
+ NETCONFA_RP_FILTER,
+ NETCONFA_MC_FORWARDING,
+ NETCONFA_PROXY_NEIGH,
+ NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
+ NETCONFA_INPUT,
+ NETCONFA_BC_FORWARDING,
+ __NETCONFA_MAX
+};
+#define NETCONFA_MAX (__NETCONFA_MAX - 1)
+#define NETCONFA_ALL -1
+
+#define NETCONFA_IFINDEX_ALL -1
+#define NETCONFA_IFINDEX_DEFAULT -2
+
+#endif /* _UAPI_LINUX_NETCONF_H_ */
diff --git a/include/subdir.am b/include/subdir.am
index 86129c4d68..a06a8e5649 100644
--- a/include/subdir.am
+++ b/include/subdir.am
@@ -5,6 +5,7 @@ noinst_HEADERS += \
include/linux/lwtunnel.h \
include/linux/mpls_iptunnel.h \
include/linux/neighbour.h \
+ include/linux/netconf.h \
include/linux/netlink.h \
include/linux/nexthop.h \
include/linux/rtnetlink.h \
diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c
index 825497fff3..260ba30b3c 100644
--- a/zebra/debug_nl.c
+++ b/zebra/debug_nl.c
@@ -20,6 +20,7 @@
#include <sys/socket.h>
+#include <linux/netconf.h>
#include <linux/netlink.h>
#include <linux/nexthop.h>
#include <linux/rtnetlink.h>
@@ -90,6 +91,11 @@ const char *nlmsg_type2str(uint16_t type)
case RTM_GETNEXTHOP:
return "GETNEXTHOP";
+ case RTM_NEWNETCONF:
+ return "RTM_NEWNETCONF";
+ case RTM_DELNETCONF:
+ return "RTM_DELNETCONF";
+
default:
return "UNKNOWN";
}
@@ -657,6 +663,37 @@ const char *frh_action2str(uint8_t action)
}
}
+static const char *ncm_rta2str(int type)
+{
+ switch (type) {
+ case NETCONFA_UNSPEC:
+ return "UNSPEC";
+ case NETCONFA_IFINDEX:
+ return "IFINDEX";
+ case NETCONFA_FORWARDING:
+ return "FORWARDING";
+ case NETCONFA_RP_FILTER:
+ return "RP_FILTER";
+ case NETCONFA_MC_FORWARDING:
+ return "MCAST";
+ case NETCONFA_PROXY_NEIGH:
+ return "PROXY_NEIGH";
+ case NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN:
+ return "IGNORE_LINKDOWN";
+ case NETCONFA_INPUT:
+ return "MPLS";
+ case NETCONFA_BC_FORWARDING:
+ return "BCAST";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void dump_on_off(uint32_t ival, const char *prefix)
+{
+ zlog_debug("%s%s", prefix, (ival != 0) ? "on" : "off");
+}
+
static inline void flag_write(int flags, int flag, const char *flagstr,
char *buf, size_t buflen)
{
@@ -1118,6 +1155,7 @@ static void nlnh_dump(struct nhmsg *nhm, size_t msglen)
struct nexthop_grp *nhgrp;
rta = RTM_NHA(nhm);
+
next_rta:
/* Check the header for valid length and for outbound access. */
if (RTA_OK(rta, msglen) == 0)
@@ -1296,6 +1334,52 @@ next_rta:
goto next_rta;
}
+static void nlncm_dump(const struct netconfmsg *ncm, size_t msglen)
+{
+ const struct rtattr *rta;
+ size_t plen;
+ uint32_t ival;
+
+ rta = (void *)((const char *)ncm +
+ NLMSG_ALIGN(sizeof(struct netconfmsg)));
+
+next_rta:
+ /* Check the attr header for valid length. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, ncm_rta2str(rta->rta_type));
+
+ switch (rta->rta_type) {
+ case NETCONFA_IFINDEX:
+ ival = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %d", (int32_t)ival);
+ break;
+
+ /* Most attrs are just on/off. */
+ case NETCONFA_FORWARDING:
+ case NETCONFA_RP_FILTER:
+ case NETCONFA_MC_FORWARDING:
+ case NETCONFA_PROXY_NEIGH:
+ case NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN:
+ case NETCONFA_INPUT:
+ case NETCONFA_BC_FORWARDING:
+ ival = *(uint32_t *)RTA_DATA(rta);
+ dump_on_off(ival, " ");
+ break;
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
void nl_dump(void *msg, size_t msglen)
{
struct nlmsghdr *nlmsg = msg;
@@ -1305,6 +1389,7 @@ void nl_dump(void *msg, size_t msglen)
struct ndmsg *ndm;
struct rtmsg *rtm;
struct nhmsg *nhm;
+ struct netconfmsg *ncm;
struct ifinfomsg *ifi;
struct fib_rule_hdr *frh;
char fbuf[128];
@@ -1422,6 +1507,14 @@ next_header:
nlnh_dump(nhm, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*nhm)));
break;
+ case RTM_NEWNETCONF:
+ case RTM_DELNETCONF:
+ ncm = NLMSG_DATA(nlmsg);
+ zlog_debug(" ncm [family=%s (%d)]",
+ af_type2str(ncm->ncm_family), ncm->ncm_family);
+ nlncm_dump(ncm, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*ncm)));
+ break;
+
default:
break;
}
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index ce3c8d4b11..8c8004190b 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -780,6 +780,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
nl_buf_len += (size_t)rv;
break;
+ /* Un-handled by FPM at this time. */
case DPLANE_OP_PW_INSTALL:
case DPLANE_OP_PW_UNINSTALL:
case DPLANE_OP_ADDR_INSTALL:
@@ -793,15 +794,27 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
case DPLANE_OP_SYS_ROUTE_DELETE:
case DPLANE_OP_ROUTE_NOTIFY:
case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ case DPLANE_OP_NEIGH_DISCOVER:
+ case DPLANE_OP_BR_PORT_UPDATE:
+ case DPLANE_OP_IPTABLE_ADD:
+ case DPLANE_OP_IPTABLE_DELETE:
+ case DPLANE_OP_IPSET_ADD:
+ case DPLANE_OP_IPSET_DELETE:
+ case DPLANE_OP_IPSET_ENTRY_ADD:
+ case DPLANE_OP_IPSET_ENTRY_DELETE:
+ case DPLANE_OP_NEIGH_IP_INSTALL:
+ case DPLANE_OP_NEIGH_IP_DELETE:
+ case DPLANE_OP_NEIGH_TABLE_UPDATE:
+ case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ case DPLANE_OP_INTF_NETCONFIG:
case DPLANE_OP_NONE:
break;
- default:
- if (IS_ZEBRA_DEBUG_FPM)
- zlog_debug("%s: unhandled data plane message (%d) %s",
- __func__, dplane_ctx_get_op(ctx),
- dplane_op2str(dplane_ctx_get_op(ctx)));
- break;
}
/* Skip empty enqueues. */
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 141e4074d5..db8ee3236c 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -74,6 +74,7 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_evpn_mh.h"
#include "zebra/zebra_l2.h"
+#include "zebra/netconf_netlink.h"
extern struct zebra_privs_t zserv_privs;
@@ -1001,6 +1002,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Hardware type and address. */
ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type);
+
netlink_interface_update_hw_addr(tb, ifp);
if_add_update(ifp);
diff --git a/zebra/interface.c b/zebra/interface.c
index 5d1080fd72..c30f43456c 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -1318,6 +1318,56 @@ done:
dplane_ctx_fini(&ctx);
}
+/*
+ * Handle netconf change from a dplane context object; runs in the main
+ * pthread so it can update zebra data structs.
+ */
+int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ enum dplane_netconf_status_e mpls;
+ int ret = 0;
+
+ zns = zebra_ns_lookup(dplane_ctx_get_netconf_ns_id(ctx));
+ if (zns == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ ifp = if_lookup_by_index_per_ns(zns,
+ dplane_ctx_get_netconf_ifindex(ctx));
+ if (ifp == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ zif = ifp->info;
+ if (zif == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ mpls = dplane_ctx_get_netconf_mpls(ctx);
+
+ if (mpls == DPLANE_NETCONF_STATUS_ENABLED)
+ zif->mpls = true;
+ else if (mpls == DPLANE_NETCONF_STATUS_DISABLED)
+ zif->mpls = false;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s, ifindex %d, mpls %s",
+ __func__, ifp->name, ifp->ifindex,
+ (zif->mpls ? "ON" : "OFF"));
+
+done:
+ /* Free the context */
+ dplane_ctx_fini(&ctx);
+
+ return ret;
+}
+
/* Dump if address information to vty. */
static void connected_dump_vty(struct vty *vty, json_object *json,
struct connected *connected)
@@ -1670,6 +1720,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
vty_out(vty, "mtu6 %d ", ifp->mtu6);
vty_out(vty, "\n flags: %s\n", if_flag_dump(ifp->flags));
+ if (zebra_if->mpls)
+ vty_out(vty, " MPLS enabled\n");
+
/* Hardware address. */
vty_out(vty, " Type: %s\n", if_link_type_str(ifp->ll_type));
if (ifp->hw_addr_len != 0) {
@@ -1990,6 +2043,8 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp,
json_object_string_add(json_if, "OsDescription",
zebra_if->desc);
+ json_object_boolean_add(json_if, "mplsEnabled", zebra_if->mpls);
+
if (ifp->ifindex == IFINDEX_INTERNAL) {
json_object_boolean_add(json_if, "pseudoInterface", true);
return;
diff --git a/zebra/interface.h b/zebra/interface.h
index 413a67469a..85617961af 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -329,6 +329,9 @@ struct zebra_if {
/* Multicast configuration. */
uint8_t multicast;
+ /* MPLS status. */
+ bool mpls;
+
/* Router advertise configuration. */
uint8_t rtadv_enable;
@@ -520,6 +523,7 @@ extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif);
extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
char *pd_buf, uint32_t pd_buf_len);
void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx);
+int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx);
#ifdef HAVE_PROC_NET_DEV
extern void ifstat_update_proc(void);
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 23e7098d04..031ac0733c 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -38,7 +38,6 @@
#include "lib_errors.h"
#include "hash.h"
-//#include "zebra/zserv.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
@@ -48,6 +47,7 @@
#include "zebra/rt_netlink.h"
#include "zebra/if_netlink.h"
#include "zebra/rule_netlink.h"
+#include "zebra/netconf_netlink.h"
#include "zebra/zebra_errors.h"
#ifndef SO_RCVBUFFORCE
@@ -108,6 +108,8 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"},
{RTM_DELNEXTHOP, "RTM_DELNEXTHOP"},
{RTM_GETNEXTHOP, "RTM_GETNEXTHOP"},
+ {RTM_NEWNETCONF, "RTM_NEWNETCONF"},
+ {RTM_DELNETCONF, "RTM_DELNETCONF"},
{0}};
static const struct message rtproto_str[] = {
@@ -373,6 +375,8 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
/* Messages handled in the dplane thread */
case RTM_NEWADDR:
case RTM_DELADDR:
+ case RTM_NEWNETCONF:
+ case RTM_DELNETCONF:
return 0;
default:
@@ -407,7 +411,12 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
case RTM_DELADDR:
return netlink_interface_addr_dplane(h, ns_id, startup);
- /* TODO */
+ case RTM_NEWNETCONF:
+ case RTM_DELNETCONF:
+ return netlink_netconf_change(h, ns_id, startup);
+
+ /* TODO -- other messages for the dplane socket and pthread */
+
case RTM_NEWLINK:
case RTM_DELLINK:
@@ -456,8 +465,8 @@ int kernel_dplane_read(struct zebra_dplane_info *info)
* then the normal course of operations). We are intentionally
* allowing some messages from ourselves through
* ( I'm looking at you Interface based netlink messages )
- * so that we only had to write one way to handle incoming
- * address add/delete changes.
+ * so that we only have to write one way to handle incoming
+ * address add/delete and xxxNETCONF changes.
*/
static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
{
@@ -473,7 +482,8 @@ static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
* if (nlmsg_pid == pid ||
* nlmsg_pid == dplane_pid) {
* if (the incoming nlmsg_type ==
- * RTM_NEWADDR | RTM_DELADDR)
+ * RTM_NEWADDR || RTM_DELADDR || RTM_NEWNETCONF ||
+ * RTM_DELNETCONF)
* keep this message
* else
* skip this message
@@ -492,7 +502,7 @@ static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
/*
* 2: Compare to dplane pid
*/
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(dplane_pid), 0, 4),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(dplane_pid), 0, 6),
/*
* 3: Load the nlmsg_type into BPF register
*/
@@ -501,17 +511,27 @@ static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
/*
* 4: Compare to RTM_NEWADDR
*/
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWADDR), 2, 0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWADDR), 4, 0),
/*
* 5: Compare to RTM_DELADDR
*/
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELADDR), 1, 0),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELADDR), 3, 0),
+ /*
+ * 6: Compare to RTM_NEWNETCONF
+ */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWNETCONF), 2,
+ 0),
/*
- * 6: This is the end state of we want to skip the
+ * 7: Compare to RTM_DELNETCONF
+ */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELNETCONF), 1,
+ 0),
+ /*
+ * 8: This is the end state of we want to skip the
* message
*/
BPF_STMT(BPF_RET | BPF_K, 0),
- /* 7: This is the end state of we want to keep
+ /* 9: This is the end state of we want to keep
* the message
*/
BPF_STMT(BPF_RET | BPF_K, 0xffff),
@@ -1470,6 +1490,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
+ case DPLANE_OP_INTF_NETCONFIG:
case DPLANE_OP_NONE:
return FRR_NETLINK_ERROR;
}
@@ -1597,7 +1618,11 @@ void kernel_init(struct zebra_ns *zns)
dplane_groups = (RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
- RTMGRP_IPV6_IFADDR);
+ RTMGRP_IPV6_IFADDR |
+ ((uint32_t) 1 << (RTNLGRP_IPV4_NETCONF - 1)) |
+ ((uint32_t) 1 << (RTNLGRP_IPV6_NETCONF - 1)) |
+ ((uint32_t) 1 << (RTNLGRP_MPLS_NETCONF - 1)));
+
snprintf(zns->netlink.name, sizeof(zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
diff --git a/zebra/netconf_netlink.c b/zebra/netconf_netlink.c
new file mode 100644
index 0000000000..587f6c749e
--- /dev/null
+++ b/zebra/netconf_netlink.c
@@ -0,0 +1,175 @@
+/*
+ * netconf_netlink.c - netconf interaction with the kernel using
+ * netlink
+ * Copyright (C) 2021 Nvidia, Inc.
+ * Donald Sharp
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#ifdef HAVE_NETLINK /* Netlink OSes only */
+
+#include <ns.h>
+
+#include "linux/netconf.h"
+
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_dplane.h"
+#include "zebra/kernel_netlink.h"
+#include "zebra/netconf_netlink.h"
+#include "zebra/debug.h"
+
+static struct rtattr *netconf_rta(struct netconfmsg *ncm)
+{
+ return (struct rtattr *)((char *)ncm +
+ NLMSG_ALIGN(sizeof(struct netconfmsg)));
+}
+
+/*
+ * Handle netconf update about a single interface: create dplane
+ * context, and enqueue for processing in the main zebra pthread.
+ */
+static int netlink_netconf_dplane_update(ns_id_t ns_id, ifindex_t ifindex,
+ enum dplane_netconf_status_e mpls_on,
+ enum dplane_netconf_status_e mcast_on)
+{
+ struct zebra_dplane_ctx *ctx;
+
+ ctx = dplane_ctx_alloc();
+ dplane_ctx_set_op(ctx, DPLANE_OP_INTF_NETCONFIG);
+ dplane_ctx_set_netconf_ns_id(ctx, ns_id);
+ dplane_ctx_set_netconf_ifindex(ctx, ifindex);
+
+ dplane_ctx_set_netconf_mpls(ctx, mpls_on);
+ dplane_ctx_set_netconf_mcast(ctx, mcast_on);
+
+ /* Enqueue ctx for main pthread to process */
+ dplane_provider_enqueue_to_zebra(ctx);
+
+ return 0;
+}
+
+/*
+ * Parse and process an incoming netlink netconf update.
+ */
+int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+ struct netconfmsg *ncm;
+ struct rtattr *tb[NETCONFA_MAX + 1] = {};
+ int len;
+ ifindex_t ifindex;
+ uint32_t ival;
+ enum dplane_netconf_status_e mpls_on = DPLANE_NETCONF_STATUS_UNKNOWN;
+ enum dplane_netconf_status_e mcast_on = DPLANE_NETCONF_STATUS_UNKNOWN;
+
+ if (h->nlmsg_type != RTM_NEWNETCONF && h->nlmsg_type != RTM_DELNETCONF)
+ return 0;
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct netconfmsg));
+ if (len < 0) {
+ zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu",
+ __func__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg)));
+ return -1;
+ }
+
+ ncm = NLMSG_DATA(h);
+
+ netlink_parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), len);
+
+ if (!tb[NETCONFA_IFINDEX]) {
+ zlog_err("NETCONF message received from netlink without an ifindex");
+ return 0;
+ }
+
+ ifindex = *(ifindex_t *)RTA_DATA(tb[NETCONFA_IFINDEX]);
+
+ switch (ifindex) {
+ case NETCONFA_IFINDEX_ALL:
+ case NETCONFA_IFINDEX_DEFAULT:
+ /*
+ * We need the ability to handle netlink messages intended
+ * for all and default interfaces. I am not 100% sure
+ * what that is yet, or where we would store it.
+ */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: Ignoring global ifindex %d",
+ __func__, ifindex);
+
+ return 0;
+ default:
+ break;
+ }
+
+ if (tb[NETCONFA_INPUT]) {
+ ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_INPUT]);
+ if (ival != 0)
+ mpls_on = DPLANE_NETCONF_STATUS_ENABLED;
+ else
+ mpls_on = DPLANE_NETCONF_STATUS_DISABLED;
+ }
+
+ if (tb[NETCONFA_MC_FORWARDING]) {
+ ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]);
+ if (ival != 0)
+ mcast_on = DPLANE_NETCONF_STATUS_ENABLED;
+ else
+ mcast_on = DPLANE_NETCONF_STATUS_DISABLED;
+ }
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: interface %u is mpls on: %d multicast on: %d",
+ __func__, ifindex, mpls_on, mcast_on);
+
+ /* Create a dplane context and pass it along for processing */
+ netlink_netconf_dplane_update(ns_id, ifindex, mpls_on, mcast_on);
+
+ return 0;
+}
+
+/*
+ * Request info from the host OS. This only sends the request; any replies
+ * are processed asynchronously.
+ */
+int netlink_request_netconf(int sockfd)
+{
+ struct nlsock *nls;
+ struct {
+ struct nlmsghdr n;
+ struct netconfmsg ncm;
+ char buf[1024];
+ } req = {};
+
+ nls = kernel_netlink_nlsock_lookup(sockfd);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: nlsock %s", __func__, nls ? nls->name : "NULL");
+
+ if (nls == NULL)
+ return -1;
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg));
+ req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETNETCONF;
+ req.ncm.ncm_family = AF_UNSPEC;
+
+ return netlink_request(nls, &req);
+}
+
+#endif /* HAVE_NETLINK */
diff --git a/zebra/netconf_netlink.h b/zebra/netconf_netlink.h
new file mode 100644
index 0000000000..3f2e7af768
--- /dev/null
+++ b/zebra/netconf_netlink.h
@@ -0,0 +1,48 @@
+/*
+ * netconf_netlink.h - netconf interaction with the kernel using
+ * netlink
+ * Copyright (C) 2021 Nvidia, Inc.
+ * Donald Sharp
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef __NETCONF_NETLINK_H__
+#define __NETCONF_NETLINK_H__
+
+#ifdef HAVE_NETLINK /* Netlink-only module */
+
+#include "zebra/zebra_ns.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Parse and handle a NETCONF message. */
+extern int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+/* Request info from the host OS. */
+int netlink_request_netconf(int sockfd);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAVE_NETLINK */
+
+#endif /* NETCONF_NETLINK_H */
diff --git a/zebra/subdir.am b/zebra/subdir.am
index f0cc6ce71b..77e0898d81 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -69,6 +69,7 @@ zebra_zebra_SOURCES = \
zebra/kernel_socket.c \
zebra/label_manager.c \
zebra/main.c \
+ zebra/netconf_netlink.c \
zebra/redistribute.c \
zebra/router-id.c \
zebra/rt_netlink.c \
@@ -148,6 +149,7 @@ noinst_HEADERS += \
zebra/kernel_netlink.h \
zebra/kernel_socket.h \
zebra/label_manager.h \
+ zebra/netconf_netlink.h \
zebra/redistribute.h \
zebra/rib.h \
zebra/router-id.h \
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 1967345d22..6de2be3ab8 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -28,6 +28,7 @@
#include "lib/memory.h"
#include "lib/queue.h"
#include "lib/zebra.h"
+#include "zebra/netconf_netlink.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_dplane.h"
#include "zebra/zebra_vxlan_private.h"
@@ -290,6 +291,19 @@ struct dplane_gre_ctx {
unsigned int mtu;
struct zebra_l2info_gre info;
};
+
+
+/*
+ * Network interface configuration info - aligned with netlink's NETCONF
+ * info. The flags values are public, in the dplane.h file...
+ */
+struct dplane_netconf_info {
+ ns_id_t ns_id;
+ ifindex_t ifindex;
+ enum dplane_netconf_status_e mpls_val;
+ enum dplane_netconf_status_e mcast_val;
+};
+
/*
* The context block used to exchange info about route updates across
* the boundary between the zebra main context (and pthread) and the
@@ -347,6 +361,7 @@ struct zebra_dplane_ctx {
} ipset_entry;
struct dplane_neigh_table neightable;
struct dplane_gre_ctx gre;
+ struct dplane_netconf_info netconf;
} u;
/* Namespace info, used especially for netlink kernel communication */
@@ -416,6 +431,9 @@ PREDECL_DLIST(zns_info_list);
struct dplane_zns_info {
struct zebra_dplane_info info;
+ /* Request data from the OS */
+ struct thread *t_request;
+
/* Read event */
struct thread *t_read;
@@ -540,8 +558,6 @@ DECLARE_DLIST(zns_info_list, struct dplane_zns_info, link);
/* Prototypes */
static void dplane_thread_loop(struct thread *event);
-static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
- struct zebra_ns *zns);
static enum zebra_dplane_result lsp_update_internal(struct zebra_lsp *lsp,
enum dplane_op_e op);
static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
@@ -769,6 +785,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
}
break;
case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_INTF_NETCONFIG:
break;
}
}
@@ -1053,6 +1070,9 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_INTF_ADDR_DEL:
return "INTF_ADDR_DEL";
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ return "INTF_NETCONFIG";
}
return ret;
@@ -2239,6 +2259,10 @@ uint32_t dplane_intf_extra_get_status(const struct dplane_intf_extra *ptr)
return ptr->status;
}
+/*
+ * End of interface extra info accessors
+ */
+
uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -2270,9 +2294,66 @@ dplane_ctx_neightable_get_mcast_probes(const struct zebra_dplane_ctx *ctx)
return ctx->u.neightable.mcast_probes;
}
-/*
- * End of interface extra info accessors
- */
+ifindex_t dplane_ctx_get_netconf_ifindex(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.ifindex;
+}
+
+ns_id_t dplane_ctx_get_netconf_ns_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.ns_id;
+}
+
+void dplane_ctx_set_netconf_ifindex(struct zebra_dplane_ctx *ctx,
+ ifindex_t ifindex)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.ifindex = ifindex;
+}
+
+void dplane_ctx_set_netconf_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t ns_id)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.ns_id = ns_id;
+}
+
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mpls(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.mpls_val;
+}
+
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mcast(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.mcast_val;
+}
+
+void dplane_ctx_set_netconf_mpls(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.mpls_val = val;
+}
+
+void dplane_ctx_set_netconf_mcast(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.mcast_val = val;
+}
/*
* Retrieve the limit on the number of pending, unprocessed updates.
@@ -2306,13 +2387,29 @@ uint32_t dplane_get_in_queue_len(void)
}
/*
+ * Internal helper that copies information from a zebra ns object; this is
+ * called in the zebra main pthread context as part of dplane ctx init.
+ */
+static void ctx_info_from_zns(struct zebra_dplane_info *ns_info,
+ struct zebra_ns *zns)
+{
+ ns_info->ns_id = zns->ns_id;
+
+#if defined(HAVE_NETLINK)
+ ns_info->is_cmd = true;
+ ns_info->sock = zns->netlink_dplane_out.sock;
+ ns_info->seq = zns->netlink_dplane_out.seq;
+#endif /* NETLINK */
+}
+
+/*
* Common dataplane context init with zebra namespace info.
*/
static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
struct zebra_ns *zns,
bool is_update)
{
- dplane_info_from_zns(&(ctx->zd_ns_info), zns);
+ ctx_info_from_zns(&(ctx->zd_ns_info), zns); /* */
ctx->zd_is_update = is_update;
@@ -4844,21 +4941,6 @@ bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
}
-/*
- * Internal helper that copies information from a zebra ns object; this is
- * called in the zebra main pthread context as part of dplane ctx init.
- */
-static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
- struct zebra_ns *zns)
-{
- ns_info->ns_id = zns->ns_id;
-
-#if defined(HAVE_NETLINK)
- ns_info->is_cmd = true;
- ns_info->sock = zns->netlink_dplane_out.sock;
-#endif /* NETLINK */
-}
-
#ifdef HAVE_NETLINK
/*
* Callback when an OS (netlink) incoming event read is ready. This runs
@@ -4874,6 +4956,40 @@ static void dplane_incoming_read(struct thread *event)
thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
zi->info.sock, &zi->t_read);
}
+
+/*
+ * Callback in the dataplane pthread that requests info from the OS and
+ * initiates netlink reads.
+ */
+static void dplane_incoming_request(struct thread *event)
+{
+ struct dplane_zns_info *zi = THREAD_ARG(event);
+
+ /* Start read task */
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
+ zi->info.sock, &zi->t_read);
+
+ /* Send requests */
+ netlink_request_netconf(zi->info.sock);
+}
+
+/*
+ * Initiate requests for existing info from the OS. This is called by the
+ * main pthread, but we want all activity on the dplane netlink socket to
+ * take place on the dplane pthread, so we schedule an event to accomplish
+ * that.
+ */
+static void dplane_kernel_info_request(struct dplane_zns_info *zi)
+{
+ /* If we happen to encounter an enabled zns before the dplane
+ * pthread is running, we'll initiate this later on.
+ */
+ if (zdplane_info.dg_master)
+ thread_add_event(zdplane_info.dg_master,
+ dplane_incoming_request, zi, 0,
+ &zi->t_request);
+}
+
#endif /* HAVE_NETLINK */
/*
@@ -4917,11 +5033,10 @@ void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled)
zi->info.is_cmd = false;
zi->info.sock = zns->netlink_dplane_in.sock;
- /* Start read task for the dplane pthread. */
- if (zdplane_info.dg_master)
- thread_add_read(zdplane_info.dg_master,
- dplane_incoming_read, zi, zi->info.sock,
- &zi->t_read);
+ /* Initiate requests for existing info from the OS, and
+ * begin reading from the netlink socket.
+ */
+ dplane_kernel_info_request(zi);
#endif
} else if (zi) {
if (IS_ZEBRA_DEBUG_DPLANE)
@@ -4931,9 +5046,14 @@ void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled)
/* Stop reading, free memory */
zns_info_list_del(&zdplane_info.dg_zns_list, zi);
- if (zdplane_info.dg_master)
+ /* Stop any outstanding tasks */
+ if (zdplane_info.dg_master) {
+ thread_cancel_async(zdplane_info.dg_master,
+ &zi->t_request, NULL);
+
thread_cancel_async(zdplane_info.dg_master, &zi->t_read,
NULL);
+ }
XFREE(MTYPE_DP_NS, zi);
}
@@ -5113,6 +5233,14 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_intf_addr(ctx));
break;
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ zlog_debug("%s: ifindex %d, mpls %d, mcast %d",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_netconf_ifindex(ctx),
+ dplane_ctx_get_netconf_mpls(ctx),
+ dplane_ctx_get_netconf_mcast(ctx));
+ break;
}
}
@@ -5258,6 +5386,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
/* TODO -- error counters for incoming events? */
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
+ case DPLANE_OP_INTF_NETCONFIG:
break;
case DPLANE_OP_NONE:
@@ -5604,8 +5733,10 @@ static void dplane_check_shutdown_status(struct thread *event)
frr_each_safe (zns_info_list, &zdplane_info.dg_zns_list, zi) {
zns_info_list_del(&zdplane_info.dg_zns_list, zi);
- if (zdplane_info.dg_master)
+ if (zdplane_info.dg_master) {
thread_cancel(&zi->t_read);
+ thread_cancel(&zi->t_request);
+ }
XFREE(MTYPE_DP_NS, zi);
}
@@ -5935,11 +6066,12 @@ void zebra_dplane_start(void)
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
- /* Enqueue reads if necessary */
+ /* Enqueue requests and reads if necessary */
frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
#if defined(HAVE_NETLINK)
thread_add_read(zdplane_info.dg_master, dplane_incoming_read,
zi, zi->info.sock, &zi->t_read);
+ dplane_kernel_info_request(zi);
#endif
}
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index a7a5f99e45..29555d5b56 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -185,6 +185,9 @@ enum dplane_op_e {
/* Incoming interface address events */
DPLANE_OP_INTF_ADDR_ADD,
DPLANE_OP_INTF_ADDR_DEL,
+
+ /* Incoming interface config events */
+ DPLANE_OP_INTF_NETCONFIG,
};
/*
@@ -222,6 +225,22 @@ enum dplane_op_e {
#define DPLANE_BR_PORT_NON_DF (1 << 0)
+/* Definitions for the dplane 'netconf' apis, corresponding to the netlink
+ * NETCONF api.
+ * Sadly, netlink sends incremental updates, so its messages may contain
+ * just a single changed attribute, and not necessarily
+ * a complete snapshot of the attributes.
+ */
+enum dplane_netconf_status_e {
+ DPLANE_NETCONF_STATUS_UNKNOWN = 0,
+ DPLANE_NETCONF_STATUS_ENABLED,
+ DPLANE_NETCONF_STATUS_DISABLED
+};
+
+/* Some special ifindex values that may be part of the dplane netconf api. */
+#define DPLANE_NETCONF_IFINDEX_ALL -1
+#define DPLANE_NETCONF_IFINDEX_DEFAULT -2
+
/* Enable system route notifications */
void dplane_enable_sys_route_notifs(void);
@@ -564,6 +583,21 @@ dplane_ctx_gre_get_mtu(const struct zebra_dplane_ctx *ctx);
const struct zebra_l2info_gre *
dplane_ctx_gre_get_info(const struct zebra_dplane_ctx *ctx);
+/* Interface netconf info */
+ifindex_t dplane_ctx_get_netconf_ifindex(const struct zebra_dplane_ctx *ctx);
+ns_id_t dplane_ctx_get_netconf_ns_id(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_netconf_ifindex(struct zebra_dplane_ctx *ctx,
+ ifindex_t ifindex);
+void dplane_ctx_set_netconf_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t ns_id);
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mpls(const struct zebra_dplane_ctx *ctx);
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mcast(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_netconf_mpls(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val);
+void dplane_ctx_set_netconf_mcast(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val);
+
/* Namespace fd info - esp. for netlink communication */
const struct zebra_dplane_info *dplane_ctx_get_ns(
const struct zebra_dplane_ctx *ctx);
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index c1b104aec7..858309f3e7 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2989,6 +2989,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_GRE_SET:
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
+ case DPLANE_OP_INTF_NETCONFIG:
break;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index c386fc4871..af159da3cc 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -4320,6 +4320,10 @@ static void rib_process_dplane_results(struct thread *thread)
zebra_if_addr_update_ctx(ctx);
break;
+ case DPLANE_OP_INTF_NETCONFIG:
+ zebra_if_netconf_update_ctx(ctx);
+ break;
+
/* Some op codes not handled here */
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
diff --git a/zebra/zebra_script.c b/zebra/zebra_script.c
index 0e19376abe..4087749fd7 100644
--- a/zebra/zebra_script.c
+++ b/zebra/zebra_script.c
@@ -412,6 +412,8 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx)
lua_setfield(L, -2, "mtu");
}
lua_setfield(L, -2, "gre");
+
+ case DPLANE_OP_INTF_NETCONFIG: /*NYI*/
case DPLANE_OP_NONE:
break;
} /* Dispatch by op code */