Devel
Threads by month
- ----- 2026 -----
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
March 2012
- 100 participants
- 289 discussions
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
2
2
[libvirt] [PATCH] qemu: change rbd auth_supported separation character to ;
by Josh Durgin 06 Apr '12
by Josh Durgin 06 Apr '12
06 Apr '12
This works with newer qemu that doesn't allow escaping spaces.
It's backwards compatible as well.
Signed-off-by: Josh Durgin <josh.durgin(a)dreamhost.com>
---
src/qemu/qemu_command.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f971a08..ee3bf48 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1673,8 +1673,8 @@ qemuBuildRBDString(virConnectPtr conn,
virReportOOMError();
goto error;
}
- virBufferEscape(opt, '\\', ":",
- ":key=%s:auth_supported=cephx none",
+ virBufferEscape(opt, '\\', ":;",
+ ":key=%s:auth_supported=cephx;none",
base64);
VIR_FREE(base64);
} else {
--
1.7.5.4
1
1
While investigating the RPC problems I've noticed that we have had a
huge performance regression for virDomainGetInfo() due to the change
which makes virDomainEndJob() save the XML status file every time it
is called.
Previous to this change, 2000 calls to virDomainGetInfo() took ~2.5 seconds
After this change, 2000 calls to virDomainGetInfo() take 2 *minutes* 45 secs
I don't have an answer right now and it shouldnt block the release, but we
have todo something to avoid this overhead.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
2
3
05 Apr '12
Hi everyone,
following our minutes, I'd like to start a discussion on what should be
done with libvirt-test-API so we can say it's stable and usable.
I would like to stress out that everything mentioned here is just an
opinion and I don't mean to talk down to someone as it may have seemed
earlier.
I think we should get some ideas from everyone, mostly QE as they will
be (are) the ones using this the most (if I understood correctly), and
then I'll be happy to help getting the code to the agreed status. I was
thinking about this from the wrong way probably and changing the angle
from what I look at it (and knowing there is some deadline) makes me
think of few levels of changes, which when introduced, could speed up
the test development and code understandability.
So here are the things I would like to do definitely (the optional
things follow later on):
- fix hard-coded options into real options (e.g. commit 65449e)
- fix some env_* and util* code (functions duplicated with different
behavior)
- fix or remove harmful and pointless code (at this point, when
creating domain on remote machine, be prepared for the test to fail with
any other user then root and with root, have backup of both local and
remote '/root/.ssh' directories as the contents will be erased!)
- fix method names for the {connect,domain,etc.}API (get_host_name vs.
lookupByUUID etc.)
The optional things:
- get rid of classes in lib and make just few utility functions
covering *only* the methods that do something else than call the same
method in underlying class from the libvirt module.
- get rid of the new exception (I don't see any other difference than
in the name, which can make a difference in "except:" clause, but it's
converted everywhere)
- be able to share variables between tests (connection object and
anything else)
- introduce new config file for tests (.ini format, can be parsed by
ConfigParser, same as env.cfg, define variables used throughout the test
specifications
- update the documentation
- use some python code style (PEP-8?), make use of True/False, None
- eliminate duplicated (and x-plicated) code (append_path in all the
files, etc.)
I have all of these figured out, so I'm willing to discuss all of them,
but in most cases changing it in the current code seems very
time-consumable to me.
Please, feel free to comment on any of these, add yours, discuss, shout
at me, etc. =)
Regards,
Martin
P.S.: I don't see any point in sending my patches until some of these
points are resolved as that could mean rewriting more code.
5
10
Hi,
I grabbed today's git head of libvirt. Created a VM (clean install of
ubuntu oneiric, installed through virt-manager), and cloned it 3 times.
Then I did
serge@ubuntu:~$ for i in `seq 1 4`; do virsh start o$i > /tmp/o$i 2>&1 & done
[1] 12184
[2] 12185
[3] 12186
[4] 12187
serge@ubuntu:~$ virsh list
error: Failed to list active domains
error: End of file while reading data: Input/output error
serge@ubuntu:~$ virsh list
Id Name State
----------------------------------------------------
5 o2 shut off
7 o3 shut off
8 o4 shut off
[1] Exit 1 virsh start o$i > /tmp/o$i 2>&1
[2] Done virsh start o$i > /tmp/o$i 2>&1
serge@ubuntu:~$ virsh list
Id Name State
----------------------------------------------------
5 o2 running
7 o3 running
8 o4 running
[3]- Done virsh start o$i > /tmp/o$i 2>&1
[4]+ Done virsh start o$i > /tmp/o$i 2>&1
serge@ubuntu:~$ cat /tmp/o1
error: Failed to start domain o1
error: Unable to wait for child process: Bad file descriptor
It's quite reproducible. An ubuntu bug report was filed at
https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/961217
It's also very reminiscent of some lxc startup handshake issues
which have been fixed.
You can find the last chunk of the log file at
http://people.canonical.com/~serge/libvirtd-parallel-startup.log
-serge
2
4
04 Apr '12
This patch set fixes two bugs:
https://bugzilla.redhat.com/show_bug.cgi?id=806449
https://bugzilla.redhat.com/show_bug.cgi?id=743671
tl;dr - we are only adding to the list, removing only on
detach-device. Need to remove on qemuProcessStop too.
And need to rebuild the list on qemuProcessReconnect.
Michal Privoznik (3):
qemu: Don't leak temporary list of USB devices
qemu: Delete USB devices used by domain on stop
qemu: Build activeUsbHostdevs list on process reconnect
src/qemu/qemu_hostdev.c | 112 +++++++++++++++++++++++++++++++++++++++++++++--
src/qemu/qemu_hostdev.h | 2 +
src/qemu/qemu_process.c | 3 +
3 files changed, 113 insertions(+), 4 deletions(-)
--
1.7.8.5
2
8
Is there a method that can be used to remove the sysctl tuning for FreeBSD
in the pipeline?
Here are the current patches I have, but would like to remove:
http://www.freebsd.org/cgi/cvsweb.cgi/ports/devel/libvirt/files/
Thanks!
Jason
2
3
Hi I want use the virDomainInterfaceStats for get statistic of domain,
but I don't now how get path parametr.
Please help me!
Thanks.
4
3
03 Apr '12
From: "Daniel P. Berrange" <berrange(a)redhat.com>
There are a number of flaws with our packaging of the libvirtd
daemon:
- Installing 'libvirt' does not install 'qemu-kvm' or 'xen'
etc which are required to actually run the hypervisor in
question
- Installing 'libvirt' pulls in the default configuration
files which may not be wanted & cause problems if installed
inside a guest
- It is not possible to explicitly required all the peices
required to manage a specific hypervisor
This change takes the 'libvirt' RPM and and changes it thus
- libvirt: just a virtual package with dep on libvirt-daemon,
libvirt-daemon-config-network & libvirt-daemon-config-nwfilter
- libvirt-daemon: the libvirt daemon and related pieces
- libvirt-daemon-config-network: the default network config
- libvirt-daemon-config-nwfilter: the network filter configs
- libvirt-docs: the website HTML
We then introduce some more virtual (empty) packages
- libvirt-daemon-qemu: Deps on libvirt-daemon & 'qemu'
- libvirt-daemon-kvm: Deps on libvirt-daemon & 'qemu-kvm'
- libvirt-daemon-lxc: Deps on libvirt-daemon
- libvirt-daemon-uml: Deps on libvirt-daemon
- libvirt-daemon-xen: Deps on libvirt-daemon & 'xen'
- libvirt-qemu: Deps on libvirt-daemon-qemu & libvirt-daemon-config-{network,nwfilter}
- libvirt-kvm: Deps on libvirt-daemon-kvm & libvirt-daemon-config-{network,nwfilter}
- libvirt-lxc: Deps on libvirt-daemon-lxc & libvirt-daemon-config-{network,nwfilter}
- libvirt-uml: Deps on libvirt-daemon-uml & libvirt-daemon-config-{network,nwfilter}
- libvirt-xen: Deps on libvirt-daemon-xen & libvirt-daemon-config-network
My intent in the future is to turn on the driver modules by
default, at which time 'libvirt-daemon' will cease to include
any specific drivers, instead we'll get libvirt-daemon-driver-XXXX
packages for each driver. The libvirt-daemon-XXX packages will
then pull in each driver that they require.
It is recommended that applications required a locally installed
libvirtd daemon, use either 'Requires: libvirt-daemon-XXXX' or
'Requires: libvirt-XXX' and *not* "Requires: libvirt-daemon"
or 'Requires: libvirt'
* libvirt.spec.in: Refactor RPMs
* docs/packaging.html.in, docs/sitemap.html.in: Document
new RPM split rationale
---
docs/packaging.html.in | 106 ++++++++++
docs/sitemap.html.in | 4 +
libvirt.spec.in | 512 +++++++++++++++++++++++++++++++++++++-----------
3 files changed, 505 insertions(+), 117 deletions(-)
create mode 100644 docs/packaging.html.in
In v2:
- Split libvirt-daemon-configs into libvirt-daemon-config-network
and libvirt-daemon-config-nwfilter
- Fix ordering of QEMU/KVM bits
- Don't install nwfilter configs if the driver is turned off
- Enable nwfilter for LXC/UML drivers explicitly, not just KVM
- Fix docs typos
- Make all %files sections conditional
Tested build on F17 and RHEL-6.3
diff --git a/docs/packaging.html.in b/docs/packaging.html.in
new file mode 100644
index 0000000..0adffd3
--- /dev/null
+++ b/docs/packaging.html.in
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<html>
+ <body>
+ <h1>Distribution packaging</h1>
+
+ <ul id="toc"></ul>
+
+ <p>
+ This page describes the rationale behind the libvirt distribution
+packaging in RPM format. The RPM specfile provided with libvirt targets
+all Fedora and RHEL releases. It is split up into a number of sub-RPMs
+in order to facilitate minimal installations, targetting specific
+feature sets.
+ </p>
+
+ <h2><a name="real">Real packages</a></h2>
+
+ <p>
+ The so called "real" packages provide the actual file payloads
+ related to libvirt. If very specific / targetted functionality
+ is required, then applications can depend on one or more of these
+ real packages.
+ </p>
+
+ <dl>
+ <dt>libvirt-client</dt>
+ <dd>This package provides the main libvirt.so library along with
+ the virsh command line tool. If a C based application only wants
+ to be able to manage remote hypervisors, this is all that they
+ need depend on</dd>
+ <dt>libvirt-devel</dt>
+ <dd>This package provides the header files and libraries required
+ to compile and link C applications using libvirt</dd>
+ <dt>libvirt-python</dt>
+ <dd>This package provides the Python binding to the C libraries.
+ It will pull in the libvirt-client RPM. If a Python application
+ only wants to be able to manage remote hypervisors, this is all
+ that they need depend on</dd>
+ <dt>libvirt-daemon</dt>
+ <dd>This package provides server side libvirtd daemon, which is
+ required in order to manage any stateful hypervisors (currently
+ QEMU, KVM, Xen, LXC and UML).</dd>
+ <dt>libvirt-daemon-config-network</dt>
+ <dd>This package provides the standard configuration files for
+ setting up a NAT based network</dd>
+ <dt>libvirt-daemon-config-nwfilter</dt>
+ <dd>This package provides the standard configuration files for
+ network filter rules for ensuring clean VM traffic.</dd>
+ </dl>
+
+ <h2><a name="virtual">Virtual packages</a></h2>
+
+ <p>
+ The virtual packages provide convenient targets for application dependencies to
+ pull in functionality related to specific hypervisors. Since the packaging of
+ the <code>libvirt-daemon</code> RPM is expected to change in the future to split
+ each hypervisor driver out into a separate RPM, applications are strongly
+ recommended to depend on one of the following virtual packages, instead of
+ depending directly on <code>libvirt-daemon</code>
+ </p>
+
+ <dl>
+ <dt>libvirt</dt>
+ <dd>This package, simply pulls in every single other server side RPM.
+ If an application wants to ensure all possible libvirt drivers are installed,
+ this is what they should depend on</dd>
+
+ <dt>libvirt-daemon-qemu</dt>
+ <dd>This package pulls in the server side daemon, drivers and the QEMU TCG binaries
+ required to provide emulation of non-native architectures</dd>
+ <dt>libvirt-daemon-kvm</dt>
+ <dd>This package pulls in the server side daemon, drivers and the KVM binaries
+ required to provide hardware accelerated virtualization of the native
+ architectures</dd>
+ <dt>libvirt-daemon-lxc</dt>
+ <dd>This package pulls in the server side daemon and drivers required to
+ run native Linux containers</dd>
+ <dt>libvirt-daemon-uml</dt>
+ <dd>This package pulls in the server side daemon and drivers required to
+ run User Mode Linux. The application must still provide the actual
+ UML binary kernels</dd>
+ <dt>libvirt-daemon-xen</dt>
+ <dd>This package pulls in the server side daemon and drivers required to
+ run guests on the Xen hypervisor.</dd>
+
+ <dt>libvirt-qemu</dt>
+ <dd>This package pulls in the server side daemon, drivers and the QEMU TCG binaries
+ required to provide emulation of non-native architectures</dd>
+ <dt>libvirt-kvm</dt>
+ <dd>This package pulls in the server side daemon, drivers and the KVM binaries
+ required to provide hardware accelerated virtualization of the native
+ architectures</dd>
+ <dt>libvirt-lxc</dt>
+ <dd>This package pulls in the server side daemon, drivers and default
+ configuration files required to run native Linux containers</dd>
+ <dt>libvirt-uml</dt>
+ <dd>This package pulls in the server side daemon, drivers and default
+ configuration files required to run User Mode Linux. The application
+ must still provide the actual UML binary kernels</dd>
+ <dt>libvirt-xen</dt>
+ <dd>This package pulls in the server side daemon, drivers and default
+ configuration files required to run guests on the Xen hypervisor.</dd>
+ </dl>
+
+ </body>
+</html>
diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in
index 1de2b20..620c989 100644
--- a/docs/sitemap.html.in
+++ b/docs/sitemap.html.in
@@ -84,6 +84,10 @@
<a href="hooks.html">Hooks</a>
<span>Hooks for system specific management</span>
</li>
+ <li>
+ <a href="packaging.html">Distribution packaging</a>
+ <span>Rationale for distribution RPM packaging</span>
+ </li>
</ul>
</li>
<li>
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 67f1c33..8730881 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -52,6 +52,14 @@
%define with_libxl 0%{!?_without_libxl:%{server_drivers}}
%define with_vmware 0%{!?_without_vmware:%{server_drivers}}
+%define with_qemu_tcg %{with_qemu}
+# Change if we ever provide qemu-kvm binaries on non-x86 hosts
+%ifarch %{ix86} x86_64
+%define with_qemu_kvm %{with_qemu}
+%else
+%define with_qemu_kvm 0
+%endif
+
# Then the hypervisor drivers that talk via a native remote protocol
%define with_phyp 0%{!?_without_phyp:1}
%define with_esx 0%{!?_without_esx:1}
@@ -125,8 +133,10 @@
# RHEL-5 has restricted QEMU to x86_64 only and is too old for LXC
%if 0%{?rhel} == 5
+%define with_qemu_tcg 0
%ifnarch x86_64
%define with_qemu 0
+%define with_qemu_kvm 0
%endif
%define with_lxc 0
%endif
@@ -134,8 +144,10 @@
# RHEL-6 has restricted QEMU to x86_64 only, stopped including Xen
# on all archs. Other archs all have LXC available though
%if 0%{?rhel} >= 6
+%define with_qemu_tcg 0
%ifnarch x86_64
%define with_qemu 0
+%define with_qemu_kvm 0
%endif
%define with_xen 0
%endif
@@ -206,10 +218,13 @@
%define with_storage_disk 0
%endif
-%if %{with_qemu}
+%if %{with_qemu} || %{with_lxc} || %{with_uml}
%define with_nwfilter 0%{!?_without_nwfilter:%{server_drivers}}
# Enable libpcap library
%define with_libpcap 0%{!?_without_libpcap:%{server_drivers}}
+%endif
+
+%if %{with_qemu}
%define with_macvtap 0%{!?_without_macvtap:%{server_drivers}}
# numad is used to manage the CPU placement dynamically,
@@ -268,109 +283,22 @@ Source: http://libvirt.org/sources/libvirt-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
URL: http://libvirt.org/
-# All runtime requirements for the libvirt package (runtime requrements
-# for subpackages are listed later in those subpackages)
-
-# The client side, i.e. shared libs and virsh are in a subpackage
-Requires: %{name}-client = %{version}-%{release}
-
-# Used by many of the drivers, so turn it on whenever the
-# daemon is present
%if %{with_libvirtd}
-# for modprobe of pci devices
-Requires: module-init-tools
-# for /sbin/ip & /sbin/tc
-Requires: iproute
-%if %{with_avahi}
-Requires: avahi-libs
-%endif
-%endif
+Requires: libvirt-daemon = %{version}-%{release}
%if %{with_network}
-Requires: dnsmasq >= 2.41
-Requires: radvd
-%endif
-%if %{with_network} || %{with_nwfilter}
-Requires: iptables
-Requires: iptables-ipv6
+Requires: libvirt-daemon-config-network = %{version}-%{release}
%endif
%if %{with_nwfilter}
-Requires: ebtables
-%endif
-# needed for device enumeration
-%if %{with_hal}
-Requires: hal
-%endif
-%if %{with_udev}
-Requires: udev >= 145
-%endif
-%if %{with_polkit}
-%if 0%{?fedora} >= 12 || 0%{?rhel} >=6
-Requires: polkit >= 0.93
-%else
-Requires: PolicyKit >= 0.6
-%endif
-%endif
-%if %{with_storage_fs}
-Requires: nfs-utils
-# For mkfs
-Requires: util-linux-ng
-# For pool-build probing for existing pools
-BuildRequires: libblkid-devel >= 2.17
-# For glusterfs
-%if 0%{?fedora} >= 11
-Requires: glusterfs-client >= 2.0.1
-%endif
-%endif
-%if %{with_qemu}
-# From QEMU RPMs
-Requires: /usr/bin/qemu-img
-# For image compression
-Requires: gzip
-Requires: bzip2
-Requires: lzop
-Requires: xz
-%else
-%if %{with_xen}
-# From Xen RPMs
-Requires: /usr/sbin/qcow-create
-%endif
-%endif
-%if %{with_storage_lvm}
-# For LVM drivers
-Requires: lvm2
-%endif
-%if %{with_storage_iscsi}
-# For ISCSI driver
-Requires: iscsi-initiator-utils
-%endif
-%if %{with_storage_disk}
-# For disk driver
-Requires: parted
-Requires: device-mapper
-%endif
-%if %{with_storage_mpath}
-# For multipath support
-Requires: device-mapper
-%endif
-%if %{with_cgconfig}
-Requires: libcgroup
-%endif
-%ifarch %{ix86} x86_64 ia64
-# For virConnectGetSysinfo
-Requires: dmidecode
+Requires: libvirt-daemon-config-nwfilter = %{version}-%{release}
%endif
-# For service management
-%if %{with_systemd}
-Requires(post): systemd-units
-Requires(post): systemd-sysv
-Requires(preun): systemd-units
-Requires(postun): systemd-units
-%endif
-%if %{with_numad}
-Requires: numad
+# XXX when we turn on driver modules, we need to add
+# deps on each driver (Requires: libvirt-daemon-drv-qemu)
%endif
+Requires: libvirt-docs = %{version}-%{release}
+Requires: libvirt-client = %{version}-%{release}
-# All build-time requirements
+# All build-time requirements. Run-time requirements are
+# listed against each sub-RPM
%if 0%{?enable_autotools}
BuildRequires: autoconf
BuildRequires: automake
@@ -537,6 +465,279 @@ Libvirt is a C toolkit to interact with the virtualization capabilities
of recent versions of Linux (and other OSes). The main package includes
the libvirtd server exporting the virtualization support.
+%package docs
+Summary: Documentation for libvirt library and daemon
+Group: Development/Libraries
+
+%description docs
+Copy of the libvirt website documentation
+
+%if %{with_libvirtd}
+%package daemon
+Summary: Server side daemon and supporting files for libvirt library
+Group: Development/Libraries
+
+# All runtime requirements for the libvirt package (runtime requrements
+# for subpackages are listed later in those subpackages)
+
+# The client side, i.e. shared libs and virsh are in a subpackage
+Requires: %{name}-client = %{version}-%{release}
+
+# Used by many of the drivers, so turn it on whenever the
+# daemon is present
+%if %{with_libvirtd}
+# for modprobe of pci devices
+Requires: module-init-tools
+# for /sbin/ip & /sbin/tc
+Requires: iproute
+%if %{with_avahi}
+Requires: avahi-libs
+%endif
+%endif
+%if %{with_network}
+Requires: dnsmasq >= 2.41
+Requires: radvd
+%endif
+%if %{with_network} || %{with_nwfilter}
+Requires: iptables
+Requires: iptables-ipv6
+%endif
+%if %{with_nwfilter}
+Requires: ebtables
+%endif
+# needed for device enumeration
+%if %{with_hal}
+Requires: hal
+%endif
+%if %{with_udev}
+Requires: udev >= 145
+%endif
+%if %{with_polkit}
+%if 0%{?fedora} >= 12 || 0%{?rhel} >=6
+Requires: polkit >= 0.93
+%else
+Requires: PolicyKit >= 0.6
+%endif
+%endif
+%if %{with_storage_fs}
+Requires: nfs-utils
+# For mkfs
+Requires: util-linux-ng
+# For pool-build probing for existing pools
+BuildRequires: libblkid-devel >= 2.17
+# For glusterfs
+%if 0%{?fedora} >= 11
+Requires: glusterfs-client >= 2.0.1
+%endif
+%endif
+%if %{with_qemu}
+# From QEMU RPMs
+Requires: /usr/bin/qemu-img
+# For image compression
+Requires: gzip
+Requires: bzip2
+Requires: lzop
+Requires: xz
+%else
+%if %{with_xen}
+# From Xen RPMs
+Requires: /usr/sbin/qcow-create
+%endif
+%endif
+%if %{with_storage_lvm}
+# For LVM drivers
+Requires: lvm2
+%endif
+%if %{with_storage_iscsi}
+# For ISCSI driver
+Requires: iscsi-initiator-utils
+%endif
+%if %{with_storage_disk}
+# For disk driver
+Requires: parted
+Requires: device-mapper
+%endif
+%if %{with_storage_mpath}
+# For multipath support
+Requires: device-mapper
+%endif
+%if %{with_cgconfig}
+Requires: libcgroup
+%endif
+%ifarch %{ix86} x86_64 ia64
+# For virConnectGetSysinfo
+Requires: dmidecode
+%endif
+# For service management
+%if %{with_systemd}
+Requires(post): systemd-units
+Requires(post): systemd-sysv
+Requires(preun): systemd-units
+Requires(postun): systemd-units
+%endif
+%if %{with_numad}
+Requires: numad
+%endif
+
+%description daemon
+Server side daemon required to manage the virtualization capabilities
+of recent versions of Linux. Requires a hypervisor specific sub-RPM
+for specific drivers.
+
+%if %{with_network}
+%package daemon-config-network
+Summary: Default configuration files for the libvirtd daemon
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+
+%description daemon-config-network
+Default configuration files for setting up NAT based networking
+%endif
+
+%if %{with_nwfilter}
+%package daemon-config-nwfilter
+Summary: Network filter configuration files for the libvirtd daemon
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+
+%description daemon-config-nwfilter
+Network filter configuration files for cleaning guest traffic
+%endif
+
+# XXX when we turn on driver modules, we will need to
+# create daemon-drv-XXX sub-RPMs and add them as deps
+# to all of the following daemon-XXX RPMs
+
+%if %{with_qemu_tcg}
+%package daemon-qemu
+Summary: Server side daemon & driver required to run QEMU guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+Requires: qemu
+
+%description daemon-qemu
+Server side daemon and driver required to manage the virtualization
+capabilities of the QEMU TCG emulators
+
+%package qemu
+Summary: Server side daemon, driver & default configs required to run QEMU guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon-qemu = %{version}-%{release}
+Requires: libvirt-daemon-config-network = %{version}-%{release}
+Requires: libvirt-daemon-config-nwfilter = %{version}-%{release}
+
+%description qemu
+Server side daemon, driver and default network & firewall configs
+required to manage the virtualization capabilities of QEMU.
+%endif
+
+
+%if %{with_qemu_kvm}
+%package daemon-kvm
+Summary: Server side daemon & driver required to run QEMU guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+Requires: qemu-kvm
+
+%description daemon-kvm
+Server side daemon and driver required to manage the virtualization
+capabilities of the QEMU KVM hypervisor
+
+%package kvm
+Summary: Server side daemon, driver & default configs required to run KVM guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon-kvm = %{version}-%{release}
+Requires: libvirt-daemon-config-network = %{version}-%{release}
+Requires: libvirt-daemon-config-nwfilter = %{version}-%{release}
+
+%description kvm
+Server side daemon, driver and default network & firewall configs
+required to manage the virtualization capabilities of KVM.
+%endif
+
+
+%if %{with_lxc}
+%package daemon-lxc
+Summary: Server side daemon & driver required to run LXC guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+
+%description daemon-lxc
+Server side daemon and driver required to manage the virtualization
+capabilities of LXC
+
+%package lxc
+Summary: Server side daemon, driver & default configs required to run LXC guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon-lxc = %{version}-%{release}
+Requires: libvirt-daemon-config-network = %{version}-%{release}
+Requires: libvirt-daemon-config-nwfilter = %{version}-%{release}
+
+%description lxc
+Server side daemon, driver and default network & firewall configs
+required to manage the virtualization capabilities of LXC.
+%endif
+
+
+%if %{with_uml}
+%package daemon-uml
+Summary: Server side daemon & driver required to run UML guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+# There are no UML kernel RPMs in Fedora/RHEL to depend on.
+
+%description daemon-uml
+Server side daemon and driver required to manage the virtualization
+capabilities of UML
+
+%package uml
+Summary: Server side daemon, driver & default configs required to run UML guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon-uml = %{version}-%{release}
+Requires: libvirt-daemon-config-network = %{version}-%{release}
+Requires: libvirt-daemon-config-nwfilter = %{version}-%{release}
+
+%description uml
+Server side daemon, driver and default network & firewall configs
+required to manage the virtualization capabilities of UML.
+%endif
+
+
+%if %{with_xen}
+%package daemon-xen
+Summary: Server side daemon & driver required to run XEN guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon = %{version}-%{release}
+Requires: xen
+
+%description daemon-xen
+Server side daemon and driver required to manage the virtualization
+capabilities of XEN
+
+%package xen
+Summary: Server side daemon, driver & default configs required to run XEN guests
+Group: Development/Libraries
+
+Requires: libvirt-daemon-xen = %{version}-%{release}
+Requires: libvirt-daemon-config-network = %{version}-%{release}
+
+%description xen
+Server side daemon, driver and default network & firewall configs
+required to manage the virtualization capabilities of Xen.
+%endif
+%endif
+
%package client
Summary: Client side library and utilities of the libvirt library
Group: Development/Libraries
@@ -582,7 +783,7 @@ Group: Development/Libraries
Requires: sanlock >= 1.8
#for virt-sanlock-cleanup require augeas
Requires: augeas
-Requires: %{name} = %{version}-%{release}
+Requires: %{name}-daemon = %{version}-%{release}
%description lock-sanlock
Includes the Sanlock lock manager plugin for the QEMU
@@ -884,6 +1085,12 @@ rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/libvirtd.lxc
rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/libvirtd.uml
%endif
+mv $RPM_BUILD_ROOT%{_datadir}/doc/libvirt-%{version} $RPM_BUILD_ROOT%{_datadir}/doc/libvirt-docs-%{version}
+
+%if ! %{with_nwfilter}
+rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/libvirt/nwfilter
+%endif
+
%clean
rm -fr %{buildroot}
@@ -898,7 +1105,8 @@ do
done
make check
-%pre
+%if %{with_libvirtd}
+%pre daemon
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
# Normally 'setup' adds this in /etc/passwd, but this is
# here for case of upgrades from earlier Fedora/RHEL. This
@@ -910,22 +1118,9 @@ getent passwd qemu >/dev/null || \
-c "qemu user" qemu
%endif
-%post
+%post daemon
-%if %{with_libvirtd}
%if %{with_network}
-# We want to install the default network for initial RPM installs
-# or on the first upgrade from a non-network aware libvirt only.
-# We check this by looking to see if the daemon is already installed
-if ! /sbin/chkconfig libvirtd && test ! -f %{_sysconfdir}/libvirt/qemu/networks/default.xml
-then
- UUID=`/usr/bin/uuidgen`
- sed -e "s,</name>,</name>\n <uuid>$UUID</uuid>," \
- < %{_datadir}/libvirt/networks/default.xml \
- > %{_sysconfdir}/libvirt/qemu/networks/default.xml
- ln -s ../default.xml %{_sysconfdir}/libvirt/qemu/networks/autostart/default.xml
-fi
-
# All newly defined networks will have a mac address for the bridge
# auto-generated, but networks already existing at the time of upgrade
# will not. We need to go through all the network configs, look for
@@ -991,8 +1186,8 @@ fi
%endif
%endif
-%preun
%if %{with_libvirtd}
+%preun daemon
%if %{with_systemd}
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
@@ -1007,8 +1202,8 @@ fi
%endif
%endif
-%postun
%if %{with_libvirtd}
+%postun daemon
%if %{with_systemd}
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
if [ $1 -ge 1 ] ; then
@@ -1019,6 +1214,20 @@ fi
%endif
%if %{with_libvirtd}
+%if %{with_network}
+%post daemon-config-network
+if test $1 -eq 1 && test ! -f %{_sysconfdir}/libvirt/qemu/networks/default.xml ; then
+ UUID=`/usr/bin/uuidgen`
+ sed -e "s,</name>,</name>\n <uuid>$UUID</uuid>," \
+ < %{_datadir}/libvirt/networks/default.xml \
+ > %{_sysconfdir}/libvirt/qemu/networks/default.xml
+ ln -s ../default.xml %{_sysconfdir}/libvirt/qemu/networks/autostart/default.xml
+fi
+%endif
+%endif
+
+
+%if %{with_libvirtd}
%if %{with_systemd}
%triggerun -- libvirt < 0.9.4
%{_bindir}/systemd-sysv-convert --save libvirtd >/dev/null 2>&1 ||:
@@ -1064,10 +1273,19 @@ fi
/bin/systemctl try-restart libvirt-guests.service >/dev/null 2>&1 || :
%endif
-%if %{with_libvirtd}
%files
%defattr(-, root, root)
+%files docs
+%defattr(-, root, root)
+%dir %{_datadir}/doc/libvirt-docs-%{version}
+%dir %{_datadir}/doc/libvirt-docs-%{version}/html
+%{_datadir}/doc/libvirt-docs-%{version}/html/*
+
+%if %{with_libvirtd}
+%files daemon
+%defattr(-, root, root)
+
%doc AUTHORS ChangeLog.gz NEWS README COPYING.LIB TODO
%dir %attr(0700, root, root) %{_sysconfdir}/libvirt/
@@ -1078,7 +1296,6 @@ fi
%endif
%dir %attr(0700, root, root) %{_sysconfdir}/libvirt/nwfilter/
-%{_sysconfdir}/libvirt/nwfilter/*.xml
%{_sysconfdir}/rc.d/init.d/libvirtd
%if %{with_systemd}
@@ -1190,6 +1407,67 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd
%{_mandir}/man8/libvirtd.8*
%doc docs/*.xml
+
+%if %{with_network}
+%files daemon-config-network
+%defattr(-, root, root)
+%endif
+
+%if %{with_nwfilter}
+%files daemon-config-nwfilter
+%defattr(-, root, root)
+%{_sysconfdir}/libvirt/nwfilter/*.xml
+%endif
+
+%if %{with_qemu_tcg}
+%files daemon-qemu
+%defattr(-, root, root)
+%endif
+
+%if %{with_qemu_kvm}
+%files daemon-kvm
+%defattr(-, root, root)
+%endif
+
+%if %{with_lxc}
+%files daemon-lxc
+%defattr(-, root, root)
+%endif
+
+%if %{with_uml}
+%files daemon-uml
+%defattr(-, root, root)
+%endif
+
+%if %{with_xen}
+%files daemon-xen
+%defattr(-, root, root)
+%endif
+
+%if %{with_qemu_tcg}
+%files qemu
+%defattr(-, root, root)
+%endif
+
+%if %{with_qemu_kvm}
+%files kvm
+%defattr(-, root, root)
+%endif
+
+%if %{with_lxc}
+%files lxc
+%defattr(-, root, root)
+%endif
+
+%if %{with_uml}
+%files uml
+%defattr(-, root, root)
+%endif
+
+%if %{with_xen}
+%files xen
+%defattr(-, root, root)
+%endif
%endif
%if %{with_sanlock}
--
1.7.7.6
4
7
Laszlo Ersek pointed out that in trying to convert a long to an
unsigned int, we used:
long long_val = ...;
if ((unsigned int)long_val == long_val)
According to C99 integer promotion rules, the if statement is
equivalent to:
(unsigned long)(unsigned int)long_val == (unsigned long)long_val
since you get an unsigned comparison if at least one side is
unsigned, using the largest rank of the two sides; but on 32-bit
platforms, where unsigned long and unsigned int are the same size,
this comparison is always true and ends up converting negative
long_val into posigive unsigned int values, rather than rejecting
the negative value as we had originally intended (python longs
are unbounded size, and we don't want to do silent modulo
arithmetic when converting to C code).
Fix this by using direct comparisons, rather than casting.
* python/typewrappers.c (libvirt_intUnwrap, libvirt_uintUnwrap)
(libvirt_ulongUnwrap, libvirt_ulonglongUnwrap): Fix conversion
checks.
---
python/typewrappers.c | 27 ++++++++++++++++++++-------
1 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/python/typewrappers.c b/python/typewrappers.c
index af209e6..4ae3ee1 100644
--- a/python/typewrappers.c
+++ b/python/typewrappers.c
@@ -132,7 +132,7 @@ libvirt_intUnwrap(PyObject *obj, int *val)
if ((long_val == -1) && PyErr_Occurred())
return -1;
- if ((int)long_val == long_val) {
+ if (long_val <= INT_MAX && long_val >= INT_MIN) {
*val = long_val;
} else {
PyErr_SetString(PyExc_OverflowError,
@@ -151,7 +151,7 @@ libvirt_uintUnwrap(PyObject *obj, unsigned int *val)
if ((long_val == -1) && PyErr_Occurred())
return -1;
- if ((unsigned int)long_val == long_val) {
+ if (long_val >= 0 && long_val <= UINT_MAX) {
*val = long_val;
} else {
PyErr_SetString(PyExc_OverflowError,
@@ -183,7 +183,13 @@ libvirt_ulongUnwrap(PyObject *obj, unsigned long *val)
if ((long_val == -1) && PyErr_Occurred())
return -1;
- *val = long_val;
+ if (long_val >= 0) {
+ *val = long_val;
+ } else {
+ PyErr_SetString(PyExc_OverflowError,
+ "negative Python int cannot be converted to C unsigned long");
+ return -1;
+ }
return 0;
}
@@ -207,16 +213,23 @@ int
libvirt_ulonglongUnwrap(PyObject *obj, unsigned long long *val)
{
unsigned long long ullong_val = -1;
+ long long llong_val;
/* The PyLong_AsUnsignedLongLong doesn't check the type of
* obj, only accept argument of PyLong_Type, so we check it instead.
*/
- if (PyInt_Check(obj))
- ullong_val = PyInt_AsLong(obj);
- else if (PyLong_Check(obj))
+ if (PyInt_Check(obj)) {
+ llong_val = PyInt_AsLong(obj);
+ if (llong_val < 0)
+ PyErr_SetString(PyExc_OverflowError,
+ "negative Python int cannot be converted to C unsigned long long");
+ else
+ ullong_val = llong_val;
+ } else if (PyLong_Check(obj)) {
ullong_val = PyLong_AsUnsignedLongLong(obj);
- else
+ } else {
PyErr_SetString(PyExc_TypeError, "an integer is required");
+ }
if ((ullong_val == -1) && PyErr_Occurred())
return -1;
--
1.7.1
3
3