[libvirt] [PATCH V1 0/4] Support for multiple IP addresses using lists

This patch series builds on the previously posted patch series https://www.redhat.com/archives/libvir-list/2011-October/msg00912.html and introduces the capability to assign a list to a variable and have multiple rules instantiated, one for each item in the list. This means, that if for example a variable like IP has been assigned the following values IP = [1.2.3.4, 5.6.7.8, 10.0.0.1] it will generate 3 rules, which then in turn allows us to build filters that can evaluate multiple possible values per field, i.e., allow the filtering for multiple IP addresses (per interface). It would then need David Steven's patch for support of 'RETURN' (and 'CONTINUE') target(s). Regards, Stefan

NWFilters can be provided name-value pairs using the following XML notiation: <filterref filter='xyz'> <parameter name='PORT' value='80'/> <parameter name='VAL' value='abc'/> </filterref> The internal representation currently is so that a name is stored as a string and the value as well. This patch now addresses the value part of it and introduces a data structure for storing a value either as a simple value or as an array for later support of lists (provided in python-like notation ( [a,b,c] ). This patch adjusts all code that was handling the values in hash tables and makes it use the new data type. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- src/conf/domain_conf.c | 2 src/conf/nwfilter_params.c | 289 ++++++++++++++++++++++++++++-- src/conf/nwfilter_params.h | 36 +++ src/libvirt_private.syms | 2 src/nwfilter/nwfilter_ebiptables_driver.c | 15 + src/nwfilter/nwfilter_gentech_driver.c | 27 ++ src/nwfilter/nwfilter_learnipaddr.c | 13 + 7 files changed, 363 insertions(+), 21 deletions(-) Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c @@ -200,14 +200,25 @@ printVar(virNWFilterHashTablePtr vars, *done = 0; if ((item->flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) { - char *val = (char *)virHashLookup(vars->hashTable, item->var); - if (!val) { + virNWFilterVarValuePtr varval; + const char *val; + + varval = virHashLookup(vars->hashTable, item->var); + if (!varval) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("cannot find value for '%s'"), item->var); return 1; } + val = virNWFilterVarValueGetSimple(varval); + if (!val) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot get simple value of '%s'"), + item->var); + return 1; + } + if (!virStrcpy(buf, val, bufsize)) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("Buffer to small to print MAC address " Index: libvirt-acl/src/conf/nwfilter_params.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.c +++ libvirt-acl/src/conf/nwfilter_params.c @@ -30,14 +30,268 @@ #include "datatypes.h" #include "nwfilter_params.h" #include "domain_conf.h" +#include "c-ctype.h" #define VIR_FROM_THIS VIR_FROM_NWFILTER +static bool isValidVarValue(const char *value); + + +static void +virNWFilterVarValueFree(virNWFilterVarValuePtr val) +{ + unsigned i; + + if (!val) + return; + + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + VIR_FREE(val->u.simple.value); + break; + case NWFILTER_VALUE_TYPE_ARRAY: + for (i = 0; i < val->u.array.nValues; i++) + VIR_FREE(val->u.array.values[i]); + VIR_FREE(val->u.array.values); + break; + case NWFILTER_VALUE_TYPE_LAST: + break; + } + VIR_FREE(val); +} + +static virNWFilterVarValuePtr +virNWFilterVarValueCopy(const virNWFilterVarValuePtr val) +{ + virNWFilterVarValuePtr res; + unsigned i; + char *str; + + if (VIR_ALLOC(res) < 0) { + virReportOOMError(); + return NULL; + } + res->valType = val->valType; + + switch (res->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + if (val->u.simple.value) { + res->u.simple.value = strdup(val->u.simple.value); + if (!res->u.simple.value) + goto err_exit; + } + break; + case NWFILTER_VALUE_TYPE_ARRAY: + if (VIR_ALLOC_N(res->u.array.values, val->u.array.nValues)) + goto err_exit; + res->u.array.nValues = val->u.array.nValues; + for (i = 0; i < val->u.array.nValues; i++) { + str = strdup(val->u.array.values[i]); + if (!str) + goto err_exit; + res->u.array.values[i] = str; + } + break; + case NWFILTER_VALUE_TYPE_LAST: + break; + } + + return res; + +err_exit: + virReportOOMError(); + virNWFilterVarValueFree(res); + return NULL; +} + +static void +virNWFilterVarValuePrint(virNWFilterVarValuePtr val, virBufferPtr buf) +{ + unsigned i; + char *item; + + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + virBufferAdd(buf, val->u.simple.value, -1); + break; + case NWFILTER_VALUE_TYPE_ARRAY: + virBufferAddLit(buf, "["); + for (i = 0; i < val->u.array.nValues; i++) { + if (i > 0) + virBufferAddLit(buf, ", "); + item = val->u.array.values[i]; + if (item) { + bool quote = false; + if (c_isspace(item[0]) || c_isspace(item[strlen(item)- 1 ])) + quote = true; + if (quote) + virBufferEscapeString(buf, "%s", "'"); + virBufferAdd(buf, val->u.array.values[i], -1); + if (quote) + virBufferEscapeString(buf, "%s", "'"); + } + } + virBufferAddLit(buf, "]"); + break; + case NWFILTER_VALUE_TYPE_LAST: + break; + } +} + +virNWFilterVarValuePtr +virNWFilterVarValueCreateSimple(const char *value, bool copy_value) +{ + virNWFilterVarValuePtr val; + + if (!isValidVarValue(value)) + return NULL; + + if (VIR_ALLOC(val) < 0) { + virReportOOMError(); + return NULL; + } + + val->valType = NWFILTER_VALUE_TYPE_SIMPLE; + if (copy_value) { + val->u.simple.value = strdup(value); + if (!val->u.simple.value) { + VIR_FREE(val); + virReportOOMError(); + return NULL; + } + } else + val->u.simple.value = (char *)value; + + return val; +} + +const char * +virNWFilterVarValueGetSimple(virNWFilterVarValuePtr val) +{ + if (val->valType == NWFILTER_VALUE_TYPE_SIMPLE) + return val->u.simple.value; + return NULL; +} + +const char * +virNWFilterVarValueGetNthValue(virNWFilterVarValuePtr val, unsigned int idx) +{ + const char *res = NULL; + + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + if (idx == 0) + res = val->u.simple.value; + break; + case NWFILTER_VALUE_TYPE_ARRAY: + if (idx < val->u.array.nValues) + res = val->u.array.values[idx]; + break; + case NWFILTER_VALUE_TYPE_LAST: + break; + } + + return res; +} + +unsigned int +virNWFilterVarValueGetCardinality(virNWFilterVarValuePtr val) +{ + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + return 1; + break; + case NWFILTER_VALUE_TYPE_ARRAY: + return val->u.array.nValues; + break; + case NWFILTER_VALUE_TYPE_LAST: + return 0; + } + return 0; +} + +bool +virNWFilterVarValueDelValue(virNWFilterVarValuePtr val, const char *value) +{ + unsigned int i; + + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + return false; + + case NWFILTER_VALUE_TYPE_ARRAY: + for (i = 0; i < val->u.array.nValues; i++) { + if (STREQ(value, val->u.array.values[i])) { + VIR_FREE(val->u.array.values[i]); + val->u.array.nValues--; + val->u.array.values[i] = + val->u.array.values[val->u.array.nValues]; + return true; + } + } + break; + + case NWFILTER_VALUE_TYPE_LAST: + break; + } + + return false; +} + +bool +virNWFilterVarValueAddValue(virNWFilterVarValuePtr val, const char *value, + bool make_copy) +{ + char *tmp; + bool rc = false; + + if (make_copy) { + value = strdup(value); + if (!value) { + virReportOOMError(); + return false; + } + } + + switch (val->valType) { + case NWFILTER_VALUE_TYPE_SIMPLE: + /* switch to array */ + tmp = val->u.simple.value; + if (VIR_ALLOC_N(val->u.array.values, 2) < 0) { + val->u.simple.value = tmp; + virReportOOMError(); + return false; + } + val->valType = NWFILTER_VALUE_TYPE_ARRAY; + val->u.array.nValues = 2; + val->u.array.values[0] = tmp; + val->u.array.values[1] = (char *)value; + rc = true; + break; + + case NWFILTER_VALUE_TYPE_ARRAY: + if (VIR_REALLOC_N(val->u.array.values, + val->u.array.nValues + 1) < 0) { + virReportOOMError(); + return false; + } + val->u.array.values[val->u.array.nValues] = (char *)value; + val->u.array.nValues += 1; + rc = true; + break; + + case NWFILTER_VALUE_TYPE_LAST: + break; + } + + return rc; +} + static void hashDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) { - VIR_FREE(payload); + virNWFilterVarValueFree(payload); } @@ -56,7 +310,7 @@ hashDataFree(void *payload, const void * int virNWFilterHashTablePut(virNWFilterHashTablePtr table, const char *name, - char *val, + virNWFilterVarValuePtr val, int copyName) { if (!virHashLookup(table->hashTable, name)) { @@ -160,12 +414,12 @@ static void addToTable(void *payload, const void *name, void *data) { struct addToTableStruct *atts = (struct addToTableStruct *)data; - char *val; + virNWFilterVarValuePtr val; if (atts->errOccurred) return; - val = strdup((char *)payload); + val = virNWFilterVarValueCopy((virNWFilterVarValuePtr)payload); if (!val) { virReportOOMError(); atts->errOccurred = 1; @@ -177,7 +431,7 @@ addToTable(void *payload, const void *na _("Could not put variable '%s' into hashmap"), (const char *)name); atts->errOccurred = 1; - VIR_FREE(val); + virNWFilterVarValueFree(val); } } @@ -215,11 +469,18 @@ isValidVarValue(const char *value) return value[strspn(value, VALID_VARVALUE)] == 0; } +static virNWFilterVarValuePtr +virNWFilterParseVarValue(const char *val) +{ + // FIXME: only handling simple values for now, no arrays + return virNWFilterVarValueCreateSimple(val, true); +} virNWFilterHashTablePtr virNWFilterParseParamAttributes(xmlNodePtr cur) { char *nam, *val; + virNWFilterVarValuePtr value; virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0); if (!table) { @@ -234,20 +495,24 @@ virNWFilterParseParamAttributes(xmlNodeP if (xmlStrEqual(cur->name, BAD_CAST "parameter")) { nam = virXMLPropString(cur, "name"); val = virXMLPropString(cur, "value"); + value = NULL; if (nam != NULL && val != NULL) { if (!isValidVarName(nam)) goto skip_entry; - if (!isValidVarValue(nam)) + value = virNWFilterParseVarValue(val); + if (!value) goto skip_entry; - if (virNWFilterHashTablePut(table, nam, val, 1)) { + if (virNWFilterHashTablePut(table, nam, value, 1)) { VIR_FREE(nam); VIR_FREE(val); + virNWFilterVarValueFree(value); virNWFilterHashTableFree(table); return NULL; } - val = NULL; + value = NULL; } skip_entry: + virNWFilterVarValueFree(value); VIR_FREE(nam); VIR_FREE(val); } @@ -268,11 +533,13 @@ static void _formatParameterAttrs(void *payload, const void *name, void *data) { struct formatterParam *fp = (struct formatterParam *)data; + virNWFilterVarValuePtr value = payload; - virBufferAsprintf(fp->buf, "%s<parameter name='%s' value='%s'/>\n", + virBufferAsprintf(fp->buf, "%s<parameter name='%s' value='", fp->indent, - (const char *)name, - (char *)payload); + (const char *)name); + virNWFilterVarValuePrint(value, fp->buf); + virBufferAddLit(fp->buf, "'/>\n"); } Index: libvirt-acl/src/conf/nwfilter_params.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.h +++ libvirt-acl/src/conf/nwfilter_params.h @@ -24,6 +24,40 @@ # include "hash.h" +enum virNWFilterVarValueType { + NWFILTER_VALUE_TYPE_SIMPLE, + NWFILTER_VALUE_TYPE_ARRAY, + + NWFILTER_VALUE_TYPE_LAST +}; + +typedef struct _virNWFilterVarValue virNWFilterVarValue; +typedef virNWFilterVarValue *virNWFilterVarValuePtr; +struct _virNWFilterVarValue { + enum virNWFilterVarValueType valType; + union { + struct { + char *value; + } simple; + struct { + char **values; + unsigned nValues; + } array; + } u; +}; + +virNWFilterVarValuePtr virNWFilterVarValueCreateSimple(const char *, + bool copy_value); +const char *virNWFilterVarValueGetSimple(virNWFilterVarValuePtr val); +const char *virNWFilterVarValueGetNthValue(virNWFilterVarValuePtr val, + unsigned int idx); +unsigned int virNWFilterVarValueGetCardinality(virNWFilterVarValuePtr val); +bool virNWFilterVarValueDelValue(virNWFilterVarValuePtr val, + const char *value); +bool virNWFilterVarValueAddValue(virNWFilterVarValuePtr val, + const char *value, + bool make_copy); + typedef struct _virNWFilterHashTable virNWFilterHashTable; typedef virNWFilterHashTable *virNWFilterHashTablePtr; struct _virNWFilterHashTable { @@ -42,7 +76,7 @@ virNWFilterHashTablePtr virNWFilterHashT void virNWFilterHashTableFree(virNWFilterHashTablePtr table); int virNWFilterHashTablePut(virNWFilterHashTablePtr table, const char *name, - char *val, + virNWFilterVarValuePtr val, int freeName); int virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr table, const char *name); Index: libvirt-acl/src/conf/domain_conf.c =================================================================== --- libvirt-acl.orig/src/conf/domain_conf.c +++ libvirt-acl/src/conf/domain_conf.c @@ -3143,7 +3143,7 @@ virDomainNetDefParseXML(virCapsPtr caps, event_idx = virXMLPropString(cur, "event_idx"); } else if (xmlStrEqual (cur->name, BAD_CAST "filterref")) { filter = virXMLPropString(cur, "filter"); - VIR_FREE(filterparams); + virNWFilterHashTableFree(filterparams); filterparams = virNWFilterParseParamAttributes(cur); } else if ((flags & VIR_DOMAIN_XML_INTERNAL_STATUS) && xmlStrEqual(cur->name, BAD_CAST "state")) { Index: libvirt-acl/src/nwfilter/nwfilter_gentech_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_gentech_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_gentech_driver.c @@ -147,10 +147,17 @@ virNWFilterVarHashmapAddStdValues(virNWF char *macaddr, char *ipaddr) { + virNWFilterVarValue *val; + if (macaddr) { + val = virNWFilterVarValueCreateSimple(macaddr, false); + if (!val) { + virReportOOMError(); + return 1; + } if (virHashAddEntry(table->hashTable, NWFILTER_STD_VAR_MAC, - macaddr) < 0) { + val) < 0) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add variable 'MAC' to hashmap")); return 1; @@ -158,9 +165,14 @@ virNWFilterVarHashmapAddStdValues(virNWF } if (ipaddr) { + val = virNWFilterVarValueCreateSimple(ipaddr, false); + if (!val) { + virReportOOMError(); + return 1; + } if (virHashAddEntry(table->hashTable, NWFILTER_STD_VAR_IP, - ipaddr) < 0) { + val) < 0) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add variable 'IP' to hashmap")); return 1; @@ -491,6 +503,7 @@ virNWFilterDetermineMissingVarsRec(virCo int rc = 0; int i, j; virNWFilterDefPtr next_filter; + virNWFilterVarValuePtr val; for (i = 0; i < filter->nentries; i++) { virNWFilterRuleDefPtr rule = filter->filterEntries[i]->rule; @@ -499,10 +512,18 @@ virNWFilterDetermineMissingVarsRec(virCo /* check all variables of this rule */ for (j = 0; j < rule->nvars; j++) { if (!virHashLookup(vars->hashTable, rule->vars[j])) { + val = virNWFilterVarValueCreateSimple("1", true); + if (!val) { + virReportOOMError(); + rc = 1; + break; + } virNWFilterHashTablePut(missing_vars, rule->vars[j], - strdup("1"), 1); + val, 1); } } + if (rc) + break; } else if (inc) { VIR_DEBUG("Following filter %s\n", inc->filterref); obj = virNWFilterObjFindByName(&driver->nwfilters, inc->filterref); Index: libvirt-acl/src/nwfilter/nwfilter_learnipaddr.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_learnipaddr.c +++ libvirt-acl/src/nwfilter/nwfilter_learnipaddr.c @@ -313,10 +313,14 @@ virNWFilterDeregisterLearnReq(int ifinde static int virNWFilterAddIpAddrForIfname(const char *ifname, char *addr) { int ret; + virNWFilterVarValuePtr val = virNWFilterVarValueCreateSimple(addr, false); + + if (!val) + return 1; virMutexLock(&ipAddressMapLock); - ret = virNWFilterHashTablePut(ipAddressMap, ifname, addr, 1); + ret = virNWFilterHashTablePut(ipAddressMap, ifname, val, 1); virMutexUnlock(&ipAddressMapLock); @@ -339,7 +343,7 @@ virNWFilterDelIpAddrForIfname(const char const char * virNWFilterGetIpAddrForIfname(const char *ifname) { - const char *res; + virNWFilterVarValuePtr res; virMutexLock(&ipAddressMapLock); @@ -347,7 +351,10 @@ virNWFilterGetIpAddrForIfname(const char virMutexUnlock(&ipAddressMapLock); - return res; + if (res) + return virNWFilterVarValueGetSimple(res); + + return NULL; } Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -878,6 +878,8 @@ virNWFilterHashTableFree; virNWFilterHashTablePut; virNWFilterHashTablePutAll; virNWFilterHashTableRemoveEntry; +virNWFilterVarValueCreateSimple; +virNWFilterVarValueGetSimple; # pci.h

This patch extends the NWFilter driver for Linux (ebiptables) to create rules for each member of a previously introduced list. If for example an attribute value looks like this: IP = [10.0.0.1, 10.0.0.2, 10.0.0.3] then 3 rules will be generated for a rule accessing the variable 'IP', one for each member of the list. The effect of this is that this now allows for filtering for multiple values in one field. This can then be used to support for filtering/allowing of multiple IP addresses per interface. An interator is introduced that extracts each member of a list and puts it into a hash table which then is passed to the function creating a rule. For the above example the iterator would cause 3 loops. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- src/conf/nwfilter_params.c | 146 ++++++++++++++++++++++++++++++ src/conf/nwfilter_params.h | 25 +++++ src/libvirt_private.syms | 4 src/nwfilter/nwfilter_ebiptables_driver.c | 44 ++++++++- 4 files changed, 218 insertions(+), 1 deletion(-) Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c @@ -2513,6 +2513,48 @@ ebiptablesCreateRuleInstance(virConnectP return rc; } +static int +ebiptablesCreateRuleInstanceCombinations( + virConnectPtr conn ATTRIBUTE_UNUSED, + enum virDomainNetType nettype ATTRIBUTE_UNUSED, + virNWFilterDefPtr nwfilter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res) +{ + int rc = 0; + virNWFilterVarCombIterPtr iter; + virNWFilterHashTablePtr itervars; + + /* rule->vars holds all the variables names that this rule will access. + * iterate over all combinations of the variables' values and instantiate + * the filtering rule with each combination. + */ + iter = virNWFilterVarCombIterCreate(vars, rule->vars, rule->nvars); + if (!iter) { + return 1; + } + + itervars = virNWFilterVarCombIterFirst(iter); + + while (rc == 0 && itervars != NULL) { + rc = ebiptablesCreateRuleInstance(conn, + nettype, + nwfilter, + rule, + ifname, + itervars, + res); + if (rc) + break; + itervars = virNWFilterVarCombIterNext(iter, &rc); + }; + + virNWFilterVarCombIterFree(iter); + + return rc; +} static int ebiptablesFreeRuleInstance(void *_inst) @@ -3896,7 +3938,7 @@ virNWFilterTechDriver ebiptables_driver .init = ebiptablesDriverInit, .shutdown = ebiptablesDriverShutdown, - .createRuleInstance = ebiptablesCreateRuleInstance, + .createRuleInstance = ebiptablesCreateRuleInstanceCombinations, .applyNewRules = ebiptablesApplyNewRules, .tearNewRules = ebiptablesTearNewRules, .tearOldRules = ebiptablesTearOldRules, Index: libvirt-acl/src/conf/nwfilter_params.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.c +++ libvirt-acl/src/conf/nwfilter_params.c @@ -288,6 +288,152 @@ virNWFilterVarValueAddValue(virNWFilterV return rc; } +void +virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci) +{ + if (!ci) + return; + + virNWFilterHashTableFree(ci->hashTable); + VIR_FREE(ci); +} + +virNWFilterVarCombIterPtr +virNWFilterVarCombIterCreate(virNWFilterHashTablePtr hash, + char * const *vars, unsigned int nVars) +{ + virNWFilterVarCombIterPtr res; + virNWFilterVarValuePtr value, orig; + unsigned int i; + const char *val; + + if (VIR_ALLOC_VAR(res, virNWFilterVarCombEntry, nVars) < 0) { + virReportOOMError(); + return NULL; + } + + res->hashTable = virNWFilterHashTableCreate(nVars); + res->nEntries = nVars; + res->orig = hash; + + for (i = 0; i < nVars; i++) { + orig = virHashLookup(hash->hashTable, vars[i]); + if (orig == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + vars[i]); + goto err_exit; + } + + res->entry[i].key = vars[i]; + res->entry[i].cardinality = virNWFilterVarValueGetCardinality(orig); + + val = virNWFilterVarValueGetNthValue(orig, 0); + if (!val) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get 0.th value of " + "variable '%s'"), + res->entry[i].key); + goto err_exit; + } + + value = virNWFilterVarValueCreateSimple(val, true); + if (!value) { + virReportOOMError(); + goto err_exit; + } + + if (virNWFilterHashTablePut(res->hashTable, vars[i], value, false)) { + virReportOOMError(); + goto err_exit; + } + } + + return res; + +err_exit: + virNWFilterVarCombIterFree(res); + return NULL; +} + +virNWFilterHashTablePtr +virNWFilterVarCombIterFirst(virNWFilterVarCombIterPtr ci) +{ + return ci->hashTable; +} + +virNWFilterHashTablePtr +virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr ci, int *error) +{ + unsigned int i; + virNWFilterVarValuePtr value; + bool done = false; + bool end = (ci->nEntries == 0) || + (ci->nEntries == 1 && ci->entry[0].cardinality == 1); + const char *val; + + *error = 0; + + if (end) + return NULL; + + for (i = 0; i < ci->nEntries; i++) { + if (ci->entry[i].cardinality == 1) + continue; + + ci->entry[i].idx++; + if (ci->entry[i].idx < ci->entry[i].cardinality) { + done = true; + } else { + ci->entry[i].idx = 0; + } + + value = virHashLookup(ci->orig->hashTable, ci->entry[i].key); + if (!value) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + ci->entry[i].key); + *error = 1; + break; + } + + val = virNWFilterVarValueGetNthValue(value, ci->entry[i].idx); + if (!val) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get nth (%u) value of " + "variable '%s'"), + ci->entry[i].idx, ci->entry[i].key); + *error = 1; + break; + } + + value = virNWFilterVarValueCreateSimple(val, true); + if (!value) { + virReportOOMError(); + *error = 1; + break; + } + + if (virNWFilterHashTablePut(ci->hashTable, ci->entry[i].key, + value, false)) { + virReportOOMError(); + *error = 1; + break; + } + + if (done || *error) + break; + } + + if (ci->nEntries == i) + end = true; + + if (end || *error) + return NULL; + + return ci->hashTable; +} + static void hashDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) { Index: libvirt-acl/src/conf/nwfilter_params.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.h +++ libvirt-acl/src/conf/nwfilter_params.h @@ -89,4 +89,29 @@ int virNWFilterHashTablePutAll(virNWFilt # define VALID_VARVALUE \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:" +typedef struct _virNWFilterVarCombEntry virNWFilterVarCombEntry; +struct _virNWFilterVarCombEntry { + const char *key; + unsigned int idx; + unsigned int cardinality; +}; + +typedef struct _virNWFilterVarCombIter virNWFilterVarCombIter; +typedef virNWFilterVarCombIter *virNWFilterVarCombIterPtr; +struct _virNWFilterVarCombIter { + virNWFilterHashTablePtr hashTable; + virNWFilterHashTablePtr orig; + unsigned int nEntries; + virNWFilterVarCombEntry entry[1]; +}; +virNWFilterVarCombIterPtr virNWFilterVarCombIterCreate( + virNWFilterHashTablePtr hash, + char * const *vars, + unsigned int nvars); + +void virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr combiter); +virNWFilterHashTablePtr virNWFilterVarCombIterFirst(virNWFilterVarCombIterPtr); +virNWFilterHashTablePtr virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr, + int *error); + #endif /* NWFILTER_PARAMS_H */ Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -878,6 +878,10 @@ virNWFilterHashTableFree; virNWFilterHashTablePut; virNWFilterHashTablePutAll; virNWFilterHashTableRemoveEntry; +virNWFilterVarCombIterCreate; +virNWFilterVarCombIterFirst; +virNWFilterVarCombIterFree; +virNWFilterVarCombIterNext; virNWFilterVarValueCreateSimple; virNWFilterVarValueGetSimple;

I rewrote some parts of this patch here. Will send out a V2. Stefan On 10/21/2011 03:31 PM, Stefan Berger wrote:
This patch extends the NWFilter driver for Linux (ebiptables) to create rules for each member of a previously introduced list. If for example an attribute value looks like this:
IP = [10.0.0.1, 10.0.0.2, 10.0.0.3]
then 3 rules will be generated for a rule accessing the variable 'IP', one for each member of the list. The effect of this is that this now allows for filtering for multiple values in one field. This can then be used to support for filtering/allowing of multiple IP addresses per interface.
An interator is introduced that extracts each member of a list and puts it into a hash table which then is passed to the function creating a rule. For the above example the iterator would cause 3 loops.
Signed-off-by: Stefan Berger<stefanb@linux.vnet.ibm.com>
--- src/conf/nwfilter_params.c | 146 ++++++++++++++++++++++++++++++ src/conf/nwfilter_params.h | 25 +++++ src/libvirt_private.syms | 4 src/nwfilter/nwfilter_ebiptables_driver.c | 44 ++++++++- 4 files changed, 218 insertions(+), 1 deletion(-)
Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c @@ -2513,6 +2513,48 @@ ebiptablesCreateRuleInstance(virConnectP return rc; }
+static int +ebiptablesCreateRuleInstanceCombinations( + virConnectPtr conn ATTRIBUTE_UNUSED, + enum virDomainNetType nettype ATTRIBUTE_UNUSED, + virNWFilterDefPtr nwfilter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res) +{ + int rc = 0; + virNWFilterVarCombIterPtr iter; + virNWFilterHashTablePtr itervars; + + /* rule->vars holds all the variables names that this rule will access. + * iterate over all combinations of the variables' values and instantiate + * the filtering rule with each combination. + */ + iter = virNWFilterVarCombIterCreate(vars, rule->vars, rule->nvars); + if (!iter) { + return 1; + } + + itervars = virNWFilterVarCombIterFirst(iter); + + while (rc == 0&& itervars != NULL) { + rc = ebiptablesCreateRuleInstance(conn, + nettype, + nwfilter, + rule, + ifname, + itervars, + res); + if (rc) + break; + itervars = virNWFilterVarCombIterNext(iter,&rc); + }; + + virNWFilterVarCombIterFree(iter); + + return rc; +}
static int ebiptablesFreeRuleInstance(void *_inst) @@ -3896,7 +3938,7 @@ virNWFilterTechDriver ebiptables_driver .init = ebiptablesDriverInit, .shutdown = ebiptablesDriverShutdown,
- .createRuleInstance = ebiptablesCreateRuleInstance, + .createRuleInstance = ebiptablesCreateRuleInstanceCombinations, .applyNewRules = ebiptablesApplyNewRules, .tearNewRules = ebiptablesTearNewRules, .tearOldRules = ebiptablesTearOldRules, Index: libvirt-acl/src/conf/nwfilter_params.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.c +++ libvirt-acl/src/conf/nwfilter_params.c @@ -288,6 +288,152 @@ virNWFilterVarValueAddValue(virNWFilterV return rc; }
+void +virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci) +{ + if (!ci) + return; + + virNWFilterHashTableFree(ci->hashTable); + VIR_FREE(ci); +} + +virNWFilterVarCombIterPtr +virNWFilterVarCombIterCreate(virNWFilterHashTablePtr hash, + char * const *vars, unsigned int nVars) +{ + virNWFilterVarCombIterPtr res; + virNWFilterVarValuePtr value, orig; + unsigned int i; + const char *val; + + if (VIR_ALLOC_VAR(res, virNWFilterVarCombEntry, nVars)< 0) { + virReportOOMError(); + return NULL; + } + + res->hashTable = virNWFilterHashTableCreate(nVars); + res->nEntries = nVars; + res->orig = hash; + + for (i = 0; i< nVars; i++) { + orig = virHashLookup(hash->hashTable, vars[i]); + if (orig == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + vars[i]); + goto err_exit; + } + + res->entry[i].key = vars[i]; + res->entry[i].cardinality = virNWFilterVarValueGetCardinality(orig); + + val = virNWFilterVarValueGetNthValue(orig, 0); + if (!val) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get 0.th value of " + "variable '%s'"), + res->entry[i].key); + goto err_exit; + } + + value = virNWFilterVarValueCreateSimple(val, true); + if (!value) { + virReportOOMError(); + goto err_exit; + } + + if (virNWFilterHashTablePut(res->hashTable, vars[i], value, false)) { + virReportOOMError(); + goto err_exit; + } + } + + return res; + +err_exit: + virNWFilterVarCombIterFree(res); + return NULL; +} + +virNWFilterHashTablePtr +virNWFilterVarCombIterFirst(virNWFilterVarCombIterPtr ci) +{ + return ci->hashTable; +} + +virNWFilterHashTablePtr +virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr ci, int *error) +{ + unsigned int i; + virNWFilterVarValuePtr value; + bool done = false; + bool end = (ci->nEntries == 0) || + (ci->nEntries == 1&& ci->entry[0].cardinality == 1); + const char *val; + + *error = 0; + + if (end) + return NULL; + + for (i = 0; i< ci->nEntries; i++) { + if (ci->entry[i].cardinality == 1) + continue; + + ci->entry[i].idx++; + if (ci->entry[i].idx< ci->entry[i].cardinality) { + done = true; + } else { + ci->entry[i].idx = 0; + } + + value = virHashLookup(ci->orig->hashTable, ci->entry[i].key); + if (!value) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + ci->entry[i].key); + *error = 1; + break; + } + + val = virNWFilterVarValueGetNthValue(value, ci->entry[i].idx); + if (!val) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get nth (%u) value of " + "variable '%s'"), + ci->entry[i].idx, ci->entry[i].key); + *error = 1; + break; + } + + value = virNWFilterVarValueCreateSimple(val, true); + if (!value) { + virReportOOMError(); + *error = 1; + break; + } + + if (virNWFilterHashTablePut(ci->hashTable, ci->entry[i].key, + value, false)) { + virReportOOMError(); + *error = 1; + break; + } + + if (done || *error) + break; + } + + if (ci->nEntries == i) + end = true; + + if (end || *error) + return NULL; + + return ci->hashTable; +} + static void hashDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) { Index: libvirt-acl/src/conf/nwfilter_params.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.h +++ libvirt-acl/src/conf/nwfilter_params.h @@ -89,4 +89,29 @@ int virNWFilterHashTablePutAll(virNWFilt # define VALID_VARVALUE \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:"
+typedef struct _virNWFilterVarCombEntry virNWFilterVarCombEntry; +struct _virNWFilterVarCombEntry { + const char *key; + unsigned int idx; + unsigned int cardinality; +}; + +typedef struct _virNWFilterVarCombIter virNWFilterVarCombIter; +typedef virNWFilterVarCombIter *virNWFilterVarCombIterPtr; +struct _virNWFilterVarCombIter { + virNWFilterHashTablePtr hashTable; + virNWFilterHashTablePtr orig; + unsigned int nEntries; + virNWFilterVarCombEntry entry[1]; +}; +virNWFilterVarCombIterPtr virNWFilterVarCombIterCreate( + virNWFilterHashTablePtr hash, + char * const *vars, + unsigned int nvars); + +void virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr combiter); +virNWFilterHashTablePtr virNWFilterVarCombIterFirst(virNWFilterVarCombIterPtr); +virNWFilterHashTablePtr virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr, + int *error); + #endif /* NWFILTER_PARAMS_H */ Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -878,6 +878,10 @@ virNWFilterHashTableFree; virNWFilterHashTablePut; virNWFilterHashTablePutAll; virNWFilterHashTableRemoveEntry; +virNWFilterVarCombIterCreate; +virNWFilterVarCombIterFirst; +virNWFilterVarCombIterFree; +virNWFilterVarCombIterNext; virNWFilterVarValueCreateSimple; virNWFilterVarValueGetSimple;

This patch introduces a parser for parsing lists of values as for example found in the XML here: <parameter name='TEST' value='[10.1.2.3,10.2.3.4, 10.1.1.1]'/> The list of values is then stored in the newly introduced data type virNWFilterVarValue. Adapt the XML schema to be able to handle lists. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- docs/schemas/nwfilter.rng | 29 ++++++---- src/conf/nwfilter_params.c | 123 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 14 deletions(-) Index: libvirt-acl/src/conf/nwfilter_params.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_params.c +++ libvirt-acl/src/conf/nwfilter_params.c @@ -616,9 +618,124 @@ isValidVarValue(const char *value) } static virNWFilterVarValuePtr -virNWFilterParseVarValue(const char *val) +virNWFilterVarValueParseAsArray(const char *val, bool verbose) +{ + unsigned int i, j, k, l; + size_t bytes_to_copy; + virNWFilterVarValuePtr res; + char stopchar; + char *item; + + i = 0; + + while (val[i] && c_isspace(val[i])) + i++; + + /* arrays start with '[' and end with ']' */ + if (val[i] == '[') { + j = strlen(val) - 1; + while (j > i && val[j] && c_isspace(val[j])) + j--; + if (val[j] != ']') { + if (verbose) + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid array syntax")); + return NULL; + } + i++; + j--; + } else { + if (verbose) + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Array does not start with '['")); + return NULL; + } + + if (VIR_ALLOC(res) < 0) { + virReportOOMError(); + return NULL; + } + + res->valType = NWFILTER_VALUE_TYPE_ARRAY; + + while (i <= j) { + while (c_isspace(val[i])) + i++; + if (val[i] == '"' || val[i] == '\'') { + stopchar = val[i]; + i++; + } else { + stopchar = ','; + } + /* i points to first letter in item */ + k = i; + while (k <= j && val[k] != stopchar) + k++; + /* k point to the stopchar or end of value */ + if (k > j) { + /* if end of value was reached test for proper stopchar */ + if ((stopchar == '\'' || stopchar == '"') && + val[k] != stopchar) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Illegal list syntax")); + goto err_exit; + } + } + /* l points to the next char to parse for the next item */ + l = k + 1; + + if (stopchar == ',') { + k--; + /* skip trailing whitespace */ + while (k > i && c_isspace(val[k])) + k--; + } else + k--; + + bytes_to_copy = (k >= i) ? ( k - i + 1) : 0; + + item = strndup(&val[i], bytes_to_copy); + + if (!item) { + virReportOOMError(); + goto err_exit; + } + + if (virNWFilterVarValueAddValue(res, item, false) == false) { + goto err_exit; + } + + i = l; + if (stopchar != ',') { + /* search for comma */ + while (i < j && c_isspace(val[i])) + i++; + if (i <= j && val[i] != ',') { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed list")); + goto err_exit; + } + i++; + } + } + + return res; + +err_exit: + virNWFilterVarValueFree(res); + return NULL; +} + +static virNWFilterVarValuePtr +virNWFilterVarValueParse(const char *val) { - // FIXME: only handling simple values for now, no arrays + virNWFilterVarValuePtr res; + + res = virNWFilterVarValueParseAsArray(val, false); + if (res) + return res; + + return virNWFilterVarValueCreateSimple(val, true); } @@ -645,7 +762,7 @@ virNWFilterParseParamAttributes(xmlNodeP if (nam != NULL && val != NULL) { if (!isValidVarName(nam)) goto skip_entry; - value = virNWFilterParseVarValue(val); + value = virNWFilterVarValueParse(val); if (!value) goto skip_entry; if (virNWFilterHashTablePut(table, nam, value, 1)) { Index: libvirt-acl/docs/schemas/nwfilter.rng =================================================================== --- libvirt-acl.orig/docs/schemas/nwfilter.rng +++ libvirt-acl/docs/schemas/nwfilter.rng @@ -313,14 +313,16 @@ <data type="NCName"/> </attribute> <optional> - <element name="parameter"> - <attribute name="name"> - <ref name="filter-param-name"/> - </attribute> - <attribute name="value"> - <ref name="filter-param-value"/> - </attribute> - </element> + <zeroOrMore> + <element name="parameter"> + <attribute name="name"> + <ref name="filter-param-name"/> + </attribute> + <attribute name="value"> + <ref name="filter-param-value"/> + </attribute> + </element> + </zeroOrMore> </optional> </define> @@ -869,9 +871,14 @@ </define> <define name="filter-param-value"> - <data type="string"> - <param name="pattern">[a-zA-Z0-9_\.:]+</param> - </data> + <choice> + <data type="string"> + <param name="pattern">[a-zA-Z0-9_\.:]+</param> + </data> + <data type="string"> + <param name="pattern">\[[a-zA-Z0-9_\.:,&"' ]*\]</param> + </data> + </choice> </define> <define name='action-type'>

This patch adds a test case for parsing of the list values. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- tests/nwfilterxml2xmlin/attr-value-test.xml | 24 ++++++++++++++++++++++++ tests/nwfilterxml2xmlout/attr-value-test.xml | 19 +++++++++++++++++++ tests/nwfilterxml2xmltest.c | 2 ++ 3 files changed, 45 insertions(+) Index: libvirt-acl/tests/nwfilterxml2xmlin/attr-value-test.xml =================================================================== --- /dev/null +++ libvirt-acl/tests/nwfilterxml2xmlin/attr-value-test.xml @@ -0,0 +1,24 @@ +<filter name='testcase'> + <uuid>83011800-f663-96d6-8841-fd836b4318c6</uuid> + <filterref filter='clean-traffic'> + <parameter name='a' value='[1.2.3.4, 10.1.2.3 , 10.3.3.3]'/> + <parameter name='b' value='[1.2.3.4]'/> + <parameter name='c' value='[a," b "]'/> + <parameter name='d' value='[]'/> + <parameter name='e' value='[a,b,c]'/> + </filterref> + <rule action='accept' direction='out'> + <mac srcmacaddr='1:2:3:4:5:6' srcmacmask='ff:ff:ff:ff:ff:ff' + protocolid='arp'/> + </rule> + <rule action='accept' direction='out'> + <tcp srcmacaddr='1:2:3:4:5:6' + dstipaddr='10.1.2.3' dstipmask='255.255.255.255' + dscp='2'/> + </rule> + <rule action='accept' direction='out'> + <udp-ipv6 srcmacaddr='1:2:3:4:5:6' + dstipaddr='a:b:c::d:e:f' dstipmask='128' + dscp='2'/> + </rule> +</filter> Index: libvirt-acl/tests/nwfilterxml2xmlout/attr-value-test.xml =================================================================== --- /dev/null +++ libvirt-acl/tests/nwfilterxml2xmlout/attr-value-test.xml @@ -0,0 +1,19 @@ +<filter name='testcase' chain='root'> + <uuid>83011800-f663-96d6-8841-fd836b4318c6</uuid> + <filterref filter='clean-traffic'> + <parameter name='b' value='[1.2.3.4]'/> + <parameter name='e' value='[a, b, c]'/> + <parameter name='d' value='[]'/> + <parameter name='c' value='[a, ' b ']'/> + <parameter name='a' value='[1.2.3.4, 10.1.2.3, 10.3.3.3]'/> + </filterref> + <rule action='accept' direction='out' priority='500'> + <mac srcmacaddr='01:02:03:04:05:06' srcmacmask='ff:ff:ff:ff:ff:ff' protocolid='arp'/> + </rule> + <rule action='accept' direction='out' priority='500'> + <tcp srcmacaddr='01:02:03:04:05:06' dstipaddr='10.1.2.3' dstipmask='32' dscp='2'/> + </rule> + <rule action='accept' direction='out' priority='500'> + <udp-ipv6 srcmacaddr='01:02:03:04:05:06' dstipaddr='a:b:c::d:e:f' dstipmask='128' dscp='2'/> + </rule> +</filter> Index: libvirt-acl/tests/nwfilterxml2xmltest.c =================================================================== --- libvirt-acl.orig/tests/nwfilterxml2xmltest.c +++ libvirt-acl/tests/nwfilterxml2xmltest.c @@ -150,6 +150,8 @@ mymain(void) DO_TEST("chain_prefixtest1", true); /* derived from arp-test */ + DO_TEST("attr-value-test", false); + return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); }
participants (1)
-
Stefan Berger