[libvirt] [PATCH] nwfilter: Some fixes to XML parser
by Stefan Berger
While writing a couple of test cases for the nwfilter's XML parser I
found some cases where the output ended up not looking as expected. So
the following changes are in the patch below:
- if the protocol ID in the MAC header is an integer, just write it into
the datastructure without trying to find a corresponding string for it
and if none is found failing
- when writing the protocol ID as string, simply write it as integer if
no corresponding string can be found
- same changes for arpOpcode parsing and printing
- same changes for protocol ID in an IP packet
- DSCP value needs to be written into the data structure
- IP protocol version number is redundant at this level, so remove it
- parse the protocol ID found inside an IP packet not only as string but
also as uint8
- arrange the display of the src and destination masks to be shown after
the src and destination ip address respectively in the XML
- the existing libvirt IP address parser accepts for example '25' as an
IP address. I want this to be parsed as a CIDR type netmask. So try to
parse it as an integer first (CIDR netmask) and if that doesn't work as
a dotted IP address style netmask.
- instantiation of rules with MAC masks didn't work because they weren't
printed into a buffer, yet.
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
src/conf/nwfilter_conf.c | 128 ++++++++++++++----------------
src/nwfilter/nwfilter_ebiptables_driver.c | 1
2 files changed, 61 insertions(+), 68 deletions(-)
Index: libvirt-acl/src/conf/nwfilter_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/nwfilter_conf.c
+++ libvirt-acl/src/conf/nwfilter_conf.c
@@ -403,15 +403,12 @@ checkMacProtocolID(enum attrDatatype dat
virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
{
int32_t res = -1;
- const char *str;
if (datatype == DATATYPE_STRING) {
if (intMapGetByString(macProtoMap, (char *)value, 1, &res) == 0)
res = -1;
} else if (datatype == DATATYPE_UINT16) {
- if (intMapGetByInt(macProtoMap,
- (int32_t)*(uint16_t *)value, &str) == 0)
- res = -1;
+ res = (uint32_t)*(uint16_t *)value;
}
if (res != -1) {
@@ -433,9 +430,10 @@ macProtocolIDFormatter(virBufferPtr buf,
nwf->p.ethHdrFilter.dataProtocolID.u.u16,
&str)) {
virBufferVSprintf(buf, "%s", str);
- return 1;
+ } else {
+ virBufferVSprintf(buf, "%d", nwf->p.ethHdrFilter.dataProtocolID.u.u16);
}
- return 0;
+ return 1;
}
@@ -500,15 +498,12 @@ arpOpcodeValidator(enum attrDatatype dat
virNWFilterRuleDefPtr nwf)
{
int32_t res = -1;
- const char *str;
if (datatype == DATATYPE_STRING) {
if (intMapGetByString(arpOpcodeMap, (char *)value, 1, &res) == 0)
res = -1;
} else if (datatype == DATATYPE_UINT16) {
- if (intMapGetByInt(arpOpcodeMap,
- (uint32_t)*(uint16_t *)value, &str) == 0)
- res = -1;
+ res = (uint32_t)*(uint16_t *)value;
}
if (res != -1) {
@@ -530,9 +525,10 @@ arpOpcodeFormatter(virBufferPtr buf,
nwf->p.arpHdrFilter.dataOpcode.u.u16,
&str)) {
virBufferVSprintf(buf, "%s", str);
- return 1;
+ } else {
+ virBufferVSprintf(buf, "%d", nwf->p.arpHdrFilter.dataOpcode.u.u16);
}
- return 0;
+ return 1;
}
@@ -560,16 +556,12 @@ static bool checkIPProtocolID(enum attrD
virNWFilterRuleDefPtr nwf)
{
int32_t res = -1;
- const char *str;
if (datatype == DATATYPE_STRING) {
if (intMapGetByString(ipProtoMap, (char *)value, 1, &res) == 0)
res = -1;
} else if (datatype == DATATYPE_UINT8) {
- // may just accept what user provides and not test...
- if (intMapGetByInt(ipProtoMap,
- (uint32_t)*(uint16_t *)value, &str) == 0)
- res = -1;
+ res = (uint32_t)*(uint16_t *)value;
}
if (res != -1) {
@@ -591,19 +583,24 @@ formatIPProtocolID(virBufferPtr buf,
nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8,
&str)) {
virBufferVSprintf(buf, "%s", str);
- return 1;
+ } else {
+ virBufferVSprintf(buf, "%d",
+ nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8);
}
- return 0;
+ return 1;
}
static bool
dscpValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *val,
- virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+ virNWFilterRuleDefPtr nwf)
{
uint8_t dscp = *(uint16_t *)val;
if (dscp > 63)
return 0;
+
+ nwf->p.ipHdrFilter.ipHdr.dataDSCP.u.u8 = dscp;
+
return 1;
}
@@ -685,11 +682,6 @@ static const virXMLAttr2Struct arpAttrib
static const virXMLAttr2Struct ipAttributes[] = {
COMMON_MAC_PROPS(ipHdrFilter),
{
- .name = "version",
- .datatype = DATATYPE_UINT8,
- .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataIPVersion),
- },
- {
.name = SRCIPADDR,
.datatype = DATATYPE_IPADDR,
.dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPAddr),
@@ -711,7 +703,7 @@ static const virXMLAttr2Struct ipAttribu
},
{
.name = "protocol",
- .datatype = DATATYPE_STRING,
+ .datatype = DATATYPE_STRING | DATATYPE_UINT8,
.dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataProtocolID),
.validator= checkIPProtocolID,
.formatter= formatIPProtocolID,
@@ -756,16 +748,16 @@ static const virXMLAttr2Struct ipv6Attri
.dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPAddr),
},
{
- .name = DSTIPADDR,
- .datatype = DATATYPE_IPV6ADDR,
- .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
- },
- {
.name = SRCIPMASK,
.datatype = DATATYPE_IPV6MASK,
.dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPMask),
},
{
+ .name = DSTIPADDR,
+ .datatype = DATATYPE_IPV6ADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
+ },
+ {
.name = DSTIPMASK,
.datatype = DATATYPE_IPV6MASK,
.dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPMask),
@@ -818,16 +810,16 @@ static const virXMLAttr2Struct ipv6Attri
.dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPAddr),\
},\
{\
- .name = DSTIPADDR,\
- .datatype = ADDRTYPE,\
- .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPAddr),\
- },\
- {\
.name = SRCIPMASK,\
.datatype = MASKTYPE,\
.dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPMask),\
},\
{\
+ .name = DSTIPADDR,\
+ .datatype = ADDRTYPE,\
+ .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPAddr),\
+ },\
+ {\
.name = DSTIPMASK,\
.datatype = MASKTYPE,\
.dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPMask),\
@@ -1276,26 +1268,26 @@ virNWFilterRuleDetailsParse(virConnectPt
case DATATYPE_IPMASK:
storage_ptr = &item->u.u8;
- if (!virNWIPv4AddressParser(prop, &ipaddr)) {
- if (sscanf(prop, "%d", &int_val) == 1) {
- if (int_val >= 0 && int_val <= 32) {
- if (!validator)
- *(uint8_t *)storage_ptr =
- (uint8_t)int_val;
- found = 1;
- data_ptr = &int_val;
- } else
- rc = -1;
+ if (virStrToLong_i(prop, NULL, 10, &int_val) == 0) {
+ if (int_val >= 0 && int_val <= 32) {
+ if (!validator)
+ *(uint8_t *)storage_ptr =
+ (uint8_t)int_val;
+ found = 1;
+ data_ptr = &int_val;
} else
rc = -1;
} else {
- int_val = virSocketGetNumNetmaskBits(
+ if (virNWIPv4AddressParser(prop, &ipaddr)) {
+ int_val = virSocketGetNumNetmaskBits(
&ipaddr.addr);
- if (int_val >= 0)
- *(uint8_t *)storage_ptr = int_val;
- else
+ if (int_val >= 0)
+ *(uint8_t *)storage_ptr = int_val;
+ else
+ rc = -1;
+ found = 1;
+ } else
rc = -1;
- found = 1;
}
break;
@@ -1330,26 +1322,26 @@ virNWFilterRuleDetailsParse(virConnectPt
case DATATYPE_IPV6MASK:
storage_ptr = &item->u.u8;
- if (!virNWIPv6AddressParser(prop, &ipaddr)) {
- if (sscanf(prop, "%d", &int_val) == 1) {
- if (int_val >= 0 && int_val <= 128) {
- if (!validator)
- *(uint8_t *)storage_ptr =
- (uint8_t)int_val;
- found = 1;
- data_ptr = &int_val;
- } else
- rc = -1;
+ if (virStrToLong_i(prop, NULL, 10, &int_val) == 0) {
+ if (int_val >= 0 && int_val <= 128) {
+ if (!validator)
+ *(uint8_t *)storage_ptr =
+ (uint8_t)int_val;
+ found = 1;
+ data_ptr = &int_val;
} else
rc = -1;
} else {
- int_val = virSocketGetNumNetmaskBits(
- &ipaddr.addr);
- if (int_val >= 0)
- *(uint8_t *)storage_ptr = int_val;
- else
- rc = -1;
- found = 1;
+ if (virNWIPv6AddressParser(prop, &ipaddr)) {
+ int_val = virSocketGetNumNetmaskBits(
+ &ipaddr.addr);
+ if (int_val >= 0)
+ *(uint8_t *)storage_ptr = int_val;
+ else
+ rc = -1;
+ found = 1;
+ } else
+ rc = -1;
}
break;
Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c
+++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -189,6 +189,7 @@ printDataType(virConnectPtr conn,
break;
case DATATYPE_MACADDR:
+ case DATATYPE_MACMASK:
if (bufsize < VIR_MAC_STRING_BUFLEN) {
virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
_("Buffer too small for MAC address"));
14 years, 9 months
[libvirt] [PATCHv2 00/10] Begin to replace scanf and atoi
by Matthias Bolte
This set of patches is a first step towards removing the scanf and atoi
usage in libvirt. I began with the simple cases and post this now to get
some feedback before I start to convert the more difficult cases.
Changes in v2:
- don't test tmp for NULL
- note in commit message if parsing is stricter now
- don't leak in PRUnicharToInt (vbox)
- strip a trailing \n in openvzGetVPSUUID, as it was done before
- don't use strspn to skip whitespaces, virStrToLong* does this already
- fix virStrToLong* return value tests to check for < 0 instead of < 3 or != 1
- add STRSKIP to skip a given prefix of a string
Thanks to Eric Blake and Jim Meyering.
Matthias
14 years, 9 months
[libvirt] [PATCH] ESX test case needs '/' in interface name
by Stefan Berger
To fix an ESX test case, valid interface names need '/' as valid letter.
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
Index: libvirt-acl/src/conf/domain_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.h
+++ libvirt-acl/src/conf/domain_conf.h
@@ -298,7 +298,7 @@ struct _virDomainNetDef {
};
# define VALID_IFNAME_CHARS \
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/"
enum virDomainChrTargetType {
VIR_DOMAIN_CHR_TARGET_TYPE_NULL = 0,
14 years, 9 months
[libvirt] [PATCH] Fix linker errors in proxy
by Matthias Bolte
domain_conf.c:494: undefined reference to 'virNWFilterHashTableFree'
domain_conf.c:5107: undefined reference to 'virNWFilterFormatParamAttributes'
Add missing source to the proxy and disable XML parsing code in
nwfilter_params.c for a proxy build.
---
proxy/Makefile.am | 2 ++
src/conf/nwfilter_params.c | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index aef11ca..9ea91d8 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -15,6 +15,7 @@ libexec_PROGRAMS = libvirt_proxy
libvirt_proxy_SOURCES = libvirt_proxy.c \
@top_srcdir(a)/src/util/buf.c \
+ @top_srcdir(a)/src/util/hash.c \
@top_srcdir(a)/src/util/logging.c \
@top_srcdir(a)/src/util/memory.c \
@top_srcdir(a)/src/util/network.c \
@@ -26,6 +27,7 @@ libvirt_proxy_SOURCES = libvirt_proxy.c \
@top_srcdir(a)/src/conf/storage_encryption_conf.c \
@top_srcdir(a)/src/conf/domain_conf.c \
@top_srcdir(a)/src/conf/cpu_conf.c \
+ @top_srcdir(a)/src/conf/nwfilter_params.c \
@top_srcdir(a)/src/xen/xend_internal.c \
@top_srcdir(a)/src/xen/xen_hypervisor.c \
@top_srcdir(a)/src/xen/sexpr.c \
diff --git a/src/conf/nwfilter_params.c b/src/conf/nwfilter_params.c
index 38a51c9..286c5ef 100644
--- a/src/conf/nwfilter_params.c
+++ b/src/conf/nwfilter_params.c
@@ -214,6 +214,7 @@ err_exit:
}
+#ifndef PROXY
virNWFilterHashTablePtr
virNWFilterParseParamAttributes(xmlNodePtr cur)
{
@@ -254,6 +255,7 @@ skip_entry:
}
return table;
}
+#endif
struct formatterParam {
--
1.6.3.3
14 years, 9 months
[libvirt] [PATCH] Fix ./configure: line 27875: [: missing `]'
by Matthias Bolte
Commit 7998714d60b997357bfea15d6f2d0f729fc8fb29 replaced '-a' with '&& test'
but this single '-a' needs to stay.
---
configure.ac | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/configure.ac b/configure.ac
index 3c3d7b3..fc87e19 100644
--- a/configure.ac
+++ b/configure.ac
@@ -240,7 +240,7 @@ AC_ARG_WITH([libvirtd],
dnl
dnl specific tests to setup DV devel environments with debug etc ...
dnl
-if [[ "${LOGNAME}" = "veillard" && test "`pwd`" = "/u/veillard/libvirt" ]] ; then
+if [[ "${LOGNAME}" = "veillard" -a "`pwd`" = "/u/veillard/libvirt" ]] ; then
STATIC_BINARIES="-static"
else
STATIC_BINARIES=
--
1.6.3.3
14 years, 9 months
[libvirt] [PATCH] nwfilter: Support for learning a VM's IP address
by Stefan Berger
Subject: Support for learning a VM's IP address
This patch implements support for learning a VM's IP address. It uses
the pcap library to listen on the VM's backend network interface (tap)
or the physical ethernet device (macvtap) and tries to capture packets
with source or destination MAC address of the VM and learn from DHCP
Offers, ARP traffic, or first-sent IPv4 packet what the IP address of
the VM's interface is. This then allows to instantiate the network
traffic filtering rules without the user having to provide the IP
parameter somewhere in the filter description or in the interface
description as a parameter. This only supports to detect the parameter
IP, which is for the assumed single IPv4 address of a VM. There is not
support for interfaces that may have multiple IP addresses (IP
aliasing) or IPv6 that may then require more than one valid IP address
to be detected. A VM can have multiple independent interfaces that each
uses a different IP address and in that case it will be attempted to
detect each one of the address independently.
So, when for example an interface description in the domain XML has
looked like this up to now:
<interface type='bridge'>
<source bridge='mybridge'/>
<model type='virtio'/>
<filterref filter='clean-traffic'>
<parameter name='IP' value='10.2.3.4'/>
</filterref>
</interface>
you may omit the IP parameter:
<interface type='bridge'>
<source bridge='mybridge'/>
<model type='virtio'/>
<filterref filter='clean-traffic'/>
</interface>
Internally I am walking the 'tree' of a VM's referenced network filters
and determine with the given variables which variables are missing. Now,
the above IP parameter may be missing and this causes a libvirt-internal
thread to be started that uses the pcap library's API to listen to the
backend interface (in case of macvtap to the physical interface) in an
attempt to determine the missing IP parameter. If the backend interface
disappears the thread terminates assuming the VM was brought down. In
case of a macvtap device a timeout is being used to wait for packets
from the given VM (filtering by VM's interface MAC address). If the VM's
macvtap device disappeared the thread also terminates. In all other
cases it tries to determine the IP address of the VM and will then apply
the rules late on the given interface, which would have happened
immediately if the IP parameter had been explicitly given. In case an
error happens while the firewall rules are applied, the VM's backend
interface is 'down'ed preventing it to communicate. Reasons for failure
for applying the network firewall rules may that an ebtables/iptables
command failes or OOM errors. Essentially the same failure reasons may
occur as when the firewall rules are applied immediately on VM start,
except that due to the late application of the filtering rules the VM
now is already running and cannot be hindered anymore from starting.
Bringing down the whole VM would probably be considered too drastic.
While a VM's IP address is attempted to be determined only limited
updates to network filters are allowed. In particular it is prevented
that filters are modified in such a way that they would introduce new
variables.
A caveat: The algorithm does not know which one is the appropriate IP
address of a VM. If the VM spoofs an IP address in its first ARP traffic
or IPv4 packets its filtering rules will be instantiated for this IP
address, thus 'locking' it to the found IP address. So, it's still
'safer' to explicitly provide the IP address of a VM's interface in the
filter description if it is known beforehand.
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
configure.ac | 39 ++
src/Makefile.am | 13
src/conf/nwfilter_conf.c | 7
src/conf/nwfilter_conf.h | 3
src/libvirt_private.syms | 12
src/nwfilter/nwfilter_driver.c | 11
src/nwfilter/nwfilter_ebiptables_driver.c | 100 +++++
src/nwfilter/nwfilter_ebiptables_driver.h | 7
src/nwfilter/nwfilter_gentech_driver.c | 408 +++++++++++++++++++--
src/nwfilter/nwfilter_gentech_driver.h | 14
src/nwfilter/nwfilter_learnipaddr.c | 579 ++++++++++++++++++++++++++++++
src/nwfilter/nwfilter_learnipaddr.h | 61 +++
12 files changed, 1216 insertions(+), 38 deletions(-)
Index: libvirt-acl/src/nwfilter/nwfilter_learnipaddr.h
===================================================================
--- /dev/null
+++ libvirt-acl/src/nwfilter/nwfilter_learnipaddr.h
@@ -0,0 +1,61 @@
+/*
+ * nwfilter_learnipaddr.h: support for learning IP address used by a VM
+ * on an interface
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 Stefan Berger
+ *
+ * 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: Stefan Berger <stefanb(a)us.ibm.com>
+ */
+
+#ifndef __NWFILTER_LEARNIPADDR_H
+#define __NWFILTER_LEARNIPADDR_H
+
+typedef struct _virNWFilterIPAddrLearnReq virNWFilterIPAddrLearnReq;
+typedef virNWFilterIPAddrLearnReq *virNWFilterIPAddrLearnReqPtr;
+struct _virNWFilterIPAddrLearnReq {
+ char ifname[IF_NAMESIZE];
+ char linkdev[IF_NAMESIZE];
+ enum virDomainNetType nettype;
+ unsigned char macaddr[VIR_MAC_BUFLEN];
+ char *filtername;
+ virNWFilterHashTablePtr filterparams;
+ virNWFilterDriverStatePtr driver;
+
+ int status;
+ pthread_t thread;
+};
+
+int virNWFilterLearnIPAddress(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver);
+
+virNWFilterIPAddrLearnReqPtr virNWFilterLookupLearnReq(const char *ifname);
+
+
+void virNWFilterDelIpAddrForIfname(const char *ifname);
+const char *virNWFilterGetIpAddrForIfname(const char *ifname);
+
+int virNWFilterLearnInit(void);
+void virNWFilterLearnShutdown(void);
+
+#endif /* __NWFILTER_LEARNIPADDR_H */
Index: libvirt-acl/src/nwfilter/nwfilter_learnipaddr.c
===================================================================
--- /dev/null
+++ libvirt-acl/src/nwfilter/nwfilter_learnipaddr.c
@@ -0,0 +1,579 @@
+/*
+ * nwfilter_learnipaddr.c: support for learning IP address used by a VM
+ * on an interface
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 Stefan Berger
+ *
+ * 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: Stefan Berger <stefanb(a)us.ibm.com>
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBPCAP
+#include <pcap.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+
+#include "internal.h"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+#include "threads.h"
+#include "conf/nwfilter_params.h"
+#include "conf/domain_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_learnipaddr.h"
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+
+/* structure of an ARP request/reply message */
+struct f_arphdr {
+ struct arphdr arphdr;
+ uint8_t ar_sha[ETH_ALEN];
+ uint32_t ar_sip;
+ uint8_t ar_tha[ETH_ALEN];
+ uint32_t ar_tip;
+} __attribute__((packed));
+
+
+/* structure representing DHCP message */
+struct dhcp {
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ uint32_t xid;
+ uint16_t secs;
+ uint16_t flags;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint32_t chaddr;
+ /* omitted */
+} __attribute__((packed));
+
+
+struct ether_vlan_header
+{
+ uint8_t dhost[ETH_ALEN];
+ uint8_t shost[ETH_ALEN];
+ uint16_t vlan_type;
+ uint16_t vlan_flags;
+ uint16_t ether_type;
+} __attribute__((packed));
+
+
+static virMutex pendingLearnReqLock;
+static virHashTablePtr pendingLearnReq;
+
+static virMutex ipAddressMapLock;
+static virNWFilterHashTablePtr ipAddressMap;
+
+
+static void
+virNWFilterIPAddrLearnReqFree(virNWFilterIPAddrLearnReqPtr req) {
+ if (!req)
+ return;
+
+ VIR_FREE(req->filtername);
+ virNWFilterHashTableFree(req->filterparams);
+
+ VIR_FREE(req);
+}
+
+
+#if HAVE_LIBPCAP
+
+static int
+virNWFilterRegisterLearnReq(virNWFilterIPAddrLearnReqPtr req) {
+ int res = -1;
+ virMutexLock(&pendingLearnReqLock);
+
+ if (!virHashLookup(pendingLearnReq, req->ifname))
+ res = virHashAddEntry(pendingLearnReq, req->ifname, req);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+#endif
+
+
+virNWFilterIPAddrLearnReqPtr
+virNWFilterLookupLearnReq(const char *ifname) {
+ void *res;
+
+ virMutexLock(&pendingLearnReqLock);
+
+ res = virHashLookup(pendingLearnReq, ifname);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+
+static void
+freeLearnReqEntry(void *payload, const char *name ATTRIBUTE_UNUSED) {
+ virNWFilterIPAddrLearnReqFree(payload);
+}
+
+
+#ifdef HAVE_LIBPCAP
+
+static virNWFilterIPAddrLearnReqPtr
+virNWFilterDeregisterLearnReq(const char *ifname) {
+ virNWFilterIPAddrLearnReqPtr res;
+
+ virMutexLock(&pendingLearnReqLock);
+
+ res = virHashLookup(pendingLearnReq, ifname);
+
+ if (res)
+ virHashRemoveEntry(pendingLearnReq, ifname, NULL);
+
+ virMutexUnlock(&pendingLearnReqLock);
+
+ return res;
+}
+
+
+
+static int
+virNWFilterAddIpAddrForIfname(const char *ifname, char *addr) {
+ int ret;
+
+ virMutexLock(&ipAddressMapLock);
+
+ ret = virNWFilterHashTablePut(ipAddressMap, ifname, addr, 1);
+
+ virMutexUnlock(&ipAddressMapLock);
+
+ return ret;
+}
+#endif
+
+
+void
+virNWFilterDelIpAddrForIfname(const char *ifname) {
+
+ virMutexLock(&ipAddressMapLock);
+
+ if (virHashLookup(ipAddressMap->hashTable, ifname))
+ virNWFilterHashTableRemoveEntry(ipAddressMap, ifname);
+
+ virMutexUnlock(&ipAddressMapLock);
+}
+
+
+const char *
+virNWFilterGetIpAddrForIfname(const char *ifname) {
+ const char *res;
+
+ virMutexLock(&ipAddressMapLock);
+
+ res = virHashLookup(ipAddressMap->hashTable, ifname);
+
+ virMutexUnlock(&ipAddressMapLock);
+
+ return res;
+}
+
+
+#ifdef HAVE_LIBPCAP
+
+/**
+ * learnIPAddressThread
+ * arg: pointer to virNWFilterIPAddrLearnReq structure
+ *
+ * Learn the IP address being used on an interface. Use ARP Request and
+ * Reply messages, DHCP offers and the first IP packet being sent from
+ * the VM to detect the IP address it is using. Detects only one IP address
+ * per interface (IP aliasing not supported)
+ */
+static void *
+learnIPAddressThread(void *arg)
+{
+ char errbuf[PCAP_ERRBUF_SIZE] = {0};
+ pcap_t *handle;
+ struct bpf_program fp;
+ struct pcap_pkthdr header;
+ const u_char *packet;
+ struct ether_header *ether_hdr;
+ struct ether_vlan_header *vlan_hdr;
+ virNWFilterIPAddrLearnReqPtr req = arg;
+ uint32_t vmaddr = 0;
+ unsigned int ethHdrSize;
+ char *listen_if = (strlen(req->linkdev) != 0) ? req->linkdev
+ : req->ifname;
+ int to_ms = (strlen(req->linkdev) != 0) ? 1000
+ : 0;
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *filter= NULL;
+ uint16_t etherType;
+
+ req->status = 0;
+
+ handle = pcap_open_live(listen_if, BUFSIZ, 0, to_ms, errbuf);
+
+ if (handle == NULL) {
+ VIR_DEBUG("Couldn't open device %s: %s\n", listen_if, errbuf);
+ req->status = ENODEV;
+ goto done;
+ }
+
+ virFormatMacAddr(req->macaddr, macaddr);
+ virBufferVSprintf(&buf, "ether host %s", macaddr);
+ if (virBufferError(&buf)) {
+ req->status = ENOMEM;
+ goto done;
+ }
+
+ filter = virBufferContentAndReset(&buf);
+
+ if (pcap_compile(handle, &fp, filter, 1, 0) != 0 ||
+ pcap_setfilter(handle, &fp) != 0) {
+ VIR_DEBUG("Couldn't compile or set filter '%s'.\n", filter);
+ req->status = EINVAL;
+ goto done;
+ }
+
+ while (req->status == 0 && vmaddr == 0) {
+ packet = pcap_next(handle, &header);
+
+ if (!packet) {
+ if (to_ms == 0) {
+ /* assuming IF disappeared */
+ req->status = ENODEV;
+ break;
+ }
+ /* listening on linkdev, check whether VM's dev is still there */
+ if (checkIf(req->ifname, req->macaddr)) {
+ req->status = ENODEV;
+ break;
+ }
+ continue;
+ }
+
+ if (header.len >= sizeof(struct ether_header)) {
+ ether_hdr = (struct ether_header*)packet;
+
+ switch (ntohs(ether_hdr->ether_type)) {
+
+ case ETHERTYPE_IP:
+ ethHdrSize = sizeof(struct ether_header);
+ etherType = ntohs(ether_hdr->ether_type);
+ break;
+
+ case ETHERTYPE_VLAN:
+ ethHdrSize = sizeof(struct ether_vlan_header);
+ vlan_hdr = (struct ether_vlan_header *)packet;
+ if (ntohs(vlan_hdr->ether_type) != ETHERTYPE_IP ||
+ header.len < ethHdrSize)
+ continue;
+ etherType = ntohs(vlan_hdr->ether_type);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (memcmp(ether_hdr->ether_shost,
+ req->macaddr,
+ VIR_MAC_BUFLEN) == 0) {
+ // packets from the VM
+
+ if ((header.len >= ethHdrSize +
+ sizeof(struct iphdr))) {
+ struct iphdr *iphdr = (struct iphdr*)(packet +
+ ethHdrSize);
+ vmaddr = iphdr->saddr;
+ // skip eth. bcast and mcast addresses,
+ // and zero address in DHCP Requests
+ if ((ntohl(vmaddr) & 0xc0000000) || vmaddr == 0) {
+ vmaddr = 0;
+ continue;
+ }
+
+ } else if (etherType == ETHERTYPE_ARP &&
+ (header.len >= ethHdrSize +
+ sizeof(struct f_arphdr))) {
+ struct f_arphdr *arphdr = (struct f_arphdr*)(packet +
+ ethHdrSize);
+ switch (ntohs(arphdr->arphdr.ar_op)) {
+ case ARPOP_REPLY:
+ vmaddr = arphdr->ar_sip;
+ break;
+ case ARPOP_REQUEST:
+ vmaddr = arphdr->ar_tip;
+ break;
+ }
+ }
+ } else if (memcmp(ether_hdr->ether_dhost,
+ req->macaddr,
+ VIR_MAC_BUFLEN) == 0) {
+ // packets to the VM
+ if (etherType == ETHERTYPE_IP &&
+ (header.len >= ethHdrSize +
+ sizeof(struct iphdr))) {
+ struct iphdr *iphdr = (struct iphdr*)(packet +
+ ethHdrSize);
+ if ((iphdr->protocol == IPPROTO_UDP) &&
+ (header.len >= ethHdrSize +
+ iphdr->ihl * 4 +
+ sizeof(struct udphdr))) {
+ struct udphdr *udphdr= (struct udphdr *)
+ ((char *)iphdr + iphdr->ihl * 4);
+ if (ntohs(udphdr->source) == 67 &&
+ ntohs(udphdr->dest) == 68 &&
+ header.len >= ethHdrSize +
+ iphdr->ihl * 4 +
+ sizeof(struct udphdr) +
+ sizeof(struct dhcp)) {
+ struct dhcp *dhcp = (struct dhcp *)
+ ((char *)udphdr + sizeof(udphdr));
+ if (dhcp->op == 2 /* DHCP OFFER */) {
+ vmaddr = dhcp->yiaddr;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ done:
+ VIR_FREE(filter);
+
+ if (handle)
+ pcap_close(handle);
+
+ ebtablesRemoveBasicRules(NULL, req->ifname);
+
+ if (req->status == 0) {
+ int ret;
+ char inetaddr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &vmaddr, inetaddr, sizeof(inetaddr));
+
+ virNWFilterAddIpAddrForIfname(req->ifname, strdup(inetaddr));
+
+ ret = virNWFilterInstantiateFilterLate(NULL,
+ req->ifname,
+ req->linkdev,
+ req->nettype,
+ req->macaddr,
+ req->filtername,
+ req->filterparams,
+ req->driver);
+ VIR_DEBUG("Result from applying firewall rules on "
+ "%s with IP addr %s : %d\n", req->ifname, inetaddr, ret);
+ }
+
+ memset(&req->thread, 0x0, sizeof(req->thread));
+
+ VIR_DEBUG("pcap thread terminating for interface %s\n",req->ifname);
+
+ virNWFilterDeregisterLearnReq(req->ifname);
+
+ virNWFilterIPAddrLearnReqFree(req);
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterLearnIPAddress
+ * @conn: pointer to virConnect object
+ * @ifname: the name of the interface
+ * @linkdev : the name of the link device; currently only used in case of a
+ * macvtap device
+ * @nettype : the type of interface
+ * @macaddr : the MAC address of the interface
+ * @filtername : the name of the top-level filter to apply to the interface
+ * once its IP address has been detected
+ * @driver : the network filter driver
+ *
+ * Instruct to learn the IP address being used on a given interface (ifname).
+ * Unless there already is a thread attempting to learn the IP address
+ * being used on the interface, a thread is started that will listen on
+ * the traffic being sent on the interface (or link device) with the
+ * MAC address that is provided. Will then launch the application of the
+ * firewall rules on the interface.
+ */
+int
+virNWFilterLearnIPAddress(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver) {
+ int rc;
+ virNWFilterIPAddrLearnReqPtr req = NULL;
+ virNWFilterHashTablePtr ht = NULL;
+
+ if (VIR_ALLOC(req) < 0) {
+ virReportOOMError();
+ goto err_exit;
+ }
+
+ ht = virNWFilterHashTableCreate(0);
+ if (ht == NULL) {
+ virReportOOMError();
+ goto err_exit;
+ }
+
+ if (virNWFilterHashTablePutAll(conn, filterparams, ht))
+ goto err_exit;
+
+ req->filtername = strdup(filtername);
+ if (req->filtername == NULL) {
+ virReportOOMError();
+ goto err_exit;
+ }
+
+ if (virStrcpyStatic(req->ifname, ifname) == NULL) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Destination buffer for ifname ('%s') "
+ "not large enough"), ifname);
+ goto err_exit;
+ }
+
+ if (linkdev) {
+ if (virStrcpyStatic(req->linkdev, linkdev) == NULL) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Destination buffer for linkdev ('%s') "
+ "not large enough"), linkdev);
+ goto err_exit;
+ }
+ }
+ req->nettype = nettype;
+ memcpy(req->macaddr, macaddr, sizeof(req->macaddr));
+ req->driver = driver;
+ req->filterparams = ht;
+ ht = NULL;
+
+ rc = virNWFilterRegisterLearnReq(req);
+
+ if (rc)
+ goto err_exit;
+
+ if (ebtablesApplyBasicRules(conn,
+ ifname,
+ macaddr)) {
+ goto err_exit;
+ }
+
+ if (pthread_create(&req->thread,
+ NULL,
+ learnIPAddressThread,
+ req) != 0)
+ goto err_exit;
+
+ return 0;
+
+err_exit:
+ ebtablesRemoveBasicRules(conn, ifname);
+ virNWFilterIPAddrLearnReqFree(req);
+ virNWFilterHashTableFree(ht);
+ return 1;
+}
+
+#else
+
+int
+virNWFilterLearnIPAddress(virConnectPtr conn ATTRIBUTE_UNUSED,
+ const char *ifname ATTRIBUTE_UNUSED,
+ const char *linkdev ATTRIBUTE_UNUSED,
+ enum virDomainNetType nettype ATTRIBUTE_UNUSED,
+ const unsigned char *macaddr ATTRIBUTE_UNUSED,
+ const char *filtername ATTRIBUTE_UNUSED,
+ virNWFilterHashTablePtr filterparams ATTRIBUTE_UNUSED,
+ virNWFilterDriverStatePtr driver ATTRIBUTE_UNUSED) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("IP parameter must be given since libvirt "
+ "was not compiled with IP address learning "
+ "support"));
+ return 1;
+}
+#endif /* HAVE_LIBPCAP */
+
+
+/**
+ * virNWFilterLearnInit
+ * Initialization of this layer
+ */
+int
+virNWFilterLearnInit(void) {
+ pendingLearnReq = virHashCreate(0);
+ if (!pendingLearnReq) {
+ virReportOOMError();
+ return 1;
+ }
+
+ if (virMutexInit(&pendingLearnReqLock)) {
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ ipAddressMap = virNWFilterHashTableCreate(0);
+ if (!ipAddressMap) {
+ virReportOOMError();
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ if (virMutexInit(&ipAddressMapLock)) {
+ virNWFilterLearnShutdown();
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterLearnShutdown
+ * Shutdown of this layer
+ */
+void
+virNWFilterLearnShutdown(void) {
+ virHashFree(pendingLearnReq, freeLearnReqEntry);
+ pendingLearnReq = NULL;
+
+ virNWFilterHashTableFree(ipAddressMap);
+ ipAddressMap = NULL;
+}
Index: libvirt-acl/src/Makefile.am
===================================================================
--- libvirt-acl.orig/src/Makefile.am
+++ libvirt-acl/src/Makefile.am
@@ -289,7 +289,8 @@ STORAGE_HELPER_DISK_SOURCES = \
NWFILTER_DRIVER_SOURCES = \
nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \
nwfilter/nwfilter_gentech_driver.c \
- nwfilter/nwfilter_ebiptables_driver.c
+ nwfilter/nwfilter_ebiptables_driver.c \
+ nwfilter/nwfilter_learnipaddr.c
# Security framework and drivers for various models
SECURITY_DRIVER_SOURCES = \
@@ -740,10 +741,11 @@ else
libvirt_la_LIBADD += libvirt_driver_nwfilter.la
noinst_LTLIBRARIES += libvirt_driver_nwfilter.la
endif
-libvirt_driver_nwfilter_la_CFLAGS = \
+libvirt_driver_nwfilter_la_CFLAGS = $(LIBPCAP_CFLAGS) \
-I@top_srcdir@/src/conf
+libvirt_driver_nwfilter_la_LDFLAGS = $(LIBPCAP_LIBS)
if WITH_DRIVER_MODULES
-libvirt_driver_nwfilter_la_LDFLAGS = -module -avoid-version ../gnulib/lib/libgnu.la
+libvirt_driver_nwfilter_la_LDFLAGS += -module -avoid-version ../gnulib/lib/libgnu.la
endif
libvirt_driver_nwfilter_la_SOURCES = $(NWFILTER_DRIVER_SOURCES)
endif
@@ -887,6 +889,7 @@ libvirt_la_LDFLAGS = $(VERSION_SCRIPT_FL
-version-info $(LIBVIRT_VERSION_INFO) \
$(COVERAGE_CFLAGS:-f%=-Wc,-f%) \
$(LIBXML_LIBS) \
+ $(LIBPCAP_LIBS) \
$(DRIVER_MODULE_LIBS) \
$(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS)
libvirt_la_CFLAGS = $(COVERAGE_CFLAGS) -DIN_LIBVIRT
@@ -935,8 +938,8 @@ libvirt_lxc_SOURCES = \
$(NODE_INFO_SOURCES) \
$(ENCRYPTION_CONF_SOURCES) \
$(DOMAIN_CONF_SOURCES) \
- $(NWFILTER_PARAM_CONF_SOURCES) \
- $(CPU_CONF_SOURCES)
+ $(CPU_CONF_SOURCES) \
+ $(NWFILTER_PARAM_CONF_SOURCES)
libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS)
libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la
libvirt_lxc_CFLAGS = \
Index: libvirt-acl/src/nwfilter/nwfilter_gentech_driver.c
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_gentech_driver.c
+++ libvirt-acl/src/nwfilter/nwfilter_gentech_driver.c
@@ -24,6 +24,9 @@
#include <config.h>
#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
#include "internal.h"
@@ -34,12 +37,16 @@
#include "virterror_internal.h"
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
+#include "nwfilter_learnipaddr.h"
#define VIR_FROM_THIS VIR_FROM_NWFILTER
#define NWFILTER_STD_VAR_MAC "MAC"
+#define NWFILTER_STD_VAR_IP "IP"
+
+static int _virNWFilterTeardownFilter(const char *ifname);
static virNWFilterTechDriverPtr filter_tech_drivers[] = {
@@ -111,6 +118,8 @@ virNWFilterRuleInstFree(virNWFilterRuleI
* @tables: pointer to hash tabel to add values to
* @macaddr: The string of the MAC address to add to the hash table,
* may be NULL
+ * @ipaddr: The string of the IP address to add to the hash table;
+ * may be NULL
*
* Returns 0 in case of success, 1 in case an error happened with
* error having been reported.
@@ -120,7 +129,8 @@ virNWFilterRuleInstFree(virNWFilterRuleI
static int
virNWFilterVarHashmapAddStdValues(virConnectPtr conn,
virNWFilterHashTablePtr table,
- char *macaddr)
+ char *macaddr,
+ char *ipaddr)
{
if (macaddr) {
if (virHashAddEntry(table->hashTable,
@@ -132,6 +142,16 @@ virNWFilterVarHashmapAddStdValues(virCon
}
}
+ if (ipaddr) {
+ if (virHashAddEntry(table->hashTable,
+ NWFILTER_STD_VAR_IP,
+ ipaddr) < 0) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not add variable 'IP' to hashmap"));
+ return 1;
+ }
+ }
+
return 0;
}
@@ -140,23 +160,26 @@ virNWFilterVarHashmapAddStdValues(virCon
* virNWFilterCreateVarHashmap:
* @conn: pointer to virConnect object
* @macaddr: pointer to string containing formatted MAC address of interface
+ * @ipaddr: pointer to string containing formatted IP address used by
+ * VM on this interface; may be NULL
*
* Create a hashmap used for evaluating the firewall rules. Initializes
- * it with the standard variable 'MAC'.
+ * it with the standard variable 'MAC' and 'IP' if provided.
*
* Returns pointer to hashmap, NULL if an error occcurred and error message
* is attached to the virConnect object.
*/
virNWFilterHashTablePtr
virNWFilterCreateVarHashmap(virConnectPtr conn,
- char *macaddr) {
+ char *macaddr,
+ char *ipaddr) {
virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
if (!table) {
virReportOOMError();
return NULL;
}
- if (virNWFilterVarHashmapAddStdValues(conn, table, macaddr)) {
+ if (virNWFilterVarHashmapAddStdValues(conn, table, macaddr, ipaddr)) {
virNWFilterHashTableFree(table);
return NULL;
}
@@ -284,9 +307,9 @@ _virNWFilterInstantiateRec(virConnectPtr
virNWFilterHashTablePtr vars,
int *nEntries,
virNWFilterRuleInstPtr **insts,
- enum instCase useNewFilter, int *foundNewFilter)
+ enum instCase useNewFilter, int *foundNewFilter,
+ virNWFilterDriverStatePtr driver)
{
- virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
virNWFilterPoolObjPtr obj;
int rc = 0;
int i;
@@ -365,7 +388,96 @@ _virNWFilterInstantiateRec(virConnectPtr
tmpvars,
nEntries, insts,
useNewFilter,
- foundNewFilter);
+ foundNewFilter,
+ driver);
+
+ virNWFilterHashTableFree(tmpvars);
+
+ virNWFilterPoolObjUnlock(obj);
+ if (rc)
+ break;
+ } else {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("referenced filter '%s' is missing"),
+ inc->filterref);
+ rc = 1;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int
+virNWFilterDetermineMissingVarsRec(virConnectPtr conn,
+ virNWFilterDefPtr filter,
+ virNWFilterHashTablePtr vars,
+ virNWFilterHashTablePtr missing_vars,
+ int useNewFilter,
+ virNWFilterDriverStatePtr driver)
+{
+ virNWFilterPoolObjPtr obj;
+ int rc = 0;
+ int i, j;
+ virNWFilterDefPtr next_filter;
+
+ for (i = 0; i < filter->nentries; i++) {
+ virNWFilterRuleDefPtr rule = filter->filterEntries[i]->rule;
+ virNWFilterIncludeDefPtr inc = filter->filterEntries[i]->include;
+ if (rule) {
+ // check all variables of this rule
+ for (j = 0; j < rule->nvars; j++) {
+ if (!virHashLookup(vars->hashTable, rule->vars[j])) {
+ virNWFilterHashTablePut(missing_vars, rule->vars[j],
+ strdup("1"), 1);
+ }
+ }
+ } else if (inc) {
+ VIR_DEBUG("Following filter %s\n", inc->filterref);
+ obj = virNWFilterPoolObjFindByName(&driver->pools,
+ inc->filterref);
+ if (obj) {
+
+ if (obj->wantRemoved) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ _("Filter '%s' is in use."),
+ inc->filterref);
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ // create a temporary hashmap for depth-first tree traversal
+ virNWFilterHashTablePtr tmpvars =
+ virNWFilterCreateVarsFrom(conn,
+ inc->params,
+ vars);
+ if (!tmpvars) {
+ virReportOOMError();
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ next_filter = obj->def;
+
+ switch (useNewFilter) {
+ case INSTANTIATE_FOLLOW_NEWFILTER:
+ if (obj->newDef) {
+ next_filter = obj->newDef;
+ }
+ break;
+ case INSTANTIATE_ALWAYS:
+ break;
+ }
+
+ rc = virNWFilterDetermineMissingVarsRec(conn,
+ next_filter,
+ tmpvars,
+ missing_vars,
+ useNewFilter,
+ driver);
virNWFilterHashTableFree(tmpvars);
@@ -438,9 +550,12 @@ virNWFilterInstantiate(virConnectPtr con
enum virDomainNetType nettype,
virNWFilterDefPtr filter,
const char *ifname,
+ const char *linkdev,
virNWFilterHashTablePtr vars,
enum instCase useNewFilter, int *foundNewFilter,
- bool teardownOld)
+ bool teardownOld,
+ const unsigned char *macaddr,
+ virNWFilterDriverStatePtr driver)
{
int rc;
int j, nptrs;
@@ -449,6 +564,44 @@ virNWFilterInstantiate(virConnectPtr con
void **ptrs = NULL;
int instantiate = 1;
+ virNWFilterLockFilterUpdates();
+
+ virNWFilterHashTablePtr missing_vars = virNWFilterHashTableCreate(0);
+ if (!missing_vars) {
+ virReportOOMError();
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = virNWFilterDetermineMissingVarsRec(conn,
+ filter,
+ vars,
+ missing_vars,
+ useNewFilter,
+ driver);
+ if (rc)
+ goto err_exit;
+
+ if (virHashSize(missing_vars->hashTable) == 1) {
+ if (virHashLookup(missing_vars->hashTable,
+ NWFILTER_STD_VAR_IP) != NULL) {
+ if (virNWFilterLookupLearnReq(ifname) == NULL) {
+ rc = virNWFilterLearnIPAddress(conn,
+ ifname,
+ linkdev,
+ nettype, macaddr,
+ filter->name,
+ vars, driver);
+ }
+ goto err_exit;
+ }
+ rc = 1;
+ goto err_exit;
+ } else if (virHashSize(missing_vars->hashTable) > 1) {
+ rc = 1;
+ goto err_exit;
+ }
+
rc = _virNWFilterInstantiateRec(conn,
techdriver,
nettype,
@@ -456,7 +609,8 @@ virNWFilterInstantiate(virConnectPtr con
ifname,
vars,
&nEntries, &insts,
- useNewFilter, foundNewFilter);
+ useNewFilter, foundNewFilter,
+ driver);
if (rc)
goto err_exit;
@@ -487,24 +641,33 @@ virNWFilterInstantiate(virConnectPtr con
err_exit:
+ virNWFilterUnlockFilterUpdates();
+
for (j = 0; j < nEntries; j++)
virNWFilterRuleInstFree(insts[j]);
VIR_FREE(insts);
+ virNWFilterHashTableFree(missing_vars);
+
return rc;
}
static int
-_virNWFilterInstantiateFilter(virConnectPtr conn,
- const virDomainNetDefPtr net,
- bool teardownOld,
- enum instCase useNewFilter)
+__virNWFilterInstantiateFilter(virConnectPtr conn,
+ bool teardownOld,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ enum instCase useNewFilter,
+ virNWFilterDriverStatePtr driver)
{
int rc;
const char *drvname = EBIPTABLES_DRIVER_ID;
- virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
virNWFilterTechDriverPtr techdriver;
virNWFilterPoolObjPtr obj;
virNWFilterHashTablePtr vars, vars1;
@@ -512,6 +675,8 @@ _virNWFilterInstantiateFilter(virConnect
char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
int foundNewFilter = 0;
char *str_macaddr = NULL;
+ const char *ipaddr;
+ char *str_ipaddr = NULL;
techdriver = virNWFilterTechDriverForName(drvname);
@@ -523,25 +688,25 @@ _virNWFilterInstantiateFilter(virConnect
return 1;
}
- VIR_DEBUG("filter name: %s\n", net->filter);
+ VIR_DEBUG("filter name: %s\n", filtername);
- obj = virNWFilterPoolObjFindByName(&driver->pools, net->filter);
+ obj = virNWFilterPoolObjFindByName(&driver->pools, filtername);
if (!obj) {
virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
_("Could not find filter '%s'"),
- net->filter);
+ filtername);
return 1;
}
if (obj->wantRemoved) {
virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
_("Filter '%s' is in use."),
- net->filter);
+ filtername);
rc = 1;
goto err_exit;
}
- virFormatMacAddr(net->mac, vmmacaddr);
+ virFormatMacAddr(macaddr, vmmacaddr);
str_macaddr = strdup(vmmacaddr);
if (!str_macaddr) {
virReportOOMError();
@@ -549,18 +714,29 @@ _virNWFilterInstantiateFilter(virConnect
goto err_exit;
}
+ ipaddr = virNWFilterGetIpAddrForIfname(ifname);
+ if (ipaddr) {
+ str_ipaddr = strdup(ipaddr);
+ if (!str_ipaddr) {
+ virReportOOMError();
+ rc = 1;
+ goto err_exit;
+ }
+ }
+
vars1 = virNWFilterCreateVarHashmap(conn,
- str_macaddr);
+ str_macaddr, str_ipaddr);
if (!vars1) {
rc = 1;
goto err_exit;
}
str_macaddr = NULL;
+ str_ipaddr = NULL;
vars = virNWFilterCreateVarsFrom(conn,
vars1,
- net->filterparams);
+ filterparams);
if (!vars) {
rc = 1;
goto err_exit_vars1;
@@ -582,12 +758,15 @@ _virNWFilterInstantiateFilter(virConnect
rc = virNWFilterInstantiate(conn,
techdriver,
- net->type,
+ nettype,
filter,
- net->ifname,
+ ifname,
+ linkdev,
vars,
useNewFilter, &foundNewFilter,
- teardownOld);
+ teardownOld,
+ macaddr,
+ driver);
virNWFilterHashTableFree(vars);
@@ -595,15 +774,180 @@ err_exit_vars1:
virNWFilterHashTableFree(vars1);
err_exit:
-
virNWFilterPoolObjUnlock(obj);
+ VIR_FREE(str_ipaddr);
VIR_FREE(str_macaddr);
return rc;
}
+static int
+_virNWFilterInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net,
+ bool teardownOld,
+ enum instCase useNewFilter)
+{
+ const char *linkdev = (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ ? net->data.direct.linkdev
+ : NULL;
+ return __virNWFilterInstantiateFilter(conn,
+ teardownOld,
+ net->ifname,
+ linkdev,
+ net->type,
+ net->mac,
+ net->filter,
+ net->filterparams,
+ useNewFilter,
+ conn->nwfilterPrivateData);
+}
+
+
+// FIXME: move chgIfFlags, ifUp, checkIf into common file & share w/ macvtap.c
+
+/*
+ * chgIfFlags: Change flags on an interface
+ * @ifname : name of the interface
+ * @flagclear : the flags to clear
+ * @flagset : the flags to set
+ *
+ * The new flags of the interface will be calculated as
+ * flagmask = (~0 ^ flagclear)
+ * newflags = (curflags & flagmask) | flagset;
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int chgIfFlags(const char *ifname, short flagclear, short flagset) {
+ struct ifreq ifr;
+ int rc = 0;
+ int flags;
+ short flagmask = (~0 ^ flagclear);
+ int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+
+ if (fd < 0)
+ return errno;
+
+ if (virStrncpy(ifr.ifr_name,
+ ifname, strlen(ifname), sizeof(ifr.ifr_name)) == NULL) {
+ rc = ENODEV;
+ goto err_exit;
+ }
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ rc = errno;
+ goto err_exit;
+ }
+
+ flags = (ifr.ifr_flags & flagmask) | flagset;
+
+ if (ifr.ifr_flags != flags) {
+ ifr.ifr_flags = flags;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
+ rc = errno;
+ }
+
+err_exit:
+ close(fd);
+ return rc;
+}
+
+/*
+ * ifUp
+ * @name: name of the interface
+ * @up: 1 for up, 0 for down
+ *
+ * Function to control if an interface is activated (up, 1) or not (down, 0)
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+static int
+ifUp(const char *name, int up)
+{
+ return chgIfFlags(name,
+ (up) ? 0 : IFF_UP,
+ (up) ? IFF_UP : 0);
+}
+
+
+/**
+ * checkIf
+ *
+ * @ifname: Name of the interface
+ * @macaddr: expected MAC address of the interface
+ *
+ * FIXME: the interface's index is another good parameter to check
+ *
+ * Determine whether a given interface is still available. If so,
+ * it must have the given MAC address.
+ *
+ * Returns an error code ENODEV in case the interface does not exist
+ * anymore or its MAC address is different, 0 otherwise.
+ */
+int
+checkIf(const char *ifname, const unsigned char *macaddr)
+{
+ struct ifreq ifr;
+ int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+ int rc = 0;
+
+ if (fd < 0)
+ return errno;
+
+ if (virStrncpy(ifr.ifr_name,
+ ifname, strlen(ifname), sizeof(ifr.ifr_name)) == NULL) {
+ rc = ENODEV;
+ goto err_exit;
+ }
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ rc = errno;
+ goto err_exit;
+ }
+
+ if (memcmp(&ifr.ifr_hwaddr.sa_data, macaddr, 6) != 0)
+ rc = ENODEV;
+
+ err_exit:
+ close(fd);
+ return rc;
+}
+
+
+int
+virNWFilterInstantiateFilterLate(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver)
+{
+ int rc;
+ rc = __virNWFilterInstantiateFilter(conn,
+ 1,
+ ifname,
+ linkdev,
+ nettype,
+ macaddr,
+ filtername,
+ filterparams,
+ INSTANTIATE_ALWAYS,
+ driver);
+ if (rc) {
+ //something went wrong... 'DOWN' the interface
+ if (ifUp(ifname ,0)) {
+ // assuming interface disappeared...
+ _virNWFilterTeardownFilter(ifname);
+ }
+ }
+ return rc;
+}
+
+
int
virNWFilterInstantiateFilter(virConnectPtr conn,
const virDomainNetDefPtr net)
@@ -660,8 +1004,8 @@ virNWFilterTearOldFilter(virConnectPtr c
}
-int
-virNWFilterTeardownFilter(const virDomainNetDefPtr net)
+static int
+_virNWFilterTeardownFilter(const char *ifname)
{
const char *drvname = EBIPTABLES_DRIVER_ID;
virNWFilterTechDriverPtr techdriver;
@@ -676,13 +1020,21 @@ virNWFilterTeardownFilter(const virDomai
#endif
return 1;
}
+ techdriver->allTeardown(ifname);
- techdriver->allTeardown(net->ifname);
+ virNWFilterDelIpAddrForIfname(ifname);
return 0;
}
+int
+virNWFilterTeardownFilter(const virDomainNetDefPtr net)
+{
+ return _virNWFilterTeardownFilter(net->ifname);
+}
+
+
void
virNWFilterDomainFWUpdateCB(void *payload,
const char *name ATTRIBUTE_UNUSED,
Index: libvirt-acl/src/nwfilter/nwfilter_driver.c
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_driver.c
+++ libvirt-acl/src/nwfilter/nwfilter_driver.c
@@ -37,6 +37,8 @@
#include "nwfilter_gentech_driver.h"
+#include "nwfilter_learnipaddr.h"
+
#define VIR_FROM_THIS VIR_FROM_NWFILTER
#define nwfilterLog(msg...) fprintf(stderr, msg)
@@ -65,9 +67,12 @@ static int
nwfilterDriverStartup(int privileged) {
char *base = NULL;
- if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB) < 0)
+ if (virNWFilterLearnInit() < 0)
return -1;
+ if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB) < 0)
+ goto conf_init_err;
+
if (VIR_ALLOC(driverState) < 0)
goto alloc_err_exit;
@@ -120,6 +125,9 @@ error:
alloc_err_exit:
virNWFilterConfLayerShutdown();
+conf_init_err:
+ virNWFilterLearnShutdown();
+
return -1;
}
@@ -413,5 +421,6 @@ static virStateDriver stateDriver = {
int nwfilterRegister(void) {
virRegisterNWFilterDriver(&nwfilterDriver);
virRegisterStateDriver(&stateDriver);
+ virNWFilterLearnInit();
return 0;
}
Index: libvirt-acl/src/nwfilter/nwfilter_gentech_driver.h
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_gentech_driver.h
+++ libvirt-acl/src/nwfilter/nwfilter_gentech_driver.h
@@ -46,13 +46,25 @@ int virNWFilterRollbackUpdateFilter(virC
int virNWFilterTearOldFilter(virConnectPtr conn,
const virDomainNetDefPtr net);
+int virNWFilterInstantiateFilterLate(virConnectPtr conn,
+ const char *ifname,
+ const char *linkdev,
+ enum virDomainNetType nettype,
+ const unsigned char *macaddr,
+ const char *filtername,
+ virNWFilterHashTablePtr filterparams,
+ virNWFilterDriverStatePtr driver);
+
int virNWFilterTeardownFilter(const virDomainNetDefPtr net);
virNWFilterHashTablePtr virNWFilterCreateVarHashmap(virConnectPtr conn,
- char *macaddr);
+ char *macaddr,
+ char *ipaddr);
void virNWFilterDomainFWUpdateCB(void *payload,
const char *name ATTRIBUTE_UNUSED,
void *data);
+int checkIf(const char *ifname, const unsigned char *macaddr);
+
#endif
Index: libvirt-acl/src/libvirt_private.syms
===================================================================
--- libvirt-acl.orig/src/libvirt_private.syms
+++ libvirt-acl/src/libvirt_private.syms
@@ -488,6 +488,8 @@ virNWFilterRegisterCallbackDriver;
virNWFilterTestUnassignDef;
virNWFilterConfLayerInit;
virNWFilterConfLayerShutdown;
+virNWFilterLockFilterUpdates;
+virNWFilterUnlockFilterUpdates;
#nwfilter_params.h
@@ -503,6 +505,16 @@ virNWFilterInstantiateFilter;
virNWFilterTeardownFilter;
+#nwfilter_learnipaddr.h
+ipAddressMap;
+ipAddressMapLock;
+pendingLearnReq;
+pendingLearnReqLock;
+virNWFilterGetIpAddrForIfname;
+virNWFilterDelIpAddrForIfname;
+virNWFilterLookupLearnReq;
+
+
# pci.h
pciGetDevice;
pciFreeDevice;
Index: libvirt-acl/src/conf/nwfilter_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/nwfilter_conf.c
+++ libvirt-acl/src/conf/nwfilter_conf.c
@@ -111,17 +111,18 @@ struct int_map {
*/
static virMutex updateMutex;
-static void
+void
virNWFilterLockFilterUpdates(void) {
virMutexLock(&updateMutex);
}
-static void
+void
virNWFilterUnlockFilterUpdates(void) {
virMutexUnlock(&updateMutex);
}
+
/*
* attribute names for the rules XML
*/
@@ -2731,7 +2732,7 @@ int virNWFilterConfLayerInit(virHashIter
{
virNWFilterDomainFWUpdateCB = domUpdateCB;
- if (virMutexInit(&updateMutex))
+ if (virMutexInitRecursive(&updateMutex))
return 1;
if (virNWFilterParamConfLayerInit())
Index: libvirt-acl/src/conf/nwfilter_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/nwfilter_conf.h
+++ libvirt-acl/src/conf/nwfilter_conf.h
@@ -565,6 +565,9 @@ virNWFilterDefPtr virNWFilterDefParseFil
void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj);
void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj);
+void virNWFilterLockFilterUpdates(void);
+void virNWFilterUnlockFilterUpdates(void);
+
int virNWFilterConfLayerInit(virHashIterator domUpdateCB);
void virNWFilterConfLayerShutdown(void);
Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c
+++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -2533,6 +2533,106 @@ ebiptablesInstCommand(virConnectPtr conn
}
+int
+ebtablesApplyBasicRules(virConnectPtr conn,
+ const char *ifname,
+ const unsigned char *macaddr)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+ char chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix = CHAINPREFIX_HOST_IN_TEMP;
+ char macaddr_str[VIR_MAC_STRING_BUFLEN];
+
+ virFormatMacAddr(macaddr, macaddr_str);
+
+ ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(conn, &buf, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname);
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+
+ ebtablesCreateTmpRootChain(conn, &buf, 1, ifname, 1);
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -s ! %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ macaddr_str,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -p IPv4 -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -p ARP -j ACCEPT") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ virBufferVSprintf(&buf,
+ CMD_DEF(EBTABLES_CMD
+ " -t %s -A %s -j DROP") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE,
+ chain,
+ CMD_STOPONERR(1));
+
+ ebtablesLinkTmpRootChain(conn, &buf, 1, ifname, 1);
+
+ if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0)
+ goto tear_down_tmpebchains;
+
+ return 0;
+
+tear_down_tmpebchains:
+ ebtablesRemoveBasicRules(conn, ifname);
+
+ virNWFilterReportError(conn, VIR_ERR_BUILD_FIREWALL,
+ "%s",
+ _("Some rules could not be created."));
+
+ return 1;
+}
+
+
+int
+ebtablesRemoveBasicRules(virConnectPtr conn,
+ const char *ifname)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+
+ ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(conn, &buf, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname);
+
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+ return 0;
+}
+
+
static int
ebiptablesRuleOrderSort(const void *a, const void *b)
{
Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.h
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.h
+++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.h
@@ -45,4 +45,11 @@ extern virNWFilterTechDriver ebiptables_
# define EBIPTABLES_DRIVER_ID "ebiptables"
+
+int ebtablesApplyBasicRules(virConnectPtr conn,
+ const char *ifname,
+ const unsigned char *macaddr);
+int ebtablesRemoveBasicRules(virConnectPtr conn,
+ const char *ifname);
+
#endif
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -41,6 +41,7 @@ XMLRPC_REQUIRED=1.14.0
HAL_REQUIRED=0.5.0
DEVMAPPER_REQUIRED=1.0.0
LIBCURL_REQUIRED="7.18.0"
+LIBPCAP_REQUIRED="1.0.0"
dnl Checks for C compiler.
AC_PROG_CC
@@ -1045,6 +1046,39 @@ AC_SUBST([NUMACTL_CFLAGS])
AC_SUBST([NUMACTL_LIBS])
+dnl pcap lib
+LIBPCAP_CONFIG="pcap-config"
+LIBPCAP_CFLAGS=""
+LIBPCAP_LIBS=""
+LIBPCAP_FOUND="no"
+
+AC_ARG_WITH([libpcap], AC_HELP_STRING([--with-libpcap=@<:@PFX@:>@], [libpcap location]))
+if test "$with_qemu" = "yes"; then
+ if test "x$with_libpcap" != "xno" ; then
+ if test "x$with_libpcap" != "x" ; then
+ LIBPCAP_CONFIG=$with_libpcap/bin/$LIBPCAP_CONFIG
+ fi
+ AC_MSG_CHECKING(libpcap $LIBPCAP_CONFIG >= $LIBPCAP_REQUIRED )
+ if ! $LIBPCAP_CONFIG --libs > /dev/null 2>&1 ; then
+ AC_MSG_RESULT(no)
+ else
+ LIBPCAP_LIBS="`$LIBPCAP_CONFIG --libs`"
+ LIBPCAP_CFLAGS="`$LIBPCAP_CONFIG --cflags`"
+ LIBPCAP_FOUND="yes"
+ AC_MSG_RESULT(yes)
+ fi
+ fi
+fi
+
+if test "x$LIBPCAP_FOUND" = "xyes"; then
+ AC_DEFINE_UNQUOTED([HAVE_LIBPCAP], 1, [whether libpcap can be used])
+fi
+
+AC_SUBST([LIBPCAP_CFLAGS])
+AC_SUBST([LIBPCAP_LIBS])
+
+
+
dnl
dnl Checks for the UML driver
dnl
@@ -2129,6 +2163,11 @@ AC_MSG_NOTICE([ xmlrpc: $XMLRPC_CFLAGS
else
AC_MSG_NOTICE([ xmlrpc: no])
fi
+if test "$with_qemu" = "yes" ; then
+AC_MSG_NOTICE([ pcap: $LIBPCAP_CFLAGS $LIBPCAP_LIBS])
+else
+AC_MSG_NOTICE([ pcap: no])
+fi
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Test suite])
AC_MSG_NOTICE([])
14 years, 9 months
[libvirt] [RFC]: Snapshot API v3
by Chris Lalancette
Hello,
After our discussions about the snapshot API last week, I went ahead and implemented
quite a bit of the API. I also went back to the ESX, Virtualbox, and QEMU API's to
try and make sure our API's matched up. What's below is my revised API based on
that survey. Following my revised API are notes that I took regarding how the
libvirt API matches up to the various API's, and some questions about semantics that
I had while doing the survey. More comments and questions are welcome.
/* NOTE: struct _virDomainSnapshot is a private structure, ala
* struct _virDomain.
*/
typedef struct _virDomainSnapshot virDomainSnapshot;
/* Take a snapshot of the current VM state. Throws an error if
* the VM is not currently running */
virDomainSnapshotPtr virDomainSnapshotCreateXML(virDomainPtr domain,
const char *xmlDesc,
unsigned int flags);
/* Dump the XML of a snapshot */
/* NOTE: see below for proposed XML */
char *virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
unsigned int flags);
/* Return the number of snapshots for this domain */
int virDomainSnapshotNum(virDomainPtr domain, unsigned int flags);
/* Get the names of all snapshots for this domain */
int virDomainListSnapshotNames(virDomainPtr domain, char **names, int nameslen,
unsigned int flags);
/* Get a handle to a named snapshot */
virDomainSnapshotPtr virDomainSnapshotLookupByName(virDomainPtr domain,
const char *name,
unsigned int flags);
/* Get a handle to the current in-use snapshot for the domain */
virDomainSnapshotPtr virDomainSnapshotCurrent(virDomainPtr domain,
unsigned int flags);
/* Start the guest from the snapshot "snapshot" */
int virDomainCreateFromSnapshot(virDomainSnapshotPtr snapshot,
unsigned int flags);
/* Rename the snapshot */
/* Do we really need this? In theory we could use
* virsh snapshot-edit <domain> <name> and then detect
* name changes, but that will require a UUID, which may
* or may not be overkill
*/
int virDomainSnapshotRename(virDomainSnapshotPtr snapshot,
char *newname,
unsigned int flags);
/* Delete a snapshot - with no flags, the snapshot is not used anymore,
* but also not removed. With a MERGE flag, it merges the snapshot into
* the parent snapshot (or the base image, if there is no parent snapshot).
* Note that if other snapshots would be discarded because of this
* MERGE action, this operation will fail. If that is really what is intended,
* use MERGE_FORCE.
*
* With a DISCARD flag, it deletes the snapshot. Note that if children snapshots
* would be discarded because of this delete action, this operation will
* fail. If this is really what is intended, use DISCARD_FORCE.
*
* MERGE, MERGE_FORCE, DISCARD, and DISCARD_FORCE are mutually-exclusive.
*
* Note that this operation can happen when the domain is running or shut
* down, though this is hypervisor specific */
typedef enum {
VIR_DOMAIN_SNAPSHOT_DELETE_MERGE,
VIR_DOMAIN_SNAPSHOT_DELETE_MERGE_FORCE,
VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD,
VIR_DOMAIN_SNAPSHOT_DELETE_DISCARD_FORCE,
} virDomainSnapshotDelete;
int virDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
unsigned int flags);
int virDomainSnapshotFree(virDomainSnapshotPtr snapshot);
NOTE: During snapshot creation, *none* of the fields are required. That is,
you can call virDomainSnapshotCreateXML() with an XML of "<domainsnapshot/>".
In this case, the individual driver will make up a <name> and <uuid> for you,
the <creationdate> will be set to the current time+date, <description> will be
empty, <state> will be the current state of the VM, and <parent> will
be set to the current snapshot (if any). If you do want to specify some
fields during virDomainSnapshotCreateXML(), note that the only ones that are
settable are <name>, <uuid>, and <description>;
the rest are ignored, and filled in by the driver when the snapshot is
actually created.
NOTE: <state> refers to the state of the VM when the snapshot was taken.
<domainsnapshot>
<name>XYZ</name>
<creationdate>...</creationdate>
<description>...</description>
<state>RUNNING</state>
<domain>
<uuid>XXXXX-XXXX-XXXX-XXXX-XXXXXXXXX</uuid>
</domain>
<parent>
<name>ABC</name>
</parent>
</domainsnapshot>
The virsh commands will be:
virsh snapshot-create <dom> <xmlfile>
virsh snapshot-list <dom>
virsh snapshot-dumpxml <dom> <name>
virsh start-with-snapshot <dom> <snapshotname>
virsh snapshot-delete <dom> <snapshotname> [--merge|--mergeforce|--delete|--deleteforce]
virsh snapshot-delete-all <dom>
Possible issues:
1) I don't see a way to support "managed" save/restore and snapshotting with
this API. I think we'll have to have a separate API for managed save/restore.
2) What is the semantic for deleting snapshots from a running domain?
Virtualbox seems to not allow you to manipulate snapshots while the domain is
running. Qemu does allow this, but it's currently unclear what the exact
semantics are. VMware seems to allow manipulation of snapshots while the
domain is running.
3) Do we need a snapshot UUID? Virtualbox allows you to have multiple snapshots
with the same name, differentiated by UUID. Confusingly, they also have a
"FindByName" method that returns the first depth-first search snapshot that matches
a given name. For qemu, if you specify the same name twice it overwrites the previous
one with the new one. I don't know what ESX does here.
Mapping of our interface to various hypervisors:
+-------------------------------+-----------------+-------------------+------------------------------+
| Libvirt | Qemu | Virtualbox | ESX |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotCreateXML | monitor command | takeSnapshot | CreateSnapshot_task |
| | "savevm"; if | Snapshots can | takes a name, description, |
| | snapshot name | be taken on | memory (true/false) and |
| | is already in | powered off, | quiesce (true/false). |
| | use, replaces | saved, running, | What does "memory" mean? |
| | the previous | or paused VMs. | Should we model "quiesce" |
| | snapshot. Also | The snapshot is | Trees of snapshots are |
| | qemu-img | always taken | supported. What happens |
| | snapshot -c can | against the | on a duplicate name? What |
| | be used to | current snapshot. | state(s) can a VM be in |
| | create a | What happens on | when calling this? Does |
| | disk-only | a duplicate | a VM get paused when this |
| | snapshot. What | name? Trees of | is called? |
| | happens if the | snapshots are | |
| | VM is running | not currently | |
| | when you do | supported. | |
| | this? Trees of | Taking a snapshot | |
| | snapshots seem | of a running VM | |
| | to be supported | pauses the VM | |
| | VM gets paused | before taking the | |
| | while this is | snapshot. | |
| | happening. What | | |
| | states can the | | |
| | VM be in? | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotGetXMLDesc | Libvirt qemu | GetId | VirtualMachine object-> |
| | snapshot | GetDescription | snapshot-> rootSnapshotList |
| | metadata | GetTimeStamp | [i].createTime |
| | | GetParent | .description |
| | | GetCurrentSnapshot| .name |
| | | | .id |
| | | | .state |
| | | | .quiesced |
| | | | .vm |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotNum | Libvirt qemu | GetSnapshotCount | VirtualMachine object-> |
| | snapshot | | snapshot-> rootSnapshotList |
| | metadata | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainListSnapshotNames | Libvirt qemu | GetSnapshotCount | VirtualMachine object-> |
| | snapshot | GetChildren | snapshot-> rootSnapshotList |
| | metadata, or | | |
| | listvm monitor | | |
| | command or | | |
| | qemu-img | | |
| | snapshot -l | | |
| | <file>. If the | | |
| | VM has multiple | | |
| | disks and the | | |
| | disks have | | |
| | different | | |
| | snapshots, what | | |
| | do you do? | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotLookupByName | " | findSnapshot | VirtualMachine object-> |
| | | Takes a name and | snapshot-> rootSnapshotList |
| | | returns a | |
| | | snapshot object. | |
| | | In case of | |
| | | multiple snapshots| |
| | | with the same | |
| | | name, it returns | |
| | | the first object | |
| | | from a depth | |
| | | first search. | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainCreateFromSnapshot | qemu-img | restoreSnapshot | RevertToSnapshot_Task |
| | snapshot -a | Takes a snapshot | Changes execution state |
| | <snapname> | object, and | of VM to state of this |
| | <file>. What | resets the VM's | snapshot. Takes a |
| | happens if the | state to that of | snapshot object, an |
| | VM has multiple | the snapshot. | optional host, and |
| | files with | If this is a | suppressPowerOn, which |
| | different | snapshot taken | forces the VM to the off |
| | snapshots? | against a running | state regardless of the |
| | | machine, then the | state when the snapshot |
| | | memory is | was taken. Implies that |
| | | restored as well. | without this flag, VM |
| | | Does *not* start | starts in whatever |
| | | the VM. The VM | <state> was when snapshot |
| | | must be off for | was taken. |
| | | this operation to | |
| | | succeed. | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotDelete | monitor command | deleteSnapshot | RemoveSnapshot_Task |
| | "delvm". What | deletes the | removes this snapshot and |
| | happens if the | specified | deletes any associated |
| | snapshot is in | snapshot. Takes | storage. Operates on a |
| | use? What | an ID. The VM | VirtualMachineSnapshot |
| | states can the | must be off. | object. What states can |
| | VM be in? Also | Differences to | the VM be in? What |
| | qemu-img | children | happens if this snapshot |
| | snapshot -d | snapshots will be | is in-use? What happens |
| | <name> <file> | merged with the | to parents and children? |
| | command can be | children to keep | |
| | used. What | children valid. | |
| | happens if the | Parent for this | |
| | disk is in-use? | snapshot will | |
| | What happens to | become parent of | |
| | parents and | any children | |
| | children? | snapshots. | |
| | How do we | | |
| | handle merges? | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotCurrent | Libvirt qemu | currentSnapshot | VirtualMachine object-> |
| | snapshot | | snapshot-> currentSnapshot |
| | metadata | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotRename | Libvirt qemu | ISnapshot->name | RenameSnapshot |
| | snapshot | | |
| | metadata | | |
+-------------------------------+-----------------+-------------------+------------------------------+
| virDomainSnapshotDelete with | Libvirt qemu | deleteSnapshot | RemoveAllSnapshots_Task |
| DISCARD_FORCE flag against | snapshot | with manual depth | |
| root snapshot | metadata | deletion of | |
| | | children | |
+-------------------------------+-----------------+-------------------+------------------------------+
| | | | RevertToCurrentSnapshot_Task |
+-------------------------------+-----------------+-------------------+------------------------------+
Attribute mapping:
+----------------+----------+-------------+----------------------------+
| Libvirt | Qemu | Virtualbox | ESX |
+----------------+----------+-------------+----------------------------+
| <name> | TAG | name | name |
+----------------+----------+-------------+----------------------------+
| <creationdate> | DATE | timeStamp | createTime |
+----------------+----------+-------------+----------------------------+
| <description> | Libvirt | description | description |
| | qemu | | |
| | metadata | | |
+----------------+----------+-------------+----------------------------+
| <state> | Libvirt | online | state (this is the *power* |
| | qemu | | state of the VM when this |
| | metadata | | snapshot was taken) |
+----------------+----------+-------------+----------------------------+
| <domain><uuid> | Libvirt | machine | vm |
| | qemu | | |
| | metadata | | |
+----------------+----------+-------------+----------------------------+
| <parent><name> | Libvirt | parent | N/A |
| | qemu | | |
| | metadata | | |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | id (uuid) | id |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | children | childSnapshotList |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | N/A | memory |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | N/A | quiesced |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | N/A | backupManifest |
+----------------+----------+-------------+----------------------------+
| N/A | N/A | N/A | replaySupported |
+----------------+----------+-------------+----------------------------+
--
Chris Lalancette
14 years, 9 months
[libvirt] [RFC] Managed save APIs
by Daniel Veillard
The current API provides the following Save abd Restore functions:
int virDomainSave (virDomainPtr domain,
const char *to);
int virDomainRestore (virConnectPtr conn,
const char *from);
where the user provide the path to the file used to hold the domain
saved state.
However it can be really useful to have a managed version where
libvirt itself stores the data. It can then automatically reuse it
once the domain is to be restarted. This also avoid the problem of
keeping stale saved data once a domain has been restarted independantly.
One of our use case was to save the running domains state on shutdown,
and unfortunately virDomainSave()/virDomainRestore() can't be used
for that because if one of the domain is labelled as autorestart,
as soon as the daemon would restart it would recreate the guest but
without knowledge of the backup state.
So we propose the 3 following new APIs to deal with managed saves:
int virDomainManagedSave(virDomainPtr dom, unsigned int flags);
int virDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags);
int virDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags);
virDomainManagedSave() is to be run on a running domain. Once the call
complete, as in virDomainSave() the domain is stopped upon completion,
but there is no restore counterpart as any order to start the domain
from the API would load the state from the managed file, similary if
the domain is autostarted when libvirtd starts.
Once a domain has restarted his managed save image is destroyed,
basically managed save image can only exist for a stopped domain,
for a running domain that would be by definition outdated data.
Suggested new entry points in libvirt.c are below, thanks in advance for
any feedback (and thanks to Chris Lalancette who started this work):
diff --git a/src/libvirt.c b/src/libvirt.c
index cc5b4c5..293ea17 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -12135,3 +12135,143 @@ error:
virDispatchError(conn);
return -1;
}
+
+/**
+ * virDomainManagedSave:
+ * @dom: pointer to the domain
+ * @flags: optional flags currently unused
+ *
+ * This method will suspend a domain and save its memory contents to
+ * a file on disk. After the call, if successful, the domain is not
+ * listed as running anymore.
+ * The difference from virDomainSave() is that libvirt is keeping track of
+ * the saved state itself, and will reuse it once the domain is being
+ * restarted (automatically or via an explicit libvirt call).
+ * As a result any running domain is sure to not have a managed saved image.
+ *
+ * Returns 0 in case of success or -1 in case of failure
+ */
+int virDomainManagedSave(virDomainPtr dom, unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DEBUG("dom=%p, flags=%u", dom, flags);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(dom)) {
+ virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = dom->conn;
+ if (conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(dom, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ goto error;
+ }
+
+ if (conn->driver->domainManagedSave) {
+ int ret;
+
+ ret = conn->driver->domainManagedSave(dom, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(conn);
+ return -1;
+}
+
+/**
+ * virDomainHasManagedSaveImage:
+ * @dom: pointer to the domain
+ * @flags: optional flags currently unused
+ *
+ * Check if a domain has a managed save image as created by
+ * virDomainManagedSave(). Note that any running domain should not have
+ * such an image, as it should have been removed on restart.
+ *
+ * Returns 0 if no image is present, 1 if an image is present, and
+ * -1 in case of error
+ */
+int virDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DEBUG("dom=%p, flags=%u", dom, flags);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(dom)) {
+ virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = dom->conn;
+
+ if (conn->driver->domainHasManagedSaveImage) {
+ int ret;
+
+ ret = conn->driver->domainHasManagedSaveImage(dom, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(conn);
+ return -1;
+}
+
+/**
+ * virDomainManagedSaveRemove:
+ * @dom: pointer to the domain
+ * @flags: optional flags currently unused
+ *
+ * Remove any managed save image as for this domain.
+ *
+ * Returns 0 in case of success, and -1 in case of error
+ */
+int virDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DEBUG("dom=%p, flags=%u", dom, flags);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(dom)) {
+ virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = dom->conn;
+ if (conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(dom, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ goto error;
+ }
+
+ if (conn->driver->domainManagedSaveRemove) {
+ int ret;
+
+ ret = conn->driver->domainManagedSaveRemove(dom, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(conn);
+ return -1;
+}
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
14 years, 9 months
[libvirt] [PATCH 00/10] Begin to replace scanf and atoi
by Matthias Bolte
This set of patches is a first step towards removing the scanf and atoi
usage in libvirt. I began with the simple cases and post this now to get
some feedback before I start to convert the more difficult ones.
01/10 Replace sscanf in legacy device address parsing
02/10 Replace sscanf in nwfilter rule parsing
03/10 Refactor major.minor.micro version parsing into a function
04/10 cgroup: Replace sscanf with virStrToLong_ll
05/10 vbox: Replace atoi with virStrToLong_i
06/10 xen: Use virParseMacAddr instead of sscanf
07/10 openvz: Use strtok_r instead of sscanf for VPS UUID parsing
08/10 xenapi: Use virStrToLong_i instead of sscanf for CPU map parsing
09/10 xen: Use virStrToLong_i instead of sscanf for XenD port parsing
10/10 Replace sscanf in PCI device address parsing
src/conf/domain_conf.c | 38 +++++++++++++++++++---------
src/conf/nwfilter_conf.c | 10 +++---
src/esx/esx_driver.c | 31 ++++------------------
src/libvirt_private.syms | 1 +
src/lxc/lxc_driver.c | 10 +------
src/openvz/openvz_conf.c | 27 ++++++++++++-------
src/uml/uml_conf.h | 2 +-
src/uml/uml_driver.c | 17 ++++++------
src/util/cgroup.c | 3 +-
src/util/pci.c | 59 ++++++++++++++++++++++++++++++++++---------
src/util/util.c | 37 +++++++++++++++++++++++++++
src/util/util.h | 1 +
src/vbox/vbox_tmpl.c | 18 +++++--------
src/xen/xend_internal.c | 20 ++------------
src/xen/xm_internal.c | 19 +++----------
src/xenapi/xenapi_driver.c | 21 +++++++--------
src/xenapi/xenapi_utils.c | 3 +-
17 files changed, 179 insertions(+), 138 deletions(-)
Matthias
14 years, 9 months
[libvirt] [PATCH] website: Add archive link for libvirt-users list
by Matthias Bolte
---
docs/contact.html.in | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/docs/contact.html.in b/docs/contact.html.in
index 09f8e35..c9dceda 100644
--- a/docs/contact.html.in
+++ b/docs/contact.html.in
@@ -21,7 +21,7 @@
</ul>
</dd>
- <dt><a href="">libvirt-users(a)redhat.com</a></dt>
+ <dt><a href="https://www.redhat.com/archives/libvirt-users/">libvirt-users(a)redhat.com</a></dt>
<dd>This list a place for discussions involving libvirt <strong>users</strong>. Topics for discussion include
<ul>
<li>Usage of libvirt / virsh</li>
--
1.6.3.3
14 years, 9 months