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 (internally) 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. v4: - changed iterator to access multiple lists' elements at the same index; previous behavior was to produce all combinations of all the elements in the given lists v2: - pass the iterator all the way to the function that accesses the hash table and provide a function to pick the value of a variable that is reflected by the current state of the iterator Signed-off-by: Stefan Berger --- src/conf/nwfilter_params.c | 205 ++++++++++++++++++++++++++++++ src/conf/nwfilter_params.h | 27 +++ src/libvirt_private.syms | 4 src/nwfilter/nwfilter_ebiptables_driver.c | 81 +++++++---- 4 files changed, 290 insertions(+), 27 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 @@ -192,7 +192,7 @@ static const struct ushort_map l3_protoc static int -printVar(virNWFilterHashTablePtr vars, +printVar(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item, int *done) @@ -200,22 +200,11 @@ printVar(virNWFilterHashTablePtr vars, *done = 0; if ((item->flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) { - 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); + val = virNWFilterVarCombIterGetVarValue(vars, item->var); if (!val) { - virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot get simple value of '%s'"), - item->var); + /* error has been reported */ return 1; } @@ -234,7 +223,7 @@ printVar(virNWFilterHashTablePtr vars, static int -_printDataType(virNWFilterHashTablePtr vars, +_printDataType(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item, bool asHex) @@ -329,7 +318,7 @@ _printDataType(virNWFilterHashTablePtr v static int -printDataType(virNWFilterHashTablePtr vars, +printDataType(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item) { @@ -338,7 +327,7 @@ printDataType(virNWFilterHashTablePtr va static int -printDataTypeAsHex(virNWFilterHashTablePtr vars, +printDataTypeAsHex(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item) { @@ -406,7 +395,7 @@ ebiptablesAddRuleInst(virNWFilterRuleIns static int ebtablesHandleEthHdr(virBufferPtr buf, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, ethHdrDataDefPtr ethHdr, bool reverse) { @@ -884,7 +873,7 @@ iptablesInstCommand(virBufferPtr buf, static int iptablesHandleSrcMacAddr(virBufferPtr buf, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, nwItemDescPtr srcMacAddr, int directionIn, bool *srcmacskipped) @@ -921,7 +910,7 @@ err_exit: static int iptablesHandleIpHdr(virBufferPtr buf, virBufferPtr afterStateMatch, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, ipHdrDataDefPtr ipHdr, int directionIn, bool *skipRule, bool *skipMatch, @@ -1095,7 +1084,7 @@ err_exit: static int iptablesHandlePortData(virBufferPtr buf, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, portDataDefPtr portData, int directionIn) { @@ -1201,7 +1190,7 @@ _iptablesCreateRuleInstance(int directio virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, virNWFilterRuleInstPtr res, const char *match, bool defMatch, const char *accept_target, @@ -1687,7 +1676,7 @@ static int iptablesCreateRuleInstanceStateCtrl(virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, virNWFilterRuleInstPtr res, bool isIPv6) { @@ -1812,7 +1801,7 @@ static int iptablesCreateRuleInstance(virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, virNWFilterRuleInstPtr res, bool isIPv6) { @@ -1937,7 +1926,7 @@ ebtablesCreateRuleInstance(char chainPre virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, virNWFilterRuleInstPtr res, bool reverse) { @@ -2429,7 +2418,7 @@ ebiptablesCreateRuleInstance(virConnectP virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, - virNWFilterHashTablePtr vars, + virNWFilterVarCombIterPtr vars, virNWFilterRuleInstPtr res) { int rc = 0; @@ -2513,6 +2502,44 @@ ebiptablesCreateRuleInstance(virConnectP return rc; } +static int +ebiptablesCreateRuleInstanceIterate( + virConnectPtr conn ATTRIBUTE_UNUSED, + enum virDomainNetType nettype ATTRIBUTE_UNUSED, + virNWFilterDefPtr nwfilter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res) +{ + int rc = 0; + virNWFilterVarCombIterPtr vciter; + + /* 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. + */ + vciter = virNWFilterVarCombIterCreate(vars, rule->vars, rule->nvars); + if (!vciter) + return 1; + + do { + rc = ebiptablesCreateRuleInstance(conn, + nettype, + nwfilter, + rule, + ifname, + vciter, + res); + if (rc) + break; + vciter = virNWFilterVarCombIterNext(vciter); + } while (vciter != NULL); + + virNWFilterVarCombIterFree(vciter); + + return rc; +} static int ebiptablesFreeRuleInstance(void *_inst) @@ -3896,7 +3923,7 @@ virNWFilterTechDriver ebiptables_driver .init = ebiptablesDriverInit, .shutdown = ebiptablesDriverShutdown, - .createRuleInstance = ebiptablesCreateRuleInstance, + .createRuleInstance = ebiptablesCreateRuleInstanceIterate, .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 @@ -291,6 +291,211 @@ virNWFilterVarValueAddValue(virNWFilterV return rc; } +void +virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci) +{ + unsigned int i; + + if (!ci) + return; + + for (i = 0; i < ci->nIter; i++) + VIR_FREE(ci->iter[i].varNames); + + VIR_FREE(ci); +} + +static int +virNWFilterVarCombIterGetIndexByIterId(virNWFilterVarCombIterPtr ci, + unsigned int iterId) +{ + unsigned int i; + + for (i = 0; i < ci->nIter; i++) + if (ci->iter[i].iterId == iterId) + return i; + + return -1; +} + +static void +virNWFilterVarCombIterEntryInit(virNWFilterVarCombIterEntryPtr cie, + unsigned int iterId) +{ + memset(cie, 0, sizeof(*cie)); + cie->iterId = iterId; +} + +static int +virNWFilterVarCombIterAddVariable(virNWFilterVarCombIterEntryPtr cie, + virNWFilterHashTablePtr hash, + const char *varName) +{ + virNWFilterVarValuePtr varValue; + unsigned int cardinality; + + varValue = virHashLookup(hash->hashTable, varName); + if (varValue == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + varName); + return 1; + } + + cardinality = virNWFilterVarValueGetCardinality(varValue); + + if (cie->nVarNames == 0) { + cie->maxValue = cardinality - 1; + } else { + if (cie->maxValue != cardinality - 1) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Cardinality of list items must be " + "the same for processing them in " + "parallel")); + return 1; + } + } + + if (VIR_REALLOC_N(cie->varNames, cie->nVarNames + 1) < 0) { + virReportOOMError(); + return 1; + } + + cie->varNames[cie->nVarNames] = varName; + cie->nVarNames++; + + return 0; +} + +/* + * Create an iterator over the contents of the given variables. All variables + * must have entries in the hash table. + * The iterator that is created processes all given variables in parallel, + * meaning it will access $ITEM1[0] and $ITEM2[0] then $ITEM1[1] and $ITEM2[1] + * upto $ITEM1[n] and $ITEM2[n]. For this to work, the cardinality of all + * processed lists must be the same. + * The notation $ITEM1 and $ITEM2 (in one rule) therefore will always have to + * process the items in parallel. This could be an implicit notiation for + * $ITEM1[@0] and $ITEM2[@0] to 'lock' the two together. Future notations of + * $ITEM1[@1] and $ITEM2[@2] will make them be processed independently, + * which then would cause all combinations of the items of the two lists to + * be created. + */ +virNWFilterVarCombIterPtr +virNWFilterVarCombIterCreate(virNWFilterHashTablePtr hash, + char * const *vars, unsigned int nVars) +{ + virNWFilterVarCombIterPtr res; + unsigned int i, iterId; + int iterIdx; + + if (VIR_ALLOC_VAR(res, virNWFilterVarCombIterEntry, nVars) < 0) { + virReportOOMError(); + return NULL; + } + + res->hashTable = hash; + + /* create the default iterator to support @0 */ + iterId = 0; + + res->nIter = 1; + virNWFilterVarCombIterEntryInit(&res->iter[0], iterId); + + for (i = 0; i < nVars; i++) { + + /* currently always access @0 */ + iterId = 0; + + iterIdx = virNWFilterVarCombIterGetIndexByIterId(res, iterId); + if (iterIdx < 0) { + /* future: create new iterator. for now it's a bug */ + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find iterator with id %u\n"), + iterId); + goto err_exit; + } + + if (virNWFilterVarCombIterAddVariable(&res->iter[iterIdx], + hash, vars[i])) + goto err_exit; + } + + return res; + +err_exit: + virNWFilterVarCombIterFree(res); + return NULL; +} + +virNWFilterVarCombIterPtr +virNWFilterVarCombIterNext(virNWFilterVarCombIterPtr ci) +{ + unsigned int i; + + for (i = 0; i < ci->nIter; i++) { + ci->iter[i].curValue++; + if (ci->iter[i].curValue <= ci->iter[i].maxValue) + break; + else + ci->iter[i].curValue = 0; + } + + if (ci->nIter == i) { + virNWFilterVarCombIterFree(ci); + return NULL; + } + + return ci; +} + +const char * +virNWFilterVarCombIterGetVarValue(virNWFilterVarCombIterPtr ci, + const char *varName) +{ + unsigned int i; + bool found = false; + const char *res = NULL; + virNWFilterVarValuePtr value; + unsigned int iterId; + + /* currently always accessing iter @0 */ + iterId = 0; + + for (i = 0; i < ci->iter[iterId].nVarNames; i++) { + if (STREQ(ci->iter[iterId].varNames[i], varName)) { + found = true; + break; + } + } + + if (!found) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find variable '%s' in iterator"), + varName); + return NULL; + } + + value = virHashLookup(ci->hashTable->hashTable, varName); + if (!value) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find value for variable '%s'"), + varName); + return NULL; + } + + res = virNWFilterVarValueGetNthValue(value, ci->iter[iterId].curValue); + if (!res) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get nth (%u) value of " + "variable '%s'"), + ci->iter[iterId].curValue, varName); + return NULL; + } + + return res; +} + 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 @@ -91,4 +91,31 @@ int virNWFilterHashTablePutAll(virNWFilt # define VALID_VARVALUE \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:" +typedef struct _virNWFilterVarCombIterEntry virNWFilterVarCombIterEntry; +typedef virNWFilterVarCombIterEntry *virNWFilterVarCombIterEntryPtr; +struct _virNWFilterVarCombIterEntry { + unsigned int iterId; + const char **varNames; + unsigned int nVarNames; + unsigned int maxValue; + unsigned int curValue; +}; + +typedef struct _virNWFilterVarCombIter virNWFilterVarCombIter; +typedef virNWFilterVarCombIter *virNWFilterVarCombIterPtr; +struct _virNWFilterVarCombIter { + virNWFilterHashTablePtr hashTable; + virNWFilterVarCombIterEntry iter[1]; + unsigned int nIter; +}; +virNWFilterVarCombIterPtr virNWFilterVarCombIterCreate( + virNWFilterHashTablePtr hash, + char * const *vars, unsigned int nVars); + +void virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci); +virNWFilterVarCombIterPtr virNWFilterVarCombIterNext( + virNWFilterVarCombIterPtr ci); +const char *virNWFilterVarCombIterGetVarValue(virNWFilterVarCombIterPtr ci, + const char *varname); + #endif /* NWFILTER_PARAMS_H */ Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -885,6 +885,10 @@ virNWFilterHashTableFree; virNWFilterHashTablePut; virNWFilterHashTablePutAll; virNWFilterHashTableRemoveEntry; +virNWFilterVarCombIterCreate; +virNWFilterVarCombIterFree; +virNWFilterVarCombIterGetVarValue; +virNWFilterVarCombIterNext; virNWFilterVarValueCreateSimple; virNWFilterVarValueGetSimple; virNWFilterVarValuePrint;