render / rpms / libvirt

Forked from rpms/libvirt 11 months ago
Clone
9119d9
From c08448e5a29385b324a5f3ac603080d28647ea5c Mon Sep 17 00:00:00 2001
9119d9
Message-Id: <c08448e5a29385b324a5f3ac603080d28647ea5c@dist-git>
9119d9
From: Laine Stump <laine@laine.org>
9119d9
Date: Mon, 15 Dec 2014 10:51:26 -0500
9119d9
Subject: [PATCH] util: functions to manage bridge fdb (forwarding database)
9119d9
9119d9
This is part of the fix for:
9119d9
9119d9
  https://bugzilla.redhat.com/show_bug.cgi?id=1099210
9119d9
9119d9
These two functions use netlink RTM_NEWNEIGH and RTM_DELNEIGH messages
9119d9
to add and delete entries from a bridge's fdb. The bridge itself is
9119d9
not referenced in the arguments to the functions, only the name of the
9119d9
device that is attached to the bridge (since a device can only be
9119d9
attached to one bridge at a time, and must be attached for this
9119d9
function to make sense, the kernel easily infers which bridge's fdb is
9119d9
being modified by looking at the device name/index).
9119d9
9119d9
(cherry picked from commit 19a5474d04a497dd12928b694c4c3ab9621c9552)
9119d9
9119d9
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
9119d9
---
9119d9
 src/libvirt_private.syms   |   2 +
9119d9
 src/util/virnetdevbridge.c | 147 +++++++++++++++++++++++++++++++++++++++++++++
9119d9
 src/util/virnetdevbridge.h |  16 +++++
9119d9
 3 files changed, 165 insertions(+)
9119d9
9119d9
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
9119d9
index 2b7526a..ec795aa 100644
9119d9
--- a/src/libvirt_private.syms
9119d9
+++ b/src/libvirt_private.syms
9119d9
@@ -1630,6 +1630,8 @@ virNetDevBandwidthUpdateRate;
9119d9
 virNetDevBridgeAddPort;
9119d9
 virNetDevBridgeCreate;
9119d9
 virNetDevBridgeDelete;
9119d9
+virNetDevBridgeFDBAdd;
9119d9
+virNetDevBridgeFDBDel;
9119d9
 virNetDevBridgeGetSTP;
9119d9
 virNetDevBridgeGetSTPDelay;
9119d9
 virNetDevBridgeGetVlanFiltering;
9119d9
diff --git a/src/util/virnetdevbridge.c b/src/util/virnetdevbridge.c
9119d9
index 1f3f221..5f31343 100644
9119d9
--- a/src/util/virnetdevbridge.c
9119d9
+++ b/src/util/virnetdevbridge.c
9119d9
@@ -37,6 +37,9 @@
9119d9
 #include <netinet/in.h>
9119d9
 
9119d9
 #ifdef __linux__
9119d9
+# if defined(HAVE_LIBNL)
9119d9
+#  include "virnetlink.h"
9119d9
+# endif
9119d9
 # include <linux/sockios.h>
9119d9
 # include <linux/param.h>     /* HZ                 */
9119d9
 # if NETINET_LINUX_WORKAROUND
9119d9
@@ -914,3 +917,147 @@ virNetDevBridgeSetVlanFiltering(const char *brname ATTRIBUTE_UNUSED,
9119d9
     return -1;
9119d9
 }
9119d9
 #endif
9119d9
+
9119d9
+
9119d9
+#if defined(__linux__) && defined(HAVE_LIBNL)
9119d9
+/* virNetDevBridgeFDBAddDel:
9119d9
+ * @mac: the MAC address being added to the table
9119d9
+ * @ifname: name of the port (interface) of the bridge that wants this MAC
9119d9
+ * @flags: any of virNetDevBridgeFDBFlags ORed together.
9119d9
+ * @isAdd: true if adding the entry, fals if deleting
9119d9
+ *
9119d9
+ * Use netlink RTM_NEWNEIGH and RTM_DELNEIGH messages to add and
9119d9
+ * delete entries from a bridge's fdb. The bridge itself is not
9119d9
+ * referenced in the arguments to the function, only the name of the
9119d9
+ * device that is attached to the bridge (since a device can only be
9119d9
+ * attached to one bridge at a time, and must be attached for this
9119d9
+ * function to make sense, the kernel easily infers which bridge's fdb
9119d9
+ * is being modified by looking at the device name/index).
9119d9
+ *
9119d9
+ * Attempting to add an existing entry, or delete a non-existing entry
9119d9
+ * *is* an error.
9119d9
+ *
9119d9
+ * returns 0 on success, -1 on failure.
9119d9
+ */
9119d9
+static int
9119d9
+virNetDevBridgeFDBAddDel(const virMacAddr *mac, const char *ifname,
9119d9
+                         unsigned int flags, bool isAdd)
9119d9
+{
9119d9
+    int ret = -1;
9119d9
+    struct nlmsghdr *resp = NULL;
9119d9
+    struct nlmsgerr *err;
9119d9
+    unsigned int recvbuflen;
9119d9
+    struct nl_msg *nl_msg;
9119d9
+    struct ndmsg ndm = { .ndm_family = PF_BRIDGE, .ndm_state = NUD_NOARP };
9119d9
+
9119d9
+    if (virNetDevGetIndex(ifname, &ndm.ndm_ifindex) < 0)
9119d9
+        return -1;
9119d9
+
9119d9
+    if (flags & VIR_NETDEVBRIDGE_FDB_FLAG_ROUTER)
9119d9
+        ndm.ndm_flags |= NTF_ROUTER;
9119d9
+    if (flags & VIR_NETDEVBRIDGE_FDB_FLAG_SELF)
9119d9
+        ndm.ndm_flags |= NTF_SELF;
9119d9
+    if (flags & VIR_NETDEVBRIDGE_FDB_FLAG_MASTER)
9119d9
+        ndm.ndm_flags |= NTF_MASTER;
9119d9
+    /* default self (same as iproute2's bridge command */
9119d9
+    if (!(ndm.ndm_flags & (NTF_MASTER | NTF_SELF)))
9119d9
+        ndm.ndm_flags |= NTF_SELF;
9119d9
+
9119d9
+    if (flags & VIR_NETDEVBRIDGE_FDB_FLAG_PERMANENT)
9119d9
+        ndm.ndm_state |= NUD_PERMANENT;
9119d9
+    if (flags & VIR_NETDEVBRIDGE_FDB_FLAG_TEMP)
9119d9
+        ndm.ndm_state |= NUD_REACHABLE;
9119d9
+    /* default permanent, same as iproute2's bridge command */
9119d9
+    if (!(ndm.ndm_state & (NUD_PERMANENT | NUD_REACHABLE)))
9119d9
+        ndm.ndm_state |= NUD_PERMANENT;
9119d9
+
9119d9
+    nl_msg = nlmsg_alloc_simple(isAdd ? RTM_NEWNEIGH : RTM_DELNEIGH,
9119d9
+                                NLM_F_REQUEST |
9119d9
+                                (isAdd ? (NLM_F_CREATE | NLM_F_EXCL) : 0));
9119d9
+    if (!nl_msg) {
9119d9
+        virReportOOMError();
9119d9
+        return -1;
9119d9
+    }
9119d9
+
9119d9
+    if (nlmsg_append(nl_msg, &ndm, sizeof(ndm), NLMSG_ALIGNTO) < 0)
9119d9
+        goto buffer_too_small;
9119d9
+    if (nla_put(nl_msg, NDA_LLADDR, VIR_MAC_BUFLEN, mac) < 0)
9119d9
+        goto buffer_too_small;
9119d9
+
9119d9
+    /* NB: this message can also accept a Destination IP, a port, a
9119d9
+     * vlan tag, and a via (see iproute2/bridge/fdb.c:fdb_modify()),
9119d9
+     * but those aren't required for our application
9119d9
+     */
9119d9
+
9119d9
+    if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
9119d9
+                          NETLINK_ROUTE, 0) < 0) {
9119d9
+        goto cleanup;
9119d9
+    }
9119d9
+    if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
9119d9
+        goto malformed_resp;
9119d9
+
9119d9
+    switch (resp->nlmsg_type) {
9119d9
+    case NLMSG_ERROR:
9119d9
+        err = (struct nlmsgerr *)NLMSG_DATA(resp);
9119d9
+        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
9119d9
+            goto malformed_resp;
9119d9
+        if (err->error) {
9119d9
+            virReportSystemError(-err->error,
9119d9
+                                 _("error adding fdb entry for %s"), ifname);
9119d9
+            goto cleanup;
9119d9
+        }
9119d9
+        break;
9119d9
+    case NLMSG_DONE:
9119d9
+        break;
9119d9
+
9119d9
+    default:
9119d9
+        goto malformed_resp;
9119d9
+    }
9119d9
+
9119d9
+    ret = 0;
9119d9
+ cleanup:
9119d9
+    nlmsg_free(nl_msg);
9119d9
+    VIR_FREE(resp);
9119d9
+    return ret;
9119d9
+
9119d9
+ malformed_resp:
9119d9
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
+                   _("malformed netlink response message"));
9119d9
+    goto cleanup;
9119d9
+
9119d9
+ buffer_too_small:
9119d9
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
9119d9
+                   _("allocated netlink buffer is too small"));
9119d9
+    goto cleanup;
9119d9
+}
9119d9
+
9119d9
+
9119d9
+#else
9119d9
+static int
9119d9
+virNetDevBridgeFDBAddDel(const virMacAddr *mac ATTRIBUTE_UNUSED,
9119d9
+                         const char *ifname ATTRIBUTE_UNUSED,
9119d9
+                         unsigned int fdbFlags ATTRIBUTE_UNUSED,
9119d9
+                         bool isAdd ATTRIBUTE_UNUSED)
9119d9
+{
9119d9
+    virReportSystemError(ENOSYS, "%s",
9119d9
+                         _("Unable to add/delete fdb entries on this platform"));
9119d9
+    return -1;
9119d9
+}
9119d9
+
9119d9
+
9119d9
+#endif
9119d9
+
9119d9
+int
9119d9
+virNetDevBridgeFDBAdd(const virMacAddr *mac, const char *ifname,
9119d9
+                      unsigned int flags)
9119d9
+{
9119d9
+    return virNetDevBridgeFDBAddDel(mac, ifname, flags, true);
9119d9
+}
9119d9
+
9119d9
+
9119d9
+int
9119d9
+virNetDevBridgeFDBDel(const virMacAddr *mac, const char *ifname,
9119d9
+                      unsigned int flags)
9119d9
+{
9119d9
+    return virNetDevBridgeFDBAddDel(mac, ifname, flags, false);
9119d9
+}
9119d9
diff --git a/src/util/virnetdevbridge.h b/src/util/virnetdevbridge.h
9119d9
index 8188a2f..141f231 100644
9119d9
--- a/src/util/virnetdevbridge.h
9119d9
+++ b/src/util/virnetdevbridge.h
9119d9
@@ -24,6 +24,7 @@
9119d9
 # define __VIR_NETDEV_BRIDGE_H__
9119d9
 
9119d9
 # include "internal.h"
9119d9
+# include "virmacaddr.h"
9119d9
 
9119d9
 int virNetDevBridgeCreate(const char *brname)
9119d9
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
9119d9
@@ -77,4 +78,19 @@ int virNetDevBridgePortSetUnicastFlood(const char *brname,
9119d9
                                        bool enable)
9119d9
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
9119d9
 
9119d9
+typedef enum {
9119d9
+    VIR_NETDEVBRIDGE_FDB_FLAG_ROUTER    = (1 << 0),
9119d9
+    VIR_NETDEVBRIDGE_FDB_FLAG_SELF      = (1 << 1),
9119d9
+    VIR_NETDEVBRIDGE_FDB_FLAG_MASTER    = (1 << 2),
9119d9
+
9119d9
+    VIR_NETDEVBRIDGE_FDB_FLAG_PERMANENT = (1 << 3),
9119d9
+    VIR_NETDEVBRIDGE_FDB_FLAG_TEMP      = (1 << 4),
9119d9
+} virNetDevBridgeFDBFlags;
9119d9
+
9119d9
+int virNetDevBridgeFDBAdd(const virMacAddr *mac, const char *ifname,
9119d9
+                          unsigned int flags)
9119d9
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
9119d9
+int virNetDevBridgeFDBDel(const virMacAddr *mac, const char *ifname,
9119d9
+                          unsigned int flags)
9119d9
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
9119d9
 #endif /* __VIR_NETDEV_BRIDGE_H__ */
9119d9
-- 
9119d9
2.2.0
9119d9