commit 09778e09 switched from using ioctl(SIOCBRDELBR) for bridge
device deletion to using a netlink RTM_DELLINK message, which is the
more modern way to delete a bridge (and also doesn't require the
bridge to be ~IFF_UP to succeed). However, although older kernels
(e.g. 2.6.32, in RHEL6/CentOS6) support deleting *some* link types
with RTM_NEWLINK, they don't support deleting bridges, and there is no
compile-time way to figure this out.
This patch moves the body of the SIOCBRDELBR version of
virNetDevBridgeDelete() into a static function, calls the new function
from the original, and also calls the new function from the
RTM_DELLINK version if the RTM_DELLINK message generates an EOPNOTSUPP
error (this version of the function had to be copied essentially
verbatim from virNetlinkDelLink() rather than just calling
virNetlinkDelLink() in order to avoid logging the "Operation Not
Supported" error prior to retrying with SIOCBRDELBR)
This resolves a similar issue to that reported in:
https://bugzilla.redhat.com/show_bug.cgi?id=1252780
---
Note: this is the version that is simpler, but duplicates an entire
function
src/util/virnetdevbridge.c | 114 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 103 insertions(+), 11 deletions(-)
diff --git a/src/util/virnetdevbridge.c b/src/util/virnetdevbridge.c
index ae38901..779b068 100644
--- a/src/util/virnetdevbridge.c
+++ b/src/util/virnetdevbridge.c
@@ -558,16 +558,9 @@ int virNetDevBridgeCreate(const char *brname)
*
* Returns 0 in case of success or an errno code in case of failure.
*/
-#if defined(__linux__) && defined(HAVE_LIBNL)
-int virNetDevBridgeDelete(const char *brname)
-{
- /* If netlink is available, use it, as it is successful at
- * deleting a bridge even if it is currently IFF_UP.
- */
- return virNetlinkDelLink(brname);
-}
-#elif defined(HAVE_STRUCT_IFREQ) && defined(SIOCBRDELBR)
-int virNetDevBridgeDelete(const char *brname)
+#if defined(HAVE_STRUCT_IFREQ) && defined(SIOCBRDELBR)
+static int
+virNetDevBridgeDeleteWithIoctl(const char *brname)
{
int fd = -1;
int ret = -1;
@@ -587,8 +580,107 @@ int virNetDevBridgeDelete(const char *brname)
VIR_FORCE_CLOSE(fd);
return ret;
}
+#endif
+
+
+#if defined(__linux__) && defined(HAVE_LIBNL)
+int
+virNetDevBridgeDelete(const char *brname)
+{
+ /* If netlink is available, use it, as it is successful at
+ * deleting a bridge even if it is currently IFF_UP.
+ */
+ int rc = -1;
+ struct nlmsghdr *resp = NULL;
+ struct nlmsgerr *err;
+ struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
+ unsigned int recvbuflen;
+ struct nl_msg *nl_msg;
+
+ nl_msg = nlmsg_alloc_simple(RTM_DELLINK,
+ NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+ if (!nl_msg) {
+ virReportOOMError();
+ return -1;
+ }
+
+ if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
+ goto buffer_too_small;
+
+ if (nla_put(nl_msg, IFLA_IFNAME, strlen(brname)+1, brname) < 0)
+ goto buffer_too_small;
+
+ if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
+ NETLINK_ROUTE, 0) < 0) {
+ goto cleanup;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
+ goto malformed_resp;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (err->error) {
+ case 0:
+ break;
+ case -EOPNOTSUPP:
+# if defined(HAVE_STRUCT_IFREQ) && defined(SIOCBRDELBR)
+ /* fallback to ioctl if netlink doesn't support deleting
+ * bridges
+ */
+ ignore_value(virNetDevSetOnline(brname, false));
+ rc = virNetDevBridgeDeleteWithIoctl(brname);
+ goto cleanup;
+# endif
+ /* intentionally fall through if virNetDevBridgeDeleteWithIoctl()
+ * isn't available.
+ */
+ default:
+ virReportSystemError(-err->error,
+ _("error destroying bridge interface %s"),
+ brname);
+ goto cleanup;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+ default:
+ goto malformed_resp;
+ }
+
+ rc = 0;
+ cleanup:
+ nlmsg_free(nl_msg);
+ VIR_FREE(resp);
+ return rc;
+
+ malformed_resp:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ goto cleanup;
+ buffer_too_small:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("allocated netlink buffer is too small"));
+ goto cleanup;
+}
+
+
+#elif defined(HAVE_STRUCT_IFREQ) && defined(SIOCBRDELBR)
+int
+virNetDevBridgeDelete(const char *brname)
+{
+ return virNetDevBridgeDeleteWithIoctl(brname);
+}
+
+
#elif defined(HAVE_STRUCT_IFREQ) && defined(SIOCIFDESTROY)
-int virNetDevBridgeDelete(const char *brname)
+int
+virNetDevBridgeDelete(const char *brname)
{
int s;
struct ifreq ifr;
--
2.1.0