On 05/15/2018 01:43 PM, Daniel P. Berrangé wrote:
A typical XML representation of the virNWFilterBindingDefPtr struct
looks like this:
<filterbinding>
<owner>
<name>f25arm7</name>
<uuid>12ac8b8c-4f23-4248-ae42-fdcd50c400fd</uuid>
</owner>
<portdev name='vnet1'/>
<mac address='52:54:00:9d:81:b1'/>
<filterref filter='clean-traffic'>
<parameter name='MAC' value='52:54:00:9d:81:b1'/>
</filterref>
</filterbinding>
I haven't had time to look at this in detail yet, or to really think
about the comment I'm going to make, but I wanted to be sure I said it
right away in case there are any public API implications
By now we all know the horror by which OpenStack's networking creates a
separate bridge device, and connects the guest's tap device to that
bridge so that (in addition to other reasons) there is a place for
libvirt's nwfilter rules to attach (what they *really* want to connect
to is an Open vSwitch, but ipfilter rules aren't in the data path when a
tap device is connected to OVS). This atrocity could be avoided if
nwfilter supported OVN (OVS's ipfilter analog) directly. In order to
support it though, nwfilter would need to know more details about the
network device that it's adding rules for. (because some guests may be
connected to OVS and others may be connected to a standard host bridge
(or nothing at all) we can't just have a system-wide config to decide).
I can't recall if there is a reasonable API to figure out what a tap
device is connected to, but I think such a thing may not exist and, if
so, we'll likely need to send some sort of indicator in the
NWFilterBinding XML. It *might* be simpler / more futureproof if we
included the <virtualport> element that is already in the domain XML
<interface> information - that's currently what we use to decide how to
connect the tap device; hopefully in the future it will continue to
conain everything that's needed (if we think that's inadequate, we could
just go for broke and include the entire <interface>, but that's
probably overkill. (although..... - thinking about the potential case
where some SRIOV network card supported some sort of filtering, if we
sent the entire <interface>, we would know that it was a hostdev...)
I started typing this wondering if the C API might need any change, but
now that I've typed this, I realize it would only require additions to
the XML, which can always be done later, so
nevermind (kind of) </EmilyLittela>
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/conf/virnwfilterbindingdef.c | 196 ++++++++++++++++++
src/conf/virnwfilterbindingdef.h | 18 ++
src/libvirt_private.syms | 5 +
tests/Makefile.am | 7 +
.../filter-vars.xml | 11 +
.../virnwfilterbindingxml2xmldata/simple.xml | 9 +
tests/virnwfilterbindingxml2xmltest.c | 113 ++++++++++
7 files changed, 359 insertions(+)
create mode 100644 tests/virnwfilterbindingxml2xmldata/filter-vars.xml
create mode 100644 tests/virnwfilterbindingxml2xmldata/simple.xml
create mode 100644 tests/virnwfilterbindingxml2xmltest.c
diff --git a/src/conf/virnwfilterbindingdef.c b/src/conf/virnwfilterbindingdef.c
index c7533d4063..23c040ab05 100644
--- a/src/conf/virnwfilterbindingdef.c
+++ b/src/conf/virnwfilterbindingdef.c
@@ -25,6 +25,7 @@
#include "virstring.h"
#include "nwfilter_params.h"
#include "virnwfilterbindingdef.h"
+#include "viruuid.h"
#define VIR_FROM_THIS VIR_FROM_NWFILTER
@@ -81,3 +82,198 @@ virNWFilterBindingDefCopy(virNWFilterBindingDefPtr src)
virNWFilterBindingDefFree(ret);
return NULL;
}
+
+
+static virNWFilterBindingDefPtr
+virNWFilterBindingDefParseXML(xmlXPathContextPtr ctxt)
+{
+ virNWFilterBindingDefPtr ret;
+ char *uuid = NULL;
+ char *mac = NULL;
+ xmlNodePtr node;
+
+ if (VIR_ALLOC(ret) < 0)
+ return NULL;
+
+ ret->portdevname = virXPathString("string(./portdev/@name)", ctxt);
+ if (!ret->portdevname) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no port dev
name"));
+ goto cleanup;
+ }
+
+ if (virXPathNode("./linkdev", ctxt)) {
+ ret->linkdevname = virXPathString("string(./linkdev/@name)",
ctxt);
+ if (!ret->linkdevname) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no link dev
name"));
+ goto cleanup;
+ }
+ }
+
+ ret->ownername = virXPathString("string(./owner/name)", ctxt);
+ if (!ret->ownername) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no owner
name"));
+ goto cleanup;
+ }
+
+ uuid = virXPathString("string(./owner/uuid)", ctxt);
+ if (!uuid) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no owner
UUID"));
+ goto cleanup;
+ }
+
+ if (virUUIDParse(uuid, ret->owneruuid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse UUID '%s'"), uuid);
+ VIR_FREE(uuid);
+ goto cleanup;
+ }
+ VIR_FREE(uuid);
+
+ mac = virXPathString("string(./mac/@address)", ctxt);
+ if (!mac) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no MAC
address"));
+ goto cleanup;
+ }
+
+ if (virMacAddrParse(mac, &ret->mac) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse MAC '%s'"), mac);
+ VIR_FREE(mac);
+ goto cleanup;
+ }
+ VIR_FREE(mac);
+
+ ret->filter = virXPathString("string(./filterref/@filter)", ctxt);
+ if (!ret->filter) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("filter binding has no filter
reference"));
+ goto cleanup;
+ }
+
+ node = virXPathNode("./filterref", ctxt);
+ if (node &&
+ !(ret->filterparams = virNWFilterParseParamAttributes(node)))
+ goto cleanup;
+
+ return ret;
+
+ cleanup:
+ virNWFilterBindingDefFree(ret);
+ return NULL;
+}
+
+
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseNode(xmlDocPtr xml,
+ xmlNodePtr root)
+{
+ xmlXPathContextPtr ctxt = NULL;
+ virNWFilterBindingDefPtr def = NULL;
+
+ if (STRNEQ((const char *)root->name, "filterbinding")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ "%s",
+ _("unknown root element for nwfilter binding"));
+ goto cleanup;
+ }
+
+ ctxt = xmlXPathNewContext(xml);
+ if (ctxt == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ctxt->node = root;
+ def = virNWFilterBindingDefParseXML(ctxt);
+
+ cleanup:
+ xmlXPathFreeContext(ctxt);
+ return def;
+}
+
+
+static virNWFilterBindingDefPtr
+virNWFilterBindingDefParse(const char *xmlStr,
+ const char *filename)
+{
+ virNWFilterBindingDefPtr def = NULL;
+ xmlDocPtr xml;
+
+ if ((xml = virXMLParse(filename, xmlStr,
_("(nwfilterbinding_definition)")))) {
+ def = virNWFilterBindingDefParseNode(xml, xmlDocGetRootElement(xml));
+ xmlFreeDoc(xml);
+ }
+
+ return def;
+}
+
+
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseString(const char *xmlStr)
+{
+ return virNWFilterBindingDefParse(xmlStr, NULL);
+}
+
+
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseFile(const char *filename)
+{
+ return virNWFilterBindingDefParse(NULL, filename);
+}
+
+char *
+virNWFilterBindingDefFormat(const virNWFilterBindingDef *def)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (virNWFilterBindingDefFormatBuf(&buf, def) < 0) {
+ virBufferFreeAndReset(&buf);
+ return NULL;
+ }
+
+ if (virBufferCheckError(&buf) < 0)
+ return NULL;
+
+ return virBufferContentAndReset(&buf);
+}
+
+
+int
+virNWFilterBindingDefFormatBuf(virBufferPtr buf,
+ const virNWFilterBindingDef *def)
+{
+ char uuid[VIR_UUID_STRING_BUFLEN];
+ char mac[VIR_MAC_STRING_BUFLEN];
+
+ virBufferAddLit(buf, "<filterbinding>\n");
+
+ virBufferAdjustIndent(buf, 2);
+
+ virBufferAddLit(buf, "<owner>\n");
+ virBufferAdjustIndent(buf, 2);
+ virBufferEscapeString(buf, "<name>%s</name>\n",
def->ownername);
+ virUUIDFormat(def->owneruuid, uuid);
+ virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid);
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</owner>\n");
+
+ virBufferEscapeString(buf, "<portdev name='%s'/>\n",
def->portdevname);
+ if (def->linkdevname)
+ virBufferEscapeString(buf, "<linkdev name='%s'/>\n",
def->linkdevname);
+
+ virMacAddrFormat(&def->mac, mac);
+ virBufferAsprintf(buf, "<mac address='%s'/>\n", mac);
+
+ if (virNWFilterFormatParamAttributes(buf, def->filterparams, def->filter) <
0)
+ return -1;
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</filterbinding>\n");
+
+ return 0;
+}
diff --git a/src/conf/virnwfilterbindingdef.h b/src/conf/virnwfilterbindingdef.h
index e3b18af151..af7fab6064 100644
--- a/src/conf/virnwfilterbindingdef.h
+++ b/src/conf/virnwfilterbindingdef.h
@@ -24,6 +24,7 @@
# include "internal.h"
# include "virmacaddr.h"
# include "virhash.h"
+# include "virbuffer.h"
typedef struct _virNWFilterBindingDef virNWFilterBindingDef;
typedef virNWFilterBindingDef *virNWFilterBindingDefPtr;
@@ -44,4 +45,21 @@ virNWFilterBindingDefFree(virNWFilterBindingDefPtr binding);
virNWFilterBindingDefPtr
virNWFilterBindingDefCopy(virNWFilterBindingDefPtr src);
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseNode(xmlDocPtr xml,
+ xmlNodePtr root);
+
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseString(const char *xml);
+
+virNWFilterBindingDefPtr
+virNWFilterBindingDefParseFile(const char *filename);
+
+char *
+virNWFilterBindingDefFormat(const virNWFilterBindingDef *def);
+
+int
+virNWFilterBindingDefFormatBuf(virBufferPtr buf,
+ const virNWFilterBindingDef *def);
+
#endif /* VIR_NWFILTER_BINDING_DEF_H */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fb754fbfea..03145c70d5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1043,7 +1043,12 @@ virNodeDeviceObjListRemove;
# conf/virnwfilterbindingdef.h
virNWFilterBindingDefCopy;
+virNWFilterBindingDefFormat;
+virNWFilterBindingDefFormatBuf;
virNWFilterBindingDefFree;
+virNWFilterBindingDefParseFile;
+virNWFilterBindingDefParseNode;
+virNWFilterBindingDefParseString;
# conf/virnwfilterobj.h
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 621480dd0c..036335e770 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -156,6 +156,7 @@ EXTRA_DIST = \
virmock.h \
virnetdaemondata \
virnetdevtestdata \
+ virnwfilterbindingxml2xmldata \
virpcitestdata \
virscsidata \
virsh-uriprecedence \
@@ -352,6 +353,7 @@ test_programs += storagebackendsheepdogtest
endif WITH_STORAGE_SHEEPDOG
test_programs += nwfilterxml2xmltest
+test_programs += virnwfilterbindingxml2xmltest
if WITH_NWFILTER
test_programs += nwfilterebiptablestest
@@ -855,6 +857,11 @@ nwfilterxml2xmltest_SOURCES = \
testutils.c testutils.h
nwfilterxml2xmltest_LDADD = $(LDADDS)
+virnwfilterbindingxml2xmltest_SOURCES = \
+ virnwfilterbindingxml2xmltest.c \
+ testutils.c testutils.h
+virnwfilterbindingxml2xmltest_LDADD = $(LDADDS)
+
if WITH_NWFILTER
nwfilterebiptablestest_SOURCES = \
nwfilterebiptablestest.c \
diff --git a/tests/virnwfilterbindingxml2xmldata/filter-vars.xml
b/tests/virnwfilterbindingxml2xmldata/filter-vars.xml
new file mode 100644
index 0000000000..dcff9640ce
--- /dev/null
+++ b/tests/virnwfilterbindingxml2xmldata/filter-vars.xml
@@ -0,0 +1,11 @@
+<filterbinding>
+ <owner>
+ <name>memtest</name>
+ <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+ </owner>
+ <portdev name='vnet0'/>
+ <mac address='52:54:00:7b:35:93'/>
+ <filterref filter='clean-traffic'>
+ <parameter name='MAC' value='52:54:00:7b:35:93'/>
+ </filterref>
+</filterbinding>
diff --git a/tests/virnwfilterbindingxml2xmldata/simple.xml
b/tests/virnwfilterbindingxml2xmldata/simple.xml
new file mode 100644
index 0000000000..4577729a3c
--- /dev/null
+++ b/tests/virnwfilterbindingxml2xmldata/simple.xml
@@ -0,0 +1,9 @@
+<filterbinding>
+ <owner>
+ <name>memtest</name>
+ <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+ </owner>
+ <portdev name='vnet0'/>
+ <mac address='52:54:00:7b:35:93'/>
+ <filterref filter='clean-traffic'/>
+</filterbinding>
diff --git a/tests/virnwfilterbindingxml2xmltest.c
b/tests/virnwfilterbindingxml2xmltest.c
new file mode 100644
index 0000000000..96edbdcf59
--- /dev/null
+++ b/tests/virnwfilterbindingxml2xmltest.c
@@ -0,0 +1,113 @@
+/*
+ * virnwfilterbindingxml2xmltest.h: network filter binding XML testing
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "internal.h"
+#include "testutils.h"
+#include "virxml.h"
+#include "virnwfilterbindingdef.h"
+#include "testutilsqemu.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+static int
+testCompareXMLToXMLFiles(const char *xml)
+{
+ char *actual = NULL;
+ int ret = -1;
+ virNWFilterBindingDefPtr dev = NULL;
+
+ virResetLastError();
+
+ if (!(dev = virNWFilterBindingDefParseFile(xml))) {
+ goto fail;
+ }
+
+ if (!(actual = virNWFilterBindingDefFormat(dev)))
+ goto fail;
+
+ if (virTestCompareToFile(actual, xml) < 0)
+ goto fail;
+
+ ret = 0;
+
+ fail:
+ VIR_FREE(actual);
+ virNWFilterBindingDefFree(dev);
+ return ret;
+}
+
+typedef struct test_parms {
+ const char *name;
+} test_parms;
+
+static int
+testCompareXMLToXMLHelper(const void *data)
+{
+ int result = -1;
+ const test_parms *tp = data;
+ char *xml = NULL;
+
+ if (virAsprintf(&xml, "%s/virnwfilterbindingxml2xmldata/%s.xml",
+ abs_srcdir, tp->name) < 0) {
+ goto cleanup;
+ }
+
+ result = testCompareXMLToXMLFiles(xml);
+
+ cleanup:
+ VIR_FREE(xml);
+
+ return result;
+}
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+#define DO_TEST(NAME) \
+ do { \
+ test_parms tp = { \
+ .name = NAME, \
+ }; \
+ if (virTestRun("NWFilter XML-2-XML " NAME, \
+ testCompareXMLToXMLHelper, (&tp)) < 0) \
+ ret = -1; \
+ } while (0)
+
+ DO_TEST("simple");
+ DO_TEST("filter-vars");
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN(mymain)