[libvirt] [PATCH] qemu: allow snapshotting of sheepdog and rbd disks
by Josh Durgin
Signed-off-by: Josh Durgin <josh.durgin(a)dreamhost.com>
---
.gnulib | 2 +-
src/qemu/qemu_driver.c | 14 ++++++++++----
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/.gnulib b/.gnulib
index d5612c7..6b93d00 160000
--- a/.gnulib
+++ b/.gnulib
@@ -1 +1 @@
-Subproject commit d5612c714c87555f1059d71d347e20271dced322
+Subproject commit 6b93d00f5410ec183e3a70ebf8e418e3b1bb0191
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 7e6d59c..fc537df 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -9571,12 +9571,18 @@ qemuDomainSnapshotIsAllowed(virDomainObjPtr vm)
* that succeed as well
*/
for (i = 0; i < vm->def->ndisks; i++) {
- if ((vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_LUN) ||
- (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
- STRNEQ_NULLABLE(vm->def->disks[i]->driverType, "qcow2"))) {
+ virDomainDiskDefPtr disk = vm->def->disks[i];
+ if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK &&
+ (disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG ||
+ disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD))
+ continue;
+
+ if ((disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) ||
+ (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
+ STRNEQ_NULLABLE(disk->driverType, "qcow2"))) {
qemuReportError(VIR_ERR_OPERATION_INVALID,
_("Disk '%s' does not support snapshotting"),
- vm->def->disks[i]->src);
+ disk->src);
return false;
}
}
--
1.7.5.4
12 years, 6 months
[libvirt] [PATCH] storage backend: Add RBD (RADOS Block Device) support
by Wido den Hollander
This patch adds support for a new storage backend with RBD support.
RBD is the RADOS Block Device and is part of the Ceph distributed storage system.
It comes in two flavours: Qemu-RBD and Kernel RBD, this storage backend only supports
Qemu-RBD, thus limiting the use of this storage driver to Qemu only.
To function this backend relies on librbd and librados being present on the local system.
The backend also supports Cephx authentication for safe authentication with the Ceph cluster.
For storing credentials it uses the build-in secret mechanism of libvirt.
Signed-off-by: Wido den Hollander <wido(a)widodh.nl>
---
configure.ac | 20 ++
include/libvirt/libvirt.h.in | 1 +
src/Makefile.am | 9 +
src/conf/storage_conf.c | 197 ++++++++++---
src/conf/storage_conf.h | 16 +
src/storage/storage_backend.c | 6 +
src/storage/storage_backend_rbd.c | 465 ++++++++++++++++++++++++++++++
src/storage/storage_backend_rbd.h | 30 ++
tests/storagepoolxml2xmlin/pool-rbd.xml | 11 +
tests/storagepoolxml2xmlout/pool-rbd.xml | 15 +
tools/virsh.c | 7 +
11 files changed, 734 insertions(+), 43 deletions(-)
create mode 100644 src/storage/storage_backend_rbd.c
create mode 100644 src/storage/storage_backend_rbd.h
create mode 100644 tests/storagepoolxml2xmlin/pool-rbd.xml
create mode 100644 tests/storagepoolxml2xmlout/pool-rbd.xml
diff --git a/configure.ac b/configure.ac
index 32cc8d0..b693e5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1743,6 +1743,8 @@ AC_ARG_WITH([storage-mpath],
AC_HELP_STRING([--with-storage-mpath], [with mpath backend for the storage driver @<:@default=check@:>@]),[],[with_storage_mpath=check])
AC_ARG_WITH([storage-disk],
AC_HELP_STRING([--with-storage-disk], [with GPartd Disk backend for the storage driver @<:@default=check@:>@]),[],[with_storage_disk=check])
+AC_ARG_WITH([storage-rbd],
+ AC_HELP_STRING([--with-storage-rbd], [with RADOS Block Device backend for the storage driver @<:@default=check@:>@]),[],[with_storage_rbd=check])
if test "$with_libvirtd" = "no"; then
with_storage_dir=no
@@ -1752,6 +1754,7 @@ if test "$with_libvirtd" = "no"; then
with_storage_scsi=no
with_storage_mpath=no
with_storage_disk=no
+ with_storage_rbd=no
fi
if test "$with_storage_dir" = "yes" ; then
AC_DEFINE_UNQUOTED([WITH_STORAGE_DIR], 1, [whether directory backend for storage driver is enabled])
@@ -1910,6 +1913,22 @@ if test "$with_storage_mpath" = "check"; then
fi
AM_CONDITIONAL([WITH_STORAGE_MPATH], [test "$with_storage_mpath" = "yes"])
+if test "$with_storage_rbd" = "yes" || test "$with_storage_rbd" = "check"; then
+ AC_CHECK_HEADER([rbd/librbd.h], [LIBRBD_FOUND=yes; break;])
+
+ LIBRBD_LIBS="-lrbd -lrados -lcrypto"
+
+ if test "$LIBRBD_FOUND" = "yes"; then
+ with_storage_rbd=yes
+ LIBS="$LIBS $LIBRBD_LIBS"
+ else
+ with_storage_rbd=no
+ fi
+
+ AC_DEFINE_UNQUOTED([WITH_STORAGE_RBD], 1, [wether RBD backend for storage driver is enabled])
+fi
+AM_CONDITIONAL([WITH_STORAGE_RBD], [test "$with_storage_rbd" = "yes"])
+
LIBPARTED_CFLAGS=
LIBPARTED_LIBS=
if test "$with_storage_disk" = "yes" ||
@@ -2673,6 +2692,7 @@ AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi])
AC_MSG_NOTICE([ SCSI: $with_storage_scsi])
AC_MSG_NOTICE([ mpath: $with_storage_mpath])
AC_MSG_NOTICE([ Disk: $with_storage_disk])
+AC_MSG_NOTICE([ RBD: $with_storage_rbd])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])
AC_MSG_NOTICE([])
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 499dcd4..ee1d5ec 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2324,6 +2324,7 @@ typedef enum {
VIR_STORAGE_VOL_FILE = 0, /* Regular file based volumes */
VIR_STORAGE_VOL_BLOCK = 1, /* Block based volumes */
VIR_STORAGE_VOL_DIR = 2, /* Directory-passthrough based volume */
+ VIR_STORAGE_VOL_NETWORK = 3, /* Network volumes like RBD (RADOS Block Device) */
#ifdef VIR_ENUM_SENTINELS
VIR_STORAGE_VOL_LAST
diff --git a/src/Makefile.am b/src/Makefile.am
index a2aae9d..e4457c3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -498,6 +498,9 @@ STORAGE_DRIVER_MPATH_SOURCES = \
STORAGE_DRIVER_DISK_SOURCES = \
storage/storage_backend_disk.h storage/storage_backend_disk.c
+STORAGE_DRIVER_RBD_SOURCES = \
+ storage/storage_backend_rbd.h storage/storage_backend_rbd.c
+
STORAGE_HELPER_DISK_SOURCES = \
storage/parthelper.c
@@ -1040,6 +1043,11 @@ if WITH_STORAGE_DISK
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
endif
+if WITH_STORAGE_RBD
+libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_RBD_SOURCES)
+libvirt_la_LIBADD += $(LIBRBD_LIBS)
+endif
+
if WITH_NODE_DEVICES
# Needed to keep automake quiet about conditionals
if WITH_DRIVER_MODULES
@@ -1139,6 +1147,7 @@ EXTRA_DIST += \
$(STORAGE_DRIVER_SCSI_SOURCES) \
$(STORAGE_DRIVER_MPATH_SOURCES) \
$(STORAGE_DRIVER_DISK_SOURCES) \
+ $(STORAGE_DRIVER_RBD_SOURCES) \
$(NODE_DEVICE_DRIVER_SOURCES) \
$(NODE_DEVICE_DRIVER_HAL_SOURCES) \
$(NODE_DEVICE_DRIVER_UDEV_SOURCES) \
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index bdf6218..2a0b5eb 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -52,7 +52,7 @@ VIR_ENUM_IMPL(virStoragePool,
VIR_STORAGE_POOL_LAST,
"dir", "fs", "netfs",
"logical", "disk", "iscsi",
- "scsi", "mpath")
+ "scsi", "mpath", "rbd")
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
VIR_STORAGE_POOL_FS_LAST,
@@ -110,6 +110,7 @@ enum {
VIR_STORAGE_POOL_SOURCE_ADAPTER = (1<<3),
VIR_STORAGE_POOL_SOURCE_NAME = (1<<4),
VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN = (1<<5),
+ VIR_STORAGE_POOL_SOURCE_NETWORK = (1<<6),
};
@@ -194,6 +195,15 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
+ { .poolType = VIR_STORAGE_POOL_RBD,
+ .poolOptions = {
+ .flags = (VIR_STORAGE_POOL_SOURCE_NETWORK |
+ VIR_STORAGE_POOL_SOURCE_NAME),
+ },
+ .volOptions = {
+ .formatToString = virStoragePoolFormatDiskTypeToString,
+ }
+ },
{ .poolType = VIR_STORAGE_POOL_MPATH,
.volOptions = {
.formatToString = virStoragePoolFormatDiskTypeToString,
@@ -277,6 +287,11 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source)
return;
VIR_FREE(source->host.name);
+ for (i = 0 ; i < source->nhost ; i++) {
+ VIR_FREE(source->hosts[i].name);
+ }
+ VIR_FREE(source->hosts);
+
for (i = 0 ; i < source->ndevice ; i++) {
VIR_FREE(source->devices[i].freeExtents);
VIR_FREE(source->devices[i].path);
@@ -293,6 +308,12 @@ virStoragePoolSourceClear(virStoragePoolSourcePtr source)
VIR_FREE(source->auth.chap.login);
VIR_FREE(source->auth.chap.passwd);
}
+
+ if (source->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ VIR_FREE(source->auth.cephx.username);
+ VIR_FREE(source->auth.cephx.secret.uuid);
+ VIR_FREE(source->auth.cephx.secret.usage);
+ }
}
void
@@ -395,6 +416,27 @@ virStoragePoolDefParseAuthChap(xmlXPathContextPtr ctxt,
}
static int
+virStoragePoolDefParseAuthCephx(xmlXPathContextPtr ctxt,
+ virStoragePoolAuthCephxPtr auth) {
+ auth->username = virXPathString("string(./auth/@username)", ctxt);
+ if (auth->username == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth username attribute"));
+ return -1;
+ }
+
+ auth->secret.uuid = virXPathString("string(./auth/secret/@uuid)", ctxt);
+ auth->secret.usage = virXPathString("string(./auth/secret/@usage)", ctxt);
+ if (auth->secret.uuid == NULL && auth->secret.usage == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing auth secret uuid or usage attribute"));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
virStoragePoolSourcePtr source,
int pool_type,
@@ -414,6 +456,12 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
}
source->name = virXPathString("string(./name)", ctxt);
+ if (pool_type == VIR_STORAGE_POOL_RBD && source->name == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ _("%s"), "missing mandatory 'name' field for RBD pool name");
+ VIR_FREE(source->name);
+ goto cleanup;
+ }
if (options->formatFromString) {
char *format = virXPathString("string(./format/@type)", ctxt);
@@ -431,17 +479,39 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
VIR_FREE(format);
}
- source->host.name = virXPathString("string(./host/@name)", ctxt);
- port = virXPathString("string(./host/@port)", ctxt);
- if (port) {
- if (virStrToLong_i(port, NULL, 10, &source->host.port) < 0) {
- virStorageReportError(VIR_ERR_XML_ERROR,
- _("Invalid port number: %s"),
- port);
+ source->nhost = virXPathNodeSet("./host", ctxt, &nodeset);
+
+ if (source->nhost) {
+ if (VIR_ALLOC_N(source->hosts, source->nhost) < 0) {
+ virReportOOMError();
goto cleanup;
}
- }
+ for (i = 0 ; i < source->nhost ; i++) {
+ char *name = virXMLPropString(nodeset[i], "name");
+ if (name == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing storage pool host name"));
+ goto cleanup;
+ }
+ source->hosts[i].name = name;
+ if(i == 0 && source->nhost == 1)
+ source->host.name = name;
+
+ port = virXMLPropString(nodeset[i], "port");
+ if (port) {
+ if (virStrToLong_i(port, NULL, 10, &source->hosts[i].port) < 0) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ _("Invalid port number: %s"),
+ port);
+ goto cleanup;
+ } else {
+ if (i == 0 && source->nhost == 1)
+ virStrToLong_i(port, NULL, 10, &source->host.port);
+ }
+ }
+ }
+ }
source->initiator.iqn = virXPathString("string(./initiator/iqn/@name)", ctxt);
@@ -478,6 +548,8 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
} else {
if (STREQ(authType, "chap")) {
source->authType = VIR_STORAGE_POOL_AUTH_CHAP;
+ } else if (STREQ(authType, "ceph")) {
+ source->authType = VIR_STORAGE_POOL_AUTH_CEPHX;
} else {
virStorageReportError(VIR_ERR_XML_ERROR,
_("unknown auth type '%s'"),
@@ -491,6 +563,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
goto cleanup;
}
+ if (source->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ if (virStoragePoolDefParseAuthCephx(ctxt, &source->auth.cephx) < 0)
+ goto cleanup;
+ }
+
source->vendor = virXPathString("string(./vendor/@name)", ctxt);
source->product = virXPathString("string(./product/@name)", ctxt);
@@ -682,6 +759,15 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
+ if (options->flags & VIR_STORAGE_POOL_SOURCE_NETWORK) {
+ if (!ret->source.host.name && ret->source.nhost < 1) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s",
+ _("missing storage pool source network host name"));
+ goto cleanup;
+ }
+ }
+
if (options->flags & VIR_STORAGE_POOL_SOURCE_DIR) {
if (!ret->source.dir) {
virStorageReportError(VIR_ERR_XML_ERROR,
@@ -717,20 +803,22 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
- if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
- virStorageReportError(VIR_ERR_XML_ERROR,
- "%s", _("missing storage pool target path"));
- goto cleanup;
- }
- ret->target.path = virFileSanitizePath(tmppath);
- VIR_FREE(tmppath);
- if (!ret->target.path)
- goto cleanup;
-
+ /* When we are working with a virtual disk we can skip the target path and permissions */
+ if (!(options->flags & VIR_STORAGE_POOL_SOURCE_NETWORK)) {
+ if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
+ virStorageReportError(VIR_ERR_XML_ERROR,
+ "%s", _("missing storage pool target path"));
+ goto cleanup;
+ }
+ ret->target.path = virFileSanitizePath(tmppath);
+ VIR_FREE(tmppath);
+ if (!ret->target.path)
+ goto cleanup;
- if (virStorageDefParsePerms(ctxt, &ret->target.perms,
- "./target/permissions", 0700) < 0)
- goto cleanup;
+ if (virStorageDefParsePerms(ctxt, &ret->target.perms,
+ "./target/permissions", 0700) < 0)
+ goto cleanup;
+ }
return ret;
@@ -800,12 +888,15 @@ virStoragePoolSourceFormat(virBufferPtr buf,
int i, j;
virBufferAddLit(buf," <source>\n");
- if ((options->flags & VIR_STORAGE_POOL_SOURCE_HOST) &&
- src->host.name) {
- virBufferAsprintf(buf, " <host name='%s'", src->host.name);
- if (src->host.port)
- virBufferAsprintf(buf, " port='%d'", src->host.port);
- virBufferAddLit(buf, "/>\n");
+ if ((options->flags & VIR_STORAGE_POOL_SOURCE_HOST ||
+ options->flags & VIR_STORAGE_POOL_SOURCE_NETWORK) &&
+ src->nhost) {
+ for (i = 0; i < src->nhost; i++) {
+ virBufferAsprintf(buf, " <host name='%s'", src->hosts[i].name);
+ if (src->hosts[i].port)
+ virBufferAsprintf(buf, " port='%d'", src->hosts[i].port);
+ virBufferAddLit(buf, "/>\n");
+ }
}
if ((options->flags & VIR_STORAGE_POOL_SOURCE_DEVICE) &&
@@ -860,6 +951,23 @@ virStoragePoolSourceFormat(virBufferPtr buf,
src->auth.chap.login,
src->auth.chap.passwd);
+ if (src->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
+ virBufferAsprintf(buf," <auth username='%s' type='ceph'>\n",
+ src->auth.cephx.username);
+
+ virBufferAsprintf(buf," %s", "<secret");
+ if (src->auth.cephx.secret.uuid != NULL) {
+ virBufferAsprintf(buf," uuid='%s'", src->auth.cephx.secret.uuid);
+ }
+
+ if (src->auth.cephx.secret.usage != NULL) {
+ virBufferAsprintf(buf," usage='%s'", src->auth.cephx.secret.usage);
+ }
+ virBufferAsprintf(buf,"%s", "/>\n");
+
+ virBufferAsprintf(buf," %s", "</auth>\n");
+ }
+
if (src->vendor != NULL) {
virBufferEscapeString(buf," <vendor name='%s'/>\n", src->vendor);
}
@@ -907,25 +1015,28 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) {
if (virStoragePoolSourceFormat(&buf, options, &def->source) < 0)
goto cleanup;
- virBufferAddLit(&buf," <target>\n");
+ /* RBD devices are no local block devs nor files, so it doesn't have a target */
+ if (def->type != VIR_STORAGE_POOL_RBD) {
+ virBufferAddLit(&buf," <target>\n");
- if (def->target.path)
- virBufferAsprintf(&buf," <path>%s</path>\n", def->target.path);
+ if (def->target.path)
+ virBufferAsprintf(&buf," <path>%s</path>\n", def->target.path);
- virBufferAddLit(&buf," <permissions>\n");
- virBufferAsprintf(&buf," <mode>0%o</mode>\n",
- def->target.perms.mode);
- virBufferAsprintf(&buf," <owner>%d</owner>\n",
- def->target.perms.uid);
- virBufferAsprintf(&buf," <group>%d</group>\n",
- def->target.perms.gid);
+ virBufferAddLit(&buf," <permissions>\n");
+ virBufferAsprintf(&buf," <mode>0%o</mode>\n",
+ def->target.perms.mode);
+ virBufferAsprintf(&buf," <owner>%d</owner>\n",
+ def->target.perms.uid);
+ virBufferAsprintf(&buf," <group>%d</group>\n",
+ def->target.perms.gid);
- if (def->target.perms.label)
- virBufferAsprintf(&buf," <label>%s</label>\n",
- def->target.perms.label);
+ if (def->target.perms.label)
+ virBufferAsprintf(&buf," <label>%s</label>\n",
+ def->target.perms.label);
- virBufferAddLit(&buf," </permissions>\n");
- virBufferAddLit(&buf," </target>\n");
+ virBufferAddLit(&buf," </permissions>\n");
+ virBufferAddLit(&buf," </target>\n");
+ }
virBufferAddLit(&buf,"</pool>\n");
if (virBufferError(&buf))
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index 1ef9295..6b91ca5 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -120,6 +120,7 @@ enum virStoragePoolType {
VIR_STORAGE_POOL_ISCSI, /* iSCSI targets */
VIR_STORAGE_POOL_SCSI, /* SCSI HBA */
VIR_STORAGE_POOL_MPATH, /* Multipath devices */
+ VIR_STORAGE_POOL_RBD, /* RADOS Block Device */
VIR_STORAGE_POOL_LAST,
};
@@ -137,6 +138,7 @@ enum virStoragePoolDeviceType {
enum virStoragePoolAuthType {
VIR_STORAGE_POOL_AUTH_NONE,
VIR_STORAGE_POOL_AUTH_CHAP,
+ VIR_STORAGE_POOL_AUTH_CEPHX,
};
typedef struct _virStoragePoolAuthChap virStoragePoolAuthChap;
@@ -146,6 +148,15 @@ struct _virStoragePoolAuthChap {
char *passwd;
};
+typedef struct _virStoragePoolAuthCephx virStoragePoolAuthCephx;
+typedef virStoragePoolAuthCephx *virStoragePoolAuthCephxPtr;
+struct _virStoragePoolAuthCephx {
+ char *username;
+ struct {
+ char *uuid;
+ char *usage;
+ } secret;
+};
/*
* For remote pools, info on how to reach the host
@@ -215,6 +226,10 @@ struct _virStoragePoolSource {
/* An optional host */
virStoragePoolSourceHost host;
+ /* Or multiple hosts */
+ int nhost;
+ virStoragePoolSourceHostPtr hosts;
+
/* And either one or more devices ... */
int ndevice;
virStoragePoolSourceDevicePtr devices;
@@ -234,6 +249,7 @@ struct _virStoragePoolSource {
int authType; /* virStoragePoolAuthType */
union {
virStoragePoolAuthChap chap;
+ virStoragePoolAuthCephx cephx;
} auth;
/* Vendor of the source */
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index caac2f8..e2e9b51 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -77,6 +77,9 @@
#if WITH_STORAGE_DIR
# include "storage_backend_fs.h"
#endif
+#if WITH_STORAGE_RBD
+# include "storage_backend_rbd.h"
+#endif
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -103,6 +106,9 @@ static virStorageBackendPtr backends[] = {
#if WITH_STORAGE_DISK
&virStorageBackendDisk,
#endif
+#if WITH_STORAGE_RBD
+ &virStorageBackendRBD,
+#endif
NULL
};
diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c
new file mode 100644
index 0000000..056059a
--- /dev/null
+++ b/src/storage/storage_backend_rbd.c
@@ -0,0 +1,465 @@
+/*
+ * storage_backend_rbd.c: storage backend for RBD (RADOS Block Device) handling
+ *
+ * Copyright (C) 2012 Wido den Hollander
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Wido den Hollander <wido(a)widodh.nl>
+ */
+
+#include <config.h>
+
+#include "virterror_internal.h"
+#include "storage_backend_rbd.h"
+#include "storage_conf.h"
+#include "util.h"
+#include "memory.h"
+#include "logging.h"
+#include "base64.h"
+#include "rados/librados.h"
+#include "rbd/librbd.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+struct _virStorageBackendRBDState {
+ rados_t cluster;
+ rados_ioctx_t ioctx;
+ time_t starttime;
+};
+
+typedef struct _virStorageBackendRBDState virStorageBackendRBDState;
+typedef virStorageBackendRBDState virStorageBackendRBDStatePtr;
+
+static int virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr *ptr,
+ virConnectPtr conn,
+ virStoragePoolObjPtr pool)
+{
+ int ret = -1;
+ unsigned char *secret_value;
+ size_t secret_value_size;
+ char *rados_key;
+ virBuffer mon_host = VIR_BUFFER_INITIALIZER;
+ virSecretPtr secret = NULL;
+
+ VIR_DEBUG("Found Cephx username: %s",
+ pool->def->source.auth.cephx.username);
+
+ if (pool->def->source.auth.cephx.username != NULL) {
+ VIR_DEBUG("Using cephx authorization");
+ if (rados_create(&ptr->cluster,
+ pool->def->source.auth.cephx.username) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to initialize RADOS"));
+ goto cleanup;
+ }
+
+ if (pool->def->source.auth.cephx.secret.uuid != NULL) {
+ VIR_DEBUG("Looking up secret by UUID: %s",
+ pool->def->source.auth.cephx.secret.uuid);
+ secret = virSecretLookupByUUIDString(conn,
+ pool->def->source.auth.cephx.secret.uuid);
+ }
+
+ if (pool->def->source.auth.cephx.secret.usage != NULL) {
+ VIR_DEBUG("Looking up secret by usage: %s",
+ pool->def->source.auth.cephx.secret.usage);
+ secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_CEPH,
+ pool->def->source.auth.cephx.secret.usage);
+ }
+
+ if (secret == NULL) {
+ virStorageReportError(VIR_ERR_NO_SECRET,
+ _("failed to find the secret"));
+ goto cleanup;
+ }
+
+ secret_value = virSecretGetValue(secret, &secret_value_size, 0);
+ base64_encode_alloc((char *)secret_value,
+ secret_value_size, &rados_key);
+ memset(secret_value, 0, secret_value_size);
+
+ if (rados_key == NULL) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to decode the RADOS key"));
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Found cephx key: %s", rados_key);
+ if (rados_conf_set(ptr->cluster, "key", rados_key) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "rados_key");
+ goto cleanup;
+ }
+
+ memset(rados_key, 0, strlen(rados_key));
+
+ if (rados_conf_set(ptr->cluster, "auth_supported", "cephx") < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "auth_supported");
+ goto cleanup;
+ }
+ } else {
+ VIR_DEBUG("Not using cephx authorization");
+ if (rados_conf_set(ptr->cluster, "auth_supported", "none") < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "auth_supported");
+ goto cleanup;
+ }
+ if (rados_create(&ptr->cluster, NULL) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RADOS cluster"));
+ goto cleanup;
+ }
+ }
+
+ VIR_DEBUG("Found %d RADOS cluster monitors in the pool configuration",
+ pool->def->source.nhost);
+
+ int i;
+ for (i = 0; i < pool->def->source.nhost; i++) {
+ if (pool->def->source.hosts[i].name != NULL &&
+ !pool->def->source.hosts[i].port) {
+ virBufferAsprintf(&mon_host, "%s:6789,",
+ pool->def->source.hosts[i].name);
+ } else if (pool->def->source.hosts[i].name != NULL &&
+ pool->def->source.hosts[i].port) {
+ virBufferAsprintf(&mon_host, "%s:%d,",
+ pool->def->source.hosts[i].name,
+ pool->def->source.hosts[i].port);
+ } else {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("received malformed monitor, check the XML definition"));
+ }
+ }
+
+ char *mon_buff = virBufferContentAndReset(&mon_host);
+ VIR_DEBUG("RADOS mon_host has been set to: %s", mon_buff);
+ if (rados_conf_set(ptr->cluster, "mon_host", mon_buff) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to set RADOS option: %s"),
+ "mon_host");
+ goto cleanup;
+ }
+
+ ptr->starttime = time(0);
+ if (rados_connect(ptr->cluster) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to connect to the RADOS monitor on: %s"),
+ virBufferContentAndReset(&mon_host));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(secret_value);
+ VIR_FREE(rados_key);
+ virSecretFree(secret);
+ virBufferFreeAndReset(&mon_host);
+ return ret;
+}
+
+static int virStorageBackendRBDCloseRADOSConn(virStorageBackendRBDStatePtr ptr)
+{
+ int ret = 0;
+
+ if (ptr.ioctx != NULL) {
+ VIR_DEBUG("Closing RADOS IoCTX");
+ rados_ioctx_destroy(ptr.ioctx);
+ ret = -1;
+ }
+
+ if (ptr.cluster != NULL) {
+ VIR_DEBUG("Closing RADOS connection");
+ rados_shutdown(ptr.cluster);
+ ret = -2;
+ }
+
+ time_t runtime = time(0) - ptr.starttime;
+ VIR_DEBUG("RADOS connection existed for %ld seconds", runtime);
+
+ return ret;
+}
+
+static int volStorageBackendRBDRefreshVolInfo(virStorageVolDefPtr vol,
+ virStoragePoolObjPtr pool,
+ virStorageBackendRBDStatePtr ptr)
+{
+ int ret = -1;
+ rbd_image_t image;
+ if (rbd_open(ptr.ioctx, vol->name, &image, NULL) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to open the RBD image '%s'"),
+ vol->name);
+ goto cleanup;
+ }
+
+ rbd_image_info_t info;
+ if (rbd_stat(image, &info, sizeof(info)) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to stat the RBD image"));
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Refreshed RBD image %s/%s (size: %llu obj_size: %llu num_objs: %llu)",
+ pool->def->source.name, vol->name, (unsigned long long)info.size,
+ (unsigned long long)info.obj_size,
+ (unsigned long long)info.num_objs);
+
+ vol->capacity = info.size;
+ vol->allocation = info.obj_size * info.num_objs;
+ vol->type = VIR_STORAGE_VOL_NETWORK;
+
+ VIR_FREE(vol->target.path);
+ if (virAsprintf(&vol->target.path, "rbd:%s/%s",
+ pool->def->source.name,
+ vol->name) == -1) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ VIR_FREE(vol->key);
+ if (virAsprintf(&vol->key, "%s/%s",
+ pool->def->source.name,
+ vol->name) == -1) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ rbd_close(image);
+ return ret;
+}
+
+static int virStorageBackendRBDRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool)
+{
+ size_t max_size = 1024;
+ int ret = -1;
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name, &ptr.ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ struct rados_cluster_stat_t stat;
+ if (rados_cluster_stat(ptr.cluster, &stat) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to stat the RADOS cluster"));
+ goto cleanup;
+ }
+
+ struct rados_pool_stat_t poolstat;
+ if (rados_ioctx_pool_stat(ptr.ioctx, &poolstat) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to stat the RADOS pool '%s'"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ pool->def->capacity = stat.kb * 1024;
+ pool->def->available = stat.kb_avail * 1024;
+ pool->def->allocation = poolstat.num_bytes;
+
+ int num_images, i;
+ char *names, *name = NULL;
+
+ if (VIR_ALLOC_N(names, 1024) < 0)
+ goto cleanup;
+
+ int len = rbd_list(ptr.ioctx, names, &max_size);
+
+ for (i = 0, num_images = 0, name = names; name < names + len; i++) {
+
+ if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) {
+ virStoragePoolObjClearVols(pool);
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ virStorageVolDefPtr vol;
+ if (VIR_ALLOC(vol) < 0)
+ goto cleanup;
+
+ vol->name = strdup(name);
+ if (vol->name == NULL)
+ goto cleanup;
+ name += strlen(name) + 1;
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0)
+ goto cleanup;
+
+ pool->volumes.objs[pool->volumes.count++] = vol;
+ }
+
+ VIR_DEBUG("Refreshed RBD pool %s (kb: %llu kb_avail: %llu num_bytes: %llu num_images: %d)",
+ pool->def->source.name, (unsigned long long)stat.kb,
+ (unsigned long long)stat.kb_avail,
+ (unsigned long long)poolstat.num_bytes, pool->volumes.count);
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(names);
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDDeleteVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol,
+ unsigned int flags)
+{
+ int ret = -1;
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+
+ VIR_DEBUG("Removing RBD image %s/%s", pool->def->source.name, vol->name);
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name, &ptr.ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (rbd_remove(ptr.ioctx, vol->name) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to remove volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDCreateVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol)
+{
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+ int order = 0;
+ int ret = -1;
+
+ VIR_DEBUG("Creating RBD image %s/%s with size %llu",
+ pool->def->source.name,
+ vol->name, vol->capacity);
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name,&ptr.ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (vol->target.encryption != NULL) {
+ virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("storage pool does not support encrypted volumes"));
+ goto cleanup;
+ }
+
+ if (rbd_create(ptr.ioctx, vol->name, vol->capacity, &order) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create volume '%s/%s'"),
+ pool->def->source.name,
+ vol->name);
+ goto cleanup;
+ }
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+static int virStorageBackendRBDRefreshVol(virConnectPtr conn,
+ virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+ virStorageVolDefPtr vol)
+{
+ virStorageBackendRBDStatePtr ptr;
+ ptr.cluster = NULL;
+ ptr.ioctx = NULL;
+ int ret = -1;
+
+ if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
+ goto cleanup;
+ }
+
+ if (rados_ioctx_create(ptr.cluster,
+ pool->def->source.name, &ptr.ioctx) < 0) {
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
+ pool->def->source.name);
+ goto cleanup;
+ }
+
+ if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virStorageBackendRBDCloseRADOSConn(ptr);
+ return ret;
+}
+
+virStorageBackend virStorageBackendRBD = {
+ .type = VIR_STORAGE_POOL_RBD,
+
+ .refreshPool = virStorageBackendRBDRefreshPool,
+ .createVol = virStorageBackendRBDCreateVol,
+ .refreshVol = virStorageBackendRBDRefreshVol,
+ .deleteVol = virStorageBackendRBDDeleteVol,
+};
diff --git a/src/storage/storage_backend_rbd.h b/src/storage/storage_backend_rbd.h
new file mode 100644
index 0000000..2ae2513
--- /dev/null
+++ b/src/storage/storage_backend_rbd.h
@@ -0,0 +1,30 @@
+/*
+ * storage_backend_rbd.h: storage backend for RBD (RADOS Block Device) handling
+ *
+ * Copyright (C) 2012 Wido den Hollander
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Wido den Hollander <wido(a)widodh.nl>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_RBD_H__
+# define __VIR_STORAGE_BACKEND_RBD_H__
+
+# include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendRBD;
+
+#endif /* __VIR_STORAGE_BACKEND_RBD_H__ */
diff --git a/tests/storagepoolxml2xmlin/pool-rbd.xml b/tests/storagepoolxml2xmlin/pool-rbd.xml
new file mode 100644
index 0000000..c9d4790
--- /dev/null
+++ b/tests/storagepoolxml2xmlin/pool-rbd.xml
@@ -0,0 +1,11 @@
+<pool type='rbd'>
+ <name>ceph</name>
+ <source>
+ <name>rbd</name>
+ <host name='localhost' port='6789'/>
+ <host name='localhost' port='6790'/>
+ <auth username='admin' type='ceph'>
+ <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87' usage='admin'/>
+ </auth>
+ </source>
+</pool>
diff --git a/tests/storagepoolxml2xmlout/pool-rbd.xml b/tests/storagepoolxml2xmlout/pool-rbd.xml
new file mode 100644
index 0000000..fa7fb34
--- /dev/null
+++ b/tests/storagepoolxml2xmlout/pool-rbd.xml
@@ -0,0 +1,15 @@
+<pool type='rbd'>
+ <name>ceph</name>
+ <uuid>47c1faee-0207-e741-f5ae-d9b019b98fe2</uuid>
+ <capacity unit='bytes'>0</capacity>
+ <allocation unit='bytes'>0</allocation>
+ <available unit='bytes'>0</available>
+ <source>
+ <host name='localhost' port='6789'/>
+ <host name='localhost' port='6790'/>
+ <name>rbd</name>
+ <auth username='admin' type='ceph'>
+ <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87' usage='admin'/>
+ </auth>
+ </source>
+</pool>
diff --git a/tools/virsh.c b/tools/virsh.c
index 8ee25c3..632c75e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -11894,6 +11894,10 @@ cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
break;
+ case VIR_STORAGE_VOL_NETWORK:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
+ break;
+
default:
vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
}
@@ -19852,6 +19856,9 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
#ifdef WITH_STORAGE_LVM
vshPrint(ctl, " LVM");
#endif
+#ifdef WITH_STORAGE_RBD
+ vshPrint(ctl, " RBD");
+#endif
vshPrint(ctl, "\n");
vshPrint(ctl, "%s", _(" Miscellaneous:"));
--
1.7.0.4
12 years, 6 months
[libvirt] [libvirt-TCK][PATCH] use 'raw' format as the format of backing file of qcow2 image
by Guannan Ren
If we don't explicitly specify the format of backing file,
it should use raw by default, if so, libvirt's security drivers
should *not* grant access to the last.img file & the guest
should not see the last.img data. That is the purpose of testing.
---
scripts/qemu/205-qcow2-double-backing-file.t | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/scripts/qemu/205-qcow2-double-backing-file.t b/scripts/qemu/205-qcow2-double-backing-file.t
index d3a5e33..ad54c7c 100644
--- a/scripts/qemu/205-qcow2-double-backing-file.t
+++ b/scripts/qemu/205-qcow2-double-backing-file.t
@@ -114,7 +114,6 @@ SKIP: {
my $volmainxml = $tck->generic_volume("tck-main", "qcow2", 1024*1024*50)
->backing_file($pathback)
- ->backing_format("qcow2")
->allocation(0)->as_xml;
--
1.7.7.5
12 years, 7 months
[libvirt] Memory leak on list_domains
by Carlos Rodrigues
Hi,
I have some problems on my application with memory leak when call
list_domains method.
I'm using libvirt 0.8.3 and Sys::Virt 0.2.4 Perl Module.
Does anyone have any idea what's the problem?
Regards,
--
Carlos Rodrigues <cmar(a)eurotux.com>
Eurotux Informática, S.A. [http://eurotux.com]
12 years, 7 months
[libvirt] [PATCH] Do not enforce source type of console[0]
by Jan Kiszka
If console[0] is an alias for serial[0], do not enforce the former to
have a PTY source type. This breaks serial consoles on stdio and makes
no sense.
Signed-off-by: Jan Kiszka <jan.kiszka(a)siemens.com>
---
src/qemu/qemu_process.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index ef311d1..216b594 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1376,7 +1376,6 @@ qemuProcessFindCharDevicePTYs(virDomainObjPtr vm,
if ((ret = virDomainChrSourceDefCopy(&chr->source,
&((vm->def->serials[0])->source))) != 0)
return ret;
- chr->source.type = VIR_DOMAIN_CHR_TYPE_PTY;
} else {
if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY &&
chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO) {
--
1.7.3.4
12 years, 7 months
[libvirt] Start of freeze for libvirt-0.9.11 and availability of rc1
by Daniel Veillard
As scheduled, we are entering the freeze for 0.9.11.
I think most API additions are now commited to git upstream (please
raise your voice quickly if you see something missing !)
I have made a release candidate 1 tarball (and associated rpms) at
ftp://libvirt.org/libvirt/libvirt-0.9.11-rc1.tar.gz
and the git tree is tagged.
I think I will make an release candidate 2 tarball on Wednesday, and
I'm hoping for a final release on Friday, or maybe Monday if there are
issues found.
Please give it a try ! Stability and portability feedback are really
welcome as we didn't had a release in Feb and the risk of having
something messed up is slightly higher than usual !
thanks in advance,
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
12 years, 7 months
[libvirt] [PATCH] qemu, util fix netlink callback registration for migration
by D. Herrendoerfer
From: "D. Herrendoerfer" <d.herrendoerfer(a)herrendoerfer.name>
This patch adds a netlink callback when migrating a VEPA enabled
virtual machine.
It fixes a Bug where a VM would not request a port association when
it was cleared by lldpad.
This patch requires the latest git version of lldpad to work.
Signed-off-by: D. Herrendoerfer <d.herrendoerfer(a)herrendoerfer.name>
---
src/qemu/qemu_migration.c | 6 ++++++
src/util/virnetdevmacvlan.c | 14 +++++++++++++-
src/util/virnetdevmacvlan.h | 8 ++++++++
3 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 77d40c0..7a8a7c4 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -2654,6 +2654,12 @@ qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) {
def->uuid,
VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH, false) < 0)
goto err_exit;
+
+ if (virNetDevMacVLanVPortProfileRegisterCallback(net->ifname, net->mac,
+ virDomainNetGetActualDirectDev(net), def->uuid,
+ virDomainNetGetActualVirtPortProfile(net),
+ VIR_NETDEV_VPORT_PROFILE_OP_CREATE))
+ goto err_exit;
}
last_good_net = i;
}
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 90888b0..b259e00 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -769,7 +769,7 @@ virNetDevMacVLanVPortProfileDestroyCallback(int watch ATTRIBUTE_UNUSED,
virNetlinkCallbackDataFree((virNetlinkCallbackDataPtr)opaque);
}
-static int
+int
virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
const unsigned char *macaddress,
const char *linkdev,
@@ -1125,4 +1125,16 @@ int virNetDevMacVLanRestartWithVPortProfile(const char *cr_ifname ATTRIBUTE_UNUS
_("Cannot create macvlan devices on this platform"));
return -1;
}
+
+int virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname ATTRIBUTE_UNUSED,
+ const unsigned char *macaddress ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ const unsigned char *vmuuid ATTRIBUTE_UNUSED,
+ virNetDevVPortProfilePtr virtPortProfile ATTRIBUTE_UNUSED,
+ enum virNetDevVPortProfileOp vmOp ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Cannot create macvlan devices on this platform"));
+ return -1;
+}
#endif /* ! WITH_MACVTAP */
diff --git a/src/util/virnetdevmacvlan.h b/src/util/virnetdevmacvlan.h
index 14640cf..2299f1d 100644
--- a/src/util/virnetdevmacvlan.h
+++ b/src/util/virnetdevmacvlan.h
@@ -84,4 +84,12 @@ int virNetDevMacVLanRestartWithVPortProfile(const char *cr_ifname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
+int virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
+ const unsigned char *macaddress ,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ enum virNetDevVPortProfileOp vmOp)
+ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
+ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
#endif /* __UTIL_MACVTAP_H__ */
--
1.7.7.6
12 years, 7 months
[libvirt] help for libvirt0.9.8 compile
by hero
my redhat6.1 has a libvirt0.8.7, and I got a libvirt0.9.8.tar that I want to update the old one.
when I use
'./configure --prefix=/usr --libdir=/usr/lib64 --localstatedir=/var --sysconfdir=/etc'
it siad 'configure: error: You must install device-mapper-devel/libdevmapper >= 1.0.0 to compile libvirt'
please tell me how to do if you know.
thanks.
best wishes!
SunYongGang
12 years, 7 months
[libvirt] [libvirt PATCHv8 1/1] add DHCP snooping
by David L Stevens
This patch adds DHCP snooping support to libvirt. The learning method for
IP addresses is specified by setting the "ip_learning" variable to one of
"any" [default] (existing IP learning code), "none" (static only addresses)
or "dhcp" (DHCP snooping).
Active leases are saved in a lease file and reloaded on restart or HUP.
Changes since v7:
- renamed functions as suggested
- collected local state into "virNWFilterSnoopState" struct
- cleaned up include file list
- misc code cleanups per review comments
Signed-off-by: David L Stevens <dlstevens(a)us.ibm.com>
---
docs/formatnwfilter.html.in | 17 +
po/POTFILES.in | 1 +
src/Makefile.am | 2 +
src/nwfilter/nwfilter_dhcpsnoop.c | 1197 ++++++++++++++++++++++++++++++++
src/nwfilter/nwfilter_dhcpsnoop.h | 38 +
src/nwfilter/nwfilter_driver.c | 6 +
src/nwfilter/nwfilter_gentech_driver.c | 59 ++-
7 files changed, 1307 insertions(+), 13 deletions(-)
create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.c
create mode 100644 src/nwfilter/nwfilter_dhcpsnoop.h
diff --git a/docs/formatnwfilter.html.in b/docs/formatnwfilter.html.in
index 9cb7644..ad10765 100644
--- a/docs/formatnwfilter.html.in
+++ b/docs/formatnwfilter.html.in
@@ -2189,6 +2189,23 @@
<br/><br/>
In case a VM is resumed after suspension or migrated, IP address
detection will be restarted.
+ <br/><br/>
+ Variable <i>ip_learning</i> may be used to specify
+ the IP address learning method. Valid values are <i>any</i>, <i>dhcp</i>,
+ or <i>none</i>. The default value is <i>any</i>, meaning that libvirt
+ may use any packet to determine the address in use by a VM. A value of
+ <i>dhcp</i> specifies that libvirt should only honor DHCP server-assigned
+ addresses with valid leases. If <i>ip_learning</i> is set to <i>none</i>,
+ libvirt does not do address learning and referencing <i>IP</i> without
+ assigning it an explicit value is an error.
+ <br/><br/>
+ Use of <i>ip_learning=dhcp</i> (DHCP snooping) provides additional
+ anti-spoofing security, especially when combined with a filter allowing
+ only trusted DHCP servers to assign addresses. If the DHCP lease expires,
+ the VM will no longer be able to use the IP address until it acquires a
+ new, valid lease from a DHCP server. If the VM is migrated, it must get
+ a new valid DHCP lease to use an IP address (e.g., by
+ bringing the VM interface down and up again).
</p>
<h3><a name="nwflimitsmigr">VM Migration</a></h3>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 88be04e..d4b0a86 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -51,6 +51,7 @@ src/node_device/node_device_hal.c
src/node_device/node_device_linux_sysfs.c
src/node_device/node_device_udev.c
src/nodeinfo.c
+src/nwfilter/nwfilter_dhcpsnoop.c
src/nwfilter/nwfilter_driver.c
src/nwfilter/nwfilter_ebiptables_driver.c
src/nwfilter/nwfilter_gentech_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index a2aae9d..4382caf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -509,6 +509,8 @@ NWFILTER_DRIVER_SOURCES = \
nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \
nwfilter/nwfilter_gentech_driver.c \
nwfilter/nwfilter_gentech_driver.h \
+ nwfilter/nwfilter_dhcpsnoop.c \
+ nwfilter/nwfilter_dhcpsnoop.h \
nwfilter/nwfilter_ebiptables_driver.c \
nwfilter/nwfilter_ebiptables_driver.h \
nwfilter/nwfilter_learnipaddr.c \
diff --git a/src/nwfilter/nwfilter_dhcpsnoop.c b/src/nwfilter/nwfilter_dhcpsnoop.c
new file mode 100644
index 0000000..8c2ff50
--- /dev/null
+++ b/src/nwfilter/nwfilter_dhcpsnoop.c
@@ -0,0 +1,1197 @@
+/*
+ * nwfilter_dhcpsnoop.c: support for DHCP snooping used by a VM
+ * on an interface
+ *
+ * Copyright (C) 2011 IBM Corp.
+ * Copyright (C) 2011 David L Stevens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David L Stevens <dlstevens(a)us.ibm.com>
+ * Based in part on work by Stefan Berger <stefanb(a)us.ibm.com>
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBPCAP
+#include <pcap.h>
+#endif
+
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+#include "conf/domain_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_dhcpsnoop.h"
+#include "virnetdev.h"
+#include "virfile.h"
+#include "configmake.h"
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+#ifdef HAVE_LIBPCAP
+
+#define LEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.leases"
+#define TMPLEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.ltmp"
+
+static struct {
+/* lease file */
+ int LeaseFD;
+ int nLeases; /* number of active leases */
+ int wLeases; /* number of written leases */
+/* thread management */
+ virHashTablePtr SnoopReqs;
+ virHashTablePtr IfnameToKey;
+ virHashTablePtr Active;
+ virMutex SnoopLock;
+ virMutex ActiveLock;
+} virNWFilterSnoopState = {
+ .LeaseFD = -1,
+ .SnoopLock = { .lock=PTHREAD_MUTEX_INITIALIZER },
+ .ActiveLock = { .lock=PTHREAD_MUTEX_INITIALIZER },
+};
+
+#define virNWFilterSnoopLock() \
+ { virMutexLock(&virNWFilterSnoopState.SnoopLock); }
+#define virNWFilterSnoopUnlock() \
+ { virMutexUnlock(&virNWFilterSnoopState.SnoopLock); }
+#define virNWFilterSnoopLockActive() \
+ { virMutexLock(&virNWFilterSnoopState.ActiveLock); }
+#define virNWFilterSnoopUnlockActive() \
+ { virMutexUnlock(&virNWFilterSnoopState.ActiveLock); }
+
+static char *
+virNWFilterSnoopActivate(virThreadPtr thread)
+{
+ char *key, *data;
+ unsigned long threadID = (unsigned long int)thread->thread;
+
+ if (virAsprintf(&key, "0x%0*lX", (int)sizeof(threadID)*2, threadID) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ virNWFilterSnoopLockActive();
+ data = strdup("1");
+ if (data == NULL) {
+ virReportOOMError();
+ VIR_FREE(key);
+ } else if (virHashAddEntry(virNWFilterSnoopState.Active, key, data)) {
+ VIR_FREE(key);
+ VIR_FREE(data);
+ }
+ virNWFilterSnoopUnlockActive();
+ return key;
+}
+
+static void
+virNWFilterSnoopCancel(char **ThreadKey)
+{
+ if (*ThreadKey == NULL)
+ return;
+
+ virNWFilterSnoopLockActive();
+ (void) virHashRemoveEntry(virNWFilterSnoopState.Active, *ThreadKey);
+ *ThreadKey = NULL;
+ virNWFilterSnoopUnlockActive();
+}
+
+static bool
+virNWFilterSnoopIsActive(char *ThreadKey)
+{
+ void *entry;
+
+ if (ThreadKey == NULL)
+ return 0;
+ virNWFilterSnoopLockActive();
+ entry = virHashLookup(virNWFilterSnoopState.Active, ThreadKey);
+ virNWFilterSnoopUnlockActive();
+ return entry != NULL;
+}
+
+#define VIR_IFKEY_LEN ((VIR_UUID_STRING_BUFLEN) + (VIR_MAC_STRING_BUFLEN))
+
+struct virNWFilterSnoopReq {
+ virNWFilterTechDriverPtr techdriver;
+ const char *ifname;
+ int ifindex;
+ const char *linkdev;
+ enum virDomainNetType nettype;
+ char ifkey[VIR_IFKEY_LEN];
+ unsigned char macaddr[VIR_MAC_BUFLEN];
+ const char *filtername;
+ virNWFilterHashTablePtr vars;
+ virNWFilterDriverStatePtr driver;
+ /* start and end of lease list, ordered by lease time */
+ struct virNWFilterSnoopIPLease *start;
+ struct virNWFilterSnoopIPLease *end;
+ char *threadkey;
+};
+
+#define POLL_INTERVAL 10*1000 /* 10 secs */
+#define MAXERRS 25 /* retries on failing device */
+
+struct virNWFilterSnoopIPLease {
+ uint32_t IPAddress;
+ uint32_t IPServer;
+ struct virNWFilterSnoopReq *SnoopReq;
+ unsigned int Timeout;
+ /* timer list */
+ struct virNWFilterSnoopIPLease *prev;
+ struct virNWFilterSnoopIPLease *next;
+};
+
+static struct virNWFilterSnoopIPLease *
+ virNWFilterSnoopGetByIP(struct virNWFilterSnoopIPLease *start,
+ uint32_t ipaddr);
+static void virNWFilterSnoopUpdateLease(struct virNWFilterSnoopIPLease *pl,
+ time_t timeout);
+
+static struct virNWFilterSnoopReq *virNWFilterSnoopNewReq(const char *ifkey);
+
+static void virNWFilterSnoopLeaseFileOpen(void);
+static void virNWFilterSnoopLeaseFileClose(void);
+static void virNWFilterSnoopLeaseFileLoad(void);
+static void virNWFilterSnoopLeaseFileSave(struct virNWFilterSnoopIPLease *ipl);
+static void virNWFilterSnoopLeaseFileRestore(struct virNWFilterSnoopReq *req);
+static void virNWFilterSnoopLeaseFileRefresh(void);
+
+/*
+ * virNWFilterSnoopListAdd - add an IP lease to a list
+ */
+static void
+virNWFilterSnoopListAdd(struct virNWFilterSnoopIPLease *plnew,
+ struct virNWFilterSnoopIPLease **start,
+ struct virNWFilterSnoopIPLease **end)
+{
+ struct virNWFilterSnoopIPLease *pl;
+
+ plnew->next = plnew->prev = 0;
+ if (!*start) {
+ *start = *end = plnew;
+ return;
+ }
+ for (pl = *end; pl && plnew->Timeout < pl->Timeout;
+ pl = pl->prev)
+ /* empty */ ;
+ if (!pl) {
+ plnew->next = *start;
+ *start = plnew;
+ } else {
+ plnew->next = pl->next;
+ pl->next = plnew;
+ }
+ plnew->prev = pl;
+ if (plnew->next)
+ plnew->next->prev = plnew;
+ else
+ *end = plnew;
+}
+
+/*
+ * virNWFilterSnoopTimerAdd - add an IP lease to the timer list
+ */
+static void
+virNWFilterSnoopTimerAdd(struct virNWFilterSnoopIPLease *plnew)
+{
+ struct virNWFilterSnoopReq *req = plnew->SnoopReq;
+
+ virNWFilterSnoopListAdd(plnew, &req->start, &req->end);
+}
+
+/*
+ * virNWFilterSnoopInstallRule - install rule for a lease
+ */
+static int
+virNWFilterSnoopInstallRule(struct virNWFilterSnoopIPLease *ipl)
+{
+ char ipbuf[INET_ADDRSTRLEN];
+ int rc;
+ virNWFilterVarValuePtr ipVar;
+
+ if (!inet_ntop(AF_INET, &ipl->IPAddress, ipbuf, sizeof(ipbuf))) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopInstallRule inet_ntop failed "
+ " (0x%08X)"), ipl->IPAddress);
+ return -1;
+ }
+ ipVar = virNWFilterVarValueCreateSimpleCopyValue(ipbuf);
+ if (!ipVar) {
+ virReportOOMError();
+ return -1;
+ }
+ if (virNWFilterHashTablePut(ipl->SnoopReq->vars, "IP", ipVar, 1)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not add variable \"IP\" to hashmap"));
+ virNWFilterVarValueFree(ipVar);
+ return -1;
+ }
+ rc = virNWFilterInstantiateFilterLate(NULL,
+ ipl->SnoopReq->ifname,
+ ipl->SnoopReq->ifindex,
+ ipl->SnoopReq->linkdev,
+ ipl->SnoopReq->nettype,
+ ipl->SnoopReq->macaddr,
+ ipl->SnoopReq->filtername,
+ ipl->SnoopReq->vars,
+ ipl->SnoopReq->driver);
+ if (rc)
+ return -1;
+ return 0;
+}
+
+/*
+ * virNWFilterSnoopLeaseAdd - create or update an IP lease
+ */
+static void
+virNWFilterSnoopLeaseAdd(struct virNWFilterSnoopIPLease *plnew,
+ bool update_leasefile)
+{
+ struct virNWFilterSnoopIPLease *pl;
+ struct virNWFilterSnoopReq *req = plnew->SnoopReq;
+
+ pl = virNWFilterSnoopGetByIP(req->start, plnew->IPAddress);
+ if (pl) {
+ virNWFilterSnoopUpdateLease(pl, plnew->Timeout);
+ if (update_leasefile)
+ virNWFilterSnoopLeaseFileSave(pl);
+ return;
+ }
+ /* support for multiple addresses requires the ability to add filters
+ * to existing chains, or to instantiate address lists via
+ * virNWFilterInstantiateFilterLate(). Until one of those capabilities
+ * is added, don't allow a new address when one is already assigned to
+ * this interface.
+ */
+ if (req->start)
+ return; /* silently ignore multiple addresses */
+
+ if (VIR_ALLOC(pl) < 0) {
+ virReportOOMError();
+ return;
+ }
+ *pl = *plnew;
+
+ if (req->threadkey && virNWFilterSnoopInstallRule(pl) < 0) {
+ VIR_FREE(pl);
+ return;
+ }
+ virNWFilterSnoopTimerAdd(pl);
+ virNWFilterSnoopState.nLeases++;
+ if (update_leasefile)
+ virNWFilterSnoopLeaseFileSave(pl);
+}
+
+/*
+ * virNWFilterSnoopListDel - remove an IP lease from a list
+ */
+static void
+virNWFilterSnoopListDel(struct virNWFilterSnoopIPLease *ipl,
+ struct virNWFilterSnoopIPLease **start,
+ struct virNWFilterSnoopIPLease **end)
+{
+ if (ipl->prev)
+ ipl->prev->next = ipl->next;
+ else
+ *start = ipl->next;
+ if (ipl->next)
+ ipl->next->prev = ipl->prev;
+ else
+ *end = ipl->prev;
+ ipl->next = ipl->prev = 0;
+}
+
+/*
+ * virNWFilterSnoopTimerDel - remove an IP lease from the timer list
+ */
+static void
+virNWFilterSnoopTimerDel(struct virNWFilterSnoopIPLease *ipl)
+{
+ struct virNWFilterSnoopReq *req = ipl->SnoopReq;
+
+ virNWFilterSnoopListDel(ipl, &req->start, &req->end);
+ ipl->Timeout = 0;
+}
+
+/*
+ * virNWFilterSnoopLeaseDel - delete an IP lease
+ */
+static void
+virNWFilterSnoopLeaseDel(struct virNWFilterSnoopReq *req,
+ uint32_t ipaddr, bool update_leasefile)
+{
+ struct virNWFilterSnoopIPLease *ipl;
+
+ ipl = virNWFilterSnoopGetByIP(req->start, ipaddr);
+ if (ipl == NULL)
+ return;
+
+ virNWFilterSnoopTimerDel(ipl);
+
+ if (update_leasefile) {
+ virNWFilterSnoopLeaseFileSave(ipl);
+
+ /*
+ * for multiple address support, this needs to remove those rules
+ * referencing "IP" with ipl's ip value.
+ */
+ if (req->techdriver->applyDHCPOnlyRules(req->ifname, req->macaddr,
+ NULL, false))
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopListDel failed"));
+ }
+ VIR_FREE(ipl);
+ virNWFilterSnoopState.nLeases--;
+}
+
+/*
+ * virNWFilterSnoopUpdateLease - update the timeout on an IP lease
+ */
+static void
+virNWFilterSnoopUpdateLease(struct virNWFilterSnoopIPLease *ipl, time_t timeout)
+{
+ if (timeout < ipl->Timeout)
+ return; /* no take-backs */
+ virNWFilterSnoopTimerDel(ipl);
+ ipl->Timeout = timeout;
+ virNWFilterSnoopTimerAdd(ipl);
+ return;
+}
+
+/*
+ * virNWFilterSnoopGetByIP - lookup IP lease by IP address
+ */
+static struct virNWFilterSnoopIPLease *
+virNWFilterSnoopGetByIP(struct virNWFilterSnoopIPLease *start, uint32_t ipaddr)
+{
+ struct virNWFilterSnoopIPLease *pl;
+
+ for (pl = start; pl && pl->IPAddress != ipaddr; pl = pl->next)
+ /* empty */ ;
+ return pl;
+}
+
+/*
+ * virNWFilterSnoopTimerRun - run the IP lease timeout list
+ */
+static unsigned int
+virNWFilterSnoopTimerRun(struct virNWFilterSnoopReq *req)
+{
+ time_t now;
+
+ now = time(0);
+ while (req->start && req->start->Timeout <= now)
+ virNWFilterSnoopLeaseDel(req, req->start->IPAddress, 1);
+ return 0;
+}
+
+typedef unsigned char Eaddr[6];
+
+struct virNWFilterSnoopEthHdr {
+ Eaddr eh_dst;
+ Eaddr eh_src;
+ unsigned short eh_type;
+ union {
+ unsigned char eu_data[0];
+ struct vlan_hdr {
+ unsigned short ev_flags;
+ unsigned short ev_type;
+ unsigned char ev_data[0];
+ } eu_vlh;
+ } eth_un;
+} ATTRIBUTE_PACKED;
+
+#define eh_data eth_un.eu_data
+#define ehv_data eth_un.eu_vlh.ev_data
+#define ehv_type eth_un.eu_vlh.ev_type
+
+struct virNWFilterSnoopDHCPHdr {
+ unsigned char d_op;
+ unsigned char d_htype;
+ unsigned char d_hlen;
+ unsigned char d_hops;
+ unsigned int d_xid;
+ unsigned short d_secs;
+ unsigned short d_flags;
+ unsigned int d_ciaddr;
+ unsigned int d_yiaddr;
+ unsigned int d_siaddr;
+ unsigned int d_giaddr;
+ unsigned char d_chaddr[16];
+ char d_sname[64];
+ char d_file[128];
+ unsigned char d_opts[0];
+};
+
+/* DHCP options */
+
+#define DHCPO_PAD 0
+#define DHCPO_LEASE 51 /* lease time in secs */
+#define DHCPO_MTYPE 53 /* message type */
+#define DHCPO_END 255 /* end of options */
+
+/* DHCP message types */
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPRELEASE 7
+
+static const unsigned char dhcp_magic[4] = { 99, 130, 83, 99 };
+
+static int
+virNWFilterSnoopDHCPGetOpt(struct virNWFilterSnoopDHCPHdr *pd, int len,
+ int *pmtype, int *pleasetime)
+{
+ int oind, olen;
+ int oend;
+
+ olen = len - sizeof(*pd);
+ oind = 0;
+
+ if (olen < 4) /* bad magic */
+ return -1;
+ if (memcmp(dhcp_magic, pd->d_opts, sizeof(dhcp_magic)) != 0)
+ return -1; /* bad magic */
+ oind += sizeof(dhcp_magic);
+
+ oend = 0;
+
+ *pmtype = *pleasetime = 0;
+
+ while (oind < olen) {
+ switch (pd->d_opts[oind]) {
+ case DHCPO_LEASE:
+ if (olen - oind < 6)
+ goto malformed;
+ if (*pleasetime)
+ return -1; /* duplicate lease time */
+ *pleasetime =
+ ntohl(*(unsigned int *) (pd->d_opts + oind + 2));
+ break;
+ case DHCPO_MTYPE:
+ if (olen - oind < 3)
+ goto malformed;
+ if (*pmtype)
+ return -1; /* duplicate message type */
+ *pmtype = pd->d_opts[oind + 2];
+ break;
+ case DHCPO_PAD:
+ oind++;
+ continue;
+
+ case DHCPO_END:
+ oend = 1;
+ break;
+ default:
+ if (olen - oind < 2)
+ goto malformed;
+ }
+ if (oend)
+ break;
+ oind += pd->d_opts[oind + 1] + 2;
+ }
+ return 0;
+malformed:
+ VIR_WARN("got lost in the options!");
+ return -1;
+}
+
+static void
+virNWFilterSnoopDHCPDecode(struct virNWFilterSnoopReq *req,
+ struct virNWFilterSnoopEthHdr *pep,
+ int len)
+{
+ struct iphdr *pip;
+ struct udphdr *pup;
+ struct virNWFilterSnoopDHCPHdr *pd;
+ struct virNWFilterSnoopIPLease ipl;
+ int mtype, leasetime;
+
+ /* go through the protocol headers */
+ switch (ntohs(pep->eh_type)) {
+ case ETHERTYPE_IP:
+ pip = (struct iphdr *) pep->eh_data;
+ len -= offsetof(struct virNWFilterSnoopEthHdr, eh_data);
+ break;
+ case ETHERTYPE_VLAN:
+ if (ntohs(pep->ehv_type) != ETHERTYPE_IP)
+ return;
+ pip = (struct iphdr *) pep->ehv_data;
+ len -= offsetof(struct virNWFilterSnoopEthHdr, ehv_data);
+ break;
+ default:
+ return;
+ }
+ pip = (struct iphdr *) pep->eh_data;
+ len -= sizeof(*pep);
+ if (len < 0)
+ return;
+ pup = (struct udphdr *) ((char *) pip + (pip->ihl << 2));
+ len -= pip->ihl << 2;
+ if (len < 0)
+ return;
+ pd = (struct virNWFilterSnoopDHCPHdr *) ((char *) pup + sizeof(*pup));
+ len -= sizeof(*pup);
+ if (len < 0)
+ return; /* invalid packet length */
+ if (virNWFilterSnoopDHCPGetOpt(pd, len, &mtype, &leasetime) < 0)
+ return;
+
+ memset(&ipl, 0, sizeof(ipl));
+ ipl.IPAddress = pd->d_yiaddr;
+ ipl.IPServer = pd->d_siaddr;
+ if (leasetime == ~0)
+ ipl.Timeout = ~0;
+ else
+ ipl.Timeout = time(0) + leasetime;
+ ipl.SnoopReq = req;
+
+ switch (mtype) {
+ case DHCPACK:
+ virNWFilterSnoopLeaseAdd(&ipl, 1);
+ break;
+ case DHCPDECLINE:
+ case DHCPRELEASE:
+ virNWFilterSnoopLeaseDel(req, ipl.IPAddress, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+#define PBUFSIZE 576 /* >= IP/TCP/DHCP headers */
+#define TIMEOUT 30 /* secs */
+
+static pcap_t *
+virNWFilterSnoopDHCPOpen(const char *intf)
+{
+ pcap_t *handle = NULL;
+ struct bpf_program fp;
+ char filter[64];
+ char pcap_errbuf[PCAP_ERRBUF_SIZE];
+ time_t start;
+
+ start = time(0);
+ while (handle == NULL && time(0) - start < TIMEOUT)
+ handle = pcap_open_live(intf, PBUFSIZE, 0, POLL_INTERVAL, pcap_errbuf);
+
+ if (handle == NULL) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("pcap_open_live: %s"), pcap_errbuf);
+ return 0;
+ }
+
+ sprintf(filter, "port 67 or dst port 68");
+ if (pcap_compile(handle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) != 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("pcap_compile: %s"), pcap_geterr(handle));
+ return 0;
+ }
+ if (pcap_setfilter(handle, &fp) != 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("pcap_setfilter: %s"), pcap_geterr(handle));
+ pcap_freecode(&fp);
+ return 0;
+ }
+ pcap_freecode(&fp);
+ return handle;
+}
+
+static void
+virNWFilterSnoopReqFree(struct virNWFilterSnoopReq *req)
+{
+ struct virNWFilterSnoopIPLease *ipl;
+
+ if (!req)
+ return;
+
+ /* free all leases */
+ for (ipl = req->start; ipl; ipl = req->start)
+ virNWFilterSnoopLeaseDel(req, ipl->IPAddress, 0);
+
+ /* free all req data */
+ VIR_FREE(req->ifname);
+ VIR_FREE(req->linkdev);
+ VIR_FREE(req->filtername);
+ virNWFilterHashTableFree(req->vars);
+ VIR_FREE(req);
+}
+
+static void
+virNWFilterDHCPSnoop(void *req0)
+{
+ struct virNWFilterSnoopReq *req = req0;
+ pcap_t *handle;
+ struct pcap_pkthdr *hdr;
+ struct virNWFilterSnoopEthHdr *packet;
+ int ifindex;
+ int errcount;
+ char *threadkey;
+ virThread thread;
+
+ handle = virNWFilterSnoopDHCPOpen(req->ifname);
+ if (!handle)
+ return;
+
+ virThreadSelf(&thread);
+ req->threadkey = virNWFilterSnoopActivate(&thread);
+ threadkey = strdup(req->threadkey);
+ if (threadkey == NULL) {
+ virReportOOMError();
+ pcap_close(handle);
+ return;
+ }
+
+ ifindex = if_nametoindex(req->ifname);
+
+ virNWFilterSnoopLock();
+ virNWFilterSnoopLeaseFileRestore(req);
+ virNWFilterSnoopUnlock();
+
+ errcount = 0;
+ while (1) {
+ int rv;
+
+ virNWFilterSnoopLock();
+ virNWFilterSnoopTimerRun(req);
+ virNWFilterSnoopUnlock();
+
+ rv = pcap_next_ex(handle, &hdr, (const u_char **)&packet);
+
+ if (!virNWFilterSnoopIsActive(threadkey)) {
+ VIR_FREE(threadkey);
+ pcap_close(handle);
+ return;
+ }
+ if (rv < 0) {
+ if (virNetDevValidateConfig(req->ifname, NULL, ifindex) <= 0)
+ break;
+ if (++errcount > MAXERRS) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ifname \"%s\" failing; reopening"),
+ req->ifname);
+ pcap_close(handle);
+ handle = virNWFilterSnoopDHCPOpen(req->ifname);
+ if (!handle)
+ break;
+ }
+ continue;
+ }
+ errcount = 0;
+ if (rv) {
+ virNWFilterSnoopLock();
+ virNWFilterSnoopDHCPDecode(req, packet, hdr->caplen);
+ virNWFilterSnoopUnlock();
+ }
+ }
+ virNWFilterSnoopCancel(&req->threadkey);
+ (void) virHashRemoveEntry(virNWFilterSnoopState.IfnameToKey, req->ifname);
+ VIR_FREE(req->ifname);
+ /* if we still have a valid lease, keep the req for restarts */
+ if (!req->start || req->start->Timeout < time(0))
+ (void) virHashRemoveEntry(virNWFilterSnoopState.SnoopReqs, req->ifkey);
+ VIR_FREE(threadkey);
+ pcap_close(handle);
+ return;
+}
+
+static void
+virNWFilterSnoopIFKeyFMT(char *ifkey, const unsigned char *vmuuid,
+ unsigned const char *macaddr)
+{
+ virUUIDFormat(vmuuid, ifkey);
+ ifkey[VIR_UUID_STRING_BUFLEN-1] = '-';
+ virMacAddrFormat(macaddr, ifkey + VIR_UUID_STRING_BUFLEN);
+}
+
+int
+virNWFilterDHCPSnoopReq(virNWFilterTechDriverPtr techdriver,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *vmuuid,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver)
+{
+ struct virNWFilterSnoopReq *req;
+ bool isnewreq;
+ char ifkey[VIR_IFKEY_LEN];
+ virThread thread;
+
+ virNWFilterSnoopIFKeyFMT(ifkey, vmuuid, macaddr);
+ virNWFilterSnoopLock();
+ req = virHashLookup(virNWFilterSnoopState.SnoopReqs, ifkey);
+ isnewreq = req == NULL;
+ if (!isnewreq) {
+ if (req->threadkey) {
+ virNWFilterSnoopUnlock();
+ return 0;
+ }
+ } else {
+ req = virNWFilterSnoopNewReq(ifkey);
+ if (!req) {
+ virNWFilterSnoopUnlock();
+ return -1;
+ }
+ }
+
+ req->techdriver = techdriver;
+ req->ifindex = if_nametoindex(ifname);
+ req->linkdev = linkdev ? strdup(linkdev) : NULL;
+ req->nettype = nettype;
+ req->ifname = strdup(ifname);
+ memcpy(req->macaddr, macaddr, sizeof(req->macaddr));
+ req->filtername = strdup(filtername);
+ if (req->ifname == NULL || req->filtername == NULL) {
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ virReportOOMError();
+ return -1;
+ }
+
+ if (techdriver->applyDHCPOnlyRules(req->ifname, req->macaddr, NULL,
+ false)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("applyDHCPOnlyRules "
+ "failed - spoofing not protected!"));
+ }
+
+ req->vars = virNWFilterHashTableCreate(0);
+ if (!req->vars) {
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ virReportOOMError();
+ return -1;
+ }
+ if (virNWFilterHashTablePutAll(filterparams, req->vars)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq: can't copy variables"
+ " on if %s"), ifkey);
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ return -1;
+ }
+ req->driver = driver;
+
+ if (virHashAddEntry(virNWFilterSnoopState.IfnameToKey, ifname,
+ req->ifkey)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq ifname map failed"
+ " on interface \"%s\" key \"%s\""), ifname,
+ ifkey);
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ return -1;
+ }
+ if (isnewreq &&
+ virHashAddEntry(virNWFilterSnoopState.SnoopReqs, ifkey, req)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq req add failed on"
+ " interface \"%s\" ifkey \"%s\""), ifname,
+ ifkey);
+ (void) virHashRemoveEntry(virNWFilterSnoopState.IfnameToKey, ifname);
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ return -1;
+ }
+ virNWFilterSnoopUnlock();
+ if (virThreadCreate(&thread, false, virNWFilterDHCPSnoop, req) != 0) {
+ virNWFilterSnoopLock();
+ (void) virHashRemoveEntry(virNWFilterSnoopState.IfnameToKey, ifname);
+ (void) virHashRemoveEntry(virNWFilterSnoopState.SnoopReqs, ifkey);
+ virNWFilterSnoopUnlock();
+ virNWFilterSnoopReqFree(req);
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq virThreadCreate "
+ "failed on interface \"%s\""), ifname);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * virNWFilterSnoopReqRelease - hash table free function to kill a request
+ */
+static void
+virNWFilterSnoopReqRelease(void *req0, const void *name ATTRIBUTE_UNUSED)
+{
+ struct virNWFilterSnoopReq *req = (struct virNWFilterSnoopReq *) req0;
+
+ if (!req)
+ return;
+
+ if (req->threadkey)
+ virNWFilterSnoopCancel(&req->threadkey);
+ virNWFilterSnoopReqFree(req);
+}
+
+static void
+virNWFilterSnoopLeaseFileClose(void)
+{
+ VIR_FORCE_CLOSE(virNWFilterSnoopState.LeaseFD);
+}
+
+static void
+virNWFilterSnoopLeaseFileOpen(void)
+{
+ virNWFilterSnoopLeaseFileClose();
+
+ virNWFilterSnoopState.LeaseFD = open(LEASEFILE, O_CREAT|O_RDWR|O_APPEND,
+ 0644);
+}
+
+int
+virNWFilterDHCPSnoopInit(void)
+{
+ if (virNWFilterSnoopState.SnoopReqs)
+ return 0;
+
+ virNWFilterSnoopLock();
+ virNWFilterSnoopState.IfnameToKey = virHashCreate(0, NULL);
+ virNWFilterSnoopState.SnoopReqs =
+ virHashCreate(0, virNWFilterSnoopReqRelease);
+ if (!virNWFilterSnoopState.IfnameToKey ||
+ !virNWFilterSnoopState.SnoopReqs) {
+ virNWFilterSnoopUnlock();
+ virReportOOMError();
+ goto errexit;
+ }
+ virNWFilterSnoopLeaseFileLoad();
+ virNWFilterSnoopLeaseFileOpen();
+
+ virNWFilterSnoopUnlock();
+
+ virNWFilterSnoopLockActive();
+ virNWFilterSnoopState.Active = virHashCreate(0, 0);
+ if (!virNWFilterSnoopState.Active) {
+ virNWFilterSnoopUnlockActive();
+ virReportOOMError();
+ goto errexit;
+ }
+ virNWFilterSnoopUnlockActive();
+ return 0;
+
+errexit:
+ if (virNWFilterSnoopState.IfnameToKey) {
+ virHashFree(virNWFilterSnoopState.IfnameToKey);
+ virNWFilterSnoopState.IfnameToKey = 0;
+ }
+ if (virNWFilterSnoopState.SnoopReqs) {
+ virHashFree(virNWFilterSnoopState.SnoopReqs);
+ virNWFilterSnoopState.SnoopReqs = 0;
+ }
+ if (virNWFilterSnoopState.Active) {
+ virHashFree(virNWFilterSnoopState.Active);
+ virNWFilterSnoopState.Active = 0;
+ }
+ return -1;
+}
+
+void
+virNWFilterDHCPSnoopEnd(const char *ifname)
+{
+ char *ifkey = NULL;
+
+ virNWFilterSnoopLock();
+ if (!virNWFilterSnoopState.SnoopReqs) {
+ virNWFilterSnoopUnlock();
+ return;
+ }
+
+ if (ifname) {
+ ifkey = (char *)virHashLookup(virNWFilterSnoopState.IfnameToKey,ifname);
+ (void) virHashRemoveEntry(virNWFilterSnoopState.IfnameToKey, ifname);
+ if (!ifkey) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ifname \"%s\" not in key map"), ifname);
+ virNWFilterSnoopUnlock();
+ return;
+ }
+ }
+
+ if (ifkey) {
+ struct virNWFilterSnoopReq *req;
+
+ req = virHashLookup(virNWFilterSnoopState.SnoopReqs, ifkey);
+ if (!req) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ifkey \"%s\" has no req"), ifkey);
+ virNWFilterSnoopUnlock();
+ return;
+ }
+ if (!req->start || req->start->Timeout < time(0)) {
+ (void) virHashRemoveEntry(virNWFilterSnoopState.SnoopReqs,
+ req->ifkey);
+ virNWFilterSnoopUnlock();
+ return;
+ }
+ /* keep valid lease req; drop interface association */
+ virNWFilterSnoopCancel(&req->threadkey);
+ VIR_FREE(req->ifname);
+ } else { /* free all of them */
+ virNWFilterSnoopLeaseFileClose();
+ virHashFree(virNWFilterSnoopState.IfnameToKey);
+ virHashFree(virNWFilterSnoopState.SnoopReqs);
+ virNWFilterSnoopState.IfnameToKey = virHashCreate(0, 0);
+ if (!virNWFilterSnoopState.IfnameToKey) {
+ virNWFilterSnoopUnlock();
+ virReportOOMError();
+ return;
+ }
+ virNWFilterSnoopState.SnoopReqs =
+ virHashCreate(0, virNWFilterSnoopReqRelease);
+ if (!virNWFilterSnoopState.SnoopReqs) {
+ virHashFree(virNWFilterSnoopState.IfnameToKey);
+ virNWFilterSnoopUnlock();
+ virReportOOMError();
+ return;
+ }
+ virNWFilterSnoopLeaseFileLoad();
+ }
+ virNWFilterSnoopUnlock();
+}
+
+static int
+virNWFilterSnoopLeaseFileWrite(int lfd, const char *ifkey,
+ struct virNWFilterSnoopIPLease *ipl)
+{
+ char lbuf[256], ipstr[INET_ADDRSTRLEN], dhcpstr[INET_ADDRSTRLEN];
+
+ if (inet_ntop(AF_INET, &ipl->IPAddress, ipstr, sizeof(ipstr)) == 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("inet_ntop(0x%08X) failed"), ipl->IPAddress);
+ return -1;
+ }
+ if (inet_ntop(AF_INET, &ipl->IPServer, dhcpstr, sizeof(dhcpstr)) == 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("inet_ntop(0x%08X) failed"), ipl->IPServer);
+ return -1;
+ }
+ /* time intf ip dhcpserver */
+ snprintf(lbuf, sizeof(lbuf), "%u %s %s %s\n", ipl->Timeout,
+ ifkey, ipstr, dhcpstr);
+ if (write(lfd, lbuf, strlen(lbuf)) < 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("lease file write failed: %s"),
+ strerror(errno));
+ return -1;
+ }
+ (void) fsync(lfd);
+ return 0;
+}
+
+static void
+virNWFilterSnoopLeaseFileSave(struct virNWFilterSnoopIPLease *ipl)
+{
+ struct virNWFilterSnoopReq *req = ipl->SnoopReq;
+
+ if (virNWFilterSnoopState.LeaseFD < 0)
+ virNWFilterSnoopLeaseFileOpen();
+ if (virNWFilterSnoopLeaseFileWrite(virNWFilterSnoopState.LeaseFD,
+ req->ifkey, ipl) < 0)
+ return;
+ /* keep dead leases at < ~95% of file size */
+ if (++virNWFilterSnoopState.wLeases >= virNWFilterSnoopState.nLeases*20)
+ virNWFilterSnoopLeaseFileLoad(); /* load & refresh lease file */
+}
+
+static struct virNWFilterSnoopReq *
+virNWFilterSnoopNewReq(const char *ifkey)
+{
+ struct virNWFilterSnoopReq *req;
+
+ if (ifkey == NULL || strlen(ifkey) != VIR_IFKEY_LEN-1) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopNewReq called with invalid "
+ "key \"%s\" (%d)"),
+ ifkey ? ifkey : "", strlen(ifkey));
+ return NULL;
+ }
+ if (VIR_ALLOC(req) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ if (virStrcpyStatic(req->ifkey, ifkey) == NULL)
+ VIR_FREE(req);
+
+ return req;
+}
+
+static void
+virNWFilterSnoopSaveIter(void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct virNWFilterSnoopReq *req = payload;
+ int tfd = *(int *)data;
+ struct virNWFilterSnoopIPLease *ipl;
+
+ /* clean up orphaned, expired leases */
+ if (!req->threadkey) {
+ time_t now;
+
+ now = time(0);
+ for (ipl = req->start; ipl; ipl = ipl->next)
+ if (ipl->Timeout < now)
+ virNWFilterSnoopLeaseDel(req, ipl->IPAddress , 0);
+ if (!req->start) {
+ virNWFilterSnoopReqFree(req);
+ return;
+ }
+ }
+ for (ipl = req->start; ipl; ipl = ipl->next)
+ (void) virNWFilterSnoopLeaseFileWrite(tfd, req->ifkey, ipl);
+}
+
+static void
+virNWFilterSnoopLeaseFileRefresh(void)
+{
+ int tfd;
+
+ (void) unlink(TMPLEASEFILE);
+ /* lease file loaded, delete old one */
+ tfd = open(TMPLEASEFILE, O_CREAT|O_RDWR|O_TRUNC|O_EXCL, 0644);
+ if (tfd < 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("open(\"%s\"): %s"),
+ TMPLEASEFILE, strerror(errno));
+ return;
+ }
+ if (virNWFilterSnoopState.SnoopReqs)
+ virHashForEach(virNWFilterSnoopState.SnoopReqs,
+ virNWFilterSnoopSaveIter, (void *)&tfd);
+ VIR_FORCE_CLOSE(tfd);
+ if (rename(TMPLEASEFILE, LEASEFILE) < 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("rename(\"%s\", \"%s\"): %s"),
+ TMPLEASEFILE, LEASEFILE, strerror(errno));
+ (void) unlink(TMPLEASEFILE);
+ }
+ virNWFilterSnoopState.wLeases = 0;
+ virNWFilterSnoopLeaseFileOpen();
+}
+
+
+static void
+virNWFilterSnoopLeaseFileLoad(void)
+{
+ char line[256], ifkey[VIR_IFKEY_LEN], ipstr[INET_ADDRSTRLEN],
+ srvstr[INET_ADDRSTRLEN];
+ struct virNWFilterSnoopIPLease ipl;
+ struct virNWFilterSnoopReq *req;
+ time_t now;
+ FILE *fp;
+ int ln = 0;
+
+ fp = fopen(LEASEFILE, "r");
+ time(&now);
+ while (fp && fgets(line, sizeof(line), fp)) {
+ if (line[strlen(line)-1] != '\n') {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopLeaseFileLoad lease file "
+ "line %d corrupt"), ln);
+ break;
+ }
+ ln++;
+ /* key len 55 = "VMUUID"+'-'+"MAC" */
+ if (sscanf(line, "%u %55s %16s %16s", &ipl.Timeout,
+ ifkey, ipstr, srvstr) < 4) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopLeaseFileLoad lease file "
+ "line %d corrupt"), ln);
+ break;
+ }
+ if (ipl.Timeout && ipl.Timeout < now)
+ continue;
+ req = virHashLookup(virNWFilterSnoopState.SnoopReqs, ifkey);
+ if (!req) {
+ req = virNWFilterSnoopNewReq(ifkey);
+ if (!req)
+ break;
+ if (virHashAddEntry(virNWFilterSnoopState.SnoopReqs, ifkey, req)) {
+ virNWFilterSnoopReqFree(req);
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterSnoopLeaseFileLoad req add"
+ " failed on interface \"%s\""), ifkey);
+ continue;
+ }
+ }
+
+ if (inet_pton(AF_INET, ipstr, &ipl.IPAddress) <= 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("line %d corrupt ipaddr \"%s\""),
+ ln, ipstr);
+ continue;
+ }
+ (void) inet_pton(AF_INET, srvstr, &ipl.IPServer);
+ ipl.SnoopReq = req;
+
+ if (ipl.Timeout)
+ virNWFilterSnoopLeaseAdd(&ipl, 0);
+ else
+ virNWFilterSnoopLeaseDel(req, ipl.IPAddress, 0);
+ }
+ if (fp != NULL)
+ (void) fclose(fp);
+ virNWFilterSnoopLeaseFileRefresh();
+}
+
+static void
+virNWFilterSnoopLeaseFileRestore(struct virNWFilterSnoopReq *req)
+{
+ struct virNWFilterSnoopIPLease *ipl;
+
+ for (ipl=req->start; ipl; ipl=ipl->next)
+ (void) virNWFilterSnoopInstallRule(ipl);
+}
+
+#else /* HAVE_LIBPCAP */
+int
+virNWFilterDHCPSnoopInit(void)
+{
+ return -1;
+}
+
+void
+virNWFilterDHCPSnoopEnd(const char *ifname ATTRIBUTE_UNUSED)
+{
+ return;
+}
+
+int
+virNWFilterDHCPSnoopReq(virNWFilterTechDriverPtr techdriver ATTRIBUTE_UNUSED,
+ const char *ifname ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ enum virDomainNetType nettype ATTRIBUTE_UNUSED,
+ char *vmuuid ATTRIBUTE_UNUSED,
+ const unsigned char *macaddr ATTRIBUTE_UNUSED,
+ const char *filtername ATTRIBUTE_UNUSED,
+ virNWFilterHashTablePtr filterparams ATTRIBUTE_UNUSED,
+ virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED)
+{
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("libvirt was not compiled "
+ "with libpcap and \"ip_learning='dhcp'\" requires"
+ " it."));
+ return 1;
+}
+#endif /* HAVE_LIBPCAP */
diff --git a/src/nwfilter/nwfilter_dhcpsnoop.h b/src/nwfilter/nwfilter_dhcpsnoop.h
new file mode 100644
index 0000000..25500e2
--- /dev/null
+++ b/src/nwfilter/nwfilter_dhcpsnoop.h
@@ -0,0 +1,38 @@
+/*
+ * nwfilter_dhcpsnoop.h: support DHCP snooping for a VM on an interface
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 David L Stevens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David L Stevens <dlstevens(a)us.ibm.com>
+ */
+
+#ifndef __NWFILTER_DHCPSNOOP_H
+#define __NWFILTER_DHCPSNOOP_H
+
+int virNWFilterDHCPSnoopInit(void);
+int virNWFilterDHCPSnoopReq(virNWFilterTechDriverPtr techdriver,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *vmuuid,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver);
+void virNWFilterDHCPSnoopEnd(const char *ifname);
+#endif /* __NWFILTER_DHCPSNOOP_H */
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index ffb4b5d..d014a19 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -39,6 +39,7 @@
#include "nwfilter_gentech_driver.h"
#include "configmake.h"
+#include "nwfilter_dhcpsnoop.h"
#include "nwfilter_learnipaddr.h"
#define VIR_FROM_THIS VIR_FROM_NWFILTER
@@ -66,6 +67,8 @@ static int
nwfilterDriverStartup(int privileged) {
char *base = NULL;
+ if (virNWFilterDHCPSnoopInit() < 0)
+ return -1;
if (virNWFilterLearnInit() < 0)
return -1;
@@ -127,6 +130,7 @@ alloc_err_exit:
conf_init_err:
virNWFilterTechDriversShutdown();
+ virNWFilterDHCPSnoopEnd(0);
virNWFilterLearnShutdown();
return -1;
@@ -149,6 +153,7 @@ nwfilterDriverReload(void) {
conn = virConnectOpen("qemu:///system");
if (conn) {
+ virNWFilterDHCPSnoopEnd(0);
/* shut down all threads -- they will be restarted if necessary */
virNWFilterLearnThreadsTerminate(true);
@@ -203,6 +208,7 @@ nwfilterDriverShutdown(void) {
virNWFilterConfLayerShutdown();
virNWFilterTechDriversShutdown();
+ virNWFilterDHCPSnoopEnd(0);
virNWFilterLearnShutdown();
nwfilterDriverLock(driverState);
diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c
index fc71e7b..07ae33c 100644
--- a/src/nwfilter/nwfilter_gentech_driver.c
+++ b/src/nwfilter/nwfilter_gentech_driver.c
@@ -32,6 +32,7 @@
#include "virterror_internal.h"
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_dhcpsnoop.h"
#include "nwfilter_learnipaddr.h"
#include "virnetdev.h"
#include "datatypes.h"
@@ -42,6 +43,8 @@
#define NWFILTER_STD_VAR_MAC "MAC"
#define NWFILTER_STD_VAR_IP "IP"
+#define NWFILTER_DFLT_LEARN "any"
+
static int _virNWFilterTeardownFilter(const char *ifname);
@@ -662,6 +665,9 @@ virNWFilterInstantiate(const unsigned char *vmuuid ATTRIBUTE_UNUSED,
void **ptrs = NULL;
int instantiate = 1;
char *buf;
+ virNWFilterVarValuePtr lv;
+ const char *learning;
+ bool reportIP = false;
virNWFilterHashTablePtr missing_vars = virNWFilterHashTableCreate(0);
if (!missing_vars) {
@@ -678,22 +684,47 @@ virNWFilterInstantiate(const unsigned char *vmuuid ATTRIBUTE_UNUSED,
if (rc < 0)
goto err_exit;
+ lv = virHashLookup(vars->hashTable, "ip_learning");
+ if (lv && lv->valType == NWFILTER_VALUE_TYPE_SIMPLE)
+ learning = lv->u.simple.value;
+ else
+ learning = NULL;
+
+ if (learning == NULL)
+ learning = NWFILTER_DFLT_LEARN;
+
if (virHashSize(missing_vars->hashTable) == 1) {
if (virHashLookup(missing_vars->hashTable,
NWFILTER_STD_VAR_IP) != NULL) {
- if (virNWFilterLookupLearnReq(ifindex) == NULL) {
- rc = virNWFilterLearnIPAddress(techdriver,
- ifname,
- ifindex,
- linkdev,
- nettype, macaddr,
- filter->name,
- vars, driver,
- DETECT_DHCP|DETECT_STATIC);
+ if (c_strcasecmp(learning, "none") == 0) { /* no learning */
+ reportIP = true;
+ goto err_unresolvable_vars;
}
- goto err_exit;
- }
- goto err_unresolvable_vars;
+ if (c_strcasecmp(learning, "dhcp") == 0) {
+ rc = virNWFilterDHCPSnoopReq(techdriver, ifname, linkdev,
+ nettype, vmuuid, macaddr,
+ filter->name, vars, driver);
+ goto err_exit;
+ } else if (c_strcasecmp(learning, "any") == 0) {
+ if (virNWFilterLookupLearnReq(ifindex) == NULL) {
+ rc = virNWFilterLearnIPAddress(techdriver,
+ ifname,
+ ifindex,
+ linkdev,
+ nettype, macaddr,
+ filter->name,
+ vars, driver,
+ DETECT_DHCP|DETECT_STATIC);
+ }
+ goto err_exit;
+ } else {
+ rc = -1;
+ virNWFilterReportError(VIR_ERR_PARSE_FAILED, _("filter '%s' "
+ "learning value '%s' invalid."),
+ filter->name, learning);
+ }
+ } else
+ goto err_unresolvable_vars;
} else if (virHashSize(missing_vars->hashTable) > 1) {
goto err_unresolvable_vars;
} else if (!forceWithPendingReq &&
@@ -761,7 +792,7 @@ err_exit:
err_unresolvable_vars:
- buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, false);
+ buf = virNWFilterPrintVars(missing_vars->hashTable, ", ", false, reportIP);
if (buf) {
virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
_("Cannot instantiate filter due to unresolvable "
@@ -1092,6 +1123,8 @@ _virNWFilterTeardownFilter(const char *ifname)
return -1;
}
+ virNWFilterDHCPSnoopEnd(ifname);
+
virNWFilterTerminateLearnReq(ifname);
if (virNWFilterLockIface(ifname) < 0)
--
1.7.6.5
12 years, 7 months
[libvirt] [PATCH] qemu: change rbd auth_supported separation character to ;
by Josh Durgin
This works with newer qemu that doesn't allow escaping spaces.
It's backwards compatible as well.
Signed-off-by: Josh Durgin <josh.durgin(a)dreamhost.com>
---
src/qemu/qemu_command.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f971a08..ee3bf48 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1673,8 +1673,8 @@ qemuBuildRBDString(virConnectPtr conn,
virReportOOMError();
goto error;
}
- virBufferEscape(opt, '\\', ":",
- ":key=%s:auth_supported=cephx none",
+ virBufferEscape(opt, '\\', ":;",
+ ":key=%s:auth_supported=cephx;none",
base64);
VIR_FREE(base64);
} else {
--
1.7.5.4
12 years, 7 months