1 /*
2  * nwfilter_params.c: parsing and data maintenance of filter parameters
3  *
4  * Copyright (C) 2011-2014 Red Hat, Inc.
5  * Copyright (C) 2010 IBM Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "internal.h"
25 
26 #include "viralloc.h"
27 #include "virerror.h"
28 #include "datatypes.h"
29 #include "nwfilter_params.h"
30 #include "domain_conf.h"
31 #include "virlog.h"
32 #include "virstring.h"
33 
34 #define VIR_FROM_THIS VIR_FROM_NWFILTER
35 
36 VIR_LOG_INIT("conf.nwfilter_params");
37 
38 static bool isValidVarValue(const char *value);
39 static void virNWFilterVarAccessSetIntIterId(virNWFilterVarAccess *,
40                                              unsigned int);
41 static unsigned int virNWFilterVarAccessGetIntIterId(const virNWFilterVarAccess *);
42 
43 void
virNWFilterVarValueFree(virNWFilterVarValue * val)44 virNWFilterVarValueFree(virNWFilterVarValue *val)
45 {
46     size_t i;
47 
48     if (!val)
49         return;
50 
51     switch (val->valType) {
52     case NWFILTER_VALUE_TYPE_SIMPLE:
53         g_free(val->u.simple.value);
54         break;
55     case NWFILTER_VALUE_TYPE_ARRAY:
56         for (i = 0; i < val->u.array.nValues; i++)
57             g_free(val->u.array.values[i]);
58         g_free(val->u.array.values);
59         break;
60     case NWFILTER_VALUE_TYPE_LAST:
61         break;
62     }
63     g_free(val);
64 }
65 
66 virNWFilterVarValue *
virNWFilterVarValueCopy(const virNWFilterVarValue * val)67 virNWFilterVarValueCopy(const virNWFilterVarValue *val)
68 {
69     virNWFilterVarValue *res;
70     size_t i;
71     char *str;
72 
73     res = g_new0(virNWFilterVarValue, 1);
74     res->valType = val->valType;
75 
76     switch (res->valType) {
77     case NWFILTER_VALUE_TYPE_SIMPLE:
78         res->u.simple.value = g_strdup(val->u.simple.value);
79         break;
80     case NWFILTER_VALUE_TYPE_ARRAY:
81         res->u.array.values = g_new0(char *, val->u.array.nValues);
82         res->u.array.nValues = val->u.array.nValues;
83         for (i = 0; i < val->u.array.nValues; i++) {
84             str = g_strdup(val->u.array.values[i]);
85             res->u.array.values[i] = str;
86         }
87         break;
88     case NWFILTER_VALUE_TYPE_LAST:
89         break;
90     }
91 
92     return res;
93 }
94 
95 virNWFilterVarValue *
virNWFilterVarValueCreateSimple(char * value)96 virNWFilterVarValueCreateSimple(char *value)
97 {
98     virNWFilterVarValue *val;
99 
100     if (!isValidVarValue(value)) {
101         virReportError(VIR_ERR_INVALID_ARG, "%s",
102                        _("Variable value contains invalid character"));
103         return NULL;
104     }
105 
106     val = g_new0(virNWFilterVarValue, 1);
107 
108     val->valType = NWFILTER_VALUE_TYPE_SIMPLE;
109     val->u.simple.value = value;
110 
111     return val;
112 }
113 
114 virNWFilterVarValue *
virNWFilterVarValueCreateSimpleCopyValue(const char * value)115 virNWFilterVarValueCreateSimpleCopyValue(const char *value)
116 {
117     char *val;
118     virNWFilterVarValue *ret;
119 
120     val = g_strdup(value);
121     ret = virNWFilterVarValueCreateSimple(val);
122     if (!ret)
123         VIR_FREE(val);
124     return ret;
125 }
126 
127 const char *
virNWFilterVarValueGetSimple(const virNWFilterVarValue * val)128 virNWFilterVarValueGetSimple(const virNWFilterVarValue *val)
129 {
130     if (val->valType == NWFILTER_VALUE_TYPE_SIMPLE)
131         return val->u.simple.value;
132     return NULL;
133 }
134 
135 const char *
virNWFilterVarValueGetNthValue(const virNWFilterVarValue * val,unsigned int idx)136 virNWFilterVarValueGetNthValue(const virNWFilterVarValue* val, unsigned int idx)
137 {
138     const char *res = NULL;
139 
140     if (!val)
141         return NULL;
142 
143     switch (val->valType) {
144     case NWFILTER_VALUE_TYPE_SIMPLE:
145         if (idx == 0)
146             res = val->u.simple.value;
147         break;
148     case NWFILTER_VALUE_TYPE_ARRAY:
149         if (idx < val->u.array.nValues)
150             res = val->u.array.values[idx];
151         break;
152     case NWFILTER_VALUE_TYPE_LAST:
153         break;
154     }
155 
156     return res;
157 }
158 
159 unsigned int
virNWFilterVarValueGetCardinality(const virNWFilterVarValue * val)160 virNWFilterVarValueGetCardinality(const virNWFilterVarValue *val)
161 {
162     switch (val->valType) {
163     case NWFILTER_VALUE_TYPE_SIMPLE:
164         return 1;
165     case NWFILTER_VALUE_TYPE_ARRAY:
166         return val->u.array.nValues;
167     case NWFILTER_VALUE_TYPE_LAST:
168         return 0;
169     }
170     return 0;
171 }
172 
173 bool
virNWFilterVarValueEqual(const virNWFilterVarValue * a,const virNWFilterVarValue * b)174 virNWFilterVarValueEqual(const virNWFilterVarValue *a,
175                          const virNWFilterVarValue *b)
176 {
177     unsigned int card;
178     size_t i, j;
179     const char *s;
180 
181     if (!a || !b)
182         return false;
183 
184     card = virNWFilterVarValueGetCardinality(a);
185     if (card != virNWFilterVarValueGetCardinality(b))
186         return false;
187 
188     /* brute force O(n^2) comparison */
189     for (i = 0; i < card; i++) {
190         bool eq = false;
191 
192         s = virNWFilterVarValueGetNthValue(a, i);
193         for (j = 0; j < card; j++) {
194             if (STREQ_NULLABLE(s, virNWFilterVarValueGetNthValue(b, j))) {
195                  eq = true;
196                  break;
197             }
198         }
199         if (!eq)
200             return false;
201     }
202     return true;
203 }
204 
205 int
virNWFilterVarValueAddValue(virNWFilterVarValue * val,char * value)206 virNWFilterVarValueAddValue(virNWFilterVarValue *val, char *value)
207 {
208     char *tmp;
209     int rc = -1;
210 
211     switch (val->valType) {
212     case NWFILTER_VALUE_TYPE_SIMPLE:
213         /* switch to array */
214         tmp = val->u.simple.value;
215         val->u.array.values = g_new0(char *, 2);
216         val->valType = NWFILTER_VALUE_TYPE_ARRAY;
217         val->u.array.nValues = 2;
218         val->u.array.values[0] = tmp;
219         val->u.array.values[1] = value;
220         rc  = 0;
221         break;
222 
223     case NWFILTER_VALUE_TYPE_ARRAY:
224         VIR_EXPAND_N(val->u.array.values, val->u.array.nValues, 1);
225         val->u.array.values[val->u.array.nValues - 1] = value;
226         rc = 0;
227         break;
228 
229     case NWFILTER_VALUE_TYPE_LAST:
230         break;
231     }
232 
233     return rc;
234 }
235 
236 
237 int
virNWFilterVarValueAddValueCopy(virNWFilterVarValue * val,const char * value)238 virNWFilterVarValueAddValueCopy(virNWFilterVarValue *val, const char *value)
239 {
240     char *valdup;
241     valdup = g_strdup(value);
242     if (virNWFilterVarValueAddValue(val, valdup) < 0) {
243         VIR_FREE(valdup);
244         return -1;
245     }
246     return 0;
247 }
248 
249 
250 static int
virNWFilterVarValueDelNthValue(virNWFilterVarValue * val,unsigned int pos)251 virNWFilterVarValueDelNthValue(virNWFilterVarValue *val, unsigned int pos)
252 {
253     switch (val->valType) {
254     case NWFILTER_VALUE_TYPE_SIMPLE:
255         return -1;
256 
257     case NWFILTER_VALUE_TYPE_ARRAY:
258         if (pos < val->u.array.nValues) {
259             VIR_FREE(val->u.array.values[pos]);
260             VIR_DELETE_ELEMENT(val->u.array.values, pos, val->u.array.nValues);
261             return 0;
262         }
263         break;
264 
265     case NWFILTER_VALUE_TYPE_LAST:
266         break;
267     }
268 
269     return -1;
270 }
271 
272 int
virNWFilterVarValueDelValue(virNWFilterVarValue * val,const char * value)273 virNWFilterVarValueDelValue(virNWFilterVarValue *val, const char *value)
274 {
275     size_t i;
276 
277     switch (val->valType) {
278     case NWFILTER_VALUE_TYPE_SIMPLE:
279         return -1;
280 
281     case NWFILTER_VALUE_TYPE_ARRAY:
282         for (i = 0; i < val->u.array.nValues; i++)
283             if (STREQ(value, val->u.array.values[i]))
284                 return virNWFilterVarValueDelNthValue(val, i);
285         break;
286 
287     case NWFILTER_VALUE_TYPE_LAST:
288         break;
289     }
290 
291     return -1;
292 }
293 
294 void
virNWFilterVarCombIterFree(virNWFilterVarCombIter * ci)295 virNWFilterVarCombIterFree(virNWFilterVarCombIter *ci)
296 {
297     size_t i;
298 
299     if (!ci)
300         return;
301 
302     for (i = 0; i < ci->nIter; i++)
303         g_free(ci->iter[i].varNames);
304 
305     g_free(ci->iter);
306 
307     g_free(ci);
308 }
309 
310 static int
virNWFilterVarCombIterGetIndexByIterId(virNWFilterVarCombIter * ci,unsigned int iterId)311 virNWFilterVarCombIterGetIndexByIterId(virNWFilterVarCombIter *ci,
312                                        unsigned int iterId)
313 {
314     size_t i;
315 
316     for (i = 0; i < ci->nIter; i++)
317         if (ci->iter[i].iterId == iterId)
318             return i;
319 
320     return -1;
321 }
322 
323 static void
virNWFilterVarCombIterEntryInit(virNWFilterVarCombIterEntry * cie,unsigned int iterId)324 virNWFilterVarCombIterEntryInit(virNWFilterVarCombIterEntry *cie,
325                                 unsigned int iterId)
326 {
327     memset(cie, 0, sizeof(*cie));
328     cie->iterId = iterId;
329 }
330 
331 static int
virNWFilterVarCombIterAddVariable(virNWFilterVarCombIterEntry * cie,GHashTable * hash,const virNWFilterVarAccess * varAccess)332 virNWFilterVarCombIterAddVariable(virNWFilterVarCombIterEntry *cie,
333                                   GHashTable *hash,
334                                   const virNWFilterVarAccess *varAccess)
335 {
336     virNWFilterVarValue *varValue;
337     unsigned int maxValue = 0, minValue = 0;
338     const char *varName = virNWFilterVarAccessGetVarName(varAccess);
339 
340     varValue = virHashLookup(hash, varName);
341     if (varValue == NULL) {
342         virReportError(VIR_ERR_INTERNAL_ERROR,
343                        _("Could not find value for variable '%s'"),
344                        varName);
345         return -1;
346     }
347 
348     switch (virNWFilterVarAccessGetType(varAccess)) {
349     case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
350         maxValue = virNWFilterVarAccessGetIndex(varAccess);
351         minValue = maxValue;
352         break;
353     case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
354         maxValue = virNWFilterVarValueGetCardinality(varValue) - 1;
355         minValue = 0;
356         break;
357     case VIR_NWFILTER_VAR_ACCESS_LAST:
358         return -1;
359     }
360 
361     if (cie->nVarNames == 0) {
362         cie->maxValue = maxValue;
363         cie->minValue = minValue;
364         cie->curValue = minValue;
365     } else {
366         if (cie->maxValue != maxValue) {
367             virReportError(VIR_ERR_INTERNAL_ERROR,
368                            _("Cardinality of list items must be "
369                              "the same for processing them in "
370                              "parallel"));
371             return -1;
372         }
373     }
374 
375     VIR_EXPAND_N(cie->varNames, cie->nVarNames, 1);
376     cie->varNames[cie->nVarNames - 1] = varName;
377 
378     return 0;
379 }
380 
381 /*
382  * Test whether the iterator entry points to a distinguished set of entries
383  * that have not been seen before at one of the previous iterations.
384  *
385  * The point of this function is to eliminate duplicates.
386  * Example with two lists:
387  *
388  * list1 = [1,2,1]
389  * list2 = [1,3,1]
390  *
391  * The 1st iteration would take the 1st items of each list -> 1,1
392  * The 2nd iteration would take the 2nd items of each list -> 2,3
393  * The 3rd iteration would take the 3rd items of each list -> 1,1 but
394  * skip them since this pair has already been encountered in the 1st iteration
395  */
396 static bool
virNWFilterVarCombIterEntryAreUniqueEntries(virNWFilterVarCombIterEntry * cie,GHashTable * hash)397 virNWFilterVarCombIterEntryAreUniqueEntries(virNWFilterVarCombIterEntry *cie,
398                                             GHashTable *hash)
399 {
400     size_t i, j;
401     virNWFilterVarValue *varValue;
402     virNWFilterVarValue *tmp;
403     const char *value;
404 
405     varValue = virHashLookup(hash, cie->varNames[0]);
406     if (!varValue) {
407         /* caller's error */
408         VIR_ERROR(_("hash lookup resulted in NULL pointer"));
409         return true;
410     }
411 
412     value = virNWFilterVarValueGetNthValue(varValue, cie->curValue);
413     if (!value) {
414         VIR_ERROR(_("Lookup of value at index %u resulted in a NULL "
415                   "pointer"), cie->curValue);
416         return true;
417     }
418 
419     for (i = 0; i < cie->curValue; i++) {
420         if (STREQ(value, virNWFilterVarValueGetNthValue(varValue, i))) {
421             bool isSame = true;
422             for (j = 1; j < cie->nVarNames; j++) {
423                 tmp = virHashLookup(hash, cie->varNames[j]);
424                 if (!tmp) {
425                     /* should never occur to step on a NULL here */
426                     return true;
427                 }
428                 if (STRNEQ(virNWFilterVarValueGetNthValue(tmp, cie->curValue),
429                            virNWFilterVarValueGetNthValue(tmp, i))) {
430                     isSame = false;
431                     break;
432                 }
433             }
434             if (isSame)
435                 return false;
436         }
437     }
438 
439     return true;
440 }
441 
442 /*
443  * Create an iterator over the contents of the given variables. All variables
444  * must have entries in the hash table.
445  * The iterator that is created processes all given variables in parallel,
446  * meaning it will access $ITEM1[0] and $ITEM2[0] then $ITEM1[1] and $ITEM2[1]
447  * up to $ITEM1[n] and $ITEM2[n]. For this to work, the cardinality of all
448  * processed lists must be the same.
449  * The notation $ITEM1 and $ITEM2 (in one rule) therefore will always have to
450  * process the items in parallel. This will be an implicit notation for
451  * $ITEM1[@0] and $ITEM2[@0] to 'lock' the two together. Future notations of
452  * $ITEM1[@1] and $ITEM2[@2] will make them be processed independently,
453  * which then would cause all combinations of the items of the two lists to
454  * be created.
455  */
456 virNWFilterVarCombIter *
virNWFilterVarCombIterCreate(GHashTable * hash,virNWFilterVarAccess ** varAccess,size_t nVarAccess)457 virNWFilterVarCombIterCreate(GHashTable *hash,
458                              virNWFilterVarAccess **varAccess,
459                              size_t nVarAccess)
460 {
461     virNWFilterVarCombIter *res;
462     size_t i;
463     unsigned int iterId;
464     int iterIndex = -1;
465     unsigned int nextIntIterId = VIR_NWFILTER_MAX_ITERID + 1;
466 
467     res = g_new0(virNWFilterVarCombIter, 1);
468     res->iter = g_new0(virNWFilterVarCombIterEntry, nVarAccess + 1);
469 
470     res->hashTable = hash;
471 
472     /* create the default iterator to support @0 */
473     iterId = 0;
474 
475     res->nIter = 1;
476     virNWFilterVarCombIterEntryInit(&res->iter[0], iterId);
477 
478     for (i = 0; i < nVarAccess; i++) {
479         switch (virNWFilterVarAccessGetType(varAccess[i])) {
480         case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
481             iterId = virNWFilterVarAccessGetIterId(varAccess[i]);
482             iterIndex = virNWFilterVarCombIterGetIndexByIterId(res, iterId);
483             if (iterIndex < 0) {
484                 iterIndex = res->nIter;
485                 virNWFilterVarCombIterEntryInit(&res->iter[iterIndex], iterId);
486                 res->nIter++;
487             }
488             break;
489         case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
490             iterIndex = res->nIter;
491             virNWFilterVarAccessSetIntIterId(varAccess[i], nextIntIterId);
492             virNWFilterVarCombIterEntryInit(&res->iter[iterIndex],
493                                             nextIntIterId);
494             nextIntIterId++;
495             res->nIter++;
496             break;
497         case VIR_NWFILTER_VAR_ACCESS_LAST:
498             goto err_exit;
499         }
500 
501         if (virNWFilterVarCombIterAddVariable(&res->iter[iterIndex],
502                                               hash, varAccess[i]) < 0)
503             goto err_exit;
504     }
505 
506     return res;
507 
508  err_exit:
509     virNWFilterVarCombIterFree(res);
510     return NULL;
511 }
512 
513 virNWFilterVarCombIter *
virNWFilterVarCombIterNext(virNWFilterVarCombIter * ci)514 virNWFilterVarCombIterNext(virNWFilterVarCombIter *ci)
515 {
516     size_t i;
517 
518     for (i = 0; i < ci->nIter; i++) {
519  next:
520         ci->iter[i].curValue++;
521         if (ci->iter[i].curValue <= ci->iter[i].maxValue) {
522             if (!virNWFilterVarCombIterEntryAreUniqueEntries(
523                                               &ci->iter[i], ci->hashTable))
524                 goto next;
525             break;
526         } else {
527             ci->iter[i].curValue = ci->iter[i].minValue;
528         }
529     }
530 
531     if (ci->nIter == i)
532         return NULL;
533 
534     return ci;
535 }
536 
537 const char *
virNWFilterVarCombIterGetVarValue(virNWFilterVarCombIter * ci,const virNWFilterVarAccess * vap)538 virNWFilterVarCombIterGetVarValue(virNWFilterVarCombIter *ci,
539                                   const virNWFilterVarAccess *vap)
540 {
541     size_t i;
542     unsigned int iterId;
543     bool found = false;
544     const char *res = NULL;
545     virNWFilterVarValue *value;
546     int iterIndex = -1;
547     const char *varName = virNWFilterVarAccessGetVarName(vap);
548 
549     switch (virNWFilterVarAccessGetType(vap)) {
550     case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
551         iterId = virNWFilterVarAccessGetIterId(vap);
552         iterIndex = virNWFilterVarCombIterGetIndexByIterId(ci, iterId);
553         if (iterIndex < 0) {
554             virReportError(VIR_ERR_INTERNAL_ERROR,
555                            _("Could not get iterator index for "
556                              "iterator ID %u"), iterId);
557             return NULL;
558         }
559         break;
560     case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
561         iterId = virNWFilterVarAccessGetIntIterId(vap);
562         iterIndex = virNWFilterVarCombIterGetIndexByIterId(ci, iterId);
563         if (iterIndex < 0) {
564             virReportError(VIR_ERR_INTERNAL_ERROR,
565                            _("Could not get iterator index for "
566                              "(internal) iterator ID %u"), iterId);
567             return NULL;
568         }
569         break;
570     case VIR_NWFILTER_VAR_ACCESS_LAST:
571         return NULL;
572     }
573 
574     for (i = 0; i < ci->iter[iterIndex].nVarNames; i++) {
575         if (STREQ(ci->iter[iterIndex].varNames[i], varName)) {
576             found = true;
577             break;
578         }
579     }
580 
581     if (!found) {
582         virReportError(VIR_ERR_INTERNAL_ERROR,
583                        _("Could not find variable '%s' in iterator"),
584                        varName);
585         return NULL;
586     }
587 
588     value = virHashLookup(ci->hashTable, varName);
589     if (!value) {
590         virReportError(VIR_ERR_INTERNAL_ERROR,
591                        _("Could not find value for variable '%s'"),
592                        varName);
593         return NULL;
594     }
595 
596     res = virNWFilterVarValueGetNthValue(value, ci->iter[iterIndex].curValue);
597     if (!res) {
598         virReportError(VIR_ERR_INTERNAL_ERROR,
599                        _("Could not get nth (%u) value of "
600                          "variable '%s'"),
601                        ci->iter[iterIndex].curValue, varName);
602         return NULL;
603     }
604 
605     return res;
606 }
607 
608 void
virNWFilterVarValueHashFree(void * payload)609 virNWFilterVarValueHashFree(void *payload)
610 {
611     virNWFilterVarValueFree(payload);
612 }
613 
614 
615 struct addToTableStruct {
616     GHashTable *target;
617     int errOccurred;
618 };
619 
620 
621 static int
addToTable(void * payload,const char * name,void * data)622 addToTable(void *payload, const char *name, void *data)
623 {
624     struct addToTableStruct *atts = (struct addToTableStruct *)data;
625     virNWFilterVarValue *val;
626 
627     if (atts->errOccurred)
628         return 0;
629 
630     val = virNWFilterVarValueCopy((virNWFilterVarValue *)payload);
631     if (!val) {
632         atts->errOccurred = 1;
633         return 0;
634     }
635 
636     if (virHashUpdateEntry(atts->target, (const char *)name, val) < 0) {
637         atts->errOccurred = 1;
638         virNWFilterVarValueFree(val);
639     }
640 
641     return 0;
642 }
643 
644 
645 int
virNWFilterHashTablePutAll(GHashTable * src,GHashTable * dest)646 virNWFilterHashTablePutAll(GHashTable *src,
647                            GHashTable *dest)
648 {
649     struct addToTableStruct atts = {
650         .target = dest,
651         .errOccurred = 0,
652     };
653 
654     virHashForEach(src, addToTable, &atts);
655     if (atts.errOccurred)
656         return -1;
657 
658     return 0;
659 }
660 
661 /* The general purpose function virNWFilterVarValueEqual returns a
662  * bool, but the comparison callback for virHashEqual (called below)
663  * needs to return an int of 0 for == and non-0 for !=
664  */
665 static int
virNWFilterVarValueCompare(const void * a,const void * b)666 virNWFilterVarValueCompare(const void *a, const void *b)
667 {
668     return virNWFilterVarValueEqual((const virNWFilterVarValue *) a,
669                                     (const virNWFilterVarValue *) b) ? 0 : 1;
670 }
671 
672 bool
virNWFilterHashTableEqual(GHashTable * a,GHashTable * b)673 virNWFilterHashTableEqual(GHashTable *a,
674                           GHashTable *b)
675 {
676     return virHashEqual(a, b, virNWFilterVarValueCompare);
677 }
678 
679 static bool
isValidVarName(const char * var)680 isValidVarName(const char *var)
681 {
682     return var[strspn(var, VALID_VARNAME)] == 0;
683 }
684 
685 
686 static bool
isValidVarValue(const char * value)687 isValidVarValue(const char *value)
688 {
689     return (value[strspn(value, VALID_VARVALUE)] == 0) && (strlen(value) != 0);
690 }
691 
692 static virNWFilterVarValue *
virNWFilterParseVarValue(const char * val)693 virNWFilterParseVarValue(const char *val)
694 {
695     return virNWFilterVarValueCreateSimpleCopyValue(val);
696 }
697 
698 GHashTable *
virNWFilterParseParamAttributes(xmlNodePtr cur)699 virNWFilterParseParamAttributes(xmlNodePtr cur)
700 {
701     g_autoptr(GHashTable) table = virHashNew(virNWFilterVarValueHashFree);
702 
703     for (cur = xmlFirstElementChild(cur); cur != NULL;
704          cur = xmlNextElementSibling(cur)) {
705         if (virXMLNodeNameEqual(cur, "parameter")) {
706             g_autofree char *nam = virXMLPropString(cur, "name");
707             g_autofree char *val = virXMLPropString(cur, "value");
708             g_autoptr(virNWFilterVarValue) value = NULL;
709 
710             if (nam == NULL || !isValidVarName(nam) ||
711                 val == NULL || !isValidVarValue(val)) {
712                 continue;
713             }
714 
715             if ((value = virHashLookup(table, nam))) {
716                 /* add value to existing value -> list */
717                 if (virNWFilterVarValueAddValue(g_steal_pointer(&value), val) < 0)
718                     return NULL;
719                 val = NULL;
720             } else if ((value = virNWFilterParseVarValue(val))) {
721                 if (virHashUpdateEntry(table, nam, value) < 0)
722                     return NULL;
723             }
724             value = NULL;
725         }
726     }
727 
728     return g_steal_pointer(&table);
729 }
730 
731 
732 int
virNWFilterFormatParamAttributes(virBuffer * buf,GHashTable * table,const char * filterref)733 virNWFilterFormatParamAttributes(virBuffer *buf,
734                                  GHashTable *table,
735                                  const char *filterref)
736 {
737     virHashKeyValuePair *items;
738     size_t i, j;
739     int card, numKeys;
740 
741     numKeys = virHashSize(table);
742 
743     if (numKeys < 0) {
744         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
745                        _("missing filter parameter table"));
746         return -1;
747     }
748 
749     items = virHashGetItems(table, NULL, true);
750     if (!items)
751         return -1;
752 
753     virBufferAsprintf(buf, "<filterref filter='%s'", filterref);
754     if (numKeys) {
755         virBufferAddLit(buf, ">\n");
756         virBufferAdjustIndent(buf, 2);
757         for (i = 0; i < numKeys; i++) {
758             const virNWFilterVarValue *value = items[i].value;
759 
760             card = virNWFilterVarValueGetCardinality(value);
761 
762             for (j = 0; j < card; j++)
763                 virBufferAsprintf(buf,
764                                   "<parameter name='%s' value='%s'/>\n",
765                                   (const char *)items[i].key,
766                                   virNWFilterVarValueGetNthValue(value, j));
767 
768         }
769         virBufferAdjustIndent(buf, -2);
770         virBufferAddLit(buf, "</filterref>\n");
771     } else {
772         virBufferAddLit(buf, "/>\n");
773     }
774 
775     VIR_FREE(items);
776 
777     return 0;
778 }
779 
780 void
virNWFilterVarAccessFree(virNWFilterVarAccess * varAccess)781 virNWFilterVarAccessFree(virNWFilterVarAccess *varAccess)
782 {
783     if (!varAccess)
784         return;
785 
786     g_free(varAccess->varName);
787     g_free(varAccess);
788 }
789 
790 bool
virNWFilterVarAccessEqual(const virNWFilterVarAccess * a,const virNWFilterVarAccess * b)791 virNWFilterVarAccessEqual(const virNWFilterVarAccess *a,
792                           const virNWFilterVarAccess *b)
793 {
794     if (a->accessType != b->accessType)
795         return false;
796 
797     if (STRNEQ(a->varName, b->varName))
798         return false;
799 
800     switch (a->accessType) {
801     case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
802         return (a->u.index.idx == b->u.index.idx &&
803                 a->u.index.intIterId == b->u.index.intIterId);
804     case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
805         return a->u.iterId == b->u.iterId;
806     case VIR_NWFILTER_VAR_ACCESS_LAST:
807         break;
808     }
809     return false;
810 }
811 
812 /*
813  * Parse a variable access like
814  * IP, IP[@2], IP[3]
815  */
816 virNWFilterVarAccess *
virNWFilterVarAccessParse(const char * varAccess)817 virNWFilterVarAccessParse(const char *varAccess)
818 {
819     size_t idx, varNameLen;
820     virNWFilterVarAccess *dest;
821     const char *input = varAccess;
822 
823     dest = g_new0(virNWFilterVarAccess, 1);
824 
825     idx = strspn(input, VALID_VARNAME);
826 
827     if (input[idx] == '\0') {
828         /* in the form 'IP', which is equivalent to IP[@0] */
829         dest->varName = g_strndup(input, idx);
830         dest->accessType = VIR_NWFILTER_VAR_ACCESS_ITERATOR;
831         dest->u.iterId = 0;
832         return dest;
833     }
834 
835     if (input[idx] == '[') {
836         char *end_ptr;
837         unsigned int result;
838         bool parseError = false;
839 
840         varNameLen = idx;
841 
842         dest->varName = g_strndup(input, varNameLen);
843 
844         input += idx + 1;
845         virSkipSpaces(&input);
846 
847         if (*input == '@') {
848             /* in the form 'IP[@<number>] -> iterator */
849             dest->accessType = VIR_NWFILTER_VAR_ACCESS_ITERATOR;
850             input++;
851         } else {
852             /* in the form 'IP[<number>] -> element */
853             dest->accessType = VIR_NWFILTER_VAR_ACCESS_ELEMENT;
854         }
855 
856         if (virStrToLong_ui(input, &end_ptr, 10, &result) < 0)
857             parseError = true;
858         if (!parseError) {
859             input = end_ptr;
860             virSkipSpaces(&input);
861             if (*input != ']')
862                 parseError = true;
863         }
864         if (parseError) {
865             if (dest->accessType == VIR_NWFILTER_VAR_ACCESS_ELEMENT)
866                 virReportError(VIR_ERR_INVALID_ARG, "%s",
867                                _("Malformatted array index"));
868             else
869                 virReportError(VIR_ERR_INVALID_ARG, "%s",
870                                _("Malformatted iterator id"));
871             goto err_exit;
872         }
873 
874         switch (dest->accessType) {
875         case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
876             dest->u.index.idx = result;
877             dest->u.index.intIterId = ~0;
878             break;
879         case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
880             if (result > VIR_NWFILTER_MAX_ITERID) {
881                 virReportError(VIR_ERR_INVALID_ARG,
882                                _("Iterator ID exceeds maximum ID "
883                                  "of %u"), VIR_NWFILTER_MAX_ITERID);
884                 goto err_exit;
885             }
886             dest->u.iterId = result;
887             break;
888         case VIR_NWFILTER_VAR_ACCESS_LAST:
889             goto err_exit;
890         }
891 
892         return dest;
893     } else {
894         virReportError(VIR_ERR_INVALID_ARG, "%s",
895                        _("Malformatted variable"));
896     }
897 
898  err_exit:
899     virNWFilterVarAccessFree(dest);
900 
901     return NULL;
902 }
903 
904 void
virNWFilterVarAccessPrint(virNWFilterVarAccess * vap,virBuffer * buf)905 virNWFilterVarAccessPrint(virNWFilterVarAccess *vap, virBuffer *buf)
906 {
907     virBufferAdd(buf, vap->varName, -1);
908     switch (vap->accessType) {
909     case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
910         virBufferAsprintf(buf, "[%u]", vap->u.index.idx);
911         break;
912     case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
913         if (vap->u.iterId != 0)
914             virBufferAsprintf(buf, "[@%u]", vap->u.iterId);
915         break;
916     case VIR_NWFILTER_VAR_ACCESS_LAST:
917         break;
918     }
919 }
920 
921 const char *
virNWFilterVarAccessGetVarName(const virNWFilterVarAccess * vap)922 virNWFilterVarAccessGetVarName(const virNWFilterVarAccess *vap)
923 {
924     return vap->varName;
925 }
926 
927 virNWFilterVarAccessType
virNWFilterVarAccessGetType(const virNWFilterVarAccess * vap)928 virNWFilterVarAccessGetType(const virNWFilterVarAccess *vap)
929 {
930     return vap->accessType;
931 }
932 
933 unsigned int
virNWFilterVarAccessGetIterId(const virNWFilterVarAccess * vap)934 virNWFilterVarAccessGetIterId(const virNWFilterVarAccess *vap)
935 {
936     return vap->u.iterId;
937 }
938 
939 unsigned int
virNWFilterVarAccessGetIndex(const virNWFilterVarAccess * vap)940 virNWFilterVarAccessGetIndex(const virNWFilterVarAccess *vap)
941 {
942     return vap->u.index.idx;
943 }
944 
945 static void
virNWFilterVarAccessSetIntIterId(virNWFilterVarAccess * vap,unsigned int intIterId)946 virNWFilterVarAccessSetIntIterId(virNWFilterVarAccess *vap,
947                                  unsigned int intIterId)
948 {
949     vap->u.index.intIterId = intIterId;
950 }
951 
952 static unsigned int
virNWFilterVarAccessGetIntIterId(const virNWFilterVarAccess * vap)953 virNWFilterVarAccessGetIntIterId(const virNWFilterVarAccess *vap)
954 {
955     return vap->u.index.intIterId;
956 }
957 
958 bool
virNWFilterVarAccessIsAvailable(const virNWFilterVarAccess * varAccess,GHashTable * hash)959 virNWFilterVarAccessIsAvailable(const virNWFilterVarAccess *varAccess,
960                                 GHashTable *hash)
961 {
962     const char *varName = virNWFilterVarAccessGetVarName(varAccess);
963     const char *res;
964     unsigned int idx;
965     virNWFilterVarValue *varValue;
966 
967     varValue = virHashLookup(hash, varName);
968     if (!varValue)
969         return false;
970 
971     switch (virNWFilterVarAccessGetType(varAccess)) {
972     case VIR_NWFILTER_VAR_ACCESS_ELEMENT:
973         idx = virNWFilterVarAccessGetIndex(varAccess);
974         res = virNWFilterVarValueGetNthValue(varValue, idx);
975         if (res == NULL)
976             return false;
977         break;
978     case VIR_NWFILTER_VAR_ACCESS_ITERATOR:
979         break;
980     case VIR_NWFILTER_VAR_ACCESS_LAST:
981         return false;
982     }
983 
984     return true;
985 }
986