]> xenbits.xensource.com Git - libvirt.git/commitdiff
Add XML parser extensions for network filtering
authorStefan Berger <stefanb@us.ibm.com>
Thu, 25 Mar 2010 17:46:07 +0000 (13:46 -0400)
committerDaniel P. Berrange <berrange@redhat.com>
Fri, 26 Mar 2010 18:01:16 +0000 (18:01 +0000)
This patch adds XML processing for the network filter schema
and extends the domain XML processing to parse the top level
referenced filter along with potentially provided parameters

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com>
po/POTFILES.in
src/Makefile.am
src/conf/domain_conf.c
src/conf/domain_conf.h
src/conf/nwfilter_conf.c [new file with mode: 0644]
src/conf/nwfilter_conf.h [new file with mode: 0644]
src/conf/nwfilter_params.c [new file with mode: 0644]
src/conf/nwfilter_params.h [new file with mode: 0644]

index 3f9fc391cb339e44f230fafd3c7044cba6dc7e6f..e14360efa3a425557dd200b3005f0da7cf6d0bd9 100644 (file)
@@ -8,6 +8,8 @@ src/conf/domain_event.c
 src/conf/interface_conf.c
 src/conf/network_conf.c
 src/conf/node_device_conf.c
+src/conf/nwfilter_conf.c
+src/conf/nwfilter_params.c
 src/conf/secret_conf.c
 src/conf/storage_conf.c
 src/conf/storage_encryption_conf.c
index 5f6b32501a05080b9ad282127a72890eb3f8fc79..b873d82d87267b305234defbb07cee40192fda55 100644 (file)
@@ -97,9 +97,17 @@ DOMAIN_EVENT_SOURCES =                                               \
                conf/domain_event.c conf/domain_event.h
 
 # Network driver generic impl APIs
-NETWORK_CONF_SOURCES =                                         \
+NETWORK_CONF_SOURCES =                                         \
                conf/network_conf.c conf/network_conf.h
 
+# Network filter driver generic impl APIs
+NWFILTER_PARAM_CONF_SOURCES =                                  \
+               conf/nwfilter_params.c conf/nwfilter_conf.h
+
+NWFILTER_CONF_SOURCES =                                        \
+               $(NWFILTER_PARAM_CONF_SOURCES)                  \
+               conf/nwfilter_conf.c conf/nwfilter_conf.h
+
 # Storage driver generic impl APIs
 STORAGE_CONF_SOURCES =                                         \
                conf/storage_conf.h conf/storage_conf.c
@@ -126,6 +134,7 @@ CONF_SOURCES =                                                      \
                $(DOMAIN_CONF_SOURCES)                          \
                $(DOMAIN_EVENT_SOURCES)                         \
                $(NETWORK_CONF_SOURCES)                         \
+               $(NWFILTER_CONF_SOURCES)                        \
                $(NODE_DEVICE_CONF_SOURCES)                     \
                $(STORAGE_CONF_SOURCES)                         \
                $(ENCRYPTION_CONF_SOURCES)                      \
@@ -761,6 +770,7 @@ EXTRA_DIST +=                                                       \
                $(NODE_DEVICE_DRIVER_SOURCES)                   \
                $(NODE_DEVICE_DRIVER_HAL_SOURCES)               \
                $(NODE_DEVICE_DRIVER_UDEV_SOURCES)              \
+               $(NWFILTER_DRIVER_SOURCES)                      \
                $(SECURITY_DRIVER_SELINUX_SOURCES)              \
                $(SECURITY_DRIVER_APPARMOR_SOURCES)             \
                $(SECRET_DRIVER_SOURCES)                        \
@@ -903,6 +913,7 @@ libvirt_lxc_SOURCES =                                               \
                $(NODE_INFO_SOURCES)                            \
                $(ENCRYPTION_CONF_SOURCES)                      \
                $(DOMAIN_CONF_SOURCES)                          \
+               $(NWFILTER_PARAM_CONF_SOURCES)                  \
                $(CPU_CONF_SOURCES)
 libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS)
 libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la
index 651dd04630f4e672d99d0d8584f891048ad7e547..8fcd7f358ea8965c421a8a73f97060c893dc966f 100644 (file)
@@ -42,6 +42,7 @@
 #include "logging.h"
 #include "network.h"
 #include "macvtap.h"
+#include "nwfilter_conf.h"
 
 #define VIR_FROM_THIS VIR_FROM_DOMAIN
 
@@ -461,6 +462,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
 
     virDomainDeviceInfoClear(&def->info);
 
+    VIR_FREE(def->filter);
+    virNWFilterHashTableFree(def->filterparams);
+
     VIR_FREE(def);
 }
 
@@ -1736,9 +1740,11 @@ virDomainNetDefParseXML(virCapsPtr caps,
     char *address = NULL;
     char *port = NULL;
     char *model = NULL;
+    char *filter = NULL;
     char *internal = NULL;
     char *devaddr = NULL;
     char *mode = NULL;
+    virNWFilterHashTablePtr filterparams = NULL;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
@@ -1807,6 +1813,9 @@ virDomainNetDefParseXML(virCapsPtr caps,
                 script = virXMLPropString(cur, "path");
             } else if (xmlStrEqual (cur->name, BAD_CAST "model")) {
                 model = virXMLPropString(cur, "type");
+            } else if (xmlStrEqual (cur->name, BAD_CAST "filterref")) {
+                filter = virXMLPropString(cur, "filter");
+                filterparams = virNWFilterParseParamAttributes(cur);
             } else if ((flags & VIR_DOMAIN_XML_INTERNAL_STATUS) &&
                        xmlStrEqual(cur->name, BAD_CAST "state")) {
                 /* Legacy back-compat. Don't add any more attributes here */
@@ -1986,6 +1995,22 @@ virDomainNetDefParseXML(virCapsPtr caps,
         model = NULL;
     }
 
+    if (filter != NULL) {
+        switch (def->type) {
+        case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        case VIR_DOMAIN_NET_TYPE_NETWORK:
+        case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        case VIR_DOMAIN_NET_TYPE_DIRECT:
+            def->filter = filter;
+            filter = NULL;
+            def->filterparams = filterparams;
+            filterparams = NULL;
+        break;
+        default:
+        break;
+        }
+    }
+
 cleanup:
     VIR_FREE(macaddr);
     VIR_FREE(network);
@@ -1996,10 +2021,12 @@ cleanup:
     VIR_FREE(script);
     VIR_FREE(bridge);
     VIR_FREE(model);
+    VIR_FREE(filter);
     VIR_FREE(type);
     VIR_FREE(internal);
     VIR_FREE(devaddr);
     VIR_FREE(mode);
+    virNWFilterHashTableFree(filterparams);
 
     return def;
 
@@ -4810,6 +4837,7 @@ virDomainNetDefFormat(virBufferPtr buf,
                       int flags)
 {
     const char *type = virDomainNetTypeToString(def->type);
+    char *attrs;
 
     if (!type) {
         virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -4888,6 +4916,17 @@ virDomainNetDefFormat(virBufferPtr buf,
     if (def->model)
         virBufferEscapeString(buf, "      <model type='%s'/>\n",
                               def->model);
+    if (def->filter) {
+        virBufferEscapeString(buf, "      <filterref filter='%s'",
+                              def->filter);
+        attrs = virNWFilterFormatParamAttributes(def->filterparams,
+                                                 "        ");
+        if (!attrs || strlen(attrs) <= 1)
+            virBufferAddLit(buf, "/>\n");
+        else
+            virBufferVSprintf(buf, ">\n%s      </filterref>\n", attrs);
+        VIR_FREE(attrs);
+    }
 
     if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0)
         return -1;
index 218e0a79d2b93664c97429baed80d7bce5a26b4f..d97aa45ec20c707205f1f87f9db2e391e66be6fe 100644 (file)
@@ -36,6 +36,8 @@
 # include "threads.h"
 # include "hash.h"
 # include "network.h"
+# include "nwfilter_params.h"
+# include "nwfilter_conf.h"
 
 /* Private component of virDomainXMLFlags */
 typedef enum {
@@ -282,6 +284,8 @@ struct _virDomainNetDef {
     } data;
     char *ifname;
     virDomainDeviceInfo info;
+    char *filter;
+    virNWFilterHashTablePtr filterparams;
 };
 
 enum virDomainChrTargetType {
diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c
new file mode 100644 (file)
index 0000000..e5d285d
--- /dev/null
@@ -0,0 +1,2211 @@
+/*
+ * nwfilter_conf.c: network filter XML processing
+ *                  (derived from storage_conf.c)
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * 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@us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <net/ethernet.h>
+
+#include "internal.h"
+
+#include "uuid.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "nwfilter_params.h"
+#include "nwfilter_conf.h"
+#include "domain_conf.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+#define virNWFilterError(conn, code, fmt...)                             \
+            virReportErrorHelper(conn, VIR_FROM_NWFILTER, code, __FILE__,\
+                                  __FUNCTION__, __LINE__, fmt)
+
+VIR_ENUM_IMPL(virNWFilterRuleAction, VIR_NWFILTER_RULE_ACTION_LAST,
+              "drop",
+              "accept");
+
+VIR_ENUM_IMPL(virNWFilterJumpTarget, VIR_NWFILTER_RULE_ACTION_LAST,
+              "DROP",
+              "ACCEPT");
+
+VIR_ENUM_IMPL(virNWFilterRuleDirection, VIR_NWFILTER_RULE_DIRECTION_LAST,
+              "in",
+              "out",
+              "inout");
+
+VIR_ENUM_IMPL(virNWFilterChainPolicy, VIR_NWFILTER_CHAIN_POLICY_LAST,
+              "ACCEPT",
+              "DROP");
+
+VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST,
+              "filter",
+              "nat",
+              "broute");
+
+VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST,
+              "root",
+              "arp",
+              "ipv4");
+
+
+/*
+ * a map entry for a simple static int-to-string map
+ */
+struct int_map {
+    int32_t attr;
+    const char *val;
+};
+
+
+/*
+ * only one filter update allowed
+ */
+static virMutex updateMutex;
+
+static void
+virNWFilterLockFilterUpdates(void) {
+    virMutexLock(&updateMutex);
+}
+
+static void
+virNWFilterUnlockFilterUpdates(void) {
+    virMutexUnlock(&updateMutex);
+}
+
+
+/*
+ * attribute names for the rules XML
+ */
+static const char srcmacaddr_str[]   = "srcmacaddr";
+static const char srcmacmask_str[]   = "srcmacmask";
+static const char dstmacaddr_str[]   = "dstmacaddr";
+static const char dstmacmask_str[]   = "dstmacmask";
+static const char arpsrcmacaddr_str[]= "arpsrcmacaddr";
+static const char arpdstmacaddr_str[]= "arpdstmacaddr";
+static const char arpsrcipaddr_str[] = "arpsrcipaddr";
+static const char arpdstipaddr_str[] = "arpdstipaddr";
+static const char srcipaddr_str[]    = "srcipaddr";
+static const char srcipmask_str[]    = "srcipmask";
+static const char dstipaddr_str[]    = "dstipaddr";
+static const char dstipmask_str[]    = "dstipmask";
+static const char srcportstart_str[] = "srcportstart";
+static const char srcportend_str[]   = "srcportend";
+static const char dstportstart_str[] = "dstportstart";
+static const char dstportend_str[]   = "dstportend";
+static const char dscp_str[]         = "dscp";
+
+#define SRCMACADDR    srcmacaddr_str
+#define SRCMACMASK    srcmacmask_str
+#define DSTMACADDR    dstmacaddr_str
+#define DSTMACMASK    dstmacmask_str
+#define ARPSRCMACADDR arpsrcmacaddr_str
+#define ARPDSTMACADDR arpdstmacaddr_str
+#define ARPSRCIPADDR  arpsrcipaddr_str
+#define ARPDSTIPADDR  arpdstipaddr_str
+#define SRCIPADDR     srcipaddr_str
+#define SRCIPMASK     srcipmask_str
+#define DSTIPADDR     dstipaddr_str
+#define DSTIPMASK     dstipmask_str
+#define SRCPORTSTART  srcportstart_str
+#define SRCPORTEND    srcportend_str
+#define DSTPORTSTART  dstportstart_str
+#define DSTPORTEND    dstportend_str
+#define DSCP          dscp_str
+
+
+/**
+ * intMapGetByInt:
+ * @intmap: Pointer to int-to-string map
+ * @attr: The attribute to look up
+ * @res: Pointer to string pointer for result
+ *
+ * Returns 1 if value was found with result returned, 0 otherwise.
+ *
+ * lookup a map entry given the integer.
+ */
+static bool
+intMapGetByInt(const struct int_map *intmap, int32_t attr, const char **res)
+{
+    int i = 0;
+    bool found = 0;
+    while (intmap[i].val && !found) {
+        if (intmap[i].attr == attr) {
+            *res = intmap[i].val;
+            found = 1;
+        }
+        i++;
+    }
+    return found;
+}
+
+
+/**
+ * intMapGetByString:
+ * @intmap: Pointer to int-to-string map
+ * @str: Pointer to string for which to find the entry
+ * @casecmp : Whether to ignore case when doing string matching
+ * @result: Pointer to int for result
+ *
+ * Returns 0 if no entry was found, 1 otherwise.
+ *
+ * Do a lookup in the map trying to find an integer key using the string
+ * value. Returns 1 if entry was found with result returned, 0 otherwise.
+ */
+static bool
+intMapGetByString(const struct int_map *intmap, const char *str, int casecmp,
+                  int32_t *result)
+{
+    int i = 0;
+    bool found = 0;
+    while (intmap[i].val && !found) {
+        if ( (casecmp && STRCASEEQ(intmap[i].val, str)) ||
+                         STREQ    (intmap[i].val, str)    ) {
+            *result = intmap[i].attr;
+            found = 1;
+        }
+        i++;
+    }
+    return found;
+}
+
+
+void
+virNWFilterRuleDefFree(virNWFilterRuleDefPtr def) {
+    int i;
+    if (!def)
+        return;
+
+    for (i = 0; i < def->nvars; i++)
+        VIR_FREE(def->vars[i]);
+
+    VIR_FREE(def->vars);
+
+    VIR_FREE(def);
+}
+
+
+static void
+virNWFilterIncludeDefFree(virNWFilterIncludeDefPtr inc) {
+    if (!inc)
+        return;
+    virNWFilterHashTableFree(inc->params);
+    VIR_FREE(inc->filterref);
+    VIR_FREE(inc);
+}
+
+
+static void
+virNWFilterEntryFree(virNWFilterEntryPtr entry) {
+    if (!entry)
+        return;
+
+    virNWFilterRuleDefFree(entry->rule);
+    virNWFilterIncludeDefFree(entry->include);
+    VIR_FREE(entry);
+}
+
+
+void
+virNWFilterDefFree(virNWFilterDefPtr def) {
+    int i;
+    if (!def)
+        return;
+
+    VIR_FREE(def->name);
+
+    for (i = 0; i < def->nentries; i++)
+        virNWFilterEntryFree(def->filterEntries[i]);
+
+    VIR_FREE(def->filterEntries);
+
+    VIR_FREE(def);
+}
+
+
+void
+virNWFilterPoolObjFree(virNWFilterPoolObjPtr obj) {
+    if (!obj)
+        return;
+
+    virNWFilterDefFree(obj->def);
+    virNWFilterDefFree(obj->newDef);
+
+    VIR_FREE(obj->configFile);
+
+    virMutexDestroy(&obj->lock);
+
+    VIR_FREE(obj);
+}
+
+
+void
+virNWFilterPoolObjListFree(virNWFilterPoolObjListPtr pools)
+{
+    unsigned int i;
+    for (i = 0 ; i < pools->count ; i++)
+        virNWFilterPoolObjFree(pools->objs[i]);
+    VIR_FREE(pools->objs);
+    pools->count = 0;
+}
+
+
+static int
+virNWFilterRuleDefAddVar(virConnectPtr conn ATTRIBUTE_UNUSED,
+                         virNWFilterRuleDefPtr nwf,
+                         nwItemDesc *item,
+                         const char *var)
+{
+    int i = 0;
+
+    if (nwf->vars) {
+        for (i = 0; i < nwf->nvars; i++)
+            if (STREQ(nwf->vars[i], var)) {
+                item->var = nwf->vars[i];
+                return 0;
+            }
+    }
+
+    if (VIR_REALLOC_N(nwf->vars, nwf->nvars+1) < 0) {
+        virReportOOMError();
+        return 1;
+    }
+
+    nwf->vars[nwf->nvars] = strdup(var);
+
+    if (!nwf->vars[nwf->nvars]) {
+        virReportOOMError();
+        return 1;
+    }
+
+    item->var = nwf->vars[nwf->nvars++];
+
+    return 0;
+}
+
+
+void
+virNWFilterPoolObjRemove(virNWFilterPoolObjListPtr pools,
+                         virNWFilterPoolObjPtr pool)
+{
+    unsigned int i;
+
+    virNWFilterPoolObjUnlock(pool);
+
+    for (i = 0 ; i < pools->count ; i++) {
+        virNWFilterPoolObjLock(pools->objs[i]);
+        if (pools->objs[i] == pool) {
+            virNWFilterPoolObjUnlock(pools->objs[i]);
+            virNWFilterPoolObjFree(pools->objs[i]);
+
+            if (i < (pools->count - 1))
+                memmove(pools->objs + i, pools->objs + i + 1,
+                        sizeof(*(pools->objs)) * (pools->count - (i + 1)));
+
+            if (VIR_REALLOC_N(pools->objs, pools->count - 1) < 0) {
+                ; /* Failure to reduce memory allocation isn't fatal */
+            }
+            pools->count--;
+
+            break;
+        }
+        virNWFilterPoolObjUnlock(pools->objs[i]);
+    }
+}
+
+
+
+typedef bool (*valueValidator)(enum attrDatatype datatype, void *valptr,
+                               virNWFilterRuleDefPtr nwf);
+typedef bool (*valueFormatter)(virBufferPtr buf,
+                               virNWFilterRuleDefPtr nwf);
+
+typedef struct _virXMLAttr2Struct virXMLAttr2Struct;
+struct _virXMLAttr2Struct
+{
+    const char *name;           // attribute name
+    enum attrDatatype datatype;
+    int dataIdx;               // offset of the hasXYZ boolean
+    valueValidator validator;   // beyond-standard checkers
+    valueFormatter formatter;   // beyond-standard formatter
+};
+
+
+static const struct int_map macProtoMap[] = {
+    {
+      .attr = ETHERTYPE_ARP,
+      .val  = "arp",
+    }, {
+      .attr = ETHERTYPE_IP,
+      .val  = "ipv4",
+    }, {
+      .val  = NULL,
+    }
+};
+
+
+static bool
+checkMacProtocolID(enum attrDatatype datatype, void *value,
+                   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;
+    }
+
+    if (res != -1) {
+        nwf->p.ethHdrFilter.dataProtocolID.u.u16 = res;
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static bool
+macProtocolIDFormatter(virBufferPtr buf,
+                       virNWFilterRuleDefPtr nwf)
+{
+    const char *str = NULL;
+
+    if (intMapGetByInt(macProtoMap,
+                       nwf->p.ethHdrFilter.dataProtocolID.u.u16,
+                       &str)) {
+        virBufferVSprintf(buf, "%s", str);
+        return 1;
+    }
+    return 0;
+}
+
+
+/* generic function to check for a valid (ipv4,ipv6, mac) mask
+ * A mask is valid of there is a sequence of 1's followed by a sequence
+ * of 0s or only 1s or only 0s
+ */
+static bool
+checkValidMask(unsigned char *data, int len)
+{
+    uint32_t idx = 0;
+    uint8_t mask = 0x80;
+    int checkones = 1;
+
+    while ((idx >> 3) < len) {
+        if (checkones) {
+            if (!(data[idx>>3] & mask))
+                checkones = 0;
+        } else {
+            if ((data[idx>>3] & mask))
+                return 0;
+        }
+
+        idx++;
+        mask >>= 1;
+        if (!mask)
+            mask = 0x80;
+    }
+    return 1;
+}
+
+
+/* check for a valid IPv4 mask */
+static bool
+checkIPv4Mask(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *maskptr,
+              virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+    return checkValidMask(maskptr, 4);
+}
+
+
+static bool
+checkMACMask(enum attrDatatype datatype ATTRIBUTE_UNUSED,
+             void *macMask,
+             virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+    return checkValidMask((unsigned char *)macMask, 6);
+}
+
+
+static int getMaskNumBits(const unsigned char *mask, int len) {
+     int i = 0;
+     while (i < (len << 3)) {
+         if (!(mask[i>>3] & (0x80 >> (i & 3))))
+             break;
+         i++;
+     }
+     return i;
+}
+
+/*
+ * supported arp opcode -- see 'ebtables -h arp' for the naming
+ */
+static const struct int_map arpOpcodeMap[] = {
+    {
+        .attr = 1,
+        .val = "Request",
+    } , {
+        .attr = 2,
+        .val = "Reply",
+    } , {
+        .attr = 3,
+        .val = "Request_Reverse",
+    } , {
+        .attr = 4,
+        .val = "Reply_Reverse",
+    } , {
+        .attr = 5,
+        .val = "DRARP_Request",
+    } , {
+        .attr = 6,
+        .val = "DRARP_Reply",
+    } , {
+        .attr = 7,
+        .val = "DRARP_Error",
+    } , {
+        .attr = 8,
+        .val = "InARP_Request",
+    } , {
+        .attr = 9,
+        .val = "ARP_NAK",
+    } , {
+        .val = NULL,
+    }
+};
+
+
+static bool
+arpOpcodeValidator(enum attrDatatype datatype,
+                   void *value,
+                   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;
+    }
+
+    if (res != -1) {
+        nwf->p.arpHdrFilter.dataOpcode.u.u16 = res;
+        nwf->p.arpHdrFilter.dataOpcode.datatype = DATATYPE_UINT16;
+        return 1;
+    }
+    return 0;
+}
+
+
+static bool
+arpOpcodeFormatter(virBufferPtr buf,
+                   virNWFilterRuleDefPtr nwf)
+{
+    const char *str = NULL;
+
+    if (intMapGetByInt(arpOpcodeMap,
+                       nwf->p.arpHdrFilter.dataOpcode.u.u16,
+                       &str)) {
+        virBufferVSprintf(buf, "%s", str);
+        return 1;
+    }
+    return 0;
+}
+
+
+static const struct int_map ipProtoMap[] = {
+    {
+        .attr = IPPROTO_TCP,
+        .val  = "tcp",
+    } , {
+        .attr = IPPROTO_UDP,
+        .val  = "udp",
+    } , {
+        .attr = IPPROTO_ICMP,
+        .val  = "icmp",
+    } , {
+        .attr = IPPROTO_IGMP,
+        .val  = "igmp",
+#ifdef IPPROTO_SCTP
+    } , {
+        .attr = IPPROTO_SCTP,
+        .val  = "sctp",
+#endif
+    } , {
+        .val = NULL,
+    }
+};
+
+
+static bool checkIPProtocolID(enum attrDatatype datatype,
+                              void *value,
+                              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;
+    }
+
+    if (res != -1) {
+        nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8 = res;
+        nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype = DATATYPE_UINT8;
+        return 1;
+    }
+    return 0;
+}
+
+
+static bool
+formatIPProtocolID(virBufferPtr buf,
+                   virNWFilterRuleDefPtr nwf)
+{
+    const char *str = NULL;
+
+    if (intMapGetByInt(ipProtoMap,
+                       nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8,
+                       &str)) {
+        virBufferVSprintf(buf, "%s", str);
+        return 1;
+    }
+    return 0;
+}
+
+
+static bool
+dscpValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *val,
+              virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+    uint8_t dscp = *(uint16_t *)val;
+    if (dscp > 63)
+        return 0;
+    return 1;
+}
+
+#define COMMON_MAC_PROPS(STRUCT) \
+    {\
+        .name = SRCMACADDR,\
+        .datatype = DATATYPE_MACADDR,\
+        .dataIdx = offsetof(virNWFilterRuleDef,p.STRUCT.ethHdr.dataSrcMACAddr),\
+    },\
+    {\
+        .name = SRCMACMASK,\
+        .datatype = DATATYPE_MACMASK,\
+        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataSrcMACMask),\
+    },\
+    {\
+        .name = DSTMACADDR,\
+        .datatype = DATATYPE_MACADDR,\
+        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataDstMACAddr),\
+    },\
+    {\
+        .name = DSTMACMASK,\
+        .datatype = DATATYPE_MACMASK,\
+        .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataDstMACMask),\
+    }
+
+
+static const virXMLAttr2Struct macAttributes[] = {
+    COMMON_MAC_PROPS(ethHdrFilter),
+    {
+        .name = "protocolid",
+        .datatype = DATATYPE_UINT16 | DATATYPE_STRING,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ethHdrFilter.dataProtocolID),
+        .validator= checkMacProtocolID,
+        .formatter= macProtocolIDFormatter,
+    },
+    {
+        .name = NULL,
+    }
+};
+
+static const virXMLAttr2Struct arpAttributes[] = {
+    COMMON_MAC_PROPS(arpHdrFilter),
+    {
+        .name = "hwtype",
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataHWType),
+    }, {
+        .name = "protocoltype",
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataProtocolType),
+    }, {
+        .name = "opcode",
+        .datatype = DATATYPE_UINT16 | DATATYPE_STRING,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataOpcode),
+        .validator= arpOpcodeValidator,
+        .formatter= arpOpcodeFormatter,
+    }, {
+        .name = ARPSRCMACADDR,
+        .datatype = DATATYPE_MACADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcMACAddr),
+    }, {
+        .name = ARPDSTMACADDR,
+        .datatype = DATATYPE_MACADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstMACAddr),
+    }, {
+        .name = ARPSRCIPADDR,
+        .datatype = DATATYPE_IPADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPAddr),
+    }, {
+        .name = ARPDSTIPADDR,
+        .datatype = DATATYPE_IPADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPAddr),
+    },
+    {
+        .name = NULL,
+    }
+};
+
+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),
+    },
+    {
+        .name = DSTIPADDR,
+        .datatype = DATATYPE_IPADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPAddr),
+    },
+    {
+        .name = SRCIPMASK,
+        .datatype = DATATYPE_IPMASK,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPMask),
+    },
+    {
+        .name = DSTIPMASK,
+        .datatype = DATATYPE_IPMASK,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPMask),
+    },
+    {
+        .name = "protocol",
+        .datatype = DATATYPE_STRING,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataProtocolID),
+        .validator= checkIPProtocolID,
+        .formatter= formatIPProtocolID,
+    },
+    {
+        .name = SRCPORTSTART,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortStart),
+    },
+    {
+        .name = SRCPORTEND,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortEnd),
+    },
+    {
+        .name = DSTPORTSTART,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortStart),
+    },
+    {
+        .name = DSTPORTEND,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortEnd),
+    },
+    {
+        .name = DSCP,
+        .datatype = DATATYPE_UINT8,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDSCP),
+        .validator = dscpValidator,
+    },
+    {
+        .name = NULL,
+    }
+};
+
+
+typedef struct _virAttributes virAttributes;
+struct _virAttributes {
+    const char *id;
+    const virXMLAttr2Struct *att;
+    enum virNWFilterRuleProtocolType prtclType;
+};
+
+
+static const virAttributes virAttr[] = {
+    {
+        .id = "arp",
+        .att = arpAttributes,
+        .prtclType = VIR_NWFILTER_RULE_PROTOCOL_ARP,
+    }, {
+        .id = "mac",
+        .att = macAttributes,
+        .prtclType = VIR_NWFILTER_RULE_PROTOCOL_MAC,
+    }, {
+        .id = "ip",
+        .att = ipAttributes,
+        .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IP,
+    }, {
+        .id = NULL,
+    }
+};
+
+
+static bool
+virNWMACAddressParser(const char *input,
+                      nwMACAddressPtr output)
+{
+    if (virParseMacAddr(input, &output->addr[0]) == 0)
+        return 1;
+    return 0;
+}
+
+
+static bool
+virNWIPv4AddressParser(const char *input,
+                       nwIPAddressPtr output)
+{
+    int i;
+    char *endptr;
+    const char *n = input;
+    long int d;
+
+    for (i = 0; i < 4; i++) {
+        d = strtol(n, &endptr, 10);
+        if (d < 0 || d > 255            ||
+            (endptr - n        > 3    ) ||
+            (i <= 2 && *endptr != '.' ) ||
+            (i == 3 && *endptr != '\0'))
+            return 0;
+        output->addr.ipv4Addr[i] = (unsigned char)d;
+        n = endptr + 1;
+    }
+    return 1;
+}
+
+
+static int
+virNWFilterRuleDetailsParse(virConnectPtr conn ATTRIBUTE_UNUSED,
+                            xmlNodePtr node,
+                            virNWFilterRuleDefPtr nwf,
+                            const virXMLAttr2Struct *att)
+{
+    int rc = 0;
+    int idx = 0;
+    char *prop;
+    int found = 0;
+    enum attrDatatype datatype, att_datatypes;
+    enum virNWFilterEntryItemFlags *flags ,match_flag = 0, flags_set = 0;
+    nwItemDesc *item;
+    int int_val;
+    void *data_ptr, *storage_ptr;
+    valueValidator validator;
+    char *match = virXMLPropString(node, "match");
+    nwIPAddress ipaddr;
+
+    if (match && STREQ(match, "no"))
+        match_flag = NWFILTER_ENTRY_ITEM_FLAG_IS_NEG;
+    VIR_FREE(match);
+    match = NULL;
+
+    while (att[idx].name != NULL && rc == 0) {
+        prop = virXMLPropString(node, att[idx].name);
+
+        item = (nwItemDesc *)((char *)nwf + att[idx].dataIdx);
+        flags = &item->flags;
+        flags_set = match_flag;
+
+        if (prop) {
+            found = 0;
+
+            validator = NULL;
+
+            if (STRPREFIX(prop, "$")) {
+                flags_set |= NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR;
+                storage_ptr = NULL;
+
+                if (virNWFilterRuleDefAddVar(conn,
+                                             nwf,
+                                             item,
+                                             &prop[1]))
+                    rc = -1;
+                found = 1;
+            }
+
+            datatype = 1;
+
+            att_datatypes = att[idx].datatype;
+
+            while (datatype <= DATATYPE_LAST && found == 0 && rc == 0) {
+                if ((att_datatypes & datatype)) {
+
+                    att_datatypes ^= datatype;
+
+                    validator = att[idx].validator;
+
+                    switch (datatype) {
+
+                        case DATATYPE_UINT8:
+                            storage_ptr = &item->u.u8;
+                            if (sscanf(prop, "%d", &int_val) == 1) {
+                                if (int_val >= 0 && int_val <= 0xff) {
+                                    if (!validator)
+                                        *(uint8_t *)storage_ptr = int_val;
+                                    found = 1;
+                                    data_ptr = &int_val;
+                                } else
+                                    rc = -1;
+                            } else
+                                rc = -1;
+                        break;
+
+                        case DATATYPE_UINT16:
+                            storage_ptr = &item->u.u16;
+                            if (sscanf(prop, "%d", &int_val) == 1) {
+                                if (int_val >= 0 && int_val <= 0xffff) {
+                                    if (!validator)
+                                        *(uint16_t *)storage_ptr = int_val;
+                                    found = 1;
+                                    data_ptr = &int_val;
+                                } else
+                                    rc = -1;
+                            } else
+                                rc = -1;
+                        break;
+
+                        case DATATYPE_IPADDR:
+                            storage_ptr = &item->u.ipaddr;
+                            if (!virNWIPv4AddressParser(prop,
+                                       (nwIPAddressPtr)storage_ptr)) {
+                                rc = -1;
+                            }
+                            found = 1;
+                        break;
+
+                        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;
+                                } else
+                                    rc = -1;
+                            } else {
+                                if (checkIPv4Mask(datatype,
+                                                  ipaddr.addr.ipv4Addr, nwf))
+                                    *(uint8_t *)storage_ptr =
+                                       getMaskNumBits(ipaddr.addr.ipv4Addr,
+                                                 sizeof(ipaddr.addr.ipv4Addr));
+                                else
+                                    rc = -1;
+                                found = 1;
+                            }
+                        break;
+
+                        case DATATYPE_MACADDR:
+                            storage_ptr = &item->u.macaddr;
+                            if (!virNWMACAddressParser(prop,
+                                        (nwMACAddressPtr)storage_ptr)) {
+                                rc = -1;
+                            }
+                            found = 1;
+                        break;
+
+                        case DATATYPE_MACMASK:
+                            validator = checkMACMask;
+                            storage_ptr = &item->u.macaddr;
+                            if (!virNWMACAddressParser(prop,
+                                        (nwMACAddressPtr)storage_ptr)) {
+                                rc = -1;
+                            }
+                            data_ptr = storage_ptr;
+                            found = 1;
+                        break;
+
+                        case DATATYPE_STRING:
+                            if (!validator) {
+                                // not supported
+                                rc = -1;
+                                break;
+                            }
+                            data_ptr = prop;
+                            found = 1;
+                        break;
+
+                        case DATATYPE_LAST:
+                        default:
+                        break;
+                    }
+                }
+
+                if (rc != 0 && att_datatypes != 0) {
+                    rc = 0;
+                    found = 0;
+                }
+
+                datatype <<= 1;
+            } /* while */
+
+            if (found == 1 && rc == 0) {
+                *flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS | flags_set;
+                item->datatype = datatype >> 1;
+                if (validator) {
+                    if (!validator(datatype >> 1, data_ptr, nwf)) {
+                        rc = -1;
+                        *flags = 0;
+                    }
+                }
+            }
+
+            if (!found || rc) {
+                virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                       _("%s has illegal value %s"),
+                                       att[idx].name, prop);
+                rc = -1;
+            }
+            VIR_FREE(prop);
+        }
+        idx++;
+    }
+
+    return rc;
+}
+
+
+
+
+static virNWFilterIncludeDefPtr
+virNWFilterIncludeParse(virConnectPtr conn,
+                        xmlNodePtr cur)
+{
+    virNWFilterIncludeDefPtr ret;
+
+    if (VIR_ALLOC(ret) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    ret->filterref = virXMLPropString(cur, "filter");
+    if (!ret->filterref) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("rule node requires action attribute"));
+        goto err_exit;
+    }
+
+    ret->params = virNWFilterParseParamAttributes(cur);
+    if (!ret->params)
+        goto err_exit;
+
+cleanup:
+    return ret;
+
+err_exit:
+    virNWFilterIncludeDefFree(ret);
+    ret = NULL;
+    goto cleanup;
+}
+
+
+static void
+virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule)
+{
+#define COPY_NEG_SIGN(A, B) \
+    (A).flags = ((A).flags & ~NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) | \
+                ((B).flags &  NWFILTER_ENTRY_ITEM_FLAG_IS_NEG);
+
+    switch (rule->prtclType) {
+    case VIR_NWFILTER_RULE_PROTOCOL_MAC:
+        COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataSrcMACMask,
+                      rule->p.ethHdrFilter.ethHdr.dataSrcMACAddr);
+        COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataDstMACMask,
+                      rule->p.ethHdrFilter.ethHdr.dataDstMACAddr);
+    break;
+
+    case VIR_NWFILTER_RULE_PROTOCOL_IP:
+        COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcIPMask,
+                      rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr);
+        COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstIPMask,
+                      rule->p.ipHdrFilter.ipHdr.dataDstIPAddr);
+    break;
+
+    case VIR_NWFILTER_RULE_PROTOCOL_ARP:
+    case VIR_NWFILTER_RULE_PROTOCOL_NONE:
+    break;
+    }
+
+#undef COPY_NEG_SIGN
+}
+
+
+static virNWFilterRuleDefPtr
+virNWFilterRuleParse(virConnectPtr conn,
+                     xmlNodePtr node)
+{
+    char *action;
+    char *direction;
+    char *prio;
+    int found;
+    int found_i;
+    unsigned int priority;
+
+    xmlNodePtr cur;
+    virNWFilterRuleDefPtr ret;
+
+    if (VIR_ALLOC(ret) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    action    = virXMLPropString(node, "action");
+    direction = virXMLPropString(node, "direction");
+    prio      = virXMLPropString(node, "priority");
+
+    if (!action) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("rule node requires action attribute"));
+        goto err_exit;
+    }
+
+    if ((ret->action = virNWFilterRuleActionTypeFromString(action)) < 0) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("unknown rule action attribute value"));
+        goto err_exit;
+    }
+
+    if (!direction) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("rule node requires direction attribute"));
+        goto err_exit;
+    }
+
+    if ((ret->tt = virNWFilterRuleDirectionTypeFromString(direction)) < 0) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("unknown rule direction attribute value"));
+        goto err_exit;
+    }
+
+    ret->priority = MAX_RULE_PRIORITY / 2;
+
+    if (prio) {
+        if (sscanf(prio, "%d", (int *)&priority) == 1) {
+            if ((int)priority >= 0 && priority <= MAX_RULE_PRIORITY)
+                ret->priority = priority;
+        }
+    }
+
+    cur = node->children;
+
+    found = 0;
+
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            int i = 0;
+            while (1) {
+                if (found)
+                    i = found_i;
+
+                if (xmlStrEqual(cur->name, BAD_CAST virAttr[i].id)) {
+
+                    found_i = i;
+                    found = 1;
+                    ret->prtclType = virAttr[i].prtclType;
+
+                    if (virNWFilterRuleDetailsParse(conn,
+                                                    cur,
+                                                    ret,
+                                                    virAttr[i].att) < 0) {
+                        /* we ignore malformed rules
+                           goto err_exit;
+                        */
+                    }
+                    break;
+                }
+                if (!found) {
+                    i++;
+                    if (!virAttr[i].id)
+                        break;
+                } else
+                   break;
+            }
+        }
+
+        cur = cur->next;
+    }
+
+    virNWFilterRuleDefFixup(ret);
+
+cleanup:
+    VIR_FREE(prio);
+    VIR_FREE(action);
+    VIR_FREE(direction);
+
+    return ret;
+
+err_exit:
+    virNWFilterRuleDefFree(ret);
+    ret = NULL;
+    goto cleanup;
+}
+
+
+static virNWFilterDefPtr
+virNWFilterDefParseXML(virConnectPtr conn,
+                       xmlXPathContextPtr ctxt) {
+    virNWFilterDefPtr ret;
+    xmlNodePtr curr = ctxt->node;
+    char *uuid = NULL;
+    char *chain = NULL;
+    virNWFilterEntryPtr entry;
+
+    if (VIR_ALLOC(ret) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    ret->name = virXPathString("string(./@name)", ctxt);
+    if (!ret->name) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("filter has no name"));
+        goto cleanup;
+    }
+
+    ret->chainsuffix = VIR_NWFILTER_CHAINSUFFIX_ROOT;
+    chain = virXPathString("string(./@chain)", ctxt);
+    if (chain) {
+        if ((ret->chainsuffix =
+             virNWFilterChainSuffixTypeFromString(chain)) < 0) {
+            virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                   _("unknown chain suffix '%s'"), chain);
+            goto cleanup;
+        }
+    }
+
+    uuid = virXPathString("string(./uuid)", ctxt);
+    if (uuid == NULL) {
+        if (virUUIDGenerate(ret->uuid) < 0) {
+            virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("unable to generate uuid"));
+            goto cleanup;
+        }
+    } else {
+        if (virUUIDParse(uuid, ret->uuid) < 0) {
+            virNWFilterReportError(conn, VIR_ERR_XML_ERROR,
+                                  "%s", _("malformed uuid element"));
+            goto cleanup;
+        }
+        VIR_FREE(uuid);
+    }
+
+    curr = curr->children;
+
+    while (curr != NULL) {
+        if (curr->type == XML_ELEMENT_NODE) {
+            if (VIR_ALLOC(entry) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+
+            /* ignore malformed rule and include elements */
+            if (xmlStrEqual(curr->name, BAD_CAST "rule"))
+                entry->rule = virNWFilterRuleParse(conn, curr);
+            else if (xmlStrEqual(curr->name, BAD_CAST "filterref"))
+                entry->include = virNWFilterIncludeParse(conn, curr);
+
+            if (entry->rule || entry->include) {
+                if (VIR_REALLOC_N(ret->filterEntries, ret->nentries+1) < 0) {
+                    VIR_FREE(entry);
+                    virReportOOMError();
+                    goto cleanup;
+                }
+                ret->filterEntries[ret->nentries++] = entry;
+            } else
+                VIR_FREE(entry);
+        }
+        curr = curr->next;
+    }
+
+    VIR_FREE(chain);
+
+    return ret;
+
+ cleanup:
+    VIR_FREE(chain);
+    VIR_FREE(uuid);
+    return NULL;
+}
+
+
+/* Called from SAX on parsing errors in the XML. */
+static void
+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+    if (ctxt) {
+        virConnectPtr conn = ctxt->_private;
+
+        if (conn &&
+            conn->err.code == VIR_ERR_NONE &&
+            ctxt->lastError.level == XML_ERR_FATAL &&
+            ctxt->lastError.message != NULL) {
+            virNWFilterReportError(conn, VIR_ERR_XML_DETAIL,
+                                   _("at line %d: %s"),
+                                   ctxt->lastError.line,
+                                   ctxt->lastError.message);
+        }
+    }
+}
+
+
+virNWFilterDefPtr
+virNWFilterDefParseNode(virConnectPtr conn,
+                        xmlDocPtr xml,
+                        xmlNodePtr root) {
+    xmlXPathContextPtr ctxt = NULL;
+    virNWFilterDefPtr def = NULL;
+
+    if (STRNEQ((const char *)root->name, "filter")) {
+        virNWFilterReportError(conn, VIR_ERR_XML_ERROR,
+                               "%s",
+                               _("unknown root element for nw filter pool"));
+        goto cleanup;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ctxt->node = root;
+    def = virNWFilterDefParseXML(conn, ctxt);
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    return def;
+}
+
+
+static virNWFilterDefPtr
+virNWFilterDefParse(virConnectPtr conn,
+                     const char *xmlStr,
+                     const char *filename) {
+    virNWFilterDefPtr ret = NULL;
+    xmlParserCtxtPtr pctxt;
+    xmlDocPtr xml = NULL;
+    xmlNodePtr node = NULL;
+
+    /* Set up a parser context so we can catch the details of XML errors. */
+    pctxt = xmlNewParserCtxt ();
+    if (!pctxt || !pctxt->sax)
+        goto cleanup;
+    pctxt->sax->error = catchXMLError;
+    pctxt->_private = conn;
+
+    if (conn) virResetError (&conn->err);
+    if (filename) {
+        xml = xmlCtxtReadFile (pctxt, filename, NULL,
+                               XML_PARSE_NOENT | XML_PARSE_NONET |
+                               XML_PARSE_NOWARNING);
+    } else {
+        xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr,
+                              "nwfilter.xml", NULL,
+                              XML_PARSE_NOENT | XML_PARSE_NONET |
+                              XML_PARSE_NOWARNING);
+    }
+
+    if (!xml) {
+        if (conn && conn->err.code == VIR_ERR_NONE)
+              virNWFilterReportError(conn, VIR_ERR_XML_ERROR,
+                                     "%s",_("failed to parse xml document"));
+        goto cleanup;
+    }
+
+    node = xmlDocGetRootElement(xml);
+    if (node == NULL) {
+        virNWFilterReportError(conn, VIR_ERR_XML_ERROR,
+                               "%s", _("missing root element"));
+        goto cleanup;
+    }
+
+    ret = virNWFilterDefParseNode(conn, xml, node);
+
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc(xml);
+
+    return ret;
+
+ cleanup:
+    xmlFreeParserCtxt (pctxt);
+    xmlFreeDoc(xml);
+    return NULL;
+}
+
+
+virNWFilterDefPtr
+virNWFilterDefParseString(virConnectPtr conn,
+                             const char *xmlStr)
+{
+    return virNWFilterDefParse(conn, xmlStr, NULL);
+}
+
+
+virNWFilterDefPtr
+virNWFilterDefParseFile(virConnectPtr conn,
+                        const char *filename)
+{
+    return virNWFilterDefParse(conn, NULL, filename);
+}
+
+
+virNWFilterPoolObjPtr
+virNWFilterPoolObjFindByUUID(virNWFilterPoolObjListPtr pools,
+                            const unsigned char *uuid)
+{
+    unsigned int i;
+
+    for (i = 0 ; i < pools->count ; i++) {
+        virNWFilterPoolObjLock(pools->objs[i]);
+        if (!memcmp(pools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
+            return pools->objs[i];
+        virNWFilterPoolObjUnlock(pools->objs[i]);
+    }
+
+    return NULL;
+}
+
+
+virNWFilterPoolObjPtr
+virNWFilterPoolObjFindByName(virNWFilterPoolObjListPtr pools,
+                            const char *name)
+{
+    unsigned int i;
+
+    for (i = 0 ; i < pools->count ; i++) {
+        virNWFilterPoolObjLock(pools->objs[i]);
+        if (STREQ(pools->objs[i]->def->name, name))
+            return pools->objs[i];
+        virNWFilterPoolObjUnlock(pools->objs[i]);
+    }
+
+    return NULL;
+}
+
+
+int virNWFilterSaveXML(virConnectPtr conn,
+                       const char *configDir,
+                       virNWFilterDefPtr def,
+                       const char *xml)
+{
+    char *configFile = NULL;
+    int fd = -1, ret = -1;
+    size_t towrite;
+    int err;
+
+    if ((configFile = virNWFilterConfigFile(conn, configDir, def->name)) == NULL)
+        goto cleanup;
+
+    if ((err = virFileMakePath(configDir))) {
+        virReportSystemError(err,
+                             _("cannot create config directory '%s'"),
+                             configDir);
+        goto cleanup;
+    }
+
+    if ((fd = open(configFile,
+                   O_WRONLY | O_CREAT | O_TRUNC,
+                   S_IRUSR | S_IWUSR )) < 0) {
+        virReportSystemError(errno,
+                             _("cannot create config file '%s'"),
+                             configFile);
+        goto cleanup;
+    }
+
+    towrite = strlen(xml);
+    if (safewrite(fd, xml, towrite) < 0) {
+        virReportSystemError(errno,
+                             _("cannot write config file '%s'"),
+                             configFile);
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        virReportSystemError(errno,
+                             _("cannot save config file '%s'"),
+                             configFile);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (fd != -1)
+        close(fd);
+
+    VIR_FREE(configFile);
+
+    return ret;
+}
+
+
+int virNWFilterSaveConfig(virConnectPtr conn,
+                          const char *configDir,
+                          virNWFilterDefPtr def)
+{
+    int ret = -1;
+    char *xml;
+
+    if (!(xml = virNWFilterDefFormat(conn, def)))
+        goto cleanup;
+
+    if (virNWFilterSaveXML(conn, configDir, def, xml))
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
+
+static int
+_virNWFilterDefLoopDetect(virConnectPtr conn,
+                          virNWFilterPoolObjListPtr pools,
+                          virNWFilterDefPtr def,
+                          const char *filtername)
+{
+    int rc = 0;
+    int i;
+    virNWFilterEntryPtr entry;
+    virNWFilterPoolObjPtr obj;
+
+    if (!def)
+        return 0;
+
+    for (i = 0; i < def->nentries; i++) {
+        entry = def->filterEntries[i];
+        if (entry->include) {
+
+            if (STREQ(filtername, entry->include->filterref)) {
+                rc = 1;
+                break;
+            }
+
+            obj = virNWFilterPoolObjFindByName(pools,
+                                               entry->include->filterref);
+            if (obj) {
+                rc = _virNWFilterDefLoopDetect(conn,
+                                               pools,
+                                               obj->def, filtername);
+
+                virNWFilterPoolObjUnlock(obj);
+                if (rc)
+                   break;
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+/*
+ * virNWFilterDefLoopDetect:
+ * @conn: pointer to virConnect object
+ * @pools : the pools to search
+ * @def : the filter definiton that may add a loop and is to be tested
+ *
+ * Detect a loop introduced through the filters being able to
+ * reference each other.
+ *
+ * Returns 0 in case no loop was detected, 1 otherwise.
+ */
+static int
+virNWFilterDefLoopDetect(virConnectPtr conn,
+                         virNWFilterPoolObjListPtr pools,
+                         virNWFilterDefPtr def)
+{
+    return _virNWFilterDefLoopDetect(conn, pools, def, def->name);
+}
+
+int nCallbackDriver;
+#define MAX_CALLBACK_DRIVER 10
+static virNWFilterCallbackDriverPtr callbackDrvArray[MAX_CALLBACK_DRIVER];
+
+void
+virNWFilterRegisterCallbackDriver(virNWFilterCallbackDriverPtr cbd)
+{
+    if (nCallbackDriver < MAX_CALLBACK_DRIVER) {
+        callbackDrvArray[nCallbackDriver++] = cbd;
+    }
+}
+
+
+enum UpdateStep {
+    STEP_APPLY_NEW,
+    STEP_TEAR_NEW,
+    STEP_TEAR_OLD,
+};
+
+struct cbStruct {
+    virConnectPtr conn;
+    enum UpdateStep step;
+    int err;
+};
+
+static void
+virNWFilterDomainFWUpdateCB(void *payload ATTRIBUTE_UNUSED,
+                            const char *name ATTRIBUTE_UNUSED,
+                            void *data ATTRIBUTE_UNUSED)
+{
+}
+
+
+static int
+virNWFilterTriggerVMFilterRebuild(virConnectPtr conn)
+{
+    int i;
+    int err;
+    struct cbStruct cb = {
+        .conn = conn,
+        .err = 0,
+        .step = STEP_APPLY_NEW,
+    };
+
+    for (i = 0; i < nCallbackDriver; i++) {
+        callbackDrvArray[i]->vmFilterRebuild(conn,
+                                             virNWFilterDomainFWUpdateCB,
+                                             &cb);
+    }
+
+    err = cb.err;
+
+    if (err) {
+        cb.step = STEP_TEAR_NEW; // rollback
+        cb.err = 0;
+
+        for (i = 0; i < nCallbackDriver; i++)
+            callbackDrvArray[i]->vmFilterRebuild(conn,
+                                                 virNWFilterDomainFWUpdateCB,
+                                                 &cb);
+    } else {
+        cb.step = STEP_TEAR_OLD; // switch over
+
+        for (i = 0; i < nCallbackDriver; i++)
+            callbackDrvArray[i]->vmFilterRebuild(conn,
+                                                 virNWFilterDomainFWUpdateCB,
+                                                 &cb);
+    }
+
+    return err;
+}
+
+
+int
+virNWFilterTestUnassignDef(virConnectPtr conn,
+                           virNWFilterPoolObjPtr pool)
+{
+    int rc = 0;
+
+    virNWFilterLockFilterUpdates();
+
+    pool->wantRemoved = 1;
+    // trigger the update on VMs referencing the filter
+    if (virNWFilterTriggerVMFilterRebuild(conn))
+        rc = 1;
+
+    pool->wantRemoved = 0;
+    virNWFilterUnlockFilterUpdates();
+    return rc;
+}
+
+
+virNWFilterPoolObjPtr
+virNWFilterPoolObjAssignDef(virConnectPtr conn,
+                           virNWFilterPoolObjListPtr pools,
+                           virNWFilterDefPtr def)
+{
+    virNWFilterPoolObjPtr pool;
+
+    if (virNWFilterDefLoopDetect(conn, pools, def)) {
+        virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+                              "%s", _("filter would introduce loop"));
+        return NULL;
+    }
+
+    if ((pool = virNWFilterPoolObjFindByName(pools, def->name))) {
+        virNWFilterLockFilterUpdates();
+        pool->newDef = def;
+        // trigger the update on VMs referencing the filter
+        if (virNWFilterTriggerVMFilterRebuild(conn)) {
+            pool->newDef = NULL;
+            virNWFilterUnlockFilterUpdates();
+            virNWFilterPoolObjUnlock(pool);
+            return NULL;
+        }
+
+        virNWFilterDefFree(pool->def);
+        pool->def = def;
+        pool->newDef = NULL;
+        virNWFilterUnlockFilterUpdates();
+        return pool;
+    }
+
+    if (VIR_ALLOC(pool) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    if (virMutexInitRecursive(&pool->lock) < 0) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("cannot initialize mutex"));
+        VIR_FREE(pool);
+        return NULL;
+    }
+    virNWFilterPoolObjLock(pool);
+    pool->active = 0;
+    pool->def = def;
+
+    if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) {
+        pool->def = NULL;
+        virNWFilterPoolObjUnlock(pool);
+        virNWFilterPoolObjFree(pool);
+        virReportOOMError();
+        return NULL;
+    }
+    pools->objs[pools->count++] = pool;
+
+    return pool;
+}
+
+
+static virNWFilterPoolObjPtr
+virNWFilterPoolObjLoad(virConnectPtr conn,
+                      virNWFilterPoolObjListPtr pools,
+                      const char *file,
+                      const char *path)
+{
+    virNWFilterDefPtr def;
+    virNWFilterPoolObjPtr pool;
+
+    if (!(def = virNWFilterDefParseFile(conn, path))) {
+        return NULL;
+    }
+
+    if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
+        virNWFilterError(conn, VIR_ERR_INVALID_NWFILTER,
+            "NWFilter pool config filename '%s' does not match pool name '%s'",
+                      path, def->name);
+        virNWFilterDefFree(def);
+        return NULL;
+    }
+
+    if (!(pool = virNWFilterPoolObjAssignDef(conn, pools, def))) {
+        virNWFilterDefFree(def);
+        return NULL;
+    }
+
+    pool->configFile = strdup(path);
+    if (pool->configFile == NULL) {
+        virReportOOMError();
+        virNWFilterDefFree(def);
+        return NULL;
+    }
+
+    return pool;
+}
+
+
+int
+virNWFilterPoolLoadAllConfigs(virConnectPtr conn,
+                             virNWFilterPoolObjListPtr pools,
+                             const char *configDir)
+{
+    DIR *dir;
+    struct dirent *entry;
+
+    if (!(dir = opendir(configDir))) {
+        if (errno == ENOENT) {
+            return 0;
+        }
+        virReportSystemError(errno, _("Failed to open dir '%s'"),
+                             configDir);
+        return -1;
+    }
+
+    while ((entry = readdir(dir))) {
+        char path[PATH_MAX];
+        virNWFilterPoolObjPtr pool;
+
+        if (entry->d_name[0] == '.')
+            continue;
+
+        if (!virFileHasSuffix(entry->d_name, ".xml"))
+            continue;
+
+        if (virFileBuildPath(configDir, entry->d_name,
+                             NULL, path, PATH_MAX) < 0) {
+            virNWFilterError(conn, VIR_ERR_INTERNAL_ERROR,
+                            "Config filename '%s/%s' is too long",
+                            configDir, entry->d_name);
+            continue;
+        }
+
+        pool = virNWFilterPoolObjLoad(conn, pools, entry->d_name, path);
+        if (pool)
+            virNWFilterPoolObjUnlock(pool);
+    }
+
+    closedir(dir);
+
+    return 0;
+}
+
+
+int
+virNWFilterPoolObjSaveDef(virConnectPtr conn,
+                         virNWFilterDriverStatePtr driver,
+                         virNWFilterPoolObjPtr pool,
+                         virNWFilterDefPtr def)
+{
+    char *xml;
+    int fd = -1, ret = -1;
+    ssize_t towrite;
+
+    if (!pool->configFile) {
+        int err;
+        char path[PATH_MAX];
+
+        if ((err = virFileMakePath(driver->configDir))) {
+            virReportSystemError(err,
+                                 _("cannot create config directory %s"),
+                                 driver->configDir);
+            return -1;
+        }
+
+        if (virFileBuildPath(driver->configDir, def->name, ".xml",
+                             path, sizeof(path)) < 0) {
+            virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("cannot construct config file path"));
+            return -1;
+        }
+        if (!(pool->configFile = strdup(path))) {
+            virReportOOMError();
+            return -1;
+        }
+    }
+
+    if (!(xml = virNWFilterDefFormat(conn, def))) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("failed to generate XML"));
+        return -1;
+    }
+
+    if ((fd = open(pool->configFile,
+                   O_WRONLY | O_CREAT | O_TRUNC,
+                   S_IRUSR | S_IWUSR )) < 0) {
+        virReportSystemError(errno,
+                             _("cannot create config file %s"),
+                             pool->configFile);
+        goto cleanup;
+    }
+
+    towrite = strlen(xml);
+    if (safewrite(fd, xml, towrite) != towrite) {
+        virReportSystemError(errno,
+                             _("cannot write config file %s"),
+                             pool->configFile);
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        virReportSystemError(errno,
+                             _("cannot save config file %s"),
+                             pool->configFile);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (fd != -1)
+        close(fd);
+
+    VIR_FREE(xml);
+
+    return ret;
+}
+
+
+int
+virNWFilterPoolObjDeleteDef(virConnectPtr conn,
+                           virNWFilterPoolObjPtr pool)
+{
+    if (!pool->configFile) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("no config file for %s"), pool->def->name);
+        return -1;
+    }
+
+    if (unlink(pool->configFile) < 0) {
+        virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("cannot remove config for %s"),
+                              pool->def->name);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static void
+virNWIPAddressFormat(virBufferPtr buf, nwIPAddressPtr ipaddr)
+{
+    if (!ipaddr->isIPv6) {
+        virBufferVSprintf(buf, "%d.%d.%d.%d",
+                          ipaddr->addr.ipv4Addr[0],
+                          ipaddr->addr.ipv4Addr[1],
+                          ipaddr->addr.ipv4Addr[2],
+                          ipaddr->addr.ipv4Addr[3]);
+    } else {
+        virBufferAddLit(buf, "MISSING IPv6 ADDRESS FORMATTER");
+    }
+}
+
+
+static void
+virNWFilterRuleDefDetailsFormat(virConnectPtr conn,
+                                virBufferPtr buf,
+                                const char *type,
+                                const virXMLAttr2Struct *att,
+                                virNWFilterRuleDefPtr def)
+{
+    int i, j;
+    bool typeShown = 0;
+    bool neverShown = 1;
+    enum match {
+        MATCH_NONE = 0,
+        MATCH_YES,
+        MATCH_NO
+    } matchShown = MATCH_NONE;
+    nwItemDesc *item;
+
+    while (att[i].name) {
+        item = (nwItemDesc *)((char *)def + att[i].dataIdx);
+        enum virNWFilterEntryItemFlags flags = item->flags;
+        void *storage_ptr;
+        if ((flags & NWFILTER_ENTRY_ITEM_FLAG_EXISTS)) {
+            if (!typeShown) {
+                virBufferVSprintf(buf, "    <%s", type);
+                typeShown = 1;
+                neverShown = 0;
+            }
+
+            if ((flags & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG)) {
+                if (matchShown == MATCH_NONE) {
+                    virBufferAddLit(buf, " match='no'");
+                    matchShown = MATCH_NO;
+                } else if (matchShown == MATCH_YES) {
+                    virBufferAddLit(buf, "/>\n");
+                    typeShown = 0;
+                    matchShown = MATCH_NONE;
+                    continue;
+                }
+            } else {
+                if (matchShown == MATCH_NO) {
+                    virBufferAddLit(buf, "/>\n");
+                    typeShown = 0;
+                    matchShown = MATCH_NONE;
+                    continue;
+                }
+                matchShown = MATCH_YES;
+            }
+
+            virBufferVSprintf(buf, " %s='",
+                              att[i].name);
+            if (att[i].formatter) {
+               if (!att[i].formatter(buf, def)) {
+                  virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                         _("formatter for %s %s reported error"),
+                                         type,
+                                         att[i].name);
+                   goto err_exit;
+               }
+            } else if ((flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
+                virBufferVSprintf(buf, "$%s", item->var);
+            } else {
+               switch (att[i].datatype) {
+
+               case DATATYPE_IPMASK:
+                   // display all masks in CIDR format
+               case DATATYPE_UINT8:
+                   storage_ptr = &item->u.u8;
+                   virBufferVSprintf(buf, "%d", *(uint8_t *)storage_ptr);
+               break;
+
+               case DATATYPE_UINT16:
+                   storage_ptr = &item->u.u16;
+                   virBufferVSprintf(buf, "%d", *(uint16_t *)storage_ptr);
+               break;
+
+               case DATATYPE_IPADDR:
+                   storage_ptr = &item->u.ipaddr;
+                   virNWIPAddressFormat(buf,
+                                        (nwIPAddressPtr)storage_ptr);
+               break;
+
+               case DATATYPE_MACMASK:
+               case DATATYPE_MACADDR:
+                   storage_ptr = &item->u.macaddr;
+                   for (j = 0; j < 6; j++)
+                       virBufferVSprintf(buf, "%02x%s",
+                                      ((nwMACAddressPtr)storage_ptr)->addr[j],
+                                      (j < 5) ? ":" : "");
+               break;
+
+               case DATATYPE_STRING:
+               default:
+                   virBufferVSprintf(buf,
+                                     "UNSUPPORTED DATATYPE 0x%02x\n",
+                                     att[i].datatype);
+               }
+            }
+            virBufferAddLit(buf, "'");
+        }
+        i++;
+    }
+    if (typeShown)
+       virBufferAddLit(buf, "/>\n");
+
+    if (neverShown)
+       virBufferVSprintf(buf,
+                         "    <%s/>\n", type);
+
+err_exit:
+    return;
+}
+
+
+static char *
+virNWFilterRuleDefFormat(virConnectPtr conn,
+                         virNWFilterRuleDefPtr def)
+{
+    int i;
+    virBuffer buf  = VIR_BUFFER_INITIALIZER;
+    virBuffer buf2 = VIR_BUFFER_INITIALIZER;
+    char *data;
+
+    virBufferVSprintf(&buf, "  <rule action='%s' direction='%s' priority='%d'",
+                      virNWFilterRuleActionTypeToString(def->action),
+                      virNWFilterRuleDirectionTypeToString(def->tt),
+                      def->priority);
+
+    i = 0;
+    while (virAttr[i].id) {
+        if (virAttr[i].prtclType == def->prtclType) {
+            virNWFilterRuleDefDetailsFormat(conn,
+                                            &buf2,
+                                            virAttr[i].id,
+                                            virAttr[i].att,
+                                            def);
+            break;
+        }
+        i++;
+    }
+
+    if (virBufferError(&buf2))
+        goto no_memory;
+
+    data = virBufferContentAndReset(&buf2);
+
+    if (data) {
+        virBufferAddLit(&buf, ">\n");
+        virBufferVSprintf(&buf, "%s  </rule>\n", data);
+        VIR_FREE(data);
+    } else
+        virBufferAddLit(&buf, "/>\n");
+
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+no_memory:
+    virReportOOMError();
+    virBufferFreeAndReset(&buf);
+    virBufferFreeAndReset(&buf2);
+
+    return NULL;
+}
+
+
+static char *
+virNWFilterIncludeDefFormat(virNWFilterIncludeDefPtr inc)
+{
+    char *attrs;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferVSprintf(&buf,"  <filterref filter='%s'",
+                      inc->filterref);
+
+    attrs = virNWFilterFormatParamAttributes(inc->params, "    ");
+
+    if (!attrs || strlen(attrs) <= 1)
+        virBufferAddLit(&buf, "/>\n");
+    else
+        virBufferVSprintf(&buf, ">\n%s  </filterref>\n", attrs);
+
+    if (virBufferError(&buf)) {
+        virReportOOMError();
+        virBufferFreeAndReset(&buf);
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+}
+
+
+static char *
+virNWFilterEntryFormat(virConnectPtr conn,
+                       virNWFilterEntryPtr entry)
+{
+    if (entry->rule)
+        return virNWFilterRuleDefFormat(conn, entry->rule);
+    return virNWFilterIncludeDefFormat(entry->include);
+}
+
+
+char *
+virNWFilterDefFormat(virConnectPtr conn,
+                     virNWFilterDefPtr def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+    int i;
+    char *xml;
+
+    virBufferVSprintf(&buf, "<filter name='%s' chain='%s'",
+                      def->name,
+                      virNWFilterChainSuffixTypeToString(def->chainsuffix));
+    virBufferAddLit(&buf, ">\n");
+
+    virUUIDFormat(def->uuid, uuid);
+    virBufferVSprintf(&buf,"  <uuid>%s</uuid>\n", uuid);
+
+    for (i = 0; i < def->nentries; i++) {
+        xml = virNWFilterEntryFormat(conn, def->filterEntries[i]);
+        if (!xml)
+            goto err_exit;
+        virBufferVSprintf(&buf, "%s", xml);
+        VIR_FREE(xml);
+    }
+
+    virBufferAddLit(&buf, "</filter>\n");
+
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+ no_memory:
+    virReportOOMError();
+
+ err_exit:
+    virBufferFreeAndReset(&buf);
+    return NULL;
+}
+
+
+char *virNWFilterConfigFile(virConnectPtr conn ATTRIBUTE_UNUSED,
+                            const char *dir,
+                            const char *name)
+{
+    char *ret = NULL;
+
+    if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    return ret;
+}
+
+
+int virNWFilterConfLayerInit(void)
+{
+    if (virMutexInit(&updateMutex))
+        return 1;
+
+    if (virNWFilterParamConfLayerInit())
+        return 1;
+
+    return 0;
+}
+
+
+void virNWFilterConfLayerShutdown(void)
+{
+    virNWFilterParamConfLayerShutdown();
+}
+
+
+void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj)
+{
+    virMutexLock(&obj->lock);
+}
+
+void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj)
+{
+    virMutexUnlock(&obj->lock);
+}
diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h
new file mode 100644 (file)
index 0000000..fe05070
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * nwfilter_conf.h: network filter XML processing
+ *                  (derived from storage_conf.h)
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * 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@us.ibm.com>
+ */
+#ifndef NWFILTER_CONF_H
+#define NWFILTER_CONF_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "internal.h"
+#include "util.h"
+#include "hash.h"
+#include "xml.h"
+
+/**
+ * Chain suffix size is:
+ * max. user define table name length -
+ *   sizeof("FO-") -
+ *   max. interface name size -
+ *   sizeof("-") -
+ *   terminating '0' =
+ * 32-3-15-1-1 = 12
+ */
+#define MAX_CHAIN_SUFFIX_SIZE  12
+
+
+enum virNWFilterEntryItemFlags {
+    NWFILTER_ENTRY_ITEM_FLAG_EXISTS   = 1 << 0,
+    NWFILTER_ENTRY_ITEM_FLAG_IS_NEG   = 1 << 1,
+    NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR  = 1 << 2,
+};
+
+
+#define HAS_ENTRY_ITEM(data) \
+  (((data)->flags) & NWFILTER_ENTRY_ITEM_FLAG_EXISTS)
+
+#define ENTRY_GET_NEG_SIGN(data) \
+  ((((data)->flags) & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) ? "!" : "")
+
+// datatypes appearing in rule attributes
+enum attrDatatype {
+    DATATYPE_UINT16           = (1 << 0),
+    DATATYPE_UINT8            = (1 << 1),
+    DATATYPE_MACADDR          = (1 << 2),
+    DATATYPE_MACMASK          = (1 << 3),
+    DATATYPE_IPADDR           = (1 << 4),
+    DATATYPE_IPMASK           = (1 << 5),
+    DATATYPE_STRING           = (1 << 6),
+
+    DATATYPE_LAST             = (1 << 7),
+};
+
+
+typedef struct _nwMACAddress nwMACAddress;
+typedef nwMACAddress *nwMACAddressPtr;
+struct _nwMACAddress {
+    unsigned char addr[6];
+};
+
+
+typedef struct _nwIPAddress nwIPAddress;
+typedef nwIPAddress *nwIPAddressPtr;
+struct _nwIPAddress {
+    int isIPv6;
+    union {
+        unsigned char ipv4Addr[4];
+        /* unsigned char ipv6Addr[16]; future :-) */
+    } addr;
+};
+
+
+typedef struct _nwItemDesc nwItemDesc;
+typedef nwItemDesc *nwItemDescPtr;
+struct _nwItemDesc {
+    enum virNWFilterEntryItemFlags flags;
+    char *var;
+    enum attrDatatype datatype;
+    union {
+        nwMACAddress macaddr;
+        nwIPAddress  ipaddr;
+        uint8_t      u8;
+        uint16_t     u16;
+        char         protocolID[10];
+    } u;
+};
+
+
+typedef struct _ethHdrDataDef ethHdrDataDef;
+typedef ethHdrDataDef *ethHdrDataDefPtr;
+struct _ethHdrDataDef {
+    nwItemDesc dataSrcMACAddr;
+    nwItemDesc dataSrcMACMask;
+    nwItemDesc dataDstMACAddr;
+    nwItemDesc dataDstMACMask;
+};
+
+
+typedef struct _ethHdrFilterDef  ethHdrFilterDef;
+typedef ethHdrFilterDef *ethHdrFilterDefPtr;
+struct _ethHdrFilterDef {
+    ethHdrDataDef ethHdr;
+    nwItemDesc dataProtocolID;
+};
+
+
+typedef struct _arpHdrFilterDef  arpHdrFilterDef;
+typedef arpHdrFilterDef *arpHdrFilterDefPtr;
+struct _arpHdrFilterDef {
+    ethHdrDataDef ethHdr;
+    nwItemDesc dataHWType;
+    nwItemDesc dataProtocolType;
+    nwItemDesc dataOpcode;
+    nwItemDesc dataARPSrcMACAddr;
+    nwItemDesc dataARPSrcIPAddr;
+    nwItemDesc dataARPDstMACAddr;
+    nwItemDesc dataARPDstIPAddr;
+};
+
+
+typedef struct _ipHdrDataDef  ipHdrDataDef;
+typedef ipHdrDataDef *ipHdrDataDefPtr;
+struct _ipHdrDataDef {
+    nwItemDesc dataIPVersion;
+    nwItemDesc dataSrcIPAddr;
+    nwItemDesc dataSrcIPMask;
+    nwItemDesc dataDstIPAddr;
+    nwItemDesc dataDstIPMask;
+    nwItemDesc dataProtocolID;
+    nwItemDesc dataDSCP;
+};
+
+
+typedef struct _portDataDef portDataDef;
+typedef portDataDef *portDataDefPtr;
+struct _portDataDef {
+    nwItemDesc dataSrcPortStart;
+    nwItemDesc dataSrcPortEnd;
+    nwItemDesc dataDstPortStart;
+    nwItemDesc dataDstPortEnd;
+};
+
+
+typedef struct _ipHdrFilterDef  ipHdrFilterDef;
+typedef ipHdrFilterDef *ipHdrFilterDefPtr;
+struct _ipHdrFilterDef {
+    ethHdrDataDef ethHdr;
+    ipHdrDataDef ipHdr;
+    portDataDef  portData;
+};
+
+
+enum virNWFilterRuleActionType {
+    VIR_NWFILTER_RULE_ACTION_DROP = 0,
+    VIR_NWFILTER_RULE_ACTION_ACCEPT,
+
+    VIR_NWFILTER_RULE_ACTION_LAST,
+};
+
+enum virNWFilterRuleDirectionType {
+    VIR_NWFILTER_RULE_DIRECTION_IN = 0,
+    VIR_NWFILTER_RULE_DIRECTION_OUT,
+    VIR_NWFILTER_RULE_DIRECTION_INOUT,
+
+    VIR_NWFILTER_RULE_DIRECTION_LAST,
+};
+
+enum virNWFilterChainPolicyType {
+    VIR_NWFILTER_CHAIN_POLICY_ACCEPT = 0,
+    VIR_NWFILTER_CHAIN_POLICY_DROP,
+
+    VIR_NWFILTER_CHAIN_POLICY_LAST,
+};
+
+enum virNWFilterRuleProtocolType {
+    VIR_NWFILTER_RULE_PROTOCOL_NONE = 0,
+    VIR_NWFILTER_RULE_PROTOCOL_MAC,
+    VIR_NWFILTER_RULE_PROTOCOL_ARP,
+    VIR_NWFILTER_RULE_PROTOCOL_IP,
+};
+
+enum virNWFilterEbtablesTableType {
+    VIR_NWFILTER_EBTABLES_TABLE_FILTER = 0,
+    VIR_NWFILTER_EBTABLES_TABLE_NAT,
+    VIR_NWFILTER_EBTABLES_TABLE_BROUTE,
+
+    VIR_NWFILTER_EBTABLES_TABLE_LAST,
+};
+
+
+#define MAX_RULE_PRIORITY  1000
+
+
+typedef struct _virNWFilterRuleDef  virNWFilterRuleDef;
+typedef virNWFilterRuleDef *virNWFilterRuleDefPtr;
+struct _virNWFilterRuleDef {
+    unsigned int priority;
+    int action; /*enum virNWFilterRuleActionType*/
+    int tt; /*enum virNWFilterRuleDirectionType*/
+    enum virNWFilterRuleProtocolType prtclType;
+    union {
+        ethHdrFilterDef  ethHdrFilter;
+        arpHdrFilterDef  arpHdrFilter;
+        ipHdrFilterDef   ipHdrFilter;
+    } p;
+
+    int nvars;
+    char **vars;
+};
+
+
+typedef struct _virNWFilterIncludeDef virNWFilterIncludeDef;
+typedef virNWFilterIncludeDef *virNWFilterIncludeDefPtr;
+struct _virNWFilterIncludeDef {
+    char *filterref;
+    virNWFilterHashTablePtr params;
+};
+
+
+typedef struct _virNWFilterEntry virNWFilterEntry;
+typedef virNWFilterEntry *virNWFilterEntryPtr;
+struct _virNWFilterEntry {
+    virNWFilterRuleDef    *rule;
+    virNWFilterIncludeDef *include;
+};
+
+enum virNWFilterChainSuffixType {
+    VIR_NWFILTER_CHAINSUFFIX_ROOT = 0,
+    VIR_NWFILTER_CHAINSUFFIX_ARP,
+    VIR_NWFILTER_CHAINSUFFIX_IPv4,
+
+    VIR_NWFILTER_CHAINSUFFIX_LAST,
+};
+
+
+typedef struct _virNWFilterDef virNWFilterDef;
+typedef virNWFilterDef *virNWFilterDefPtr;
+
+struct _virNWFilterDef {
+    char *name;
+    unsigned char uuid[VIR_UUID_BUFLEN];
+
+    int chainsuffix; /*enum virNWFilterChainSuffixType */
+
+    int nentries;
+    virNWFilterEntryPtr *filterEntries;
+};
+
+
+typedef struct _virNWFilterPoolObj virNWFilterPoolObj;
+typedef virNWFilterPoolObj *virNWFilterPoolObjPtr;
+
+struct _virNWFilterPoolObj {
+    virMutex lock;
+
+    char *configFile;
+    int active;
+    int wantRemoved;
+
+    virNWFilterDefPtr def;
+    virNWFilterDefPtr newDef;
+};
+
+
+typedef struct _virNWFilterPoolObjList virNWFilterPoolObjList;
+typedef virNWFilterPoolObjList *virNWFilterPoolObjListPtr;
+struct _virNWFilterPoolObjList {
+    unsigned int count;
+    virNWFilterPoolObjPtr *objs;
+};
+
+
+typedef struct _virNWFilterDriverState virNWFilterDriverState;
+typedef virNWFilterDriverState *virNWFilterDriverStatePtr;
+struct _virNWFilterDriverState {
+    virMutex lock;
+
+    virNWFilterPoolObjList pools;
+
+    char *configDir;
+};
+
+
+typedef struct _virNWFilterTechDriver virNWFilterTechDriver;
+typedef virNWFilterTechDriver *virNWFilterTechDriverPtr;
+
+
+typedef struct _virNWFilterRuleInst virNWFilterRuleInst;
+typedef virNWFilterRuleInst *virNWFilterRuleInstPtr;
+struct _virNWFilterRuleInst {
+   int ndata;
+   void **data;
+   virNWFilterTechDriverPtr techdriver;
+};
+
+
+enum virDomainNetType;
+
+typedef int (*virNWFilterRuleCreateInstance)(virConnectPtr conn,
+                                             enum virDomainNetType nettype,
+                                             virNWFilterDefPtr filter,
+                                             virNWFilterRuleDefPtr rule,
+                                             const char *ifname,
+                                             virNWFilterHashTablePtr vars,
+                                             virNWFilterRuleInstPtr res);
+
+typedef int (*virNWFilterRuleApplyNewRules)(virConnectPtr conn,
+                                            const char *ifname,
+                                            int nruleInstances,
+                                            void **_inst);
+
+typedef int (*virNWFilterRuleTeardownNewRules)(virConnectPtr conn,
+                                               const char *ifname);
+
+typedef int (*virNWFilterRuleTeardownOldRules)(virConnectPtr conn,
+                                               const char *ifname);
+
+typedef int (*virNWFilterRuleRemoveRules)(virConnectPtr conn,
+                                          const char *ifname,
+                                          int nruleInstances,
+                                          void **_inst);
+
+typedef int (*virNWFilterRuleAllTeardown)(const char *ifname);
+
+typedef int (*virNWFilterRuleFreeInstanceData)(void * _inst);
+
+typedef int (*virNWFilterRuleDisplayInstanceData)(virConnectPtr conn,
+                                                  void *_inst);
+
+
+struct _virNWFilterTechDriver {
+    const char *name;
+
+    virNWFilterRuleCreateInstance createRuleInstance;
+    virNWFilterRuleApplyNewRules applyNewRules;
+    virNWFilterRuleTeardownNewRules tearNewRules;
+    virNWFilterRuleTeardownOldRules tearOldRules;
+    virNWFilterRuleRemoveRules removeRules;
+    virNWFilterRuleAllTeardown allTeardown;
+    virNWFilterRuleFreeInstanceData freeRuleInstance;
+    virNWFilterRuleDisplayInstanceData displayRuleInstance;
+};
+
+
+
+void virNWFilterRuleDefFree(virNWFilterRuleDefPtr def);
+
+void virNWFilterDefFree(virNWFilterDefPtr def);
+void virNWFilterPoolObjListFree(virNWFilterPoolObjListPtr pools);
+void virNWFilterPoolObjRemove(virNWFilterPoolObjListPtr pools,
+                              virNWFilterPoolObjPtr pool);
+
+void virNWFilterPoolObjFree(virNWFilterPoolObjPtr obj);
+
+virNWFilterPoolObjPtr
+        virNWFilterPoolObjFindByUUID(virNWFilterPoolObjListPtr pools,
+                                     const unsigned char *uuid);
+
+virNWFilterPoolObjPtr
+        virNWFilterPoolObjFindByName(virNWFilterPoolObjListPtr pools,
+                                     const char *name);
+
+
+int virNWFilterPoolObjSaveDef(virConnectPtr conn,
+                              virNWFilterDriverStatePtr driver,
+                              virNWFilterPoolObjPtr pool,
+                              virNWFilterDefPtr def);
+
+int virNWFilterPoolObjDeleteDef(virConnectPtr conn,
+                                virNWFilterPoolObjPtr pool);
+
+virNWFilterPoolObjPtr virNWFilterPoolObjAssignDef(virConnectPtr conn,
+                                                  virNWFilterPoolObjListPtr pools,
+                                                  virNWFilterDefPtr def);
+
+int virNWFilterTestUnassignDef(virConnectPtr conn,
+                               virNWFilterPoolObjPtr pool);
+
+virNWFilterDefPtr virNWFilterDefParseNode(virConnectPtr conn,
+                                          xmlDocPtr xml,
+                                          xmlNodePtr root);
+
+char *virNWFilterDefFormat(virConnectPtr conn,
+                           virNWFilterDefPtr def);
+
+int virNWFilterSaveXML(virConnectPtr conn,
+                       const char *configDir,
+                       virNWFilterDefPtr def,
+                       const char *xml);
+
+int virNWFilterSaveConfig(virConnectPtr conn,
+                          const char *configDir,
+                          virNWFilterDefPtr def);
+
+int virNWFilterPoolLoadAllConfigs(virConnectPtr conn,
+                                  virNWFilterPoolObjListPtr pools,
+                                  const char *configDir);
+
+char *virNWFilterConfigFile(virConnectPtr conn,
+                            const char *dir,
+                            const char *name);
+
+virNWFilterDefPtr virNWFilterDefParseString(virConnectPtr conn,
+                                            const char *xml);
+virNWFilterDefPtr virNWFilterDefParseFile(virConnectPtr conn,
+                                          const char *filename);
+
+void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj);
+void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj);
+
+int virNWFilterConfLayerInit(void);
+void virNWFilterConfLayerShutdown(void);
+
+int virNWFilterParamConfLayerInit(void);
+void virNWFilterParamConfLayerShutdown(void);
+
+#define virNWFilterReportError(conn, code, fmt...)                          \
+        virReportErrorHelper(conn, VIR_FROM_NWFILTER, code, __FILE__,       \
+                               __FUNCTION__, __LINE__, fmt)
+
+
+typedef int (*virNWFilterRebuild)(virConnectPtr conn,
+                                  virHashIterator, void *data);
+
+typedef struct _virNWFilterCallbackDriver virNWFilterCallbackDriver;
+typedef virNWFilterCallbackDriver *virNWFilterCallbackDriverPtr;
+struct _virNWFilterCallbackDriver {
+    const char *name;
+
+    virNWFilterRebuild vmFilterRebuild;
+};
+
+void virNWFilterRegisterCallbackDriver(virNWFilterCallbackDriverPtr);
+
+
+VIR_ENUM_DECL(virNWFilterRuleAction);
+VIR_ENUM_DECL(virNWFilterRuleDirection);
+VIR_ENUM_DECL(virNWFilterRuleProtocol);
+VIR_ENUM_DECL(virNWFilterJumpTarget);
+VIR_ENUM_DECL(virNWFilterChainPolicy);
+VIR_ENUM_DECL(virNWFilterEbtablesTable);
+VIR_ENUM_DECL(virNWFilterChainSuffix);
+
+#endif /* NWFILTER_CONF_H */
diff --git a/src/conf/nwfilter_params.c b/src/conf/nwfilter_params.c
new file mode 100644 (file)
index 0000000..38a51c9
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * nwfilter_params.c: parsing and data maintenance of filter parameters
+ *
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * 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@us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <regex.h>
+
+#include "internal.h"
+
+#include "memory.h"
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "nwfilter_params.h"
+#include "domain_conf.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+/*
+ * regular expressions for parameter names and values
+ */
+static regex_t regex_nam;
+static regex_t regex_val;
+
+
+static void
+hashDealloc(void *payload, const char *name ATTRIBUTE_UNUSED)
+{
+    VIR_FREE(payload);
+}
+
+
+/**
+ * virNWFilterHashTablePut:
+ * @table: Pointer to a virNWFilterHashTable
+ * @name: name of the key to enter
+ * @val: The value associated with the key
+ * @freeName: Whether the name must be freed on table destruction
+ *
+ * Returns 0 on success, 1 on failure.
+ *
+ * Put an entry into the hashmap replacing and freeing an existing entry
+ * if one existed.
+ */
+int
+virNWFilterHashTablePut(virNWFilterHashTablePtr table,
+                        const char *name,
+                        char *val,
+                        int copyName)
+{
+    if (!virHashLookup(table->hashTable, name)) {
+        if (copyName) {
+            name = strdup(name);
+            if (!name)
+                return 1;
+
+            if (VIR_REALLOC_N(table->names, table->nNames + 1) < 0) {
+                VIR_FREE(name);
+                return 1;
+            }
+            table->names[table->nNames++] = (char *)name;
+        }
+
+        if (virHashAddEntry(table->hashTable, name, val) != 0) {
+            if (copyName) {
+                VIR_FREE(name);
+                table->nNames--;
+            }
+            return 1;
+        }
+    } else {
+        if (virHashUpdateEntry(table->hashTable, name, val, hashDealloc) != 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+/**
+ * virNWFilterHashTableFree:
+ * @table: Pointer to virNWFilterHashTable
+ *
+ * Free a hashtable de-allocating memory for all its entries.
+ *
+ * All hash tables within the NWFilter driver must use this
+ * function to deallocate and free their content.
+ */
+void
+virNWFilterHashTableFree(virNWFilterHashTablePtr table)
+{
+    int i;
+    if (!table)
+        return;
+    virHashFree(table->hashTable, hashDealloc);
+
+    for (i = 0; i < table->nNames; i++)
+        VIR_FREE(table->names[i]);
+    VIR_FREE(table->names);
+    VIR_FREE(table);
+}
+
+
+virNWFilterHashTablePtr
+virNWFilterHashTableCreate(int n) {
+    virNWFilterHashTablePtr ret;
+
+    if (VIR_ALLOC(ret) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+    ret->hashTable = virHashCreate(n);
+    if (!ret->hashTable) {
+        virReportOOMError();
+        VIR_FREE(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+
+int
+virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr ht,
+                                const char *entry)
+{
+    int i;
+    int rc = virHashRemoveEntry(ht->hashTable, entry, hashDealloc);
+
+    if (rc == 0) {
+        for (i = 0; i < ht->nNames; i++) {
+            if (STREQ(ht->names[i], entry)) {
+                VIR_FREE(ht->names[i]);
+                ht->names[i] = ht->names[--ht->nNames];
+                ht->names[ht->nNames] = NULL;
+                break;
+            }
+        }
+    }
+    return rc;
+}
+
+
+struct addToTableStruct {
+    virNWFilterHashTablePtr target;
+    int errOccurred;
+    virConnectPtr conn;
+};
+
+
+static void
+addToTable(void *payload, const char *name, void *data)
+{
+    struct addToTableStruct *atts = (struct addToTableStruct *)data;
+    char *val;
+
+    if (atts->errOccurred)
+        return;
+
+    val = strdup((char *)payload);
+    if (!val) {
+        virReportOOMError();
+        atts->errOccurred = 1;
+        return;
+    }
+
+    if (virNWFilterHashTablePut(atts->target, name, val, 1) != 0) {
+        virNWFilterReportError(atts->conn, VIR_ERR_INTERNAL_ERROR,
+                               _("Could not put variable '%s' into hashmap"),
+                               name);
+        atts->errOccurred = 1;
+        VIR_FREE(val);
+    }
+}
+
+
+int
+virNWFilterHashTablePutAll(virConnectPtr conn,
+                           virNWFilterHashTablePtr src,
+                           virNWFilterHashTablePtr dest)
+{
+    struct addToTableStruct atts = {
+        .target = dest,
+        .errOccurred = 0,
+        .conn = conn,
+    };
+
+    virHashForEach(src->hashTable, addToTable, &atts);
+    if (atts.errOccurred)
+        goto err_exit;
+
+    return 0;
+
+err_exit:
+    return 1;
+}
+
+
+virNWFilterHashTablePtr
+virNWFilterParseParamAttributes(xmlNodePtr cur)
+{
+    char *nam, *val;
+
+    virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
+    if (!table) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    cur = cur->children;
+
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if (xmlStrEqual(cur->name, BAD_CAST "parameter")) {
+                nam = virXMLPropString(cur, "name");
+                val = virXMLPropString(cur, "value");
+                if (nam != NULL && val != NULL) {
+                    if (regexec(&regex_nam, nam, 0, NULL, 0) != 0)
+                        goto skip_entry;
+                    if (regexec(&regex_val, val, 0, NULL, 0) != 0)
+                        goto skip_entry;
+                    if (virNWFilterHashTablePut(table, nam, val, 1)) {
+                        VIR_FREE(nam);
+                        VIR_FREE(val);
+                        virNWFilterHashTableFree(table);
+                        return NULL;
+                    }
+                    val = NULL;
+                }
+skip_entry:
+                VIR_FREE(nam);
+                VIR_FREE(val);
+            }
+        }
+        cur = cur->next;
+    }
+    return table;
+}
+
+
+struct formatterParam {
+    virBufferPtr buf;
+    const char *indent;
+};
+
+
+static void
+_formatParameterAttrs(void *payload, const char *name, void *data)
+{
+    struct formatterParam *fp = (struct formatterParam *)data;
+
+    virBufferVSprintf(fp->buf, "%s<parameter name='%s' value='%s'/>\n",
+                      fp->indent,
+                      name,
+                      (char *)payload);
+}
+
+
+char *
+virNWFilterFormatParamAttributes(virNWFilterHashTablePtr table,
+                                 const char *indent)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    struct formatterParam fp = {
+        .buf = &buf,
+        .indent = indent,
+    };
+
+    virHashForEach(table->hashTable, _formatParameterAttrs, &fp);
+
+    if (virBufferError(&buf)) {
+        virReportOOMError();
+        virBufferFreeAndReset(&buf);
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+}
+
+
+int virNWFilterParamConfLayerInit(void) {
+
+    if (regcomp(&regex_nam, "^[a-zA-Z0-9_]+$"  ,
+                            REG_NOSUB|REG_EXTENDED) != 0)
+        return 1;
+
+    if (regcomp(&regex_val, "^[a-zA-Z0-9_.:]+$",
+                            REG_NOSUB|REG_EXTENDED) != 0) {
+        regfree(&regex_nam);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+void virNWFilterParamConfLayerShutdown(void) {
+    regfree(&regex_nam);
+    regfree(&regex_val);
+}
diff --git a/src/conf/nwfilter_params.h b/src/conf/nwfilter_params.h
new file mode 100644 (file)
index 0000000..7a9b1ee
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * nwfilter_params.h: parsing and data maintenance of filter parameters
+ *
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * 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@us.ibm.com>
+ */
+#ifndef NWFILTER_PARAMS_H
+#define NWFILTER_PARAMS_H
+
+#include "hash.h"
+
+typedef struct _virNWFilterHashTable virNWFilterHashTable;
+typedef virNWFilterHashTable *virNWFilterHashTablePtr;
+struct _virNWFilterHashTable {
+    virHashTablePtr hashTable;
+
+    int nNames;
+    char **names;
+};
+
+
+virNWFilterHashTablePtr virNWFilterParseParamAttributes(xmlNodePtr cur);
+char * virNWFilterFormatParamAttributes(virNWFilterHashTablePtr table,
+                                        const char *indent);
+
+virNWFilterHashTablePtr virNWFilterHashTableCreate(int n);
+void virNWFilterHashTableFree(virNWFilterHashTablePtr table);
+int virNWFilterHashTablePut(virNWFilterHashTablePtr table,
+                            const char *name,
+                            char *val,
+                            int freeName);
+int virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr table,
+                                    const char *name);
+int virNWFilterHashTablePutAll(virConnectPtr conn,
+                               virNWFilterHashTablePtr src,
+                               virNWFilterHashTablePtr dest);
+
+#endif /* NWFILTER_PARAMS_H */