]> git.proxmox.com Git - mirror_frr.git/commitdiff
zebra: handle STP state change for SVD per vlan ID
authorStephen Worley <sworley@nvidia.com>
Wed, 28 Apr 2021 19:45:29 +0000 (15:45 -0400)
committerStephen Worley <sworley@nvidia.com>
Mon, 13 Feb 2023 23:12:05 +0000 (18:12 -0500)
Read in STP state changes for a Single Vxlan Device
via bridge vlan netlink messages. Map the vlanid to a
VNI in the SVD table and treat it similar to how
we handle proto down of the Vxlan device traditionally
in a non-SVD device scenario.

Forwarding == Interface UP
Blocking == Interface DOWN

Signed-off-by: Stephen Worley <sworley@nvidia.com>
zebra/if_netlink.c
zebra/if_netlink.h
zebra/kernel_netlink.c
zebra/rt.h
zebra/rtread_netlink.c
zebra/rtread_sysctl.c
zebra/zebra_ns.c
zebra/zebra_vxlan_if.c
zebra/zebra_vxlan_if.h

index 02b9ab9d7f6186c2c250250c2312c0939e491f18..22dd2b956d0e859542c05540c0348d7972933303 100644 (file)
@@ -2522,4 +2522,236 @@ int netlink_tunneldump_read(struct zebra_ns *zns)
 
        return 0;
 }
+
+static const char *port_state2str(uint8_t state)
+{
+       switch (state) {
+       case BR_STATE_DISABLED:
+               return "DISABLED";
+       case BR_STATE_LISTENING:
+               return "LISTENING";
+       case BR_STATE_LEARNING:
+               return "LEARNING";
+       case BR_STATE_FORWARDING:
+               return "FORWARDING";
+       case BR_STATE_BLOCKING:
+               return "BLOCKING";
+       }
+
+       return "UNKNOWN";
+}
+
+static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id,
+                                  uint8_t state)
+{
+       struct zebra_vxlan_vni *vnip;
+
+       vnip = zebra_vxlan_if_vlanid_vni_find(zif, id);
+
+       if (!vnip) {
+               if (IS_ZEBRA_DEBUG_VXLAN)
+                       zlog_debug(
+                               "Cannot find VNI for VID (%u) IF %s for vlan state update",
+                               id, zif->ifp->name);
+
+               return;
+       }
+
+       switch (state) {
+       case BR_STATE_FORWARDING:
+               zebra_vxlan_if_vni_up(zif->ifp, vnip);
+               break;
+       case BR_STATE_BLOCKING:
+               zebra_vxlan_if_vni_down(zif->ifp, vnip);
+               break;
+       case BR_STATE_DISABLED:
+       case BR_STATE_LISTENING:
+       case BR_STATE_LEARNING:
+       default:
+               /* Not used for anything at the moment */
+               break;
+       }
+}
+
+static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start,
+                                      uint16_t id_end, uint8_t state)
+{
+       struct zebra_if *zif;
+
+       zif = (struct zebra_if *)ifp->info;
+
+       if (!zif)
+               return;
+
+       for (uint16_t i = id_start; i <= id_end; i++)
+               vxlan_vni_state_change(zif, i, state);
+}
+
+/**
+ * netlink_vlan_change() - Read in change about vlans from the kernel
+ *
+ * @h:         Netlink message header
+ * @ns_id:     Namspace id
+ * @startup:   Are we reading under startup conditions?
+ *
+ * Return:     Result status
+ */
+int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+       int len, rem;
+       struct interface *ifp;
+       struct br_vlan_msg *bvm;
+       struct bridge_vlan_info *vinfo;
+       struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1] = {};
+       struct rtattr *attr;
+       uint8_t state;
+       uint32_t vrange;
+       int type;
+
+       /* We only care about state changes for now */
+       if (!(h->nlmsg_type == RTM_NEWVLAN))
+               return 0;
+
+       len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct br_vlan_msg));
+       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(sizeof(struct br_vlan_msg)));
+               return -1;
+       }
+
+       bvm = NLMSG_DATA(h);
+
+       if (bvm->family != AF_BRIDGE)
+               return 0;
+
+       ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), bvm->ifindex);
+       if (!ifp) {
+               zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update",
+                          bvm->ifindex);
+               return 0;
+       }
+
+       if (!IS_ZEBRA_IF_VXLAN(ifp)) {
+               if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("Ignoring non-vxlan IF (%s) for vlan update",
+                                  ifp->name);
+
+               return 0;
+       }
+
+       if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN)
+               zlog_debug("%s %s IF %s NS %u",
+                          nl_msg_type_to_str(h->nlmsg_type),
+                          nl_family_to_str(bvm->family), ifp->name, ns_id);
+
+       /* Loop over "ALL" BRIDGE_VLANDB_ENTRY */
+       rem = len;
+       for (attr = BRVLAN_RTA(bvm); RTA_OK(attr, rem);
+            attr = RTA_NEXT(attr, rem)) {
+               vinfo = NULL;
+               state = 0;
+               vrange = 0;
+
+               type = attr->rta_type & NLA_TYPE_MASK;
+
+               if (type != BRIDGE_VLANDB_ENTRY)
+                       continue;
+
+               /* Parse nested entry data */
+               netlink_parse_rtattr_nested(vtb, BRIDGE_VLANDB_ENTRY_MAX, attr);
+
+               /* It must have info for the ID */
+               if (!vtb[BRIDGE_VLANDB_ENTRY_INFO])
+                       continue;
+
+               vinfo = (struct bridge_vlan_info *)RTA_DATA(
+                       vtb[BRIDGE_VLANDB_ENTRY_INFO]);
+
+               /*
+                * We only care about state info, if there is none, just ignore
+                * it.
+                */
+               if (!vtb[BRIDGE_VLANDB_ENTRY_STATE])
+                       continue;
+
+               state = *(uint8_t *)RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
+
+               if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
+                       vrange = *(uint32_t *)RTA_DATA(
+                               vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
+
+               if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) {
+                       if (vrange)
+                               zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s",
+                                          vinfo->vid, vrange,
+                                          port_state2str(state));
+                       else
+                               zlog_debug("VLANDB_ENTRY: VID (%u) state=%s",
+                                          vinfo->vid, port_state2str(state));
+               }
+
+               vlan_id_range_state_change(
+                       ifp, vinfo->vid, (vrange ? vrange : vinfo->vid), state);
+       }
+
+       return 0;
+}
+
+/**
+ * netlink_request_vlan() - Request vlan information from the kernel
+ * @zns:       Zebra namespace
+ * @family:    AF_* netlink family
+ * @type:      RTM_* type
+ *
+ * Return:     Result status
+ */
+static int netlink_request_vlan(struct zebra_ns *zns, int family, int type)
+{
+       struct {
+               struct nlmsghdr n;
+               struct br_vlan_msg bvm;
+               char buf[256];
+       } req;
+
+       /* Form the request, specifying filter (rtattr) if needed. */
+       memset(&req, 0, sizeof(req));
+       req.n.nlmsg_type = type;
+       req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg));
+       req.bvm.family = family;
+
+       nl_attr_put32(&req.n, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS,
+                     BRIDGE_VLANDB_DUMPF_STATS);
+
+       return netlink_request(&zns->netlink_cmd, &req);
+}
+
+/**
+ * netlink_vlan_read() - Vlan read function using netlink interface
+ *
+ * @zns:       Zebra name space
+ *
+ * Return:     Result status
+ * Only called at bootstrap time.
+ */
+int netlink_vlan_read(struct zebra_ns *zns)
+{
+       int ret;
+       struct zebra_dplane_info dp_info;
+
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
+
+       /* Get bridg vlan info */
+       ret = netlink_request_vlan(zns, PF_BRIDGE, RTM_GETVLAN);
+       if (ret < 0)
+               return ret;
+
+       ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd,
+                                &dp_info, 0, 1);
+
+       return ret;
+}
+
 #endif /* GNU_LINUX */
index 21ae1713be9f7beee2757be86e0fe7c1e9ff2eac..f215968657b51df2186c0432c1ce335b84e47394 100644 (file)
@@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
 extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
 extern int interface_lookup_netlink(struct zebra_ns *zns);
 
+extern int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
+extern int netlink_vlan_read(struct zebra_ns *zns);
+
 extern ssize_t netlink_intf_msg_encode(uint16_t cmd,
                                       const struct zebra_dplane_ctx *ctx,
                                       void *buf, size_t buflen);
index 45fb24a210c1835393f6dbdc64449a2a7d4cdfd0..2fd1543b3174410ed8e04f752337af648c790dac 100644 (file)
@@ -124,6 +124,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
                                           {RTM_NEWTFILTER, "RTM_NEWTFILTER"},
                                           {RTM_DELTFILTER, "RTM_DELTFILTER"},
                                           {RTM_GETTFILTER, "RTM_GETTFILTER"},
+                                          {RTM_NEWVLAN, "RTM_NEWVLAN"},
+                                          {RTM_DELVLAN, "RTM_DELVLAN"},
+                                          {RTM_GETVLAN, "RTM_GETVLAN"},
                                           {0}};
 
 static const struct message rtproto_str[] = {
@@ -432,6 +435,10 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
        case RTM_NEWTFILTER:
        case RTM_DELTFILTER:
                return netlink_tfilter_change(h, ns_id, startup);
+       case RTM_NEWVLAN:
+               return netlink_vlan_change(h, ns_id, startup);
+       case RTM_DELVLAN:
+               return netlink_vlan_change(h, ns_id, startup);
 
        /* Messages handled in the dplane thread */
        case RTM_NEWADDR:
index f17d11a5fb4b36f59db816bb2c327019b942f9a0..4ebf47975430ebedbb952bdf07fa4fb350d9de21 100644 (file)
@@ -129,6 +129,7 @@ extern void kernel_update_multi(struct dplane_ctx_list_head *ctx_list);
  * Called by the dplane pthread to read incoming OS messages and dispatch them.
  */
 int kernel_dplane_read(struct zebra_dplane_info *info);
+extern void vlan_read(struct zebra_ns *zns);
 
 #ifdef __cplusplus
 }
index a85d8a48940bfcb6f8d7da24352371eced22e8c9..f9d4605562d77a2c21cfaeda1596391966a0f7de 100644 (file)
@@ -28,6 +28,7 @@
 #include "zebra/zebra_pbr.h"
 #include "zebra/zebra_tc.h"
 #include "zebra/rt_netlink.h"
+#include "zebra/if_netlink.h"
 #include "zebra/rule_netlink.h"
 #include "zebra/tc_netlink.h"
 
@@ -84,4 +85,9 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns)
        netlink_qdisc_read(zns);
 }
 
+void vlan_read(struct zebra_ns *zns)
+{
+       netlink_vlan_read(zns);
+}
+
 #endif /* GNU_LINUX */
index c8a89b3a55e8a36d88049486dc63f01f56f4a851..f5b918c0223e6f54a984a8a9880d3de08fd0b068 100644 (file)
@@ -118,4 +118,8 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns)
 {
 }
 
+void vlan_read(struct zebra_ns *zns)
+{
+}
+
 #endif /* !defined(GNU_LINUX) */
index 7b076b8a4a9fa7a30743c0be512150c839053946..457a4955b4258b6ed61bfcaac79819659002b8b2 100644 (file)
@@ -127,6 +127,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
        zebra_dplane_ns_enable(zns, true);
        interface_list(zns);
        route_read(zns);
+
+       vlan_read(zns);
        kernel_read_pbr_rules(zns);
        kernel_read_tc_qdisc(zns);
 
index df9ad7795f589f1a793af434bceb69a31c19ad39..e01d06c71adcd6758a7aaa37561a5156f18461b7 100644 (file)
@@ -672,6 +672,36 @@ struct zebra_vxlan_vni *zebra_vxlan_if_vni_find(const struct zebra_if *zif,
        return vnip;
 }
 
+static int zif_vlanid_vni_walker(struct zebra_if *zif,
+                                struct zebra_vxlan_vni *vnip, void *arg)
+{
+       struct zebra_vxlan_if_vlan_ctx *ctx;
+
+       ctx = (struct zebra_vxlan_if_vlan_ctx *)arg;
+
+       if (vnip->access_vlan == ctx->vid) {
+               ctx->vni = vnip;
+               return HASHWALK_ABORT;
+       }
+
+       return HASHWALK_CONTINUE;
+}
+
+struct zebra_vxlan_vni *zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif,
+                                                      vlanid_t vid)
+{
+       struct zebra_vxlan_if_vlan_ctx ctx = {};
+
+       if (!IS_ZEBRA_VXLAN_IF_SVD(zif))
+               return NULL;
+
+       ctx.vid = vid;
+
+       zebra_vxlan_if_vni_walk(zif, zif_vlanid_vni_walker, &ctx);
+
+       return ctx.vni;
+}
+
 void zebra_vxlan_if_vni_iterate(struct zebra_if *zif,
                                int (*func)(struct zebra_if *zif,
                                            struct zebra_vxlan_vni *, void *),
index a09e3dbe8cb1bbd2c62b59dd8abed2c653f4376f..e5ee6dc2d9042102e75a0efcaf788a5498c144f7 100644 (file)
@@ -50,6 +50,8 @@ extern int zebra_vxlan_if_vni_table_create(struct zebra_if *zif);
 extern int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif);
 extern struct zebra_vxlan_vni *
 zebra_vxlan_if_vni_find(const struct zebra_if *zif, vni_t vni);
+extern struct zebra_vxlan_vni *
+zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, vlanid_t vni);
 extern void zebra_vxlan_if_vni_iterate(struct zebra_if *zif,
                                       int (*func)(struct zebra_if *zif,
                                                   struct zebra_vxlan_vni *,