[libvirt] [PATCH] qemu_monitor: Don't output snapshot format argument if type is NULL
by Peter Krempa
If the snapshot format type string was NULL, the JSON framework created
an invalid JSON string.
---
The other option would be to fix qemuMonitorJSONMakeCommandRaw that string arguments with a NULL
argument would suppress outputing the complete option, but I'm afraid of breaking something.
Background:
http://www.redhat.com/archives/libvir-list/2012-March/msg01198.html
src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++--------
1 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index eeeb6a6..6fce58a 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -3112,14 +3112,25 @@ qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
- cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
- "blockdev-snapshot-sync",
- "s:device", device,
- "s:snapshot-file", file,
- "s:format", format,
- reuse ? "s:mode" : NULL,
- reuse ? "existing" : NULL,
- NULL);
+ if (format) {
+ cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
+ "blockdev-snapshot-sync",
+ "s:device", device,
+ "s:snapshot-file", file,
+ "s:format", format,
+ reuse ? "s:mode" : NULL,
+ reuse ? "existing" : NULL,
+ NULL);
+ } else {
+ cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
+ "blockdev-snapshot-sync",
+ "s:device", device,
+ "s:snapshot-file", file,
+ reuse ? "s:mode" : NULL,
+ reuse ? "existing" : NULL,
+ NULL);
+ }
+
if (!cmd)
return -1;
--
1.7.3.4
12 years, 9 months
[libvirt] [PATCH] Enable build of test suite programs by default for GIT checkouts
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
Add a new flag '--with-test-suite' to configure to control whether
the test suite binaries are built by default. ie built with a
plain 'make', as opposed to delayed until 'make check'
For builds from tar.gz tests will not be built by default. For
builds from GIT, tests with be on by default, to try and ensure
that patch developers don't accidentally break the test suites
without noticing.
* configure.ac: Add --with-test-suite
* tests/Makefile.am: Use noinst_PROGRAMS instead of check_PROGRAMS
if building tests by default. Consolidate setting of TESTS and
{noinst,check}_PROGRAMS to avoid duplication
---
configure.ac | 19 ++++++++
tests/Makefile.am | 119 +++++++++++++---------------------------------------
2 files changed, 49 insertions(+), 89 deletions(-)
diff --git a/configure.ac b/configure.ac
index c1f9f45..740129c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2162,6 +2162,25 @@ AM_CONDITIONAL([WITH_PYTHON], [test "$with_python" = "yes"])
AC_SUBST([PYTHON_VERSION])
AC_SUBST([PYTHON_INCLUDES])
+AC_ARG_ENABLE([with-test-suite],
+ AC_HELP_STRING([--with-test-suite], [build test suite by default @<:@default=check@:>@]),
+ [case "${withval}" in
+ yes|no|check) ;;
+ *) AC_MSG_ERROR([bad value ${withval} for tests option]) ;;
+ esac],
+ [withval=check])
+
+AC_MSG_CHECKING([Whether to build test suite by default])
+if test "$withval" = "check" ; then
+ if test -d $srcdir/.git ; then
+ withval=yes
+ else
+ withval=no
+ fi
+fi
+AC_MSG_RESULT([$withval])
+AM_CONDITIONAL([WITH_TESTS], [test "$withval" = "yes"])
+
AC_ARG_ENABLE([test-coverage],
AC_HELP_STRING([--enable-test-coverage], [turn on code coverage instrumentation @<:@default=no@:>@]),
[case "${enableval}" in
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4a0686f..dd8bf4f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -92,69 +92,68 @@ EXTRA_DIST = \
xml2vmxdata \
.valgrind.supp
-check_PROGRAMS = virshtest conftest sockettest \
+test_helpers = commandhelper ssh conftest
+test_programs = virshtest sockettest \
nodeinfotest virbuftest \
- commandtest commandhelper seclabeltest \
- virhashtest virnetmessagetest virnetsockettest ssh \
+ commandtest seclabeltest \
+ virhashtest virnetmessagetest virnetsockettest \
utiltest virnettlscontexttest shunloadtest \
virtimetest viruritest virkeyfiletest \
virauthconfigtest
-check_LTLIBRARIES = libshunload.la
-
# This is a fake SSH we use from virnetsockettest
ssh_SOURCES = ssh.c
ssh_LDADD = $(COVERAGE_LDFLAGS)
if WITH_XEN
-check_PROGRAMS += xml2sexprtest sexpr2xmltest \
+test_programs += xml2sexprtest sexpr2xmltest \
xmconfigtest xencapstest statstest reconnect
endif
if WITH_QEMU
-check_PROGRAMS += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \
+test_programs += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \
qemuargv2xmltest qemuhelptest domainsnapshotxml2xmltest \
qemumonitortest
endif
if WITH_LXC
-check_PROGRAMS += lxcxml2xmltest
+test_programs += lxcxml2xmltest
endif
if WITH_OPENVZ
-check_PROGRAMS += openvzutilstest
+test_programs += openvzutilstest
endif
if WITH_ESX
-check_PROGRAMS += esxutilstest
+test_programs += esxutilstest
endif
if WITH_VMX
-check_PROGRAMS += vmx2xmltest xml2vmxtest
+test_programs += vmx2xmltest xml2vmxtest
endif
if WITH_CIL
-check_PROGRAMS += object-locking
+test_programs += object-locking
endif
if HAVE_YAJL
-check_PROGRAMS += jsontest
+test_programs += jsontest
endif
-check_PROGRAMS += networkxml2xmltest
+test_programs += networkxml2xmltest
if WITH_NETWORK
-check_PROGRAMS += networkxml2argvtest
+test_programs += networkxml2argvtest
endif
-check_PROGRAMS += nwfilterxml2xmltest
+test_programs += nwfilterxml2xmltest
-check_PROGRAMS += storagevolxml2xmltest storagepoolxml2xmltest
+test_programs += storagevolxml2xmltest storagepoolxml2xmltest
-check_PROGRAMS += nodedevxml2xmltest
+test_programs += nodedevxml2xmltest
-check_PROGRAMS += interfacexml2xmltest
+test_programs += interfacexml2xmltest
-check_PROGRAMS += cputest
+test_programs += cputest
test_scripts = \
capabilityschematest \
@@ -185,6 +184,8 @@ test_scripts += \
virsh-optparse \
virsh-schedinfo \
virsh-synopsis
+
+test_programs += eventtest
else
EXTRA_DIST += \
test_conf.sh \
@@ -213,77 +214,16 @@ endif
EXTRA_DIST += $(test_scripts)
-TESTS = virshtest \
- nodeinfotest \
- virbuftest \
- sockettest \
- commandtest \
- seclabeltest \
- virhashtest \
- virnetmessagetest \
- virnetsockettest \
- virnettlscontexttest \
- virtimetest \
- viruritest \
- virkeyfiletest \
- virauthconfigtest \
- shunloadtest \
- utiltest \
- $(test_scripts)
-
-if HAVE_YAJL
-TESTS += jsontest
-endif
-
-if WITH_XEN
-TESTS += xml2sexprtest \
- sexpr2xmltest \
- xmconfigtest \
- xencapstest \
- reconnect \
- statstest
-endif
-
-if WITH_QEMU
-TESTS += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest qemuargv2xmltest \
- qemuhelptest domainsnapshotxml2xmltest nwfilterxml2xmltest \
- qemumonitortest
-endif
-
-if WITH_LXC
-TESTS += lxcxml2xmltest
-endif
-
-if WITH_OPENVZ
-TESTS += openvzutilstest
-endif
-
-if WITH_ESX
-TESTS += esxutilstest
-endif
-
-if WITH_VMX
-TESTS += vmx2xmltest xml2vmxtest
-endif
-
-if WITH_LIBVIRTD
-check_PROGRAMS += eventtest
-TESTS += eventtest
-endif
-
-TESTS += networkxml2xmltest
-
-if WITH_NETWORK
-TESTS += networkxml2argvtest
+if WITH_TESTS
+noinst_PROGRAMS = $(test_programs) $(test_helpers)
+noinst_LTLIBRARIES = libshunload.la
+else
+check_PROGRAMS = $(test_programs) $(test_helpers)
+check_LTLIBRARIES = libshunload.la
endif
-TESTS += storagevolxml2xmltest storagepoolxml2xmltest
-
-TESTS += nodedevxml2xmltest
-
-TESTS += interfacexml2xmltest
-
-TESTS += cputest
+TESTS = $(test_programs) \
+ $(test_scripts)
# NB, automake < 1.10 does not provide the real
# abs_top_{src/build}dir or builddir variables, so don't rely
@@ -306,6 +246,7 @@ TESTS_ENVIRONMENT = \
LC_ALL=C \
$(VG)
+
valgrind:
$(MAKE) check VG="valgrind --quiet --leak-check=full --suppressions=$(srcdir)/.valgrind.supp"
--
1.7.7.6
12 years, 9 months
[libvirt] [libvirt PATCHv7 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 v6:
- replace pthread_cancel() with synchronous cancelation method
Signed-off-by: David L Stevens <dlstevens(a)us.ibm.com>
---
docs/formatnwfilter.html.in | 17 +
src/Makefile.am | 2 +
src/nwfilter/nwfilter_dhcpsnoop.c | 1139 ++++++++++++++++++++++++++++++++
src/nwfilter/nwfilter_dhcpsnoop.h | 38 ++
src/nwfilter/nwfilter_driver.c | 6 +
src/nwfilter/nwfilter_gentech_driver.c | 59 ++-
6 files changed, 1248 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/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..8e5dcc5
--- /dev/null
+++ b/src/nwfilter/nwfilter_dhcpsnoop.c
@@ -0,0 +1,1139 @@
+/*
+ * 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 <sys/ioctl.h>
+#include <signal.h>
+
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <intprops.h>
+
+#include "internal.h"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+#include "threads.h"
+#include "conf/nwfilter_params.h"
+#include "conf/domain_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_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 int lease_fd = -1;
+static int nleases = 0; /* number of active leases */
+static int wleases = 0; /* number of written leases */
+
+static virHashTablePtr SnoopReqs;
+static virHashTablePtr IfnameToKey;
+static pthread_mutex_t SnoopLock = PTHREAD_MUTEX_INITIALIZER;
+
+/* snooper thread management */
+
+static virHashTablePtr Active;
+static pthread_mutex_t ActiveLock = PTHREAD_MUTEX_INITIALIZER;
+
+#define active_lock() { pthread_mutex_lock(&ActiveLock); }
+#define active_unlock() { pthread_mutex_unlock(&ActiveLock); }
+
+static char *
+SnoopActivate(pthread_t thread)
+{
+ int len;
+ char *key;
+
+ len = sizeof(thread)*2 + 3; /* "0x"+'\0' */
+ if (VIR_ALLOC_N(key, len)) {
+ virReportOOMError();
+ return NULL;
+ }
+ (void) snprintf(key, len, "0x%0*lX", sizeof(thread)*2,
+ (unsigned long int)thread);
+ key[len-1] = '\0';
+
+ active_lock();
+ if (virHashAddEntry(Active, key, strdup("1")))
+ VIR_FREE(key);
+ active_unlock();
+ return key;
+}
+
+static void
+SnoopCancel(char **ThreadKey)
+{
+ if (*ThreadKey == NULL)
+ return;
+
+ active_lock();
+ (void) virHashRemoveEntry(Active, *ThreadKey);
+ *ThreadKey = NULL;
+ active_unlock();
+}
+
+static bool
+SnoopIsActive(char *ThreadKey)
+{
+ void *entry;
+
+ if (ThreadKey == NULL)
+ return 0;
+ active_lock();
+ entry = virHashLookup(Active, ThreadKey);
+ active_unlock();
+ return entry != NULL;
+}
+
+#define snoop_lock() { pthread_mutex_lock(&SnoopLock); }
+#define snoop_unlock() { pthread_mutex_unlock(&SnoopLock); }
+
+#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 iplease *start;
+ struct iplease *end;
+ char *threadkey;
+};
+
+#define POLL_INTERVAL 10*1000 /* 10 secs */
+#define MAXERRS 25 /* retries on failing device */
+
+struct iplease {
+ uint32_t ipl_ipaddr;
+ uint32_t ipl_server;
+ struct virNWFilterSnoopReq *ipl_req;
+ unsigned int ipl_timeout;
+ /* timer list */
+ struct iplease *ipl_prev;
+ struct iplease *ipl_next;
+};
+
+static struct iplease *ipl_getbyip(struct iplease *start, uint32_t ipaddr);
+static void ipl_update(struct iplease *pl, uint32_t timeout);
+
+static struct virNWFilterSnoopReq *newreq(const char *ifkey);
+
+static void lease_open(void);
+static void lease_close(void);
+static void lease_load(void);
+static void lease_save(struct iplease *ipl);
+static void lease_restore(struct virNWFilterSnoopReq *req);
+static void lease_refresh(void);
+
+/*
+ * ipl_ladd - add an IP lease to a list
+ */
+static void
+ipl_ladd(struct iplease *plnew, struct iplease **start, struct iplease **end)
+{
+ struct iplease *pl;
+
+ plnew->ipl_next = plnew->ipl_prev = 0;
+ if (!*start) {
+ *start = *end = plnew;
+ return;
+ }
+ for (pl = *end; pl && plnew->ipl_timeout < pl->ipl_timeout;
+ pl = pl->ipl_prev)
+ /* empty */ ;
+ if (!pl) {
+ plnew->ipl_next = *start;
+ *start = plnew;
+ } else {
+ plnew->ipl_next = pl->ipl_next;
+ pl->ipl_next = plnew;
+ }
+ plnew->ipl_prev = pl;
+ if (plnew->ipl_next)
+ plnew->ipl_next->ipl_prev = plnew;
+ else
+ *end = plnew;
+}
+
+/*
+ * ipl_tadd - add an IP lease to the timer list
+ */
+static void
+ipl_tadd(struct iplease *plnew)
+{
+ struct virNWFilterSnoopReq *req = plnew->ipl_req;
+
+ ipl_ladd(plnew, &req->start, &req->end);
+}
+
+/*
+ * ipl_install - install rule for a lease
+ */
+static int
+ipl_install(struct iplease *ipl)
+{
+ char ipbuf[20]; /* dotted decimal IP addr string */
+ int rc;
+ virNWFilterVarValuePtr ipVar;
+
+ if (!inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipbuf, sizeof(ipbuf))) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ipl_install inet_ntop " "failed (0x%08X)"),
+ ipl->ipl_ipaddr);
+ return -1;
+ }
+ ipVar = virNWFilterVarValueCreateSimpleCopyValue(ipbuf);
+ if (!ipVar) {
+ virReportOOMError();
+ return -1;
+ }
+ if (virNWFilterHashTablePut(ipl->ipl_req->vars, "IP", ipVar, 1)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not add variable \"IP\" to hashmap"));
+ virNWFilterVarValueFree(ipVar);
+ return -1;
+ }
+ rc = virNWFilterInstantiateFilterLate(NULL,
+ ipl->ipl_req->ifname,
+ ipl->ipl_req->ifindex,
+ ipl->ipl_req->linkdev,
+ ipl->ipl_req->nettype,
+ ipl->ipl_req->macaddr,
+ ipl->ipl_req->filtername,
+ ipl->ipl_req->vars,
+ ipl->ipl_req->driver);
+ if (rc)
+ return -1;
+ return 0;
+}
+
+/*
+ * ipl_add - create or update an IP lease
+ */
+static void
+ipl_add(struct iplease *plnew, bool update_leasefile)
+{
+ struct iplease *pl;
+ struct virNWFilterSnoopReq *req = plnew->ipl_req;
+
+ pl = ipl_getbyip(req->start, plnew->ipl_ipaddr);
+ if (pl) {
+ ipl_update(pl, plnew->ipl_timeout);
+ if (update_leasefile)
+ lease_save(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 && ipl_install(pl) < 0) {
+ VIR_FREE(pl);
+ return;
+ }
+ ipl_tadd(pl);
+ nleases++;
+ if (update_leasefile)
+ lease_save(pl);
+}
+
+/*
+ * ipl_tdel - remove an IP lease from a list
+ */
+static void
+ipl_ldel(struct iplease *ipl, struct iplease **start, struct iplease **end)
+{
+ if (ipl->ipl_prev)
+ ipl->ipl_prev->ipl_next = ipl->ipl_next;
+ else
+ *start = ipl->ipl_next;
+ if (ipl->ipl_next)
+ ipl->ipl_next->ipl_prev = ipl->ipl_prev;
+ else
+ *end = ipl->ipl_prev;
+ ipl->ipl_next = ipl->ipl_prev = 0;
+}
+
+/*
+ * ipl_tdel - remove an IP lease from the timer list
+ */
+static void
+ipl_tdel(struct iplease *ipl)
+{
+ struct virNWFilterSnoopReq *req = ipl->ipl_req;
+
+ ipl_ldel(ipl, &req->start, &req->end);
+ ipl->ipl_timeout = 0;
+}
+
+/*
+ * ipl_del - delete an IP lease
+ */
+static void
+ipl_del(struct virNWFilterSnoopReq *req, uint32_t ipaddr, bool update_leasefile)
+{
+ struct iplease *ipl;
+
+ ipl = ipl_getbyip(req->start, ipaddr);
+ if (ipl == NULL)
+ return;
+
+ ipl_tdel(ipl);
+
+ if (update_leasefile) {
+ lease_save(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, "ipl_ldel failed");
+ }
+ VIR_FREE(ipl);
+ nleases--;
+}
+
+/*
+ * ipl_update - update the timeout on an IP lease
+ */
+static void
+ipl_update(struct iplease *ipl, uint32_t timeout)
+{
+ if (timeout < ipl->ipl_timeout)
+ return; /* no take-backs */
+ ipl_tdel(ipl);
+ ipl->ipl_timeout = timeout;
+ ipl_tadd(ipl);
+ return;
+}
+
+/*
+ * ipl_getbyip - lookup IP lease by IP address
+ */
+static struct iplease *
+ipl_getbyip(struct iplease *start, uint32_t ipaddr)
+{
+ struct iplease *pl;
+
+ for (pl = start; pl && pl->ipl_ipaddr != ipaddr; pl = pl->ipl_next)
+ /* empty */ ;
+ return pl;
+}
+
+/*
+ * ipl_trun - run the IP lease timeout list
+ */
+static unsigned int
+ipl_trun(struct virNWFilterSnoopReq *req)
+{
+ uint32_t now;
+
+ now = time(0);
+ while (req->start && req->start->ipl_timeout <= now)
+ ipl_del(req, req->start->ipl_ipaddr, 1);
+ return 0;
+}
+
+typedef unsigned char Eaddr[6];
+
+struct eth {
+ 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 dhcp {
+ 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
+
+unsigned char dhcp_magic[4] = { 99, 130, 83, 99 };
+
+static int
+dhcp_getopt(struct dhcp *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
+dhcpdecode(struct virNWFilterSnoopReq *req, struct eth *pep, int len)
+{
+ struct iphdr *pip;
+ struct udphdr *pup;
+ struct dhcp *pd;
+ struct iplease 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 eth, eh_data);
+ break;
+ case ETHERTYPE_VLAN:
+ if (ntohs(pep->ehv_type) != ETHERTYPE_IP)
+ return;
+ pip = (struct iphdr *) pep->ehv_data;
+ len -= offsetof(struct eth, ehv_data);
+ break;
+ default:
+ return;
+ }
+ pip = (struct iphdr *) pep->eh_data;
+ len -= sizeof(*pep);
+ pup = (struct udphdr *) ((char *) pip + (pip->ihl << 2));
+ len -= pip->ihl << 2;
+ pd = (struct dhcp *) ((char *) pup + sizeof(*pup));
+ len -= sizeof(*pup);
+ if (len < 0)
+ return; /* dhcpdecode: invalid packet length */
+ if (dhcp_getopt(pd, len, &mtype, &leasetime) < 0)
+ return;
+
+ memset(&ipl, 0, sizeof(ipl));
+ ipl.ipl_ipaddr = pd->d_yiaddr;
+ ipl.ipl_server = pd->d_siaddr;
+ if (leasetime == ~0)
+ ipl.ipl_timeout = ~0;
+ else
+ ipl.ipl_timeout = time(0) + leasetime;
+ ipl.ipl_req = req;
+
+ switch (mtype) {
+ case DHCPACK:
+ ipl_add(&ipl, 1);
+ break;
+ case DHCPDECLINE:
+ case DHCPRELEASE:
+ ipl_del(req, ipl.ipl_ipaddr, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+#define PBUFSIZE 576 /* >= IP/TCP/DHCP headers */
+#define TIMEOUT 30 /* secs */
+
+static pcap_t *
+dhcpopen(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));
+ return 0;
+ }
+ pcap_freecode(&fp);
+ return handle;
+}
+
+static void
+snoopReqFree(struct virNWFilterSnoopReq *req)
+{
+ struct iplease *ipl;
+
+ if (!req)
+ return;
+
+ /* free all leases */
+ for (ipl = req->start; ipl; ipl = req->start)
+ ipl_del(req, ipl->ipl_ipaddr, 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 eth *packet;
+ int ifindex;
+ int errcount;
+ char *threadkey;
+
+ handle = dhcpopen(req->ifname);
+ if (!handle)
+ return 0;
+
+ req->threadkey = SnoopActivate(pthread_self());
+ threadkey = strdup(req->threadkey);
+
+ ifindex = if_nametoindex(req->ifname);
+
+ snoop_lock();
+ lease_restore(req);
+ snoop_unlock();
+
+ errcount = 0;
+ while (1) {
+ int rv;
+
+ snoop_lock();
+ ipl_trun(req);
+ snoop_unlock();
+
+ rv = pcap_next_ex(handle, &hdr, (const u_char **)&packet);
+
+ if (!SnoopIsActive(threadkey)) {
+ VIR_FREE(threadkey);
+ pcap_close(handle);
+ return 0;
+ }
+ 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 = dhcpopen(req->ifname);
+ if (!handle)
+ break;
+ }
+ continue;
+ }
+ errcount = 0;
+ if (rv) {
+ snoop_lock();
+ dhcpdecode(req, packet, hdr->caplen);
+ snoop_unlock();
+ }
+ }
+ SnoopCancel(&req->threadkey);
+ (void) virHashRemoveEntry(IfnameToKey, req->ifname);
+ VIR_FREE(req->ifname);
+ /* if we still have a valid lease, keep the req for restarts */
+ if (!req->start || req->start->ipl_timeout < time(0))
+ (void) virHashRemoveEntry(SnoopReqs, req->ifkey);
+ VIR_FREE(threadkey);
+ pcap_close(handle);
+ return 0;
+}
+
+static void
+ifkeyFormat(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];
+ pthread_t thread;
+
+ ifkeyFormat(ifkey, vmuuid, macaddr);
+ snoop_lock();
+ req = virHashLookup(SnoopReqs, ifkey);
+ isnewreq = req == NULL;
+ if (!isnewreq) {
+ if (req->threadkey) {
+ snoop_unlock();
+ return 0;
+ }
+ } else {
+ req = newreq(ifkey);
+ if (!req) {
+ snoop_unlock();
+ 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->filtername == NULL) {
+ snoop_unlock();
+ snoopReqFree(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) {
+ snoop_unlock();
+ snoopReqFree(req);
+ virReportOOMError();
+ return 1;
+ }
+ if (virNWFilterHashTablePutAll(filterparams, req->vars)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq: can't copy variables"
+ " on if %s"), ifkey);
+ snoop_unlock();
+ snoopReqFree(req);
+ return 1;
+ }
+ req->driver = driver;
+
+ if (virHashAddEntry(IfnameToKey, ifname, req->ifkey)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq ifname map failed"
+ " on interface \"%s\" key \"%s\""), ifname,
+ ifkey);
+ snoop_unlock();
+ snoopReqFree(req);
+ return 1;
+ }
+ if (isnewreq && virHashAddEntry(SnoopReqs, ifkey, req)) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq req add failed on"
+ " interface \"%s\" ifkey \"%s\""), ifname,
+ ifkey);
+ (void) virHashRemoveEntry(IfnameToKey, ifname);
+ snoop_unlock();
+ snoopReqFree(req);
+ return 1;
+ }
+ snoop_unlock();
+ if (pthread_create(&thread, NULL, virNWFilterDHCPSnoop, req) != 0) {
+ snoop_lock();
+ (void) virHashRemoveEntry(IfnameToKey, ifname);
+ (void) virHashRemoveEntry(SnoopReqs, ifkey);
+ snoop_unlock();
+ snoopReqFree(req);
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("virNWFilterDHCPSnoopReq pthread_create failed"
+ " on interface \"%s\""), ifname);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * freeReq - hash table free function to kill a request
+ */
+static void
+freeReq(void *req0, const void *name ATTRIBUTE_UNUSED)
+{
+ struct virNWFilterSnoopReq *req = (struct virNWFilterSnoopReq *) req0;
+
+ if (!req)
+ return;
+
+ if (req->threadkey)
+ SnoopCancel(&req->threadkey);
+ snoopReqFree(req);
+}
+
+static void
+lease_close(void)
+{
+ VIR_FORCE_CLOSE(lease_fd);
+}
+
+static void
+lease_open(void)
+{
+ lease_close();
+
+ lease_fd = open(LEASEFILE, O_CREAT|O_RDWR|O_APPEND, 0644);
+}
+
+int
+virNWFilterDHCPSnoopInit(void)
+{
+ if (SnoopReqs)
+ return 0;
+
+ snoop_lock();
+ IfnameToKey = virHashCreate(0, NULL);
+ SnoopReqs = virHashCreate(0, freeReq);
+ if (!SnoopReqs) {
+ snoop_unlock();
+ virReportOOMError();
+ return -1;
+ }
+ lease_load();
+ lease_open();
+
+ snoop_unlock();
+
+ active_lock();
+ Active = virHashCreate(0, 0);
+ if (!Active) {
+ active_unlock();
+ virReportOOMError();
+ return -1;
+ }
+ active_unlock();
+ return 0;
+}
+
+void
+virNWFilterDHCPSnoopEnd(const char *ifname)
+{
+ char *ifkey = NULL;
+
+ snoop_lock();
+ if (!SnoopReqs) {
+ snoop_unlock();
+ return;
+ }
+
+ if (ifname) {
+ ifkey = (char *)virHashLookup(IfnameToKey, ifname);
+ (void) virHashRemoveEntry(IfnameToKey, ifname);
+ if (!ifkey) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ifname \"%s\" not in key map"), ifname);
+ snoop_unlock();
+ return;
+ }
+ }
+
+ if (ifkey) {
+ struct virNWFilterSnoopReq *req;
+
+ req = virHashLookup(SnoopReqs, ifkey);
+ if (!req) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("ifkey \"%s\" has no req"), ifkey);
+ snoop_unlock();
+ return;
+ }
+ if (!req->start || req->start->ipl_timeout < time(0)) {
+ (void) virHashRemoveEntry(SnoopReqs, req->ifkey);
+ snoop_unlock();
+ return;
+ }
+ /* keep valid lease req; drop interface association */
+ SnoopCancel(&req->threadkey);
+ VIR_FREE(req->ifname);
+ } else { /* free all of them */
+ lease_close();
+ virHashFree(IfnameToKey);
+ virHashFree(SnoopReqs);
+ IfnameToKey = virHashCreate(0, 0);
+ if (!IfnameToKey) {
+ snoop_unlock();
+ virReportOOMError();
+ return;
+ }
+ SnoopReqs = virHashCreate(0, freeReq);
+ if (!SnoopReqs) {
+ virHashFree(IfnameToKey);
+ snoop_unlock();
+ virReportOOMError();
+ return;
+ }
+ lease_load();
+ }
+ snoop_unlock();
+}
+
+static int
+lease_write(int lfd, const char *ifkey, struct iplease *ipl)
+{
+ char lbuf[256],ipstr[16],dhcpstr[16];
+
+ if (inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipstr, sizeof ipstr) == 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("inet_ntop(0x%08X) failed"), ipl->ipl_ipaddr);
+ return -1;
+ }
+ if (inet_ntop(AF_INET, &ipl->ipl_server, dhcpstr, sizeof dhcpstr) == 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("inet_ntop(0x%08X) failed"), ipl->ipl_server);
+ return -1;
+ }
+ /* time intf ip dhcpserver */
+ snprintf(lbuf, sizeof(lbuf), "%u %s %s %s\n", ipl->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
+lease_save(struct iplease *ipl)
+{
+ struct virNWFilterSnoopReq *req = ipl->ipl_req;
+
+ if (lease_fd < 0)
+ lease_open();
+ if (lease_write(lease_fd, req->ifkey, ipl) < 0)
+ return;
+ /* keep dead leases at < ~95% of file size */
+ if (++wleases >= nleases*20)
+ lease_load(); /* load & refresh lease file */
+}
+
+static struct virNWFilterSnoopReq *
+newreq(const char *ifkey)
+{
+ struct virNWFilterSnoopReq *req;
+
+ if (VIR_ALLOC(req) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ strncpy(req->ifkey, ifkey, sizeof req->ifkey);
+
+ return req;
+}
+
+static void
+SaveSnoopReqIter(void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct virNWFilterSnoopReq *req = payload;
+ int tfd = (int)data;
+ struct iplease *ipl;
+
+ /* clean up orphaned, expired leases */
+ if (!req->threadkey) {
+ uint32_t now;
+
+ now = time(0);
+ for (ipl = req->start; ipl; ipl = ipl->ipl_next)
+ if (ipl->ipl_timeout < now)
+ ipl_del(req, ipl->ipl_ipaddr , 0);
+ if (!req->start) {
+ snoopReqFree(req);
+ return;
+ }
+ }
+ for (ipl = req->start; ipl; ipl = ipl->ipl_next)
+ (void) lease_write(tfd, req->ifkey, ipl);
+}
+
+static void
+lease_refresh(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 (SnoopReqs)
+ virHashForEach(SnoopReqs, SaveSnoopReqIter, (void *)tfd);
+ (void) close(tfd);
+ if (rename(TMPLEASEFILE, LEASEFILE) < 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("rename(\"%s\", \"%s\"): %s"),
+ TMPLEASEFILE, LEASEFILE, strerror(errno));
+ (void) unlink(TMPLEASEFILE);
+ }
+ wleases = 0;
+ lease_open();
+}
+
+
+static void
+lease_load(void)
+{
+ char line[256], ifkey[VIR_IFKEY_LEN], ipstr[16], srvstr[16];
+ struct iplease 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,
+ _("lease_load lease file line %d corrupt"),
+ ln);
+ break;
+ }
+ ln++;
+ /* key len 55 = "VMUUID"+'-'+"MAC" */
+ if (sscanf(line, "%u %55s %16s %16s", &ipl.ipl_timeout,
+ ifkey, ipstr, srvstr) < 4) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("lease_load lease file line %d corrupt"),
+ ln);
+ break;;
+ }
+ if (ipl.ipl_timeout && ipl.ipl_timeout < now)
+ continue;
+ req = virHashLookup(SnoopReqs, ifkey);
+ if (!req) {
+ req = newreq(ifkey);
+ if (!req)
+ break;
+ if (virHashAddEntry(SnoopReqs, ifkey, req)) {
+ snoopReqFree(req);
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("lease_load req add failed on "
+ "interface \"%s\""), ifkey);
+ continue;
+ }
+ }
+
+ if (inet_pton(AF_INET, ipstr, &ipl.ipl_ipaddr) <= 0) {
+ virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
+ _("line %d corrupt ipaddr \"%s\""),
+ ln, ipstr);
+ continue;
+ }
+ (void) inet_pton(AF_INET, srvstr, &ipl.ipl_server);
+ ipl.ipl_req = req;
+
+ if (ipl.ipl_timeout)
+ ipl_add(&ipl, 0);
+ else
+ ipl_del(req, ipl.ipl_ipaddr, 0);
+ }
+ if (fp != NULL)
+ (void) fclose(fp);
+ lease_refresh();
+}
+
+static void
+lease_restore(struct virNWFilterSnoopReq *req)
+{
+ struct iplease *ipl;
+
+ for (ipl=req->start; ipl; ipl=ipl->ipl_next)
+ (void) ipl_install(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..245adb0 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, 9 months
[libvirt] [PATCH] qemu: Avoid entering monitor with locked driver
by Jiri Denemark
This avoids possible deadlock of the qemu driver in case a domain is
begin migrated (in Begin phase) and unrelated connection to qemu driver
is closed at the right time.
I checked all callers of qemuDomainCheckEjectableMedia() and they are
calling this function with qemu driver locked.
---
src/qemu/qemu_hotplug.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index f1c1283..38163ba 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -160,9 +160,9 @@ qemuDomainCheckEjectableMedia(struct qemud_driver *driver,
int ret = -1;
int i;
- qemuDomainObjEnterMonitor(driver, vm);
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
table = qemuMonitorGetBlockInfo(priv->mon);
- qemuDomainObjExitMonitor(driver, vm);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
if (!table)
goto cleanup;
--
1.7.8.5
12 years, 9 months
[libvirt] [PATCH v3] qemu, util: on restart of libvirt restart vepa callbacks
by D. Herrendoerfer
From: "D. Herrendoerfer" <d.herrendoerfer(a)herrendoerfer.name>
When libvirtd is restarted, also restart the netlink event
message callbacks for existing VEPA connections and send
a message to lldpad for these existing links, so it learns
the new libvirtd pid.
This patch includes the nits sent by Laine Stump, and it tests out ok.
Signed-off-by: D. Herrendoerfer <d.herrendoerfer(a)herrendoerfer.name>
---
src/qemu/qemu_driver.c | 31 ++++++++++
src/util/virnetdevmacvlan.c | 129 +++++++++++++++++++++++++++++++++---------
src/util/virnetdevmacvlan.h | 9 +++
src/util/virnetlink.c | 6 ++-
4 files changed, 146 insertions(+), 29 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 538a419..8e6663c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -412,6 +412,35 @@ cleanup:
virDomainObjUnlock(vm);
}
+
+static void qemuDomainNetsRestart(void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ int i;
+ virDomainObjPtr vm = (virDomainObjPtr)payload;
+ virDomainDefPtr def = vm->def;
+
+ virDomainObjLock(vm);
+
+ for (i = 0; i < def->nnets; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT &&
+ virDomainNetGetActualDirectMode(net) == VIR_NETDEV_MACVLAN_MODE_VEPA) {
+ VIR_DEBUG("VEPA mode device %s active in domain %s. Reassociating.",
+ net->ifname, def->name);
+ ignore_value(virNetDevMacVLanRestartWithVPortProfile(net->ifname,
+ net->mac,
+ virDomainNetGetActualDirectDev(net),
+ def->uuid,
+ virDomainNetGetActualVirtPortProfile(net),
+ VIR_NETDEV_VPORT_PROFILE_OP_CREATE));
+ }
+ }
+
+ virDomainObjUnlock(vm);
+}
+
/**
* qemudStartup:
*
@@ -668,6 +697,8 @@ qemudStartup(int privileged) {
NULL, NULL) < 0)
goto error;
+ virHashForEach(qemu_driver->domains.objs, qemuDomainNetsRestart, NULL);
+
conn = virConnectOpen(qemu_driver->privileged ?
"qemu:///system" :
"qemu:///session");
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 647679f..90888b0 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -769,6 +769,50 @@ virNetDevMacVLanVPortProfileDestroyCallback(int watch ATTRIBUTE_UNUSED,
virNetlinkCallbackDataFree((virNetlinkCallbackDataPtr)opaque);
}
+static int
+virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
+ const unsigned char *macaddress,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ enum virNetDevVPortProfileOp vmOp)
+{
+ virNetlinkCallbackDataPtr calld = NULL;
+
+ if (virtPortProfile && virNetlinkEventServiceIsRunning()) {
+ if (VIR_ALLOC(calld) < 0)
+ goto memory_error;
+ if ((calld->cr_ifname = strdup(ifname)) == NULL)
+ goto memory_error;
+ if (VIR_ALLOC(calld->virtPortProfile) < 0)
+ goto memory_error;
+ memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile));
+ if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0)
+ goto memory_error;
+ memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN);
+ if ((calld->linkdev = strdup(linkdev)) == NULL)
+ goto memory_error;
+ if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0)
+ goto memory_error;
+ memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN);
+
+ calld->vmOp = vmOp;
+
+ if (virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback,
+ virNetDevMacVLanVPortProfileDestroyCallback,
+ calld, macaddress) < 0)
+ goto error;
+ }
+
+ return 0;
+
+memory_error:
+ virReportOOMError();
+error:
+ virNetlinkCallbackDataFree(calld);
+ return -1;
+}
+
/**
* virNetDevMacVLanCreateWithVPortProfile:
@@ -810,7 +854,6 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname,
int retries, do_retry = 0;
uint32_t macvtapMode;
const char *cr_ifname;
- virNetlinkCallbackDataPtr calld = NULL;
int ret;
int vf = -1;
@@ -917,36 +960,12 @@ create_name:
goto disassociate_exit;
}
- if (virtPortProfile && virNetlinkEventServiceIsRunning()) {
- if (VIR_ALLOC(calld) < 0)
- goto memory_error;
- if ((calld->cr_ifname = strdup(cr_ifname)) == NULL)
- goto memory_error;
- if (VIR_ALLOC(calld->virtPortProfile) < 0)
- goto memory_error;
- memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile));
- if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0)
- goto memory_error;
- memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN);
- if ((calld->linkdev = strdup(linkdev)) == NULL)
- goto memory_error;
- if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0)
- goto memory_error;
- memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN);
-
- calld->vmOp = vmOp;
-
- virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback,
- virNetDevMacVLanVPortProfileDestroyCallback,
- calld, macaddress);
- }
+ if (virNetDevMacVLanVPortProfileRegisterCallback(cr_ifname, macaddress,
+ linkdev, vmuuid, virtPortProfile, vmOp) < 0 )
+ goto disassociate_exit;
return rc;
- memory_error:
- virReportOOMError();
- virNetlinkCallbackDataFree(calld);
-
disassociate_exit:
ignore_value(virNetDevVPortProfileDisassociate(cr_ifname,
virtPortProfile,
@@ -1003,6 +1022,48 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname,
return ret;
}
+/**
+ * virNetDevMacVLanRestartWithVPortProfile:
+ * Register a port profile callback handler for a VM that
+ * is already running
+ * .
+ * @cr_ifname: Interface name that the macvtap has.
+ * @macaddress: The MAC address for the macvtap device
+ * @linkdev: The interface name of the NIC to connect to the external bridge
+ * @vmuuid: The UUID of the VM the macvtap belongs to
+ * @virtPortProfile: pointer to object holding the virtual port profile data
+ * @vmOp: Operation to use during setup of the association
+ *
+ * Returns 0; returns -1 on error.
+ */
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_ifname,
+ const unsigned char *macaddress,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ enum virNetDevVPortProfileOp vmOp)
+{
+ int rc = 0;
+
+ rc = virNetDevMacVLanVPortProfileRegisterCallback(cr_ifname, macaddress,
+ linkdev, vmuuid,
+ virtPortProfile, vmOp);
+ if (rc < 0)
+ goto error;
+
+ ignore_value(virNetDevVPortProfileAssociate(cr_ifname,
+ virtPortProfile,
+ macaddress,
+ linkdev,
+ -1,
+ vmuuid,
+ vmOp, true));
+
+error:
+ return rc;
+
+}
+
#else /* ! WITH_MACVTAP */
int virNetDevMacVLanCreate(const char *ifname ATTRIBUTE_UNUSED,
const char *type ATTRIBUTE_UNUSED,
@@ -1052,4 +1113,16 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname ATTRIBUTE_UNUSED,
_("Cannot create macvlan devices on this platform"));
return -1;
}
+
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_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 130ecea..14640cf 100644
--- a/src/util/virnetdevmacvlan.h
+++ b/src/util/virnetdevmacvlan.h
@@ -75,4 +75,13 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(6) ATTRIBUTE_RETURN_CHECK;
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_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__ */
diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index 59f3e39..2cb3656 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -428,8 +428,11 @@ virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB,
int i, r, ret = -1;
virNetlinkEventSrvPrivatePtr srv = server;
- if (handleCB == NULL)
+ if (handleCB == NULL) {
+ netlinkError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Invalid NULL callback provided"));
return -1;
+ }
virNetlinkEventServerLock(srv);
@@ -449,6 +452,7 @@ virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB,
srv->handlesAlloc, NETLINK_EVENT_ALLOC_EXTENT);
if (VIR_RESIZE_N(srv->handles, srv->handlesAlloc,
srv->handlesCount, NETLINK_EVENT_ALLOC_EXTENT) < 0) {
+ virReportOOMError();
goto error;
}
}
--
1.7.7.6
12 years, 9 months
[libvirt] [test-API PATCH 0/6] General improvements and fixes
by Martin Kletzander
This series fixed some of the issues I came across while trying to
make some tests working. I still haven't managed to run more than
define, start, destroy and undefine tests, but at least this should
help someone to get a start at this.
There are many things that could (should?) be changed (either with or
without a discussion), but maybe I missed something, so feel free to
comment on all the patches if you have any input, I'll be glad to
discuss/change anything in order to move forward.
Each of the patches has it's own description and they are not that
much related to each other, so I won't describe it all here again, I
just didn't want to create unnecessary threads in the mailing list, so
I'm sending this as a series.
Martin Kletzander (6):
Slight cross-distribution support
Added support for Gentoo
Fixed domain/start.py
Add default 'uri' parameter to all tests
Cleanup and fix of domain/define test
Make use of new 'uri' parameter in tests
env_inspect.py => dist/gentoo/env_inspect.py | 88 +++++---------
env_inspect.py => dist/redhat/env_inspect.py | 0
generator.py | 18 +++-
repos/domain/attach_disk.py | 2 +-
repos/domain/attach_interface.py | 2 +-
repos/domain/autostart.py | 2 +-
repos/domain/balloon_memory.py | 2 +-
repos/domain/blkstats.py | 2 +-
repos/domain/cpu_affinity.py | 2 +-
repos/domain/cpu_topology.py | 2 +-
repos/domain/create.py | 2 +-
repos/domain/define.py | 132 +++----------------
repos/domain/destroy.py | 2 +-
repos/domain/detach_disk.py | 2 +-
repos/domain/detach_interface.py | 2 +-
repos/domain/domain_blkinfo.py | 2 +-
repos/domain/domain_id.py | 2 +-
repos/domain/domain_uuid.py | 2 +-
repos/domain/dump.py | 2 +-
repos/domain/eventhandler.py | 2 +-
repos/domain/ifstats.py | 2 +-
repos/domain/install_image.py | 6 +-
repos/domain/install_linux_cdrom.py | 6 +-
repos/domain/install_linux_check.py | 2 +-
repos/domain/install_linux_net.py | 7 +-
repos/domain/install_windows_cdrom.py | 7 +-
repos/domain/ownership_test.py | 2 +-
repos/domain/reboot.py | 2 +-
repos/domain/restore.py | 2 +-
repos/domain/resume.py | 2 +-
repos/domain/save.py | 2 +-
repos/domain/sched_params.py | 2 +-
repos/domain/shutdown.py | 2 +-
repos/domain/start.py | 17 ++-
repos/domain/suspend.py | 2 +-
repos/domain/undefine.py | 2 +-
repos/domain/update_devflag.py | 2 +-
repos/interface/create.py | 2 +-
repos/interface/define.py | 2 +-
repos/interface/destroy.py | 2 +-
repos/interface/undefine.py | 2 +-
repos/libvirtd/qemu_hang.py | 2 +-
repos/libvirtd/restart.py | 2 +-
repos/network/autostart.py | 2 +-
repos/network/create.py | 2 +-
repos/network/define.py | 2 +-
repos/network/destroy.py | 2 +-
repos/network/network_list.py | 2 +-
repos/network/network_name.py | 2 +-
repos/network/network_uuid.py | 2 +-
repos/network/start.py | 2 +-
repos/network/undefine.py | 2 +-
repos/nodedevice/detach.py | 2 +-
repos/nodedevice/reattach.py | 2 +-
repos/nodedevice/reset.py | 2 +-
repos/npiv/create_virtual_hba.py | 2 +-
.../multiple_thread_block_on_domain_create.py | 2 +-
repos/sVirt/domain_nfs_start.py | 4 +-
repos/snapshot/delete.py | 2 +-
repos/snapshot/file_flag.py | 2 +-
repos/snapshot/flag_check.py | 2 +-
repos/snapshot/internal_create.py | 2 +-
repos/snapshot/revert.py | 2 +-
repos/storage/activate_pool.py | 2 +-
repos/storage/build_dir_pool.py | 2 +-
repos/storage/build_disk_pool.py | 2 +-
repos/storage/build_logical_pool.py | 2 +-
repos/storage/build_netfs_pool.py | 2 +-
repos/storage/create_dir_pool.py | 2 +-
repos/storage/create_dir_volume.py | 2 +-
repos/storage/create_fs_pool.py | 2 +-
repos/storage/create_iscsi_pool.py | 2 +-
repos/storage/create_logical_volume.py | 2 +-
repos/storage/create_netfs_pool.py | 2 +-
repos/storage/create_netfs_volume.py | 2 +-
repos/storage/create_partition_volume.py | 2 +-
repos/storage/define_dir_pool.py | 2 +-
repos/storage/define_disk_pool.py | 2 +-
repos/storage/define_iscsi_pool.py | 2 +-
repos/storage/define_logical_pool.py | 2 +-
repos/storage/define_mpath_pool.py | 2 +-
repos/storage/define_netfs_pool.py | 2 +-
repos/storage/define_scsi_pool.py | 2 +-
repos/storage/delete_dir_volume.py | 2 +-
repos/storage/delete_logical_pool.py | 2 +-
repos/storage/delete_logical_volume.py | 2 +-
repos/storage/delete_netfs_volume.py | 2 +-
repos/storage/delete_partition_volume.py | 2 +-
repos/storage/destroy_pool.py | 2 +-
repos/storage/pool_name.py | 2 +-
repos/storage/pool_uuid.py | 2 +-
repos/storage/undefine_pool.py | 2 +-
utils/Python/utils.py | 45 +++++++-
93 files changed, 223 insertions(+), 271 deletions(-)
create mode 100644 dist/__init__.py
create mode 100644 dist/gentoo/__init__.py
copy env_inspect.py => dist/gentoo/env_inspect.py (57%)
create mode 100644 dist/redhat/__init__.py
rename env_inspect.py => dist/redhat/env_inspect.py (100%)
--
1.7.3.4
12 years, 9 months
[libvirt] [PATCH v2 1/1] qemu, util: on restart of libvirt restart vepa callbacks
by D. Herrendoerfer
From: "D. Herrendoerfer" <d.herrendoerfer(a)herrendoerfer.name>
When libvirtd is restarted, also restart the netlink event
message callbacks for existing VEPA connections and send
a message to lldpad for these existing links, so it learns
the new libvirtd pid.
This only includes qemu support as a base if this works out
I'll add others.
Signed-off-by: D. Herrendoerfer <d.herrendoerfer(a)herrendoerfer.name>
---
src/qemu/qemu_driver.c | 30 ++++++++++
src/util/virnetdevmacvlan.c | 128 +++++++++++++++++++++++++++++++++---------
src/util/virnetdevmacvlan.h | 9 +++
3 files changed, 139 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 538a419..c93ece3 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -412,6 +412,34 @@ cleanup:
virDomainObjUnlock(vm);
}
+
+static void qemuDomainNetsRestart(void *payload,
+ const void *name ATTRIBUTE_UNUSED,
+ void *data)
+{
+ int i;
+ virDomainObjPtr vm = (virDomainObjPtr)payload;
+ virDomainDefPtr def = vm->def;
+
+ virDomainObjLock(vm);
+
+ for (i = 0; i < def->nnets; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT &&
+ net->data.direct.mode == VIR_NETDEV_MACVLAN_MODE_VEPA) {
+ VIR_DEBUG("Device active on %s in VEPA mode. Reassociating.",net->ifname);
+ ignore_value(virNetDevMacVLanRestartWithVPortProfile(net->ifname,
+ net->mac,
+ net->data.direct.linkdev,
+ def->uuid,
+ net->data.direct.virtPortProfile,
+ VIR_NETDEV_VPORT_PROFILE_OP_CREATE));
+ }
+ }
+
+ virDomainObjUnlock(vm);
+}
+
/**
* qemudStartup:
*
@@ -668,6 +696,8 @@ qemudStartup(int privileged) {
NULL, NULL) < 0)
goto error;
+ virHashForEach(qemu_driver->domains.objs, qemuDomainNetsRestart, NULL);
+
conn = virConnectOpen(qemu_driver->privileged ?
"qemu:///system" :
"qemu:///session");
diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 647679f..eca4b6e 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -769,6 +769,49 @@ virNetDevMacVLanVPortProfileDestroyCallback(int watch ATTRIBUTE_UNUSED,
virNetlinkCallbackDataFree((virNetlinkCallbackDataPtr)opaque);
}
+static int
+virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
+ const unsigned char *macaddress,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ enum virNetDevVPortProfileOp vmOp)
+{
+ virNetlinkCallbackDataPtr calld = NULL;
+
+ if (virtPortProfile && virNetlinkEventServiceIsRunning()) {
+ if (VIR_ALLOC(calld) < 0)
+ goto memory_error;
+ if ((calld->cr_ifname = strdup(ifname)) == NULL)
+ goto memory_error;
+ if (VIR_ALLOC(calld->virtPortProfile) < 0)
+ goto memory_error;
+ memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile));
+ if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0)
+ goto memory_error;
+ memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN);
+ if ((calld->linkdev = strdup(linkdev)) == NULL)
+ goto memory_error;
+ if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0)
+ goto memory_error;
+ memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN);
+
+ calld->vmOp = vmOp;
+
+ virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback,
+ virNetDevMacVLanVPortProfileDestroyCallback,
+ calld, macaddress);
+ }
+
+ return 0;
+
+ memory_error:
+ virReportOOMError();
+ virNetlinkCallbackDataFree(calld);
+
+ return -1;
+}
+
/**
* virNetDevMacVLanCreateWithVPortProfile:
@@ -810,7 +853,6 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname,
int retries, do_retry = 0;
uint32_t macvtapMode;
const char *cr_ifname;
- virNetlinkCallbackDataPtr calld = NULL;
int ret;
int vf = -1;
@@ -917,36 +959,12 @@ create_name:
goto disassociate_exit;
}
- if (virtPortProfile && virNetlinkEventServiceIsRunning()) {
- if (VIR_ALLOC(calld) < 0)
- goto memory_error;
- if ((calld->cr_ifname = strdup(cr_ifname)) == NULL)
- goto memory_error;
- if (VIR_ALLOC(calld->virtPortProfile) < 0)
- goto memory_error;
- memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile));
- if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0)
- goto memory_error;
- memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN);
- if ((calld->linkdev = strdup(linkdev)) == NULL)
- goto memory_error;
- if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0)
- goto memory_error;
- memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN);
-
- calld->vmOp = vmOp;
-
- virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback,
- virNetDevMacVLanVPortProfileDestroyCallback,
- calld, macaddress);
- }
+ if (virNetDevMacVLanVPortProfileRegisterCallback(cr_ifname, macaddress,
+ linkdev, vmuuid, virtPortProfile, vmOp) < 0 )
+ goto disassociate_exit;
return rc;
- memory_error:
- virReportOOMError();
- virNetlinkCallbackDataFree(calld);
-
disassociate_exit:
ignore_value(virNetDevVPortProfileDisassociate(cr_ifname,
virtPortProfile,
@@ -1003,6 +1021,48 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname,
return ret;
}
+/**
+ * virNetDevMacVLanRestartWithVPortProfile:
+ * Register a port profile callback handler for a VM that
+ * is already running
+ * .
+ * @cr_ifname: Interface name that the macvtap has.
+ * @macaddress: The MAC address for the macvtap device
+ * @linkdev: The interface name of the NIC to connect to the external bridge
+ * @vmuuid: The UUID of the VM the macvtap belongs to
+ * @virtPortProfile: pointer to object holding the virtual port profile data
+ * @vmOp: Operation to use during setup of the association
+ *
+ * Returns 0; returns -1 on error.
+ */
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_ifname,
+ const unsigned char *macaddress,
+ const char *linkdev,
+ const unsigned char *vmuuid,
+ virNetDevVPortProfilePtr virtPortProfile,
+ enum virNetDevVPortProfileOp vmOp)
+{
+ int rc = 0;
+
+ rc = virNetDevMacVLanVPortProfileRegisterCallback(cr_ifname, macaddress,
+ linkdev, vmuuid,
+ virtPortProfile, vmOp);
+ if (rc < 0)
+ goto error;
+
+ ignore_value(virNetDevVPortProfileAssociate(cr_ifname,
+ virtPortProfile,
+ macaddress,
+ linkdev,
+ -1,
+ vmuuid,
+ vmOp, true));
+
+error:
+ return rc;
+
+}
+
#else /* ! WITH_MACVTAP */
int virNetDevMacVLanCreate(const char *ifname ATTRIBUTE_UNUSED,
const char *type ATTRIBUTE_UNUSED,
@@ -1052,4 +1112,16 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname ATTRIBUTE_UNUSED,
_("Cannot create macvlan devices on this platform"));
return -1;
}
+
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_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 130ecea..14640cf 100644
--- a/src/util/virnetdevmacvlan.h
+++ b/src/util/virnetdevmacvlan.h
@@ -75,4 +75,13 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_NONNULL(6) ATTRIBUTE_RETURN_CHECK;
+int virNetDevMacVLanRestartWithVPortProfile(const char *cr_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, 9 months
[libvirt] LIBVIRT-PHP List of domains
by Ali Raza Memon
Hi.. I am trying to get the names of all the xen domains using libvirt-php. I saw this php script: <?php
$conn = libvirt_connect('null', false);
$doms = libvirt_list_domains($conn);
print_r($doms); ?> It gives me error. Could you please tell me how to connect to xen hypervisor? I have also tried: $conn = libvirt_connect('xen:///', false); but it wont work..
12 years, 9 months
Re: [libvirt] [PATCH] Add migration APIs for libxl driver
by Chunyan Liu
>>>> "Chun Yan Liu" <cyliu(a)suse.com> 3/6/2012 2:29 PM >>>
>> I didn't get a chance to test this yet, but have some initial review
>> comments.
>>
>>> Signed-off-by: Chunyan Liu
>>> ---
>>> src/libxl/libxl_driver.c | 617
>>> ++++++++++++++++++++++++++++++++++++++++++++++
>>> src/libxl/libxl_driver.h | 17 ++-
>>> 2 files changed, 632 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
>>> index d5fa64a..4037635 100644
>>> --- a/src/libxl/libxl_driver.c
>>> +++ b/src/libxl/libxl_driver.c
>>> @@ -30,6 +30,12 @@
>>> #include
>>> #include
>>> #include
>>> +#include
>>> +#include
>>> +#include
>>> +#include
>>> +#include
>>> +#include
>>>
>>> #include "internal.h"
>>> #include "logging.h"
>>> @@ -60,6 +66,13 @@
>>> #define XEN_SCHED_CREDIT_NPARAM 2
>>>
>>> static libxlDriverPrivatePtr libxl_driver = NULL;
>>> +static virThread migrate_receive_thread;
>>>
>>
>> This prevents receiving concurrent migrations.
> Yes. It is a problem. Defined as "static" is to be used in Begin3 function
> virThreadCreate and in Finish3() function virThreadJoin, but actually the
> thread will exit when receiving data completed or meets error, so
> virThreadJoin doesn't have much effect here. If a call of virThreadJoin is
> not needed, then can specify migrate_receive_thread as a local variable.
About concurrent migrations, there is another problem in migration
port. Every time a migration operation is issued, it will create a
socket connection between source and host to transfer data. If a port
specified, target side will create a socket and bind to that port,
otherwise will bind to a DEFAULT_MIGRATION_PORT (current
implementation). But if 1st migration occupies the
DEFAULT_MIGRATION_PORT, then 2nd migration (without specifying port)
will fail to bind to the DEFAULT_MIGRATION_PORT again. So, to ensure
concurrent migrations, perhaps we need a group of ports for migration
usage, every migration operation probes for a usable port. How do you
think?
Thanks,
Chunyan
12 years, 9 months