[libvirt] [PATCH] Fix comment about GNUTLS initialization/cleanup
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
---
src/rpc/virnettlscontext.c | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
index 74a61e0..7440c7a 100644
--- a/src/rpc/virnettlscontext.c
+++ b/src/rpc/virnettlscontext.c
@@ -1423,9 +1423,13 @@ void virNetTLSSessionFree(virNetTLSSessionPtr sess)
* virNetTLS* because it initializes
* underlying GnuTLS library. According to
* it's documentation, it's safe to be called
- * many times, but is not thread safe. Each
- * call SHOULD be later followed by
- * virNetTLSContextDeinit.
+ * many times, but is not thread safe.
+ *
+ * There is no corresponding "Deinit" / "Cleanup"
+ * function because there is no safe way to call
+ * 'gnutls_global_deinit' from a multi-threaded
+ * library, where other libraries linked into the
+ * application may also be using gnutls.
*/
void virNetTLSInit(void)
{
--
1.7.7.6
13 years, 1 month
[libvirt] Memory leak in libvirt / gnutls
by Richard W.M. Jones
I'm trying to track down:
https://bugzilla.redhat.com/show_bug.cgi?id=810613
I *think* this is a libvirt bug, but I can only reproduce it on one
machine, not on another machine that has a virtually identical setup.
Anyway, something is confusing me about libvirt. In this commit:
----------------------------------------------------------------------
commit 74c75671331d284e1f777f9692b72e9737520bf0
Author: Michal Privoznik <mprivozn(a)redhat.com>
Date: Thu Aug 18 10:44:08 2011 +0200
daemon: initialize GnuTLS
When spice_tls is set but listen_tls is not, we don't initialize
GnuTLS library. So any later gnutls call (e.g. during migration,
where we initialize a certificate) will access uninitialized GnuTLS
internal structs and throws an error.
Although, we might now initialize GnuTLS twice, it is safe according
to the documentation:
This function can be called many times,
but will only do something the first time.
This patch creates 2 functions: virNetTLSInit and virNetTLSDeinit
with respect to written above.
----------------------------------------------------------------------
... a pair of functions called virNetTLSInit & virNetTLSDeinit are
introduced, which would appear to do the right thing, calling first
gnutls_global_init, then gnutls_global_deinit. This looks correct to
me.
However the above commit is later amended by this commit:
----------------------------------------------------------------------
commit eaddec976ef06457fee4a4ce86b8c7ee906183b7
Author: Michal Privoznik <mprivozn(a)redhat.com>
Date: Wed Aug 24 16:16:45 2011 +0200
daemon: Move TLS initialization to virInitialize
My previous patch 74c75671331d284e1f777f9692b72e9737520bf0
introduced a regression by removing TLS initialization from client.
----------------------------------------------------------------------
which removes virNetTLSDeinit. This appears to be a mistake, or at
least I can't see the logical reason for it, and according to the
gnutls docs, it would introduce a memory leak looking exactly like the
one I am chasing down.
The current code mentions virNetTLSDeinit only in a comment, and never
calls gnutls_global_deinit.
Any ideas?
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
virt-top is 'top' for virtual machines. Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://et.redhat.com/~rjones/virt-top
13 years, 1 month
[libvirt] [PATCH] Fix compilation error on 32bit
by Stefan Berger
Below code failed to compile on a 32 bit machine with error
typewrappers.c: In function 'libvirt_intUnwrap':
typewrappers.c:135:5: error: logical 'and' of mutually exclusive tests
is always false [-Werror=logical-op]
cc1: all warnings being treated as errors
The patch fixes this error.
Stefan
---
python/typewrappers.c | 4 ++++
1 file changed, 4 insertions(+)
Index: libvirt-acl/python/typewrappers.c
===================================================================
--- libvirt-acl.orig/python/typewrappers.c
+++ libvirt-acl/python/typewrappers.c
@@ -132,6 +132,7 @@ libvirt_intUnwrap(PyObject *obj, int *va
if ((long_val == -1) && PyErr_Occurred())
return -1;
+#if LONG_MAX != INT_MAX
if (long_val >= INT_MIN && long_val <= INT_MAX) {
*val = long_val;
} else {
@@ -139,6 +140,9 @@ libvirt_intUnwrap(PyObject *obj, int *va
"Python int too large to convert to C int");
return -1;
}
+#else
+ *val = long_val;
+#endif
return 0;
}
13 years, 1 month
[libvirt] [PATCH 0/4] Replace daemon-conf test case
by Daniel P. Berrange
The daemon-conf test case is terminally broken by design, despite
numerous patches over the years, due to its need to test functionality
indirectly by running libvirtd. Replace it with a test case which
directly unit tests the code in question.
13 years, 1 month
[libvirt] [test-API PATCH 1/3] test-API: Get rid of utils/Python
by Osier Yang
test-API tends to only support testing via libvirt Python
binding now, and moving it to utils makes sense.
---
utils/{Python => }/XMLParser.py | 0
utils/{Python => }/check.py | 0
utils/{Python => }/env_parser.py | 0
utils/{Python => }/format.py | 0
utils/{Python => }/log.py | 0
utils/{Python => }/utils.py | 0
utils/{Python => }/xmlbuilder.py | 0
utils/{Python => }/xmlgenerator.py | 0
8 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 utils/Python/__init__.py
rename utils/{Python => }/XMLParser.py (100%)
rename utils/{Python => }/check.py (100%)
rename utils/{Python => }/env_parser.py (100%)
rename utils/{Python => }/format.py (100%)
rename utils/{Python => }/log.py (100%)
rename utils/{Python => }/utils.py (100%)
rename utils/{Python => }/xmlbuilder.py (100%)
rename utils/{Python => }/xmlgenerator.py (100%)
diff --git a/utils/Python/__init__.py b/utils/Python/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/utils/Python/XMLParser.py b/utils/XMLParser.py
similarity index 100%
rename from utils/Python/XMLParser.py
rename to utils/XMLParser.py
diff --git a/utils/Python/check.py b/utils/check.py
similarity index 100%
rename from utils/Python/check.py
rename to utils/check.py
diff --git a/utils/Python/env_parser.py b/utils/env_parser.py
similarity index 100%
rename from utils/Python/env_parser.py
rename to utils/env_parser.py
diff --git a/utils/Python/format.py b/utils/format.py
similarity index 100%
rename from utils/Python/format.py
rename to utils/format.py
diff --git a/utils/Python/log.py b/utils/log.py
similarity index 100%
rename from utils/Python/log.py
rename to utils/log.py
diff --git a/utils/Python/utils.py b/utils/utils.py
similarity index 100%
rename from utils/Python/utils.py
rename to utils/utils.py
diff --git a/utils/Python/xmlbuilder.py b/utils/xmlbuilder.py
similarity index 100%
rename from utils/Python/xmlbuilder.py
rename to utils/xmlbuilder.py
diff --git a/utils/Python/xmlgenerator.py b/utils/xmlgenerator.py
similarity index 100%
rename from utils/Python/xmlgenerator.py
rename to utils/xmlgenerator.py
--
1.7.7.5
13 years, 1 month
[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
13 years, 1 month
[libvirt] [PATCHv3 00/16] live block migration via virDomainBlockCopy
by Eric Blake
v1 was here:
https://www.redhat.com/archives/libvir-list/2012-April/msg00068.html
v2 was here:
https://www.redhat.com/archives/libvir-list/2012-April/msg00222.html
changes from v2: added patch 12/16, addressed some review comments
and made minor changes from more of my own testing
CAVEAT: Paolo and I had an IRC conversation, where we decided that
'drive-mirror' might do better to take a mandatory 'full':'bool'
(or an even more powerful optional '*base':'str') argument for
determining how much to stream (full vs. shallow, or even the
backing file of the copy as matching the base argument of
'block_stream') rather than overloading the 'mode':'no-backing-file'
as the only way to get a full pull. If that change goes in qemu,
then we will need a v4, to tweak how the 'drive-mirror' monitor
command is called, and take advantage of the additional flexibility
it offers.
changes from v1: Paolo has updated the qemu side of things, and built
a scratch image for RHEL that I was able to test with for the new
semantics. I was actually able to successfully run two different
block copy jobs, one canceled and one pivoted, with SELinux disabled
and this patch series.
Patch 13/16 is for reference only when working with Paolo's build;
it will not go upstream.
Patches 14-16 are optional; the extra flexibility might be nice, but
I haven't yet played with those three enough to know if Paolo's build
behaves like I was expecting.
Adam Litke (2):
blockjob: add API for async virDomainBlockJobAbort
blockjob: wire up qemu async virDomainBlockJobAbort
Eric Blake (14):
blockjob: allow for fast-finishing job
blockjob: add new API flags
blockjob: add 'blockcopy' to virsh
blockjob: enhance xml to track mirrors across libvirtd restart
blockjob: react to active block copy
blockjob: expose qemu commands for mirrored storage migration
blockjob: return appropriate event and info
blockjob: support pivot operation on cancel
blockjob: implement block copy for qemu
blockjob: allow for existing files
blockjob: accommodate RHEL backport names
blockjob: add virDomainBlockCopy
blockjob: enhance virsh 'blockcopy'
blockjob: wire up qemu and RPC for block copy
docs/apibuild.py | 1 +
docs/formatdomain.html.in | 11 ++
docs/schemas/domaincommon.rng | 19 ++-
include/libvirt/libvirt.h.in | 50 +++++-
include/libvirt/virterror.h | 1 +
src/conf/domain_conf.c | 54 ++++++
src/conf/domain_conf.h | 6 +
src/driver.h | 5 +
src/libvirt.c | 220 +++++++++++++++++++++++-
src/libvirt_private.syms | 1 +
src/libvirt_public.syms | 5 +
src/qemu/qemu_capabilities.c | 3 +
src/qemu/qemu_capabilities.h | 2 +
src/qemu/qemu_driver.c | 379 +++++++++++++++++++++++++++++++++++++++--
src/qemu/qemu_hotplug.c | 7 +
src/qemu/qemu_monitor.c | 50 ++++++
src/qemu/qemu_monitor.h | 26 +++-
src/qemu/qemu_monitor_json.c | 166 +++++++++++++++---
src/qemu/qemu_monitor_json.h | 19 ++-
src/remote/remote_driver.c | 1 +
src/remote/remote_protocol.x | 12 ++-
src/remote_protocol-structs | 9 +
src/rpc/gendispatch.pl | 1 +
src/util/virterror.c | 6 +
tools/virsh.c | 140 ++++++++++++---
tools/virsh.pod | 40 ++++-
26 files changed, 1143 insertions(+), 91 deletions(-)
--
1.7.7.6
13 years, 1 month
[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
13 years, 1 month
[libvirt] [PATCH] UML: may be typo
by MATSUDA, Daiki
I found typo in UML driver.
MATSUDA Daiki
--- libvirt-0.9.11/src/uml/uml_driver.c.orig 2012-04-09
11:56:47.105695476 +0900
+++ libvirt-0.9.11/src/uml/uml_driver.c 2012-04-09 11:56:56.653820665 +0900
@@ -249,7 +249,7 @@ umlIdentifyChrPTY(struct uml_driver *dri
{
int i;
- for (i = 0 ; i < dom->def->nserials; i++)
+ for (i = 0 ; i < dom->def->nconsoles; i++)
if (dom->def->consoles[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY)
if (umlIdentifyOneChrPTY(driver, dom,
dom->def->consoles[i], "con") < 0)
13 years, 1 month