[PATCH v5 1/4] util: add netlink bridge vlan filtering

Leigh Brown posted 4 patches 1 week, 5 days ago
[PATCH v5 1/4] util: add netlink bridge vlan filtering
Posted by Leigh Brown 1 week, 5 days ago
Enable capability to add and remove vlan filters for a standard
linux bridge using netlink.

New function virNetlinkBridgeVlanFilterSet can be used to add or
remove a vlan filter to a given bridge interface.

Signed-off-by: Leigh Brown <leigh@solinno.co.uk>
Reviewed-by: Laine Stump <laine@redhat.com>
---
 src/util/virnetlink.c | 66 +++++++++++++++++++++++++++++++++++++++++++
 src/util/virnetlink.h |  7 +++++
 2 files changed, 73 insertions(+)

diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 24cd69a385..206646d9d7 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -701,6 +701,72 @@ virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback)
     return 0;
 }
 
+/**
+ * virNetlinkBridgeVlanFilterSet:
+ *
+ * @ifname: name of the link
+ * @cmd:    netlink command, either RTM_SETLINK or RTM_DELLINK
+ * @flags:  flags to use when adding the vlan filter
+ * @vid:    vlan id to add or remove
+ * @error:  netlink error code
+ *
+ * Add or remove a vlan filter from an interface associated with a
+ * bridge.
+ *
+ * Returns 0 on success, -1 on error. Additionally, if the @error is
+ * non-zero, then a netlink failure occurred, but no error message
+ * is generated leaving it up to the caller to handle the condition.
+ */
+int
+virNetlinkBridgeVlanFilterSet(const char *ifname,
+                              int cmd,
+                              const unsigned short flags,
+                              const short vid,
+                              int *error)
+{
+    struct ifinfomsg ifm = { .ifi_family = PF_BRIDGE };
+    struct bridge_vlan_info vinfo = { .flags = flags, .vid = vid };
+    struct nlattr *afspec = NULL;
+    g_autoptr(virNetlinkMsg) nl_msg = NULL;
+    g_autofree struct nlmsghdr *resp = NULL;
+    unsigned int resp_len = 0;
+
+    *error = 0;
+
+    if (vid < 1 || vid > 4095) {
+        virReportError(ERANGE, _("vlanid out of range: %1$d"), vid);
+        return -1;
+    }
+
+    if (!(cmd == RTM_SETLINK || cmd == RTM_DELLINK)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Invalid vlan filter command %1$d"), cmd);
+        return -1;
+    }
+
+    if (virNetDevGetIndex(ifname, &ifm.ifi_index) < 0)
+        return -1;
+
+    nl_msg = virNetlinkMsgNew(cmd, NLM_F_REQUEST);
+
+    NETLINK_MSG_APPEND(nl_msg, sizeof(ifm), &ifm);
+
+    NETLINK_MSG_NEST_START(nl_msg, afspec, IFLA_AF_SPEC);
+    NETLINK_MSG_PUT(nl_msg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
+    NETLINK_MSG_NEST_END(nl_msg, afspec);
+
+    if (virNetlinkTalk(ifname, nl_msg, 0, 0, &resp, &resp_len, error, NULL) < 0)
+        return -1;
+
+    if (resp->nlmsg_type != NLMSG_ERROR && resp->nlmsg_type != NLMSG_DONE) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("malformed netlink response message"));
+        return -1;
+    }
+
+    return 0;
+}
+
 /**
  * virNetlinkGetNeighbor:
  *
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index 75192f645f..327fb426a1 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -25,6 +25,7 @@
 #if defined(WITH_LIBNL)
 
 # include <netlink/msg.h>
+# include <linux/if_bridge.h>
 
 typedef struct nl_msg virNetlinkMsg;
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetlinkMsg, nlmsg_free);
@@ -76,6 +77,12 @@ typedef int (*virNetlinkTalkFallback)(const char *ifname);
 
 int virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback);
 
+int virNetlinkBridgeVlanFilterSet(const char *ifname,
+                                  int cmd,
+                                  const unsigned short flags,
+                                  const short vid,
+                                  int *error);
+
 int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen);
 
 int virNetlinkDumpLink(const char *ifname, int ifindex,
-- 
2.39.5