|
|
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 |
|