1 /*
2  * nwfilter_conf.c: network filter XML processing
3  *                  (derived from storage_conf.c)
4  *
5  * Copyright (C) 2006-2018 Red Hat, Inc.
6  * Copyright (C) 2006-2008 Daniel P. Berrange
7  *
8  * Copyright (C) 2010-2011 IBM Corporation
9  * Copyright (C) 2010-2011 Stefan Berger
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library.  If not, see
23  * <http://www.gnu.org/licenses/>.
24  */
25 
26 #include <config.h>
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #if WITH_NET_ETHERNET_H
32 # include <net/ethernet.h>
33 #endif
34 #include <unistd.h>
35 
36 #include "internal.h"
37 
38 #include "viruuid.h"
39 #include "viralloc.h"
40 #include "virerror.h"
41 #include "datatypes.h"
42 #include "nwfilter_params.h"
43 #include "nwfilter_conf.h"
44 #include "domain_conf.h"
45 #include "virfile.h"
46 #include "virstring.h"
47 
48 #define VIR_FROM_THIS VIR_FROM_NWFILTER
49 
50 
51 VIR_ENUM_IMPL(virNWFilterRuleAction,
52               VIR_NWFILTER_RULE_ACTION_LAST,
53               "drop",
54               "accept",
55               "reject",
56               "return",
57               "continue",
58 );
59 
60 VIR_ENUM_IMPL(virNWFilterJumpTarget,
61               VIR_NWFILTER_RULE_ACTION_LAST,
62               "DROP",
63               "ACCEPT",
64               "REJECT",
65               "RETURN",
66               "CONTINUE",
67 );
68 
69 VIR_ENUM_IMPL(virNWFilterRuleDirection,
70               VIR_NWFILTER_RULE_DIRECTION_LAST,
71               "in",
72               "out",
73               "inout",
74 );
75 
76 VIR_ENUM_IMPL(virNWFilterChainPolicy,
77               VIR_NWFILTER_CHAIN_POLICY_LAST,
78               "ACCEPT",
79               "DROP",
80 );
81 
82 VIR_ENUM_IMPL(virNWFilterEbtablesTable,
83               VIR_NWFILTER_EBTABLES_TABLE_LAST,
84               "filter",
85               "nat",
86               "broute",
87 );
88 
89 VIR_ENUM_IMPL(virNWFilterChainSuffix,
90               VIR_NWFILTER_CHAINSUFFIX_LAST,
91               "root",
92               "mac",
93               "vlan",
94               "stp",
95               "arp",
96               "rarp",
97               "ipv4",
98               "ipv6",
99 );
100 
101 VIR_ENUM_IMPL(virNWFilterRuleProtocol,
102               VIR_NWFILTER_RULE_PROTOCOL_LAST,
103               "none",
104               "mac",
105               "vlan",
106               "stp",
107               "arp",
108               "rarp",
109               "ip",
110               "ipv6",
111               "tcp",
112               "icmp",
113               "igmp",
114               "udp",
115               "udplite",
116               "esp",
117               "ah",
118               "sctp",
119               "all",
120               "tcp-ipv6",
121               "icmpv6",
122               "udp-ipv6",
123               "udplite-ipv6",
124               "esp-ipv6",
125               "ah-ipv6",
126               "sctp-ipv6",
127               "all-ipv6",
128 );
129 
130 
131 /*
132  * a map entry for a simple static int-to-string map
133  */
134 struct int_map {
135     int32_t attr;
136     const char *val;
137 };
138 
139 #define INTMAP_ENTRY(ATT, VAL) { .attr = ATT, .val = VAL }
140 #define INTMAP_ENTRY_LAST      { .val = NULL }
141 
142 static const struct int_map chain_priorities[] = {
143     INTMAP_ENTRY(NWFILTER_ROOT_FILTER_PRI, "root"),
144     INTMAP_ENTRY(NWFILTER_MAC_FILTER_PRI,  "mac"),
145     INTMAP_ENTRY(NWFILTER_VLAN_FILTER_PRI, "vlan"),
146     INTMAP_ENTRY(NWFILTER_IPV4_FILTER_PRI, "ipv4"),
147     INTMAP_ENTRY(NWFILTER_IPV6_FILTER_PRI, "ipv6"),
148     INTMAP_ENTRY(NWFILTER_ARP_FILTER_PRI,  "arp"),
149     INTMAP_ENTRY(NWFILTER_RARP_FILTER_PRI, "rarp"),
150     INTMAP_ENTRY_LAST,
151 };
152 
153 
154 /*
155  * only one filter update allowed
156  */
157 static virRWLock updateLock;
158 static bool initialized;
159 
160 void
virNWFilterReadLockFilterUpdates(void)161 virNWFilterReadLockFilterUpdates(void)
162 {
163     virRWLockRead(&updateLock);
164 }
165 
166 
167 void
virNWFilterWriteLockFilterUpdates(void)168 virNWFilterWriteLockFilterUpdates(void)
169 {
170     virRWLockWrite(&updateLock);
171 }
172 
173 
174 void
virNWFilterUnlockFilterUpdates(void)175 virNWFilterUnlockFilterUpdates(void)
176 {
177     virRWLockUnlock(&updateLock);
178 }
179 
180 
181 /*
182  * attribute names for the rules XML
183  */
184 static const char srcmacaddr_str[]    = "srcmacaddr";
185 static const char srcmacmask_str[]    = "srcmacmask";
186 static const char dstmacaddr_str[]    = "dstmacaddr";
187 static const char dstmacmask_str[]    = "dstmacmask";
188 static const char arpsrcmacaddr_str[] = "arpsrcmacaddr";
189 static const char arpdstmacaddr_str[] = "arpdstmacaddr";
190 static const char arpsrcipaddr_str[]  = "arpsrcipaddr";
191 static const char arpsrcipmask_str[]  = "arpsrcipmask";
192 static const char arpdstipaddr_str[]  = "arpdstipaddr";
193 static const char arpdstipmask_str[]  = "arpdstipmask";
194 static const char srcipaddr_str[]     = "srcipaddr";
195 static const char srcipmask_str[]     = "srcipmask";
196 static const char dstipaddr_str[]     = "dstipaddr";
197 static const char dstipmask_str[]     = "dstipmask";
198 static const char srcipfrom_str[]     = "srcipfrom";
199 static const char srcipto_str[]       = "srcipto";
200 static const char dstipfrom_str[]     = "dstipfrom";
201 static const char dstipto_str[]       = "dstipto";
202 static const char srcportstart_str[]  = "srcportstart";
203 static const char srcportend_str[]    = "srcportend";
204 static const char dstportstart_str[]  = "dstportstart";
205 static const char dstportend_str[]    = "dstportend";
206 static const char dscp_str[]          = "dscp";
207 static const char state_str[]         = "state";
208 static const char ipset_str[]         = "ipset";
209 static const char ipsetflags_str[]    = "ipsetflags";
210 
211 #define SRCMACADDR    srcmacaddr_str
212 #define SRCMACMASK    srcmacmask_str
213 #define DSTMACADDR    dstmacaddr_str
214 #define DSTMACMASK    dstmacmask_str
215 #define ARPSRCMACADDR arpsrcmacaddr_str
216 #define ARPDSTMACADDR arpdstmacaddr_str
217 #define ARPSRCIPADDR  arpsrcipaddr_str
218 #define ARPSRCIPMASK  arpsrcipmask_str
219 #define ARPDSTIPADDR  arpdstipaddr_str
220 #define ARPDSTIPMASK  arpdstipmask_str
221 #define SRCIPADDR     srcipaddr_str
222 #define SRCIPMASK     srcipmask_str
223 #define DSTIPADDR     dstipaddr_str
224 #define DSTIPMASK     dstipmask_str
225 #define SRCIPFROM     srcipfrom_str
226 #define SRCIPTO       srcipto_str
227 #define DSTIPFROM     dstipfrom_str
228 #define DSTIPTO       dstipto_str
229 #define SRCPORTSTART  srcportstart_str
230 #define SRCPORTEND    srcportend_str
231 #define DSTPORTSTART  dstportstart_str
232 #define DSTPORTEND    dstportend_str
233 #define DSCP          dscp_str
234 #define STATE         state_str
235 #define IPSET         ipset_str
236 #define IPSETFLAGS    ipsetflags_str
237 
238 
239 /**
240  * intMapGetByInt:
241  * @intmap: Pointer to int-to-string map
242  * @attr: The attribute to look up
243  * @res: Pointer to string pointer for result
244  *
245  * Returns 0 if value was found with result returned, -1 otherwise.
246  *
247  * lookup a map entry given the integer.
248  */
249 static int
intMapGetByInt(const struct int_map * intmap,int32_t attr,const char ** res)250 intMapGetByInt(const struct int_map *intmap,
251                int32_t attr,
252                const char **res)
253 {
254     size_t i = 0;
255     bool found = false;
256 
257     while (intmap[i].val && !found) {
258         if (intmap[i].attr == attr) {
259             *res = intmap[i].val;
260             found = true;
261         }
262         i++;
263     }
264     return (found) ? 0 : -1;
265 }
266 
267 
268 /**
269  * intMapGetByString:
270  * @intmap: Pointer to int-to-string map
271  * @str: Pointer to string for which to find the entry
272  * @casecmp : Whether to ignore case when doing string matching
273  * @result: Pointer to int for result
274  *
275  * Returns 0 if entry was found, -1 otherwise.
276  *
277  * Do a lookup in the map trying to find an integer key using the string
278  * value. Returns 0 if entry was found with result returned, -1 otherwise.
279  */
280 static int
intMapGetByString(const struct int_map * intmap,const char * str,int casecmp,int32_t * result)281 intMapGetByString(const struct int_map *intmap,
282                   const char *str,
283                   int casecmp,
284                   int32_t *result)
285 {
286     size_t i = 0;
287     bool found = false;
288 
289     while (intmap[i].val && !found) {
290         if ((casecmp && STRCASEEQ(intmap[i].val, str)) ||
291             STREQ(intmap[i].val, str)) {
292             *result = intmap[i].attr;
293             found = true;
294         }
295         i++;
296     }
297     return (found) ? 0 : -1;
298 }
299 
300 
301 void
virNWFilterRuleDefFree(virNWFilterRuleDef * def)302 virNWFilterRuleDefFree(virNWFilterRuleDef *def)
303 {
304     size_t i;
305     if (!def)
306         return;
307 
308     for (i = 0; i < def->nVarAccess; i++)
309         virNWFilterVarAccessFree(def->varAccess[i]);
310 
311     for (i = 0; i < def->nstrings; i++)
312         g_free(def->strings[i]);
313 
314     g_free(def->varAccess);
315     g_free(def->strings);
316 
317     g_free(def);
318 }
319 
320 
321 static void
virNWFilterIncludeDefFree(virNWFilterIncludeDef * inc)322 virNWFilterIncludeDefFree(virNWFilterIncludeDef *inc)
323 {
324     if (!inc)
325         return;
326     virHashFree(inc->params);
327     g_free(inc->filterref);
328     g_free(inc);
329 }
330 
331 
332 static void
virNWFilterEntryFree(virNWFilterEntry * entry)333 virNWFilterEntryFree(virNWFilterEntry *entry)
334 {
335     if (!entry)
336         return;
337 
338     virNWFilterRuleDefFree(entry->rule);
339     virNWFilterIncludeDefFree(entry->include);
340     g_free(entry);
341 }
342 
343 
344 void
virNWFilterDefFree(virNWFilterDef * def)345 virNWFilterDefFree(virNWFilterDef *def)
346 {
347     size_t i;
348     if (!def)
349         return;
350 
351     g_free(def->name);
352 
353     for (i = 0; i < def->nentries; i++)
354         virNWFilterEntryFree(def->filterEntries[i]);
355 
356     g_free(def->filterEntries);
357     g_free(def->chainsuffix);
358 
359     g_free(def);
360 }
361 
362 
363 static int
virNWFilterRuleDefAddVar(virNWFilterRuleDef * nwf,nwItemDesc * item,const char * var)364 virNWFilterRuleDefAddVar(virNWFilterRuleDef *nwf,
365                          nwItemDesc *item,
366                          const char *var)
367 {
368     size_t i = 0;
369     virNWFilterVarAccess *varAccess;
370 
371     varAccess = virNWFilterVarAccessParse(var);
372     if (varAccess == NULL)
373         return -1;
374 
375     if (nwf->varAccess) {
376         for (i = 0; i < nwf->nVarAccess; i++)
377             if (virNWFilterVarAccessEqual(nwf->varAccess[i], varAccess)) {
378                 virNWFilterVarAccessFree(varAccess);
379                 item->varAccess = nwf->varAccess[i];
380                 return 0;
381             }
382     }
383 
384     VIR_EXPAND_N(nwf->varAccess, nwf->nVarAccess, 1);
385     nwf->varAccess[nwf->nVarAccess - 1] = varAccess;
386     item->varAccess = varAccess;
387 
388     return 0;
389 }
390 
391 
392 static char *
virNWFilterRuleDefAddString(virNWFilterRuleDef * nwf,const char * string,size_t maxstrlen)393 virNWFilterRuleDefAddString(virNWFilterRuleDef *nwf,
394                             const char *string,
395                             size_t maxstrlen)
396 {
397     char *tmp = g_strndup(string, maxstrlen);
398 
399     VIR_APPEND_ELEMENT_COPY(nwf->strings, nwf->nstrings, tmp);
400 
401     return tmp;
402 }
403 
404 
405 union data {
406     void *v;
407     char *c;
408     unsigned char *uc;
409     unsigned int ui;
410 };
411 
412 typedef bool (*valueValidator)(enum attrDatatype datatype, union data *valptr,
413                                virNWFilterRuleDef *nwf,
414                                nwItemDesc *item);
415 typedef bool (*valueFormatter)(virBuffer *buf,
416                                virNWFilterRuleDef *nwf,
417                                nwItemDesc *item);
418 
419 typedef struct _virXMLAttr2Struct virXMLAttr2Struct;
420 struct _virXMLAttr2Struct
421 {
422     const char *name;           /* attribute name */
423     enum attrDatatype datatype;
424     int dataIdx;                /* offset of the hasXYZ boolean */
425     valueValidator validator;   /* beyond-standard checkers */
426     valueFormatter formatter;   /* beyond-standard formatter */
427     size_t maxstrlen;
428 };
429 
430 
431 static const struct int_map macProtoMap[] = {
432     INTMAP_ENTRY(ETHERTYPE_ARP,    "arp"),
433     INTMAP_ENTRY(ETHERTYPE_REVARP, "rarp"),
434     INTMAP_ENTRY(ETHERTYPE_IP,     "ipv4"),
435     INTMAP_ENTRY(ETHERTYPE_IPV6,   "ipv6"),
436     INTMAP_ENTRY(ETHERTYPE_VLAN,   "vlan"),
437     INTMAP_ENTRY_LAST
438 };
439 
440 
441 static bool
checkMacProtocolID(enum attrDatatype datatype,union data * value,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item G_GNUC_UNUSED)442 checkMacProtocolID(enum attrDatatype datatype,
443                    union data *value,
444                    virNWFilterRuleDef *nwf G_GNUC_UNUSED,
445                    nwItemDesc *item G_GNUC_UNUSED)
446 {
447     int32_t res = -1;
448 
449     if (datatype == DATATYPE_STRING) {
450         if (intMapGetByString(macProtoMap, value->c, 1, &res) < 0)
451             res = -1;
452         datatype = DATATYPE_UINT16;
453     } else if (datatype == DATATYPE_UINT16 ||
454                datatype == DATATYPE_UINT16_HEX) {
455         res = value->ui;
456         if (res < 0x600)
457             res = -1;
458     }
459 
460     if (res != -1) {
461         nwf->p.ethHdrFilter.dataProtocolID.u.u16 = res;
462         nwf->p.ethHdrFilter.dataProtocolID.datatype = datatype;
463         return true;
464     }
465 
466     return false;
467 }
468 
469 
470 static bool
macProtocolIDFormatter(virBuffer * buf,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)471 macProtocolIDFormatter(virBuffer *buf,
472                        virNWFilterRuleDef *nwf,
473                        nwItemDesc *item G_GNUC_UNUSED)
474 {
475     const char *str = NULL;
476     bool asHex = true;
477 
478     if (intMapGetByInt(macProtoMap,
479                        nwf->p.ethHdrFilter.dataProtocolID.u.u16,
480                        &str) == 0) {
481         virBufferAdd(buf, str, -1);
482     } else {
483         if (nwf->p.ethHdrFilter.dataProtocolID.datatype == DATATYPE_UINT16)
484             asHex = false;
485         virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
486                           nwf->p.ethHdrFilter.dataProtocolID.u.u16);
487     }
488     return true;
489 }
490 
491 
492 static bool
checkVlanVlanID(enum attrDatatype datatype,union data * value,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)493 checkVlanVlanID(enum attrDatatype datatype,
494                 union data *value,
495                 virNWFilterRuleDef *nwf,
496                 nwItemDesc *item G_GNUC_UNUSED)
497 {
498     int32_t res;
499 
500     res = value->ui;
501     if (res < 0 || res > 4095)
502         res = -1;
503 
504     if (res != -1) {
505         nwf->p.vlanHdrFilter.dataVlanID.u.u16 = res;
506         nwf->p.vlanHdrFilter.dataVlanID.datatype = datatype;
507         return true;
508     }
509 
510     return false;
511 }
512 
513 
514 static bool
checkVlanProtocolID(enum attrDatatype datatype,union data * value,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)515 checkVlanProtocolID(enum attrDatatype datatype,
516                     union data *value,
517                     virNWFilterRuleDef *nwf,
518                     nwItemDesc *item G_GNUC_UNUSED)
519 {
520     int32_t res = -1;
521 
522     if (datatype == DATATYPE_STRING) {
523         if (intMapGetByString(macProtoMap, value->c, 1, &res) < 0)
524             res = -1;
525         datatype = DATATYPE_UINT16;
526     } else if (datatype == DATATYPE_UINT16 ||
527                datatype == DATATYPE_UINT16_HEX) {
528         res = value->ui;
529         if (res < 0x3c)
530             res = -1;
531     }
532 
533     if (res != -1) {
534         nwf->p.vlanHdrFilter.dataVlanEncap.u.u16 = res;
535         nwf->p.vlanHdrFilter.dataVlanEncap.datatype = datatype;
536         return true;
537     }
538 
539     return false;
540 }
541 
542 
543 static bool
vlanProtocolIDFormatter(virBuffer * buf,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)544 vlanProtocolIDFormatter(virBuffer *buf,
545                         virNWFilterRuleDef *nwf,
546                         nwItemDesc *item G_GNUC_UNUSED)
547 {
548     const char *str = NULL;
549     bool asHex = true;
550 
551     if (intMapGetByInt(macProtoMap,
552                        nwf->p.vlanHdrFilter.dataVlanEncap.u.u16,
553                        &str) == 0) {
554         virBufferAdd(buf, str, -1);
555     } else {
556         if (nwf->p.vlanHdrFilter.dataVlanEncap.datatype == DATATYPE_UINT16)
557             asHex = false;
558         virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
559                           nwf->p.vlanHdrFilter.dataVlanEncap.u.u16);
560     }
561     return true;
562 }
563 
564 
565 /* generic function to check for a valid (ipv4,ipv6, mac) mask
566  * A mask is valid of there is a sequence of 1's followed by a sequence
567  * of 0s or only 1s or only 0s
568  */
569 static bool
checkValidMask(unsigned char * data,int len)570 checkValidMask(unsigned char *data,
571                int len)
572 {
573     uint32_t idx = 0;
574     uint8_t mask = 0x80;
575     bool checkones = true;
576 
577     while ((idx >> 3) < len) {
578         if (checkones) {
579             if (!(data[idx>>3] & mask))
580                 checkones = false;
581         } else {
582             if ((data[idx>>3] & mask))
583                 return false;
584         }
585 
586         idx++;
587         mask >>= 1;
588         if (!mask)
589             mask = 0x80;
590     }
591     return true;
592 }
593 
594 
595 static bool
checkMACMask(enum attrDatatype datatype G_GNUC_UNUSED,union data * macMask,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item G_GNUC_UNUSED)596 checkMACMask(enum attrDatatype datatype G_GNUC_UNUSED,
597              union data *macMask,
598              virNWFilterRuleDef *nwf G_GNUC_UNUSED,
599              nwItemDesc *item G_GNUC_UNUSED)
600 {
601     return checkValidMask(macMask->uc, 6);
602 }
603 
604 
605 /*
606  * supported arp opcode -- see 'ebtables -h arp' for the naming
607  */
608 static const struct int_map arpOpcodeMap[] = {
609     INTMAP_ENTRY(1, "Request"),
610     INTMAP_ENTRY(2, "Reply"),
611     INTMAP_ENTRY(3, "Request_Reverse"),
612     INTMAP_ENTRY(4, "Reply_Reverse"),
613     INTMAP_ENTRY(5, "DRARP_Request"),
614     INTMAP_ENTRY(6, "DRARP_Reply"),
615     INTMAP_ENTRY(7, "DRARP_Error"),
616     INTMAP_ENTRY(8, "InARP_Request"),
617     INTMAP_ENTRY(9, "ARP_NAK"),
618     INTMAP_ENTRY_LAST
619 };
620 
621 
622 static bool
arpOpcodeValidator(enum attrDatatype datatype,union data * value,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)623 arpOpcodeValidator(enum attrDatatype datatype,
624                    union data *value,
625                    virNWFilterRuleDef *nwf,
626                    nwItemDesc *item G_GNUC_UNUSED)
627 {
628     int32_t res = -1;
629 
630     if (datatype == DATATYPE_STRING) {
631         if (intMapGetByString(arpOpcodeMap, value->c, 1, &res) < 0)
632             res = -1;
633         datatype = DATATYPE_UINT16;
634     } else if (datatype == DATATYPE_UINT16 ||
635                datatype == DATATYPE_UINT16_HEX) {
636         res = (uint32_t)value->ui;
637     }
638 
639     if (res != -1) {
640         nwf->p.arpHdrFilter.dataOpcode.u.u16 = res;
641         nwf->p.arpHdrFilter.dataOpcode.datatype = datatype;
642         return true;
643     }
644     return false;
645 }
646 
647 
648 static bool
arpOpcodeFormatter(virBuffer * buf,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)649 arpOpcodeFormatter(virBuffer *buf,
650                    virNWFilterRuleDef *nwf,
651                    nwItemDesc *item G_GNUC_UNUSED)
652 {
653     const char *str = NULL;
654 
655     if (intMapGetByInt(arpOpcodeMap,
656                        nwf->p.arpHdrFilter.dataOpcode.u.u16,
657                        &str) == 0) {
658         virBufferAdd(buf, str, -1);
659     } else {
660         virBufferAsprintf(buf, "%d", nwf->p.arpHdrFilter.dataOpcode.u.u16);
661     }
662     return true;
663 }
664 
665 
666 static const struct int_map ipProtoMap[] = {
667     INTMAP_ENTRY(IPPROTO_TCP, "tcp"),
668     INTMAP_ENTRY(IPPROTO_UDP, "udp"),
669 #ifdef IPPROTO_UDPLITE
670     INTMAP_ENTRY(IPPROTO_UDPLITE, "udplite"),
671 #endif
672     INTMAP_ENTRY(IPPROTO_ESP, "esp"),
673     INTMAP_ENTRY(IPPROTO_AH,  "ah"),
674     INTMAP_ENTRY(IPPROTO_ICMP, "icmp"),
675     INTMAP_ENTRY(IPPROTO_IGMP, "igmp"),
676 #ifdef IPPROTO_SCTP
677     INTMAP_ENTRY(IPPROTO_SCTP, "sctp"),
678 #endif
679     INTMAP_ENTRY(IPPROTO_ICMPV6, "icmpv6"),
680     INTMAP_ENTRY_LAST
681 };
682 
683 
684 static bool
checkIPProtocolID(enum attrDatatype datatype,union data * value,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)685 checkIPProtocolID(enum attrDatatype datatype,
686                   union data *value,
687                   virNWFilterRuleDef *nwf,
688                   nwItemDesc *item G_GNUC_UNUSED)
689 {
690     int32_t res = -1;
691 
692     if (datatype == DATATYPE_STRING) {
693         if (intMapGetByString(ipProtoMap, value->c, 1, &res) < 0)
694             res = -1;
695         datatype = DATATYPE_UINT8_HEX;
696     } else if (datatype == DATATYPE_UINT8 ||
697                datatype == DATATYPE_UINT8_HEX) {
698         res = (uint32_t)value->ui;
699     }
700 
701     if (res != -1) {
702         nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8 = res;
703         nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype = datatype;
704         return true;
705     }
706     return false;
707 }
708 
709 
710 static bool
formatIPProtocolID(virBuffer * buf,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)711 formatIPProtocolID(virBuffer *buf,
712                    virNWFilterRuleDef *nwf,
713                    nwItemDesc *item G_GNUC_UNUSED)
714 {
715     const char *str = NULL;
716     bool asHex = true;
717 
718     if (intMapGetByInt(ipProtoMap,
719                        nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8,
720                        &str) == 0) {
721         virBufferAdd(buf, str, -1);
722     } else {
723         if (nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype == DATATYPE_UINT8)
724             asHex = false;
725         virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
726                           nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8);
727     }
728     return true;
729 }
730 
731 
732 static bool
dscpValidator(enum attrDatatype datatype,union data * val,virNWFilterRuleDef * nwf,nwItemDesc * item G_GNUC_UNUSED)733 dscpValidator(enum attrDatatype datatype,
734               union data *val,
735               virNWFilterRuleDef *nwf,
736               nwItemDesc *item G_GNUC_UNUSED)
737 {
738     uint8_t dscp = val->ui;
739     if (dscp > 63)
740         return false;
741 
742     nwf->p.ipHdrFilter.ipHdr.dataDSCP.datatype = datatype;
743 
744     return true;
745 }
746 
747 
748 static const struct int_map stateMatchMap[] = {
749     INTMAP_ENTRY(RULE_FLAG_STATE_NEW,           "NEW"),
750     INTMAP_ENTRY(RULE_FLAG_STATE_ESTABLISHED,   "ESTABLISHED"),
751     INTMAP_ENTRY(RULE_FLAG_STATE_RELATED,       "RELATED"),
752     INTMAP_ENTRY(RULE_FLAG_STATE_INVALID,       "INVALID"),
753     INTMAP_ENTRY(RULE_FLAG_STATE_NONE,          "NONE"),
754     INTMAP_ENTRY_LAST,
755 };
756 
757 
758 static int
parseStringItems(const struct int_map * int_map,const char * input,int32_t * flags,char sep)759 parseStringItems(const struct int_map *int_map,
760                  const char *input,
761                  int32_t *flags,
762                  char sep)
763 {
764     int rc = 0;
765     size_t i, j;
766     bool found;
767 
768     i = 0;
769     while (input[i]) {
770         found = false;
771         while (g_ascii_isspace(input[i]) || input[i] == sep)
772             i++;
773         if (!input[i])
774             break;
775         for (j = 0; int_map[j].val; j++) {
776             if (STRCASEEQLEN(&input[i], int_map[j].val,
777                              strlen(int_map[j].val))) {
778                 *flags |= int_map[j].attr;
779                 i += strlen(int_map[j].val);
780                 found = true;
781                 break;
782             }
783         }
784         if (!found) {
785             rc = -1;
786             break;
787         }
788     }
789     return rc;
790 }
791 
792 
793 static int
printStringItems(virBuffer * buf,const struct int_map * int_map,int32_t flags,const char * sep)794 printStringItems(virBuffer *buf,
795                  const struct int_map *int_map,
796                  int32_t flags,
797                  const char *sep)
798 {
799     size_t i;
800     unsigned int c = 0;
801     int32_t mask = 0x1;
802 
803     while (mask) {
804         if ((mask & flags)) {
805             for (i = 0; int_map[i].val; i++) {
806                 if (mask == int_map[i].attr) {
807                     if (c >= 1)
808                         virBufferAdd(buf, sep, -1);
809                     virBufferAdd(buf, int_map[i].val, -1);
810                     c++;
811                 }
812             }
813             flags ^= mask;
814         }
815         if (!flags)
816             break;
817         mask <<= 1;
818     }
819 
820     return 0;
821 }
822 
823 
824 static int
parseStateMatch(const char * statematch,int32_t * flags)825 parseStateMatch(const char *statematch,
826                 int32_t *flags)
827 {
828     int rc = parseStringItems(stateMatchMap, statematch, flags, ',');
829 
830     if ((*flags & RULE_FLAG_STATE_NONE))
831         *flags = RULE_FLAG_STATE_NONE;
832 
833     return rc;
834 }
835 
836 
837 void
virNWFilterPrintStateMatchFlags(virBuffer * buf,const char * prefix,int32_t flags,bool disp_none)838 virNWFilterPrintStateMatchFlags(virBuffer *buf,
839                                 const char *prefix,
840                                 int32_t flags,
841                                 bool disp_none)
842 {
843     if (!disp_none && (flags & RULE_FLAG_STATE_NONE))
844         return;
845 
846     virBufferAdd(buf, prefix, -1);
847 
848     printStringItems(buf, stateMatchMap, flags, ",");
849 }
850 
851 
852 static bool
stateValidator(enum attrDatatype datatype G_GNUC_UNUSED,union data * val,virNWFilterRuleDef * nwf,nwItemDesc * item)853 stateValidator(enum attrDatatype datatype G_GNUC_UNUSED,
854                union data *val,
855                virNWFilterRuleDef *nwf,
856                nwItemDesc *item)
857 {
858     char *input = val->c;
859     int32_t flags = 0;
860 
861     if (parseStateMatch(input, &flags) < 0)
862         return false;
863 
864     item->u.u16 = flags;
865     nwf->flags |= flags;
866 
867     item->datatype = DATATYPE_UINT16;
868 
869     return true;
870 }
871 
872 
873 static bool
stateFormatter(virBuffer * buf,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)874 stateFormatter(virBuffer *buf,
875                virNWFilterRuleDef *nwf G_GNUC_UNUSED,
876                nwItemDesc *item)
877 {
878     virNWFilterPrintStateMatchFlags(buf, "", item->u.u16, true);
879 
880     return true;
881 }
882 
883 
884 static const struct int_map tcpFlags[] = {
885     INTMAP_ENTRY(0x1,  "FIN"),
886     INTMAP_ENTRY(0x2,  "SYN"),
887     INTMAP_ENTRY(0x4,  "RST"),
888     INTMAP_ENTRY(0x8,  "PSH"),
889     INTMAP_ENTRY(0x10, "ACK"),
890     INTMAP_ENTRY(0x20, "URG"),
891     INTMAP_ENTRY(0x3F, "ALL"),
892     INTMAP_ENTRY(0x0,  "NONE"),
893     INTMAP_ENTRY_LAST
894 };
895 
896 
897 static bool
tcpFlagsValidator(enum attrDatatype datatype G_GNUC_UNUSED,union data * val,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)898 tcpFlagsValidator(enum attrDatatype datatype G_GNUC_UNUSED,
899                   union data *val,
900                   virNWFilterRuleDef *nwf G_GNUC_UNUSED,
901                   nwItemDesc *item)
902 {
903     bool rc = false;
904     char *s_mask = val->c;
905     char *sep = strchr(val->c, '/');
906     char *s_flags;
907     int32_t mask = 0, flags = 0;
908 
909     if (!sep)
910         return false;
911 
912     s_flags = sep + 1;
913 
914     *sep = '\0';
915 
916     if (parseStringItems(tcpFlags, s_mask, &mask, ',') == 0 &&
917         parseStringItems(tcpFlags, s_flags, &flags, ',') == 0) {
918         item->u.tcpFlags.mask  = mask  & 0x3f;
919         item->u.tcpFlags.flags = flags & 0x3f;
920         rc = true;
921     }
922 
923     *sep = '/';
924 
925     return rc;
926 }
927 
928 
929 static void
printTCPFlags(virBuffer * buf,uint8_t flags)930 printTCPFlags(virBuffer *buf,
931               uint8_t flags)
932 {
933     if (flags == 0)
934         virBufferAddLit(buf, "NONE");
935     else if (flags == 0x3f)
936         virBufferAddLit(buf, "ALL");
937     else
938         printStringItems(buf, tcpFlags, flags, ",");
939 }
940 
941 
942 char *
virNWFilterPrintTCPFlags(uint8_t flags)943 virNWFilterPrintTCPFlags(uint8_t flags)
944 {
945     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
946     printTCPFlags(&buf, flags);
947     return virBufferContentAndReset(&buf);
948 }
949 
950 
951 static bool
tcpFlagsFormatter(virBuffer * buf,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)952 tcpFlagsFormatter(virBuffer *buf,
953                   virNWFilterRuleDef *nwf G_GNUC_UNUSED,
954                   nwItemDesc *item)
955 {
956     printTCPFlags(buf, item->u.tcpFlags.mask);
957     virBufferAddLit(buf, "/");
958     printTCPFlags(buf, item->u.tcpFlags.flags);
959 
960     return true;
961 }
962 
963 
964 static bool
ipsetValidator(enum attrDatatype datatype G_GNUC_UNUSED,union data * val,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)965 ipsetValidator(enum attrDatatype datatype G_GNUC_UNUSED,
966                union data *val,
967                virNWFilterRuleDef *nwf G_GNUC_UNUSED,
968                nwItemDesc *item)
969 {
970     const char *errmsg = NULL;
971 
972     if (virStrcpyStatic(item->u.ipset.setname, val->c) < 0) {
973         errmsg = _("ipset name is too long");
974         goto arg_err_exit;
975     }
976 
977     if (item->u.ipset.setname[strspn(item->u.ipset.setname,
978                                      VALID_IPSETNAME)] != 0) {
979         errmsg = _("ipset name contains invalid characters");
980         goto arg_err_exit;
981     }
982 
983     return true;
984 
985  arg_err_exit:
986     virReportError(VIR_ERR_INVALID_ARG,
987                    "%s", errmsg);
988     return false;
989 }
990 
991 
992 static bool
ipsetFormatter(virBuffer * buf,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)993 ipsetFormatter(virBuffer *buf,
994                virNWFilterRuleDef *nwf G_GNUC_UNUSED,
995                nwItemDesc *item)
996 {
997     virBufferAdd(buf, item->u.ipset.setname, -1);
998 
999     return true;
1000 }
1001 
1002 
1003 static bool
ipsetFlagsValidator(enum attrDatatype datatype G_GNUC_UNUSED,union data * val,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)1004 ipsetFlagsValidator(enum attrDatatype datatype G_GNUC_UNUSED,
1005                     union data *val,
1006                     virNWFilterRuleDef *nwf G_GNUC_UNUSED,
1007                     nwItemDesc *item)
1008 {
1009     const char *errmsg = NULL;
1010     size_t idx = 0;
1011 
1012     item->u.ipset.numFlags = 0;
1013     item->u.ipset.flags = 0;
1014 
1015     errmsg = _("malformed ipset flags");
1016 
1017     while (item->u.ipset.numFlags < 6) {
1018         if (STRCASEEQLEN(&val->c[idx], "src", 3)) {
1019             item->u.ipset.flags |= (1 << item->u.ipset.numFlags);
1020         } else if (!STRCASEEQLEN(&val->c[idx], "dst", 3)) {
1021             goto arg_err_exit;
1022         }
1023         item->u.ipset.numFlags++;
1024         idx += 3;
1025         if (val->c[idx] != ',')
1026             break;
1027         idx++;
1028     }
1029 
1030     if (val->c[idx] != '\0')
1031         goto arg_err_exit;
1032 
1033     return true;
1034 
1035  arg_err_exit:
1036     virReportError(VIR_ERR_INVALID_ARG,
1037                    "%s", errmsg);
1038     return false;
1039 }
1040 
1041 
1042 static bool
ipsetFlagsFormatter(virBuffer * buf,virNWFilterRuleDef * nwf G_GNUC_UNUSED,nwItemDesc * item)1043 ipsetFlagsFormatter(virBuffer *buf,
1044                     virNWFilterRuleDef *nwf G_GNUC_UNUSED,
1045                     nwItemDesc *item)
1046 {
1047     uint8_t ctr;
1048 
1049     for (ctr = 0; ctr < item->u.ipset.numFlags; ctr++) {
1050         if (ctr != 0)
1051             virBufferAddLit(buf, ",");
1052         if ((item->u.ipset.flags & (1 << ctr)))
1053             virBufferAddLit(buf, "src");
1054         else
1055             virBufferAddLit(buf, "dst");
1056     }
1057 
1058     return true;
1059 }
1060 
1061 
1062 #define COMMON_MAC_PROPS(STRUCT) \
1063     {\
1064         .name = SRCMACADDR,\
1065         .datatype = DATATYPE_MACADDR,\
1066             .dataIdx = offsetof(virNWFilterRuleDef,\
1067                             p.STRUCT.ethHdr.dataSrcMACAddr),\
1068     },\
1069     {\
1070         .name = SRCMACMASK,\
1071         .datatype = DATATYPE_MACMASK,\
1072         .dataIdx = offsetof(virNWFilterRuleDef,\
1073                             p.STRUCT.ethHdr.dataSrcMACMask),\
1074     },\
1075     {\
1076         .name = DSTMACADDR,\
1077         .datatype = DATATYPE_MACADDR,\
1078         .dataIdx = offsetof(virNWFilterRuleDef,\
1079                             p.STRUCT.ethHdr.dataDstMACAddr),\
1080     },\
1081     {\
1082         .name = DSTMACMASK,\
1083         .datatype = DATATYPE_MACMASK,\
1084         .dataIdx = offsetof(virNWFilterRuleDef,\
1085                             p.STRUCT.ethHdr.dataDstMACMask),\
1086     }
1087 
1088 
1089 #define COMMENT_PROP(STRUCT) \
1090     {\
1091         .name = "comment",\
1092         .datatype = DATATYPE_STRINGCOPY,\
1093         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.dataComment),\
1094         .maxstrlen = MAX_COMMENT_LENGTH,\
1095     }
1096 
1097 #define COMMENT_PROP_IPHDR(STRUCT) \
1098     COMMENT_PROP(STRUCT.ipHdr)
1099 
1100 
1101 static const virXMLAttr2Struct macAttributes[] = {
1102     COMMON_MAC_PROPS(ethHdrFilter),
1103     {
1104         .name = "protocolid",
1105         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
1106         .dataIdx = offsetof(virNWFilterRuleDef, p.ethHdrFilter.dataProtocolID),
1107         .validator = checkMacProtocolID,
1108         .formatter = macProtocolIDFormatter,
1109     },
1110     COMMENT_PROP(ethHdrFilter),
1111     {
1112         .name = NULL,
1113     }
1114 };
1115 
1116 static const virXMLAttr2Struct vlanAttributes[] = {
1117     COMMON_MAC_PROPS(ethHdrFilter),
1118     {
1119         .name = "vlanid",
1120         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1121         .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanID),
1122         .validator = checkVlanVlanID,
1123     },
1124     {
1125         .name = "encap-protocol",
1126         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
1127         .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanEncap),
1128         .validator = checkVlanProtocolID,
1129         .formatter = vlanProtocolIDFormatter,
1130     },
1131     COMMENT_PROP(vlanHdrFilter),
1132     {
1133         .name = NULL,
1134     }
1135 };
1136 
1137 /* STP is documented by IEEE 802.1D; for a synopsis,
1138  * see http://www.javvin.com/protocolSTP.html */
1139 static const virXMLAttr2Struct stpAttributes[] = {
1140     /* spanning tree uses a special destination MAC address */
1141     {
1142         .name = SRCMACADDR,
1143         .datatype = DATATYPE_MACADDR,
1144         .dataIdx = offsetof(virNWFilterRuleDef,
1145                             p.stpHdrFilter.ethHdr.dataSrcMACAddr),
1146     },
1147     {
1148         .name = SRCMACMASK,
1149         .datatype = DATATYPE_MACMASK,
1150         .dataIdx = offsetof(virNWFilterRuleDef,
1151                             p.stpHdrFilter.ethHdr.dataSrcMACMask),
1152     },
1153     {
1154         .name = "type",
1155         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1156         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataType),
1157     },
1158     {
1159         .name = "flags",
1160         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1161         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFlags),
1162     },
1163     {
1164         .name = "root-priority",
1165         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1166         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootPri),
1167     },
1168     {
1169         .name = "root-priority-hi",
1170         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1171         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootPriHi),
1172     },
1173     {
1174         .name = "root-address",
1175         .datatype = DATATYPE_MACADDR,
1176         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootAddr),
1177     },
1178     {
1179         .name = "root-address-mask",
1180         .datatype = DATATYPE_MACMASK,
1181         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootAddrMask),
1182     },
1183     {
1184         .name = "root-cost",
1185         .datatype = DATATYPE_UINT32 | DATATYPE_UINT32_HEX,
1186         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootCost),
1187     },
1188     {
1189         .name = "root-cost-hi",
1190         .datatype = DATATYPE_UINT32 | DATATYPE_UINT32_HEX,
1191         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataRootCostHi),
1192     },
1193     {
1194         .name = "sender-priority",
1195         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1196         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrPrio),
1197     },
1198     {
1199         .name = "sender-priority-hi",
1200         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1201         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrPrioHi),
1202     },
1203     {
1204         .name = "sender-address",
1205         .datatype = DATATYPE_MACADDR,
1206         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrAddr),
1207     },
1208     {
1209         .name = "sender-address-mask",
1210         .datatype = DATATYPE_MACMASK,
1211         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataSndrAddrMask),
1212     },
1213     {
1214         .name = "port",
1215         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1216         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataPort),
1217     },
1218     {
1219         .name = "port-hi",
1220         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1221         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataPortHi),
1222     },
1223     {
1224         .name = "age",
1225         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1226         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataAge),
1227     },
1228     {
1229         .name = "age-hi",
1230         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1231         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataAgeHi),
1232     },
1233     {
1234         .name = "max-age",
1235         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1236         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataMaxAge),
1237     },
1238     {
1239         .name = "max-age-hi",
1240         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1241         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataMaxAgeHi),
1242     },
1243     {
1244         .name = "hello-time",
1245         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1246         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataHelloTime),
1247     },
1248     {
1249         .name = "hello-time-hi",
1250         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1251         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataHelloTimeHi),
1252     },
1253     {
1254         .name = "forward-delay",
1255         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1256         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFwdDelay),
1257     },
1258     {
1259         .name = "forward-delay-hi",
1260         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1261         .dataIdx = offsetof(virNWFilterRuleDef, p.stpHdrFilter.dataFwdDelayHi),
1262     },
1263     COMMENT_PROP(stpHdrFilter),
1264     {
1265         .name = NULL,
1266     }
1267 };
1268 
1269 static const virXMLAttr2Struct arpAttributes[] = {
1270     COMMON_MAC_PROPS(arpHdrFilter),
1271     {
1272         .name = "hwtype",
1273         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1274         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataHWType),
1275     }, {
1276         .name = "protocoltype",
1277         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1278         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataProtocolType),
1279     }, {
1280         .name = "opcode",
1281         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING,
1282         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataOpcode),
1283         .validator = arpOpcodeValidator,
1284         .formatter = arpOpcodeFormatter,
1285     }, {
1286         .name = ARPSRCMACADDR,
1287         .datatype = DATATYPE_MACADDR,
1288         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcMACAddr),
1289     }, {
1290         .name = ARPDSTMACADDR,
1291         .datatype = DATATYPE_MACADDR,
1292         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstMACAddr),
1293     }, {
1294         .name = ARPSRCIPADDR,
1295         .datatype = DATATYPE_IPADDR,
1296         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPAddr),
1297     }, {
1298         .name = ARPSRCIPMASK,
1299         .datatype = DATATYPE_IPMASK,
1300         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPMask),
1301     }, {
1302         .name = ARPDSTIPADDR,
1303         .datatype = DATATYPE_IPADDR,
1304         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPAddr),
1305     }, {
1306         .name = ARPDSTIPMASK,
1307         .datatype = DATATYPE_IPMASK,
1308         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPMask),
1309     }, {
1310         .name = "gratuitous",
1311         .datatype = DATATYPE_BOOLEAN,
1312         .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataGratuitousARP),
1313     },
1314     COMMENT_PROP(arpHdrFilter),
1315     {
1316         .name = NULL,
1317     }
1318 };
1319 
1320 static const virXMLAttr2Struct ipAttributes[] = {
1321     COMMON_MAC_PROPS(ipHdrFilter),
1322     {
1323         .name = SRCIPADDR,
1324         .datatype = DATATYPE_IPADDR,
1325         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPAddr),
1326     },
1327     {
1328         .name = SRCIPMASK,
1329         .datatype = DATATYPE_IPMASK,
1330         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPMask),
1331     },
1332     {
1333         .name = DSTIPADDR,
1334         .datatype = DATATYPE_IPADDR,
1335         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPAddr),
1336     },
1337     {
1338         .name = DSTIPMASK,
1339         .datatype = DATATYPE_IPMASK,
1340         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPMask),
1341     },
1342     {
1343         .name = "protocol",
1344         .datatype = DATATYPE_STRING | DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1345         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataProtocolID),
1346         .validator = checkIPProtocolID,
1347         .formatter = formatIPProtocolID,
1348     },
1349     {
1350         .name = SRCPORTSTART,
1351         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1352         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortStart),
1353     },
1354     {
1355         .name = SRCPORTEND,
1356         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1357         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortEnd),
1358     },
1359     {
1360         .name = DSTPORTSTART,
1361         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1362         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortStart),
1363     },
1364     {
1365         .name = DSTPORTEND,
1366         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1367         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortEnd),
1368     },
1369     {
1370         .name = DSCP,
1371         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1372         .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDSCP),
1373         .validator = dscpValidator,
1374     },
1375     COMMENT_PROP_IPHDR(ipHdrFilter),
1376     {
1377         .name = NULL,
1378     }
1379 };
1380 
1381 
1382 static const virXMLAttr2Struct ipv6Attributes[] = {
1383     COMMON_MAC_PROPS(ipv6HdrFilter),
1384     {
1385         .name = SRCIPADDR,
1386         .datatype = DATATYPE_IPV6ADDR,
1387         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPAddr),
1388     },
1389     {
1390         .name = SRCIPMASK,
1391         .datatype = DATATYPE_IPV6MASK,
1392         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPMask),
1393     },
1394     {
1395         .name = DSTIPADDR,
1396         .datatype = DATATYPE_IPV6ADDR,
1397         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
1398     },
1399     {
1400         .name = DSTIPMASK,
1401         .datatype = DATATYPE_IPV6MASK,
1402         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPMask),
1403     },
1404     {
1405         .name = "protocol",
1406         .datatype = DATATYPE_STRING | DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1407         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataProtocolID),
1408         .validator = checkIPProtocolID,
1409         .formatter = formatIPProtocolID,
1410     },
1411     {
1412         .name = SRCPORTSTART,
1413         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1414         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortStart),
1415     },
1416     {
1417         .name = SRCPORTEND,
1418         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1419         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortEnd),
1420     },
1421     {
1422         .name = DSTPORTSTART,
1423         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1424         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortStart),
1425     },
1426     {
1427         .name = DSTPORTEND,
1428         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
1429         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortEnd),
1430     },
1431     {
1432         .name = "type",
1433         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1434         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeStart),
1435     },
1436     {
1437         .name = "typeend",
1438         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1439         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeEnd),
1440     },
1441     {
1442         .name = "code",
1443         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1444         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeStart),
1445     },
1446     {
1447         .name = "codeend",
1448         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1449         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeEnd),
1450     },
1451     COMMENT_PROP_IPHDR(ipv6HdrFilter),
1452     {
1453         .name = NULL,
1454     }
1455 };
1456 
1457 
1458 #define COMMON_L3_MAC_PROPS(STRUCT) \
1459     {\
1460         .name = SRCMACADDR,\
1461         .datatype = DATATYPE_MACADDR,\
1462         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.dataSrcMACAddr),\
1463     }
1464 
1465 #define COMMON_IP_PROPS(STRUCT, ADDRTYPE, MASKTYPE) \
1466     COMMON_L3_MAC_PROPS(STRUCT),\
1467     {\
1468         .name = SRCIPADDR,\
1469         .datatype = ADDRTYPE,\
1470         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPAddr),\
1471     },\
1472     {\
1473         .name = SRCIPMASK,\
1474         .datatype = MASKTYPE,\
1475         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPMask),\
1476     },\
1477     {\
1478         .name = DSTIPADDR,\
1479         .datatype = ADDRTYPE,\
1480         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPAddr),\
1481     },\
1482     {\
1483         .name = DSTIPMASK,\
1484         .datatype = MASKTYPE,\
1485         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPMask),\
1486     },\
1487     {\
1488         .name = SRCIPFROM,\
1489         .datatype = ADDRTYPE,\
1490         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPFrom),\
1491     },\
1492     {\
1493         .name = SRCIPTO,\
1494         .datatype = ADDRTYPE,\
1495         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPTo),\
1496     },\
1497     {\
1498         .name = DSTIPFROM,\
1499         .datatype = ADDRTYPE,\
1500         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPFrom),\
1501     },\
1502     {\
1503         .name = DSTIPTO,\
1504         .datatype = DATATYPE_IPADDR,\
1505         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPTo),\
1506     },\
1507     {\
1508         .name = DSCP,\
1509         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,\
1510         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDSCP),\
1511         .validator = dscpValidator,\
1512     },\
1513     {\
1514         .name = "connlimit-above",\
1515         .datatype = DATATYPE_UINT16,\
1516         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataConnlimitAbove),\
1517     },\
1518     {\
1519         .name = STATE,\
1520         .datatype = DATATYPE_STRING,\
1521         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataState),\
1522         .validator = stateValidator,\
1523         .formatter = stateFormatter,\
1524     },\
1525     {\
1526         .name = IPSET,\
1527         .datatype = DATATYPE_IPSETNAME,\
1528         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSet),\
1529         .validator = ipsetValidator,\
1530         .formatter = ipsetFormatter,\
1531     },\
1532     {\
1533         .name = IPSETFLAGS,\
1534         .datatype = DATATYPE_IPSETFLAGS,\
1535         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSetFlags),\
1536         .validator = ipsetFlagsValidator,\
1537         .formatter = ipsetFlagsFormatter,\
1538     }
1539 
1540 #define COMMON_PORT_PROPS(STRUCT) \
1541     {\
1542         .name = SRCPORTSTART,\
1543         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
1544         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortStart),\
1545     },\
1546     {\
1547         .name = SRCPORTEND,\
1548         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
1549         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortEnd),\
1550     },\
1551     {\
1552         .name = DSTPORTSTART,\
1553         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
1554         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortStart),\
1555     },\
1556     {\
1557         .name = DSTPORTEND,\
1558         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,\
1559         .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortEnd),\
1560     }
1561 
1562 static const virXMLAttr2Struct tcpAttributes[] = {
1563     COMMON_IP_PROPS(tcpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1564     COMMON_PORT_PROPS(tcpHdrFilter),
1565     {
1566         .name = "option",
1567         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1568         .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPOption),
1569     },
1570     {
1571         .name = "flags",
1572         .datatype = DATATYPE_STRING,
1573         .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPFlags),
1574         .validator = tcpFlagsValidator,
1575         .formatter = tcpFlagsFormatter,
1576     },
1577     COMMENT_PROP_IPHDR(tcpHdrFilter),
1578     {
1579         .name = NULL,
1580     }
1581 };
1582 
1583 static const virXMLAttr2Struct udpAttributes[] = {
1584     COMMON_IP_PROPS(udpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1585     COMMON_PORT_PROPS(udpHdrFilter),
1586     COMMENT_PROP_IPHDR(udpHdrFilter),
1587     {
1588         .name = NULL,
1589     }
1590 };
1591 
1592 static const virXMLAttr2Struct udpliteAttributes[] = {
1593     COMMON_IP_PROPS(udpliteHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1594     COMMENT_PROP_IPHDR(udpliteHdrFilter),
1595     {
1596         .name = NULL,
1597     }
1598 };
1599 
1600 static const virXMLAttr2Struct espAttributes[] = {
1601     COMMON_IP_PROPS(espHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1602     COMMENT_PROP_IPHDR(espHdrFilter),
1603     {
1604         .name = NULL,
1605     }
1606 };
1607 
1608 static const virXMLAttr2Struct ahAttributes[] = {
1609     COMMON_IP_PROPS(ahHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1610     COMMENT_PROP_IPHDR(ahHdrFilter),
1611     {
1612         .name = NULL,
1613     }
1614 };
1615 
1616 static const virXMLAttr2Struct sctpAttributes[] = {
1617     COMMON_IP_PROPS(sctpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1618     COMMON_PORT_PROPS(sctpHdrFilter),
1619     COMMENT_PROP_IPHDR(sctpHdrFilter),
1620     {
1621         .name = NULL,
1622     }
1623 };
1624 
1625 
1626 static const virXMLAttr2Struct icmpAttributes[] = {
1627     COMMON_IP_PROPS(icmpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1628     {
1629         .name = "type",
1630         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1631         .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPType),
1632     },
1633     {
1634         .name = "code",
1635         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1636         .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPCode),
1637     },
1638     COMMENT_PROP_IPHDR(icmpHdrFilter),
1639     {
1640         .name = NULL,
1641     }
1642 };
1643 
1644 
1645 static const virXMLAttr2Struct allAttributes[] = {
1646     COMMON_IP_PROPS(allHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1647     COMMENT_PROP_IPHDR(allHdrFilter),
1648     {
1649         .name = NULL,
1650     }
1651 };
1652 
1653 
1654 static const virXMLAttr2Struct igmpAttributes[] = {
1655     COMMON_IP_PROPS(igmpHdrFilter, DATATYPE_IPADDR, DATATYPE_IPMASK),
1656     COMMENT_PROP_IPHDR(igmpHdrFilter),
1657     {
1658         .name = NULL,
1659     }
1660 };
1661 
1662 
1663 static const virXMLAttr2Struct tcpipv6Attributes[] = {
1664     COMMON_IP_PROPS(tcpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1665     COMMON_PORT_PROPS(tcpHdrFilter),
1666     {
1667         .name = "option",
1668         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1669         .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPOption),
1670     },
1671     COMMENT_PROP_IPHDR(tcpHdrFilter),
1672     {
1673         .name = NULL,
1674     }
1675 };
1676 
1677 static const virXMLAttr2Struct udpipv6Attributes[] = {
1678     COMMON_IP_PROPS(udpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1679     COMMON_PORT_PROPS(udpHdrFilter),
1680     COMMENT_PROP_IPHDR(udpHdrFilter),
1681     {
1682         .name = NULL,
1683     }
1684 };
1685 
1686 
1687 static const virXMLAttr2Struct udpliteipv6Attributes[] = {
1688     COMMON_IP_PROPS(udpliteHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1689     COMMENT_PROP_IPHDR(udpliteHdrFilter),
1690     {
1691         .name = NULL,
1692     }
1693 };
1694 
1695 
1696 static const virXMLAttr2Struct espipv6Attributes[] = {
1697     COMMON_IP_PROPS(espHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1698     COMMENT_PROP_IPHDR(espHdrFilter),
1699     {
1700         .name = NULL,
1701     }
1702 };
1703 
1704 
1705 static const virXMLAttr2Struct ahipv6Attributes[] = {
1706     COMMON_IP_PROPS(ahHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1707     COMMENT_PROP_IPHDR(ahHdrFilter),
1708     {
1709         .name = NULL,
1710     }
1711 };
1712 
1713 
1714 static const virXMLAttr2Struct sctpipv6Attributes[] = {
1715     COMMON_IP_PROPS(sctpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1716     COMMON_PORT_PROPS(sctpHdrFilter),
1717     COMMENT_PROP_IPHDR(sctpHdrFilter),
1718     {
1719         .name = NULL,
1720     }
1721 };
1722 
1723 
1724 static const virXMLAttr2Struct icmpv6Attributes[] = {
1725     COMMON_IP_PROPS(icmpHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1726     {
1727         .name = "type",
1728         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1729         .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPType),
1730     },
1731     {
1732         .name = "code",
1733         .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
1734         .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPCode),
1735     },
1736     COMMENT_PROP_IPHDR(icmpHdrFilter),
1737     {
1738         .name = NULL,
1739     }
1740 };
1741 
1742 
1743 static const virXMLAttr2Struct allipv6Attributes[] = {
1744     COMMON_IP_PROPS(allHdrFilter, DATATYPE_IPV6ADDR, DATATYPE_IPV6MASK),
1745     COMMENT_PROP_IPHDR(allHdrFilter),
1746     {
1747         .name = NULL,
1748     }
1749 };
1750 
1751 
1752 typedef struct _virAttributes virAttributes;
1753 struct _virAttributes {
1754     const char *id;
1755     const virXMLAttr2Struct *att;
1756     virNWFilterRuleProtocolType prtclType;
1757 };
1758 
1759 #define PROTOCOL_ENTRY(ID, ATT, PRTCLTYPE) \
1760     { .id = ID, .att = ATT, .prtclType = PRTCLTYPE }
1761 #define PROTOCOL_ENTRY_LAST { .id = NULL }
1762 
1763 
1764 static const virAttributes virAttr[] = {
1765     PROTOCOL_ENTRY("arp",     arpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ARP),
1766     PROTOCOL_ENTRY("rarp",    arpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_RARP),
1767     PROTOCOL_ENTRY("mac",     macAttributes,     VIR_NWFILTER_RULE_PROTOCOL_MAC),
1768     PROTOCOL_ENTRY("vlan",    vlanAttributes,    VIR_NWFILTER_RULE_PROTOCOL_VLAN),
1769     PROTOCOL_ENTRY("stp",     stpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_STP),
1770     PROTOCOL_ENTRY("ip",      ipAttributes,      VIR_NWFILTER_RULE_PROTOCOL_IP),
1771     PROTOCOL_ENTRY("ipv6",    ipv6Attributes,    VIR_NWFILTER_RULE_PROTOCOL_IPV6),
1772     PROTOCOL_ENTRY("tcp",     tcpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_TCP),
1773     PROTOCOL_ENTRY("udp",     udpAttributes,     VIR_NWFILTER_RULE_PROTOCOL_UDP),
1774     PROTOCOL_ENTRY("udplite", udpliteAttributes, VIR_NWFILTER_RULE_PROTOCOL_UDPLITE),
1775     PROTOCOL_ENTRY("esp",     espAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ESP),
1776     PROTOCOL_ENTRY("ah",      ahAttributes,      VIR_NWFILTER_RULE_PROTOCOL_AH),
1777     PROTOCOL_ENTRY("sctp",    sctpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_SCTP),
1778     PROTOCOL_ENTRY("icmp",    icmpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_ICMP),
1779     PROTOCOL_ENTRY("all",     allAttributes,     VIR_NWFILTER_RULE_PROTOCOL_ALL),
1780     PROTOCOL_ENTRY("igmp",    igmpAttributes,    VIR_NWFILTER_RULE_PROTOCOL_IGMP),
1781     PROTOCOL_ENTRY("tcp-ipv6",     tcpipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6),
1782     PROTOCOL_ENTRY("udp-ipv6",     udpipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6),
1783     PROTOCOL_ENTRY("udplite-ipv6", udpliteipv6Attributes, VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6),
1784     PROTOCOL_ENTRY("esp-ipv6",     espipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6),
1785     PROTOCOL_ENTRY("ah-ipv6",      ahipv6Attributes,      VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6),
1786     PROTOCOL_ENTRY("sctp-ipv6",    sctpipv6Attributes,    VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6),
1787     PROTOCOL_ENTRY("icmpv6",       icmpv6Attributes,      VIR_NWFILTER_RULE_PROTOCOL_ICMPV6),
1788     PROTOCOL_ENTRY("all-ipv6",     allipv6Attributes,     VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6),
1789     PROTOCOL_ENTRY_LAST
1790 };
1791 
1792 
1793 static int
virNWFilterRuleDetailsParse(xmlNodePtr node,virNWFilterRuleDef * nwf,const virXMLAttr2Struct * att)1794 virNWFilterRuleDetailsParse(xmlNodePtr node,
1795                             virNWFilterRuleDef *nwf,
1796                             const virXMLAttr2Struct *att)
1797 {
1798     int rc = 0, g_rc = 0;
1799     int idx = 0;
1800     char *prop;
1801     bool found = false;
1802     enum attrDatatype datatype, att_datatypes;
1803     virNWFilterEntryItemFlags *flags, match_flag = 0, flags_set = 0;
1804     nwItemDesc *item;
1805     int int_val;
1806     unsigned int uint_val;
1807     union data data;
1808     valueValidator validator;
1809     char *match = virXMLPropString(node, "match");
1810     virSocketAddr ipaddr;
1811     int base;
1812 
1813     if (match && STREQ(match, "no"))
1814         match_flag = NWFILTER_ENTRY_ITEM_FLAG_IS_NEG;
1815     VIR_FREE(match);
1816     match = NULL;
1817 
1818     while (att[idx].name != NULL) {
1819         prop = virXMLPropString(node, att[idx].name);
1820 
1821         VIR_WARNINGS_NO_CAST_ALIGN
1822         item = (nwItemDesc *)((char *)nwf + att[idx].dataIdx);
1823         VIR_WARNINGS_RESET
1824         flags = &item->flags;
1825         flags_set = match_flag;
1826 
1827         if (prop) {
1828             found = false;
1829 
1830             validator = NULL;
1831 
1832             if (STRPREFIX(prop, "$")) {
1833                 flags_set |= NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR;
1834                 if (virNWFilterRuleDefAddVar(nwf,
1835                                              item,
1836                                              &prop[1]) < 0)
1837                     rc = -1;
1838                 found = true;
1839             }
1840 
1841             datatype = 1;
1842 
1843             att_datatypes = att[idx].datatype;
1844 
1845             while (datatype <= DATATYPE_LAST && !found && rc == 0) {
1846                 if ((att_datatypes & datatype)) {
1847 
1848                     att_datatypes ^= datatype;
1849 
1850                     validator = att[idx].validator;
1851 
1852                     base = 10;
1853 
1854                     switch (datatype) {
1855                         case DATATYPE_UINT8_HEX:
1856                             base = 16;
1857                             G_GNUC_FALLTHROUGH;
1858                         case DATATYPE_UINT8:
1859                             if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
1860                                 if (uint_val <= 0xff) {
1861                                     item->u.u8 = uint_val;
1862                                     found = true;
1863                                     data.ui = uint_val;
1864                                 } else {
1865                                     rc = -1;
1866                                 }
1867                             } else {
1868                                 rc = -1;
1869                             }
1870                         break;
1871 
1872                         case DATATYPE_UINT16_HEX:
1873                             base = 16;
1874                             G_GNUC_FALLTHROUGH;
1875                         case DATATYPE_UINT16:
1876                             if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
1877                                 if (uint_val <= 0xffff) {
1878                                     item->u.u16 = uint_val;
1879                                     found = true;
1880                                     data.ui = uint_val;
1881                                 } else {
1882                                     rc = -1;
1883                                 }
1884                             } else {
1885                                 rc = -1;
1886                             }
1887                         break;
1888 
1889                         case DATATYPE_UINT32_HEX:
1890                             base = 16;
1891                             G_GNUC_FALLTHROUGH;
1892                         case DATATYPE_UINT32:
1893                             if (virStrToLong_ui(prop, NULL, base, &uint_val) >= 0) {
1894                                 item->u.u32 = uint_val;
1895                                 found = true;
1896                                 data.ui = uint_val;
1897                             } else {
1898                                 rc = -1;
1899                             }
1900                         break;
1901 
1902                         case DATATYPE_IPADDR:
1903                             if (virSocketAddrParseIPv4(&item->u.ipaddr, prop) < 0)
1904                                 rc = -1;
1905                             found = true;
1906                         break;
1907 
1908                         case DATATYPE_IPMASK:
1909                             if (virStrToLong_ui(prop, NULL, 10, &uint_val) == 0) {
1910                                 if (uint_val <= 32) {
1911                                     if (!validator)
1912                                         item->u.u8 = (uint8_t)uint_val;
1913                                     found = true;
1914                                     data.ui = uint_val;
1915                                 } else {
1916                                     rc = -1;
1917                                 }
1918                             } else {
1919                                 if (virSocketAddrParseIPv4(&ipaddr, prop) < 0) {
1920                                     rc = -1;
1921                                 } else {
1922                                     int_val = virSocketAddrGetNumNetmaskBits(&ipaddr);
1923                                     if (int_val >= 0)
1924                                         item->u.u8 = int_val;
1925                                     else
1926                                         rc = -1;
1927                                     found = true;
1928                                 }
1929                             }
1930                         break;
1931 
1932                         case DATATYPE_MACADDR:
1933                             if (virMacAddrParse(prop,
1934                                                 &item->u.macaddr) < 0) {
1935                                 rc = -1;
1936                             }
1937                             found = true;
1938                         break;
1939 
1940                         case DATATYPE_MACMASK:
1941                             validator = checkMACMask;
1942                             if (virMacAddrParse(prop,
1943                                                 &item->u.macaddr) < 0) {
1944                                 rc = -1;
1945                             }
1946                             data.v = &item->u.macaddr;
1947                             found = true;
1948                         break;
1949 
1950                         case DATATYPE_IPV6ADDR:
1951                             if (virSocketAddrParseIPv6(&item->u.ipaddr, prop) < 0)
1952                                 rc = -1;
1953                             found = true;
1954                         break;
1955 
1956                         case DATATYPE_IPV6MASK:
1957                             if (virStrToLong_ui(prop, NULL, 10, &uint_val) == 0) {
1958                                 if (uint_val <= 128) {
1959                                     if (!validator)
1960                                         item->u.u8 = (uint8_t)uint_val;
1961                                     found = true;
1962                                     data.ui = uint_val;
1963                                 } else {
1964                                     rc = -1;
1965                                 }
1966                             } else {
1967                                 if (virSocketAddrParseIPv6(&ipaddr, prop) < 0) {
1968                                     rc = -1;
1969                                 } else {
1970                                     int_val = virSocketAddrGetNumNetmaskBits(&ipaddr);
1971                                     if (int_val >= 0)
1972                                         item->u.u8 = int_val;
1973                                     else
1974                                         rc = -1;
1975                                     found = true;
1976                                 }
1977                             }
1978                         break;
1979 
1980                         case DATATYPE_STRING:
1981                         case DATATYPE_IPSETFLAGS:
1982                         case DATATYPE_IPSETNAME:
1983                             if (!validator) {
1984                                 /* not supported */
1985                                 rc = -1;
1986                                 break;
1987                             }
1988                             data.c = prop;
1989                             found = true;
1990                         break;
1991 
1992                         case DATATYPE_STRINGCOPY:
1993                             if (!(item->u.string =
1994                                   virNWFilterRuleDefAddString(nwf, prop,
1995                                                        att[idx].maxstrlen))) {
1996                                 rc = -1;
1997                                 break;
1998                             }
1999                             data.c = item->u.string;
2000                             found = true;
2001                         break;
2002 
2003                         case DATATYPE_BOOLEAN:
2004                             if (STREQ(prop, "true") ||
2005                                 STREQ(prop, "1") ||
2006                                 STREQ(prop, "yes"))
2007                                 item->u.boolean = true;
2008                             else
2009                                 item->u.boolean = false;
2010 
2011                             data.ui = item->u.boolean;
2012                             found = true;
2013                         break;
2014 
2015                         case DATATYPE_LAST:
2016                         default:
2017                         break;
2018                     }
2019                 }
2020 
2021                 if (rc != 0 && att_datatypes != 0) {
2022                     rc = 0;
2023                     found = false;
2024                 }
2025 
2026                 datatype <<= 1;
2027             } /* while */
2028 
2029             if (found && rc == 0) {
2030                 *flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS | flags_set;
2031                 item->datatype = datatype >> 1;
2032                 if (validator) {
2033                     if (!validator(datatype >> 1, &data, nwf, item)) {
2034                         rc = -1;
2035                         *flags = 0;
2036                     }
2037                 }
2038             }
2039 
2040             if (!found || rc) {
2041                 virReportError(VIR_ERR_INTERNAL_ERROR,
2042                                _("%s has illegal value %s"),
2043                                att[idx].name, prop);
2044                 rc = -1;
2045             }
2046             VIR_FREE(prop);
2047         }
2048 
2049         if (rc) {
2050             g_rc = rc;
2051             rc = 0;
2052         }
2053 
2054         idx++;
2055     }
2056 
2057     return g_rc;
2058 }
2059 
2060 
2061 static virNWFilterIncludeDef *
virNWFilterIncludeParse(xmlNodePtr cur)2062 virNWFilterIncludeParse(xmlNodePtr cur)
2063 {
2064     virNWFilterIncludeDef *ret;
2065 
2066     ret = g_new0(virNWFilterIncludeDef, 1);
2067 
2068     ret->filterref = virXMLPropString(cur, "filter");
2069     if (!ret->filterref) {
2070         virReportError(VIR_ERR_INTERNAL_ERROR,
2071                        "%s",
2072                        _("rule node requires action attribute"));
2073         goto err_exit;
2074     }
2075 
2076     ret->params = virNWFilterParseParamAttributes(cur);
2077     if (!ret->params)
2078         goto err_exit;
2079 
2080     return ret;
2081 
2082  err_exit:
2083     virNWFilterIncludeDefFree(ret);
2084     return NULL;
2085 }
2086 
2087 
2088 static void
virNWFilterRuleDefFixupIPSet(ipHdrDataDef * ipHdr)2089 virNWFilterRuleDefFixupIPSet(ipHdrDataDef *ipHdr)
2090 {
2091     if (HAS_ENTRY_ITEM(&ipHdr->dataIPSet) &&
2092         !HAS_ENTRY_ITEM(&ipHdr->dataIPSetFlags)) {
2093         ipHdr->dataIPSetFlags.flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS;
2094         ipHdr->dataIPSetFlags.u.ipset.numFlags = 1;
2095         ipHdr->dataIPSetFlags.u.ipset.flags = 1;
2096     } else {
2097         ipHdr->dataIPSet.flags = 0;
2098         ipHdr->dataIPSetFlags.flags = 0;
2099     }
2100 }
2101 
2102 
2103 /*
2104  * virNWFilterRuleValidate
2105  *
2106  * Perform some basic rule validation to prevent rules from being
2107  * defined that cannot be instantiated.
2108  */
2109 static int
virNWFilterRuleValidate(virNWFilterRuleDef * rule)2110 virNWFilterRuleValidate(virNWFilterRuleDef *rule)
2111 {
2112     int ret = 0;
2113     portDataDef *portData = NULL;
2114     nwItemDesc *dataProtocolID = NULL;
2115     const char *protocol = NULL;
2116 
2117     switch (rule->prtclType) {
2118     case VIR_NWFILTER_RULE_PROTOCOL_IP:
2119         portData = &rule->p.ipHdrFilter.portData;
2120         protocol = "IP";
2121         dataProtocolID = &rule->p.ipHdrFilter.ipHdr.dataProtocolID;
2122         G_GNUC_FALLTHROUGH;
2123     case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
2124         if (portData == NULL) {
2125             portData = &rule->p.ipv6HdrFilter.portData;
2126             protocol = "IPv6";
2127             dataProtocolID = &rule->p.ipv6HdrFilter.ipHdr.dataProtocolID;
2128         }
2129         if (HAS_ENTRY_ITEM(&portData->dataSrcPortStart) ||
2130             HAS_ENTRY_ITEM(&portData->dataDstPortStart) ||
2131             HAS_ENTRY_ITEM(&portData->dataSrcPortEnd) ||
2132             HAS_ENTRY_ITEM(&portData->dataDstPortEnd)) {
2133             if (HAS_ENTRY_ITEM(dataProtocolID)) {
2134                 switch (dataProtocolID->u.u8) {
2135                 case 6:   /* tcp */
2136                 case 17:  /* udp */
2137                 case 33:  /* dccp */
2138                 case 132: /* sctp */
2139                     break;
2140                 default:
2141                     ret = -1;
2142                 }
2143             } else {
2144                 ret = -1;
2145             }
2146             if (ret < 0) {
2147                 virReportError(VIR_ERR_INTERNAL_ERROR,
2148                                _("%s rule with port specification requires "
2149                                  "protocol specification with protocol to be "
2150                                  "either one of tcp(6), udp(17), dccp(33), or "
2151                                  "sctp(132)"), protocol);
2152             }
2153         }
2154         break;
2155     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
2156     case VIR_NWFILTER_RULE_PROTOCOL_MAC:
2157     case VIR_NWFILTER_RULE_PROTOCOL_VLAN:
2158     case VIR_NWFILTER_RULE_PROTOCOL_STP:
2159     case VIR_NWFILTER_RULE_PROTOCOL_ARP:
2160     case VIR_NWFILTER_RULE_PROTOCOL_RARP:
2161     case VIR_NWFILTER_RULE_PROTOCOL_TCP:
2162     case VIR_NWFILTER_RULE_PROTOCOL_ICMP:
2163     case VIR_NWFILTER_RULE_PROTOCOL_IGMP:
2164     case VIR_NWFILTER_RULE_PROTOCOL_UDP:
2165     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITE:
2166     case VIR_NWFILTER_RULE_PROTOCOL_ESP:
2167     case VIR_NWFILTER_RULE_PROTOCOL_AH:
2168     case VIR_NWFILTER_RULE_PROTOCOL_SCTP:
2169     case VIR_NWFILTER_RULE_PROTOCOL_ALL:
2170     case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6:
2171     case VIR_NWFILTER_RULE_PROTOCOL_ICMPV6:
2172     case VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6:
2173     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6:
2174     case VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6:
2175     case VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6:
2176     case VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6:
2177     case VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6:
2178         break;
2179     case VIR_NWFILTER_RULE_PROTOCOL_LAST:
2180     default:
2181         virReportEnumRangeError(virNWFilterRuleProtocolType, rule->prtclType);
2182         return -1;
2183     }
2184 
2185     return ret;
2186 }
2187 
2188 
2189 static void
virNWFilterRuleDefFixup(virNWFilterRuleDef * rule)2190 virNWFilterRuleDefFixup(virNWFilterRuleDef *rule)
2191 {
2192 #define COPY_NEG_SIGN(A, B) \
2193     (A).flags = ((A).flags & ~NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) | \
2194                 ((B).flags &  NWFILTER_ENTRY_ITEM_FLAG_IS_NEG);
2195 
2196     switch (rule->prtclType) {
2197     case VIR_NWFILTER_RULE_PROTOCOL_MAC:
2198         COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataSrcMACMask,
2199                       rule->p.ethHdrFilter.ethHdr.dataSrcMACAddr);
2200         COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataDstMACMask,
2201                       rule->p.ethHdrFilter.ethHdr.dataDstMACAddr);
2202     break;
2203 
2204     case VIR_NWFILTER_RULE_PROTOCOL_VLAN:
2205         COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataSrcMACMask,
2206                       rule->p.vlanHdrFilter.ethHdr.dataSrcMACAddr);
2207         COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataDstMACMask,
2208                       rule->p.vlanHdrFilter.ethHdr.dataDstMACAddr);
2209     break;
2210 
2211     case VIR_NWFILTER_RULE_PROTOCOL_STP:
2212         COPY_NEG_SIGN(rule->p.stpHdrFilter.ethHdr.dataSrcMACMask,
2213                       rule->p.stpHdrFilter.ethHdr.dataSrcMACAddr);
2214         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootPriHi,
2215                       rule->p.stpHdrFilter.dataRootPri);
2216         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootAddrMask,
2217                       rule->p.stpHdrFilter.dataRootAddr);
2218         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataRootCostHi,
2219                       rule->p.stpHdrFilter.dataRootCost);
2220         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataSndrPrioHi,
2221                       rule->p.stpHdrFilter.dataSndrPrio);
2222         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataSndrAddrMask,
2223                       rule->p.stpHdrFilter.dataSndrAddr);
2224         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataPortHi,
2225                       rule->p.stpHdrFilter.dataPort);
2226         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataAgeHi,
2227                       rule->p.stpHdrFilter.dataAge);
2228         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataMaxAgeHi,
2229                       rule->p.stpHdrFilter.dataMaxAge);
2230         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataHelloTimeHi,
2231                       rule->p.stpHdrFilter.dataHelloTime);
2232         COPY_NEG_SIGN(rule->p.stpHdrFilter.dataFwdDelayHi,
2233                       rule->p.stpHdrFilter.dataFwdDelay);
2234     break;
2235 
2236     case VIR_NWFILTER_RULE_PROTOCOL_IP:
2237         COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcIPMask,
2238                       rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr);
2239         COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstIPMask,
2240                       rule->p.ipHdrFilter.ipHdr.dataDstIPAddr);
2241         virNWFilterRuleDefFixupIPSet(&rule->p.ipHdrFilter.ipHdr);
2242     break;
2243 
2244     case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
2245         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask,
2246                       rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr);
2247         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask,
2248                       rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr);
2249         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPTypeEnd,
2250                       rule->p.ipv6HdrFilter.dataICMPTypeStart);
2251         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeStart,
2252                       rule->p.ipv6HdrFilter.dataICMPTypeStart);
2253         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeEnd,
2254                       rule->p.ipv6HdrFilter.dataICMPTypeStart);
2255         virNWFilterRuleDefFixupIPSet(&rule->p.ipv6HdrFilter.ipHdr);
2256     break;
2257 
2258     case VIR_NWFILTER_RULE_PROTOCOL_ARP:
2259     case VIR_NWFILTER_RULE_PROTOCOL_RARP:
2260     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
2261     break;
2262 
2263     case VIR_NWFILTER_RULE_PROTOCOL_TCP:
2264     case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6:
2265         COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPMask,
2266                       rule->p.tcpHdrFilter.ipHdr.dataSrcIPAddr);
2267         COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPMask,
2268                       rule->p.tcpHdrFilter.ipHdr.dataDstIPAddr);
2269         COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPTo,
2270                       rule->p.tcpHdrFilter.ipHdr.dataSrcIPFrom);
2271         COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPTo,
2272                       rule->p.tcpHdrFilter.ipHdr.dataDstIPFrom);
2273         COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataSrcPortEnd,
2274                       rule->p.tcpHdrFilter.portData.dataSrcPortStart);
2275         COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortStart,
2276                       rule->p.tcpHdrFilter.portData.dataSrcPortStart);
2277         COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortEnd,
2278                       rule->p.tcpHdrFilter.portData.dataSrcPortStart);
2279         virNWFilterRuleDefFixupIPSet(&rule->p.tcpHdrFilter.ipHdr);
2280     break;
2281 
2282     case VIR_NWFILTER_RULE_PROTOCOL_UDP:
2283     case VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6:
2284         COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPMask,
2285                       rule->p.udpHdrFilter.ipHdr.dataSrcIPAddr);
2286         COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPMask,
2287                       rule->p.udpHdrFilter.ipHdr.dataDstIPAddr);
2288         COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPTo,
2289                       rule->p.udpHdrFilter.ipHdr.dataSrcIPFrom);
2290         COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPTo,
2291                       rule->p.udpHdrFilter.ipHdr.dataDstIPFrom);
2292         COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataSrcPortEnd,
2293                       rule->p.udpHdrFilter.portData.dataSrcPortStart);
2294         COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortStart,
2295                       rule->p.udpHdrFilter.portData.dataSrcPortStart);
2296         COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortEnd,
2297                       rule->p.udpHdrFilter.portData.dataSrcPortStart);
2298         virNWFilterRuleDefFixupIPSet(&rule->p.udpHdrFilter.ipHdr);
2299     break;
2300 
2301     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITE:
2302     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6:
2303         COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataSrcIPMask,
2304                       rule->p.udpliteHdrFilter.ipHdr.dataSrcIPAddr);
2305         COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataDstIPMask,
2306                       rule->p.udpliteHdrFilter.ipHdr.dataDstIPAddr);
2307         COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataSrcIPTo,
2308                       rule->p.udpliteHdrFilter.ipHdr.dataSrcIPFrom);
2309         COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataDstIPTo,
2310                       rule->p.udpliteHdrFilter.ipHdr.dataDstIPFrom);
2311         virNWFilterRuleDefFixupIPSet(&rule->p.udpliteHdrFilter.ipHdr);
2312     break;
2313 
2314     case VIR_NWFILTER_RULE_PROTOCOL_ESP:
2315     case VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6:
2316         COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataSrcIPMask,
2317                       rule->p.espHdrFilter.ipHdr.dataSrcIPAddr);
2318         COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataDstIPMask,
2319                       rule->p.espHdrFilter.ipHdr.dataDstIPAddr);
2320         COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataSrcIPTo,
2321                       rule->p.espHdrFilter.ipHdr.dataSrcIPFrom);
2322         COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataDstIPTo,
2323                       rule->p.espHdrFilter.ipHdr.dataDstIPFrom);
2324         virNWFilterRuleDefFixupIPSet(&rule->p.espHdrFilter.ipHdr);
2325     break;
2326 
2327     case VIR_NWFILTER_RULE_PROTOCOL_AH:
2328     case VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6:
2329         COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataSrcIPMask,
2330                       rule->p.ahHdrFilter.ipHdr.dataSrcIPAddr);
2331         COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataDstIPMask,
2332                       rule->p.ahHdrFilter.ipHdr.dataDstIPAddr);
2333         COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataSrcIPTo,
2334                       rule->p.ahHdrFilter.ipHdr.dataSrcIPFrom);
2335         COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataDstIPTo,
2336                       rule->p.ahHdrFilter.ipHdr.dataDstIPFrom);
2337         virNWFilterRuleDefFixupIPSet(&rule->p.ahHdrFilter.ipHdr);
2338     break;
2339 
2340     case VIR_NWFILTER_RULE_PROTOCOL_SCTP:
2341     case VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6:
2342         COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPMask,
2343                       rule->p.sctpHdrFilter.ipHdr.dataSrcIPAddr);
2344         COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPMask,
2345                       rule->p.sctpHdrFilter.ipHdr.dataDstIPAddr);
2346         COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPTo,
2347                       rule->p.sctpHdrFilter.ipHdr.dataSrcIPFrom);
2348         COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPTo,
2349                       rule->p.sctpHdrFilter.ipHdr.dataDstIPFrom);
2350         COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataSrcPortEnd,
2351                       rule->p.sctpHdrFilter.portData.dataSrcPortStart);
2352         COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortStart,
2353                       rule->p.sctpHdrFilter.portData.dataSrcPortStart);
2354         COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortEnd,
2355                       rule->p.sctpHdrFilter.portData.dataSrcPortStart);
2356         virNWFilterRuleDefFixupIPSet(&rule->p.sctpHdrFilter.ipHdr);
2357     break;
2358 
2359     case VIR_NWFILTER_RULE_PROTOCOL_ICMP:
2360     case VIR_NWFILTER_RULE_PROTOCOL_ICMPV6:
2361         COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPMask,
2362                       rule->p.icmpHdrFilter.ipHdr.dataSrcIPAddr);
2363         COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPMask,
2364                       rule->p.icmpHdrFilter.ipHdr.dataDstIPAddr);
2365         COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPTo,
2366                       rule->p.icmpHdrFilter.ipHdr.dataSrcIPFrom);
2367         COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPTo,
2368                       rule->p.icmpHdrFilter.ipHdr.dataDstIPFrom);
2369         COPY_NEG_SIGN(rule->p.icmpHdrFilter.dataICMPCode,
2370                       rule->p.icmpHdrFilter.dataICMPType);
2371         virNWFilterRuleDefFixupIPSet(&rule->p.icmpHdrFilter.ipHdr);
2372     break;
2373 
2374     case VIR_NWFILTER_RULE_PROTOCOL_ALL:
2375     case VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6:
2376         COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPMask,
2377                       rule->p.allHdrFilter.ipHdr.dataSrcIPAddr);
2378         COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPMask,
2379                       rule->p.allHdrFilter.ipHdr.dataDstIPAddr);
2380         COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPTo,
2381                       rule->p.allHdrFilter.ipHdr.dataSrcIPFrom);
2382         COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPTo,
2383                       rule->p.allHdrFilter.ipHdr.dataDstIPFrom);
2384     break;
2385 
2386     case VIR_NWFILTER_RULE_PROTOCOL_IGMP:
2387         COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPMask,
2388                       rule->p.igmpHdrFilter.ipHdr.dataSrcIPAddr);
2389         COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPMask,
2390                       rule->p.igmpHdrFilter.ipHdr.dataDstIPAddr);
2391         COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPTo,
2392                       rule->p.igmpHdrFilter.ipHdr.dataSrcIPFrom);
2393         COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPTo,
2394                       rule->p.igmpHdrFilter.ipHdr.dataDstIPFrom);
2395         virNWFilterRuleDefFixupIPSet(&rule->p.igmpHdrFilter.ipHdr);
2396     break;
2397 
2398     case VIR_NWFILTER_RULE_PROTOCOL_LAST:
2399     break;
2400     }
2401 #undef COPY_NEG_SIGN
2402 }
2403 
2404 
2405 static virNWFilterRuleDef *
virNWFilterRuleParse(xmlNodePtr node)2406 virNWFilterRuleParse(xmlNodePtr node)
2407 {
2408     char *action;
2409     char *direction;
2410     char *prio;
2411     char *statematch;
2412     bool found;
2413     int found_i = 0;
2414     int priority;
2415 
2416     xmlNodePtr cur;
2417     virNWFilterRuleDef *ret;
2418 
2419     ret = g_new0(virNWFilterRuleDef, 1);
2420 
2421     action     = virXMLPropString(node, "action");
2422     direction  = virXMLPropString(node, "direction");
2423     prio       = virXMLPropString(node, "priority");
2424     statematch = virXMLPropString(node, "statematch");
2425 
2426     if (!action) {
2427         virReportError(VIR_ERR_INTERNAL_ERROR,
2428                        "%s",
2429                        _("rule node requires action attribute"));
2430         goto err_exit;
2431     }
2432 
2433     if ((ret->action = virNWFilterRuleActionTypeFromString(action)) < 0) {
2434         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
2435                        "%s",
2436                        _("unknown rule action attribute value"));
2437         goto err_exit;
2438     }
2439 
2440     if (!direction) {
2441         virReportError(VIR_ERR_INTERNAL_ERROR,
2442                        "%s",
2443                        _("rule node requires direction attribute"));
2444         goto err_exit;
2445     }
2446 
2447     if ((ret->tt = virNWFilterRuleDirectionTypeFromString(direction)) < 0) {
2448         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
2449                        "%s",
2450                        _("unknown rule direction attribute value"));
2451         goto err_exit;
2452     }
2453 
2454     ret->priority = MAX_RULE_PRIORITY / 2;
2455 
2456     if (prio) {
2457         if (virStrToLong_i(prio, NULL, 10, &priority) >= 0) {
2458             if (priority <= MAX_RULE_PRIORITY &&
2459                 priority >= MIN_RULE_PRIORITY)
2460                 ret->priority = priority;
2461         }
2462     }
2463 
2464     if (statematch &&
2465         (STREQ(statematch, "0") || STRCASEEQ(statematch, "false")))
2466         ret->flags |= RULE_FLAG_NO_STATEMATCH;
2467 
2468     cur = node->children;
2469 
2470     found = false;
2471 
2472     while (cur != NULL) {
2473         if (cur->type == XML_ELEMENT_NODE) {
2474             size_t i = 0;
2475             while (1) {
2476                 if (found)
2477                     i = found_i;
2478 
2479                 if (virXMLNodeNameEqual(cur, virAttr[i].id)) {
2480 
2481                     found_i = i;
2482                     found = true;
2483                     ret->prtclType = virAttr[i].prtclType;
2484 
2485                     if (virNWFilterRuleDetailsParse(cur,
2486                                                     ret,
2487                                                     virAttr[i].att) < 0) {
2488                         goto err_exit;
2489                     }
2490                     if (virNWFilterRuleValidate(ret) < 0)
2491                         goto err_exit;
2492                     break;
2493                 }
2494                 if (!found) {
2495                     i++;
2496                     if (!virAttr[i].id)
2497                         break;
2498                 } else {
2499                    break;
2500                 }
2501             }
2502         }
2503 
2504         cur = cur->next;
2505     }
2506 
2507     virNWFilterRuleDefFixup(ret);
2508 
2509  cleanup:
2510     VIR_FREE(prio);
2511     VIR_FREE(action);
2512     VIR_FREE(direction);
2513     VIR_FREE(statematch);
2514 
2515     return ret;
2516 
2517  err_exit:
2518     virNWFilterRuleDefFree(ret);
2519     ret = NULL;
2520     goto cleanup;
2521 }
2522 
2523 
2524 static bool
virNWFilterIsValidChainName(const char * chainname)2525 virNWFilterIsValidChainName(const char *chainname)
2526 {
2527     if (strlen(chainname) > MAX_CHAIN_SUFFIX_SIZE) {
2528         virReportError(VIR_ERR_INVALID_ARG,
2529                        _("Name of chain is longer than "
2530                          "%u characters"),
2531                        MAX_CHAIN_SUFFIX_SIZE);
2532         return false;
2533     }
2534 
2535     if (chainname[strspn(chainname, VALID_CHAINNAME)] != 0) {
2536         virReportError(VIR_ERR_INVALID_ARG, "%s",
2537                        _("Chain name contains invalid characters"));
2538         return false;
2539     }
2540 
2541     return true;
2542 }
2543 
2544 
2545 /*
2546  * Test whether the name of the chain is supported.
2547  * It current has to have a prefix of either one of the strings found in
2548  * virNWFilterChainSuffixTypeToString().
2549  */
2550 static const char *
virNWFilterIsAllowedChain(const char * chainname)2551 virNWFilterIsAllowedChain(const char *chainname)
2552 {
2553     virNWFilterChainSuffixType i;
2554     const char *name;
2555     char *msg;
2556     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
2557     bool printed = false;
2558 
2559     if (!virNWFilterIsValidChainName(chainname))
2560         return NULL;
2561 
2562     for (i = 0; i < VIR_NWFILTER_CHAINSUFFIX_LAST; i++) {
2563         name = virNWFilterChainSuffixTypeToString(i);
2564         if (i == VIR_NWFILTER_CHAINSUFFIX_ROOT) {
2565             /* allow 'root' as a complete name but not as a prefix */
2566             if (STREQ(chainname, name))
2567                 return name;
2568             if (STRPREFIX(chainname, name))
2569                 return NULL;
2570         }
2571         if (STRPREFIX(chainname, name))
2572             return name;
2573     }
2574 
2575     virBufferAsprintf(&buf,
2576                       _("Invalid chain name '%s'. Please use a chain name "
2577                       "called '%s' or any of the following prefixes: "),
2578                       chainname,
2579                       virNWFilterChainSuffixTypeToString(
2580                           VIR_NWFILTER_CHAINSUFFIX_ROOT));
2581     for (i = 0; i < VIR_NWFILTER_CHAINSUFFIX_LAST; i++) {
2582         if (i == VIR_NWFILTER_CHAINSUFFIX_ROOT)
2583             continue;
2584         if (printed)
2585             virBufferAddLit(&buf, ", ");
2586         virBufferAdd(&buf, virNWFilterChainSuffixTypeToString(i), -1);
2587         printed = true;
2588     }
2589 
2590     msg = virBufferContentAndReset(&buf);
2591 
2592     virReportError(VIR_ERR_INVALID_ARG, "%s", msg);
2593     VIR_FREE(msg);
2594 
2595     return NULL;
2596 }
2597 
2598 
2599 static virNWFilterDef *
virNWFilterDefParseXML(xmlXPathContextPtr ctxt)2600 virNWFilterDefParseXML(xmlXPathContextPtr ctxt)
2601 {
2602     virNWFilterDef *ret;
2603     xmlNodePtr curr = ctxt->node;
2604     char *uuid = NULL;
2605     char *chain = NULL;
2606     char *chain_pri_s = NULL;
2607     virNWFilterEntry *entry;
2608     int chain_priority;
2609     const char *name_prefix;
2610 
2611     ret = g_new0(virNWFilterDef, 1);
2612 
2613     ret->name = virXPathString("string(./@name)", ctxt);
2614     if (!ret->name) {
2615         virReportError(VIR_ERR_INTERNAL_ERROR,
2616                        "%s", _("filter has no name"));
2617         goto cleanup;
2618     }
2619 
2620     chain_pri_s = virXPathString("string(./@priority)", ctxt);
2621     if (chain_pri_s) {
2622         if (virStrToLong_i(chain_pri_s, NULL, 10, &chain_priority) < 0) {
2623             virReportError(VIR_ERR_INVALID_ARG,
2624                            _("Could not parse chain priority '%s'"),
2625                            chain_pri_s);
2626             goto cleanup;
2627         }
2628         if (chain_priority < NWFILTER_MIN_FILTER_PRIORITY ||
2629             chain_priority > NWFILTER_MAX_FILTER_PRIORITY) {
2630             virReportError(VIR_ERR_INVALID_ARG,
2631                            _("Priority '%d' is outside valid "
2632                              "range of [%d,%d]"),
2633                            chain_priority,
2634                            NWFILTER_MIN_FILTER_PRIORITY,
2635                            NWFILTER_MAX_FILTER_PRIORITY);
2636             goto cleanup;
2637         }
2638     }
2639 
2640     chain = virXPathString("string(./@chain)", ctxt);
2641     if (chain) {
2642         name_prefix = virNWFilterIsAllowedChain(chain);
2643         if (name_prefix == NULL)
2644             goto cleanup;
2645         ret->chainsuffix = g_steal_pointer(&chain);
2646 
2647         if (chain_pri_s) {
2648             ret->chainPriority = chain_priority;
2649         } else {
2650             /* assign default priority if none can be found via lookup */
2651             if (intMapGetByString(chain_priorities, name_prefix,
2652                                   0, &ret->chainPriority) < 0) {
2653                 ret->chainPriority = (NWFILTER_MAX_FILTER_PRIORITY +
2654                                       NWFILTER_MIN_FILTER_PRIORITY) / 2;
2655             }
2656         }
2657     } else {
2658         ret->chainsuffix = g_strdup(virNWFilterChainSuffixTypeToString(VIR_NWFILTER_CHAINSUFFIX_ROOT));
2659     }
2660 
2661     uuid = virXPathString("string(./uuid)", ctxt);
2662     ret->uuid_specified = (uuid != NULL);
2663     if (uuid == NULL) {
2664         if (virUUIDGenerate(ret->uuid) < 0) {
2665             virReportError(VIR_ERR_INTERNAL_ERROR,
2666                            "%s", _("unable to generate uuid"));
2667             goto cleanup;
2668         }
2669     } else {
2670         if (virUUIDParse(uuid, ret->uuid) < 0) {
2671             virReportError(VIR_ERR_XML_ERROR,
2672                            "%s", _("malformed uuid element"));
2673             goto cleanup;
2674         }
2675         VIR_FREE(uuid);
2676     }
2677 
2678     curr = curr->children;
2679 
2680     while (curr != NULL) {
2681         if (curr->type == XML_ELEMENT_NODE) {
2682             entry = g_new0(virNWFilterEntry, 1);
2683 
2684             if (virXMLNodeNameEqual(curr, "rule")) {
2685                 if (!(entry->rule = virNWFilterRuleParse(curr))) {
2686                     virNWFilterEntryFree(entry);
2687                     goto cleanup;
2688                 }
2689             } else if (virXMLNodeNameEqual(curr, "filterref")) {
2690                 if (!(entry->include = virNWFilterIncludeParse(curr))) {
2691                     virNWFilterEntryFree(entry);
2692                     goto cleanup;
2693                 }
2694             }
2695 
2696             if (entry->rule || entry->include) {
2697                 VIR_APPEND_ELEMENT_COPY(ret->filterEntries, ret->nentries, entry);
2698             } else {
2699                 virNWFilterEntryFree(entry);
2700             }
2701         }
2702         curr = curr->next;
2703     }
2704 
2705     VIR_FREE(chain);
2706     VIR_FREE(chain_pri_s);
2707 
2708     return ret;
2709 
2710  cleanup:
2711     virNWFilterDefFree(ret);
2712     VIR_FREE(chain);
2713     VIR_FREE(uuid);
2714     VIR_FREE(chain_pri_s);
2715     return NULL;
2716 }
2717 
2718 
2719 virNWFilterDef *
virNWFilterDefParseNode(xmlDocPtr xml,xmlNodePtr root)2720 virNWFilterDefParseNode(xmlDocPtr xml,
2721                         xmlNodePtr root)
2722 {
2723     g_autoptr(xmlXPathContext) ctxt = NULL;
2724 
2725     if (STRNEQ((const char *)root->name, "filter")) {
2726         virReportError(VIR_ERR_XML_ERROR,
2727                        "%s",
2728                        _("unknown root element for nw filter"));
2729         return NULL;
2730     }
2731 
2732     if (!(ctxt = virXMLXPathContextNew(xml)))
2733         return NULL;
2734 
2735     ctxt->node = root;
2736     return virNWFilterDefParseXML(ctxt);
2737 }
2738 
2739 
2740 static virNWFilterDef *
virNWFilterDefParse(const char * xmlStr,const char * filename,unsigned int flags)2741 virNWFilterDefParse(const char *xmlStr,
2742                     const char *filename,
2743                     unsigned int flags)
2744 {
2745     virNWFilterDef *def = NULL;
2746     g_autoptr(xmlDoc) xml = NULL;
2747 
2748     if ((xml = virXMLParse(filename, xmlStr, _("(nwfilter_definition)"), "nwfilter.rng",
2749                            flags & VIR_NWFILTER_DEFINE_VALIDATE))) {
2750         def = virNWFilterDefParseNode(xml, xmlDocGetRootElement(xml));
2751     }
2752 
2753     return def;
2754 }
2755 
2756 
2757 virNWFilterDef *
virNWFilterDefParseString(const char * xmlStr,unsigned int flags)2758 virNWFilterDefParseString(const char *xmlStr,
2759                           unsigned int flags)
2760 {
2761     return virNWFilterDefParse(xmlStr, NULL, flags);
2762 }
2763 
2764 
2765 virNWFilterDef *
virNWFilterDefParseFile(const char * filename)2766 virNWFilterDefParseFile(const char *filename)
2767 {
2768     return virNWFilterDefParse(NULL, filename, 0);
2769 }
2770 
2771 
2772 int
virNWFilterSaveConfig(const char * configDir,virNWFilterDef * def)2773 virNWFilterSaveConfig(const char *configDir,
2774                       virNWFilterDef *def)
2775 {
2776     int ret = -1;
2777     char *xml;
2778     char uuidstr[VIR_UUID_STRING_BUFLEN];
2779     char *configFile = NULL;
2780 
2781     if (!(xml = virNWFilterDefFormat(def)))
2782         goto cleanup;
2783 
2784     if (!(configFile = virFileBuildPath(configDir, def->name, ".xml")))
2785         goto cleanup;
2786 
2787     virUUIDFormat(def->uuid, uuidstr);
2788     ret = virXMLSaveFile(configFile,
2789                          virXMLPickShellSafeComment(def->name, uuidstr),
2790                          "nwfilter-edit", xml);
2791 
2792  cleanup:
2793     VIR_FREE(configFile);
2794     VIR_FREE(xml);
2795     return ret;
2796 }
2797 
2798 
2799 int
virNWFilterDeleteDef(const char * configDir,virNWFilterDef * def)2800 virNWFilterDeleteDef(const char *configDir,
2801                      virNWFilterDef *def)
2802 {
2803     int ret = -1;
2804     char *configFile = NULL;
2805 
2806     if (!(configFile = virFileBuildPath(configDir, def->name, ".xml")))
2807         goto error;
2808 
2809     if (unlink(configFile) < 0) {
2810         virReportError(VIR_ERR_INTERNAL_ERROR,
2811                        _("cannot remove config for %s"),
2812                        def->name);
2813         goto error;
2814     }
2815 
2816     ret = 0;
2817  error:
2818     VIR_FREE(configFile);
2819     return ret;
2820 }
2821 
2822 
2823 static void
virNWIPAddressFormat(virBuffer * buf,virSocketAddr * ipaddr)2824 virNWIPAddressFormat(virBuffer *buf,
2825                      virSocketAddr *ipaddr)
2826 {
2827     char *output = virSocketAddrFormat(ipaddr);
2828 
2829     if (output) {
2830         virBufferAdd(buf, output, -1);
2831         VIR_FREE(output);
2832     }
2833 }
2834 
2835 
2836 static void
virNWFilterRuleDefDetailsFormat(virBuffer * buf,const char * type,const virXMLAttr2Struct * att,virNWFilterRuleDef * def)2837 virNWFilterRuleDefDetailsFormat(virBuffer *buf,
2838                                 const char *type,
2839                                 const virXMLAttr2Struct *att,
2840                                 virNWFilterRuleDef *def)
2841 {
2842     size_t i = 0, j;
2843     bool typeShown = false;
2844     bool neverShown = true;
2845     bool asHex;
2846     enum match {
2847         MATCH_NONE = 0,
2848         MATCH_YES,
2849         MATCH_NO
2850     } matchShown = MATCH_NONE;
2851     nwItemDesc *item;
2852 
2853     while (att[i].name) {
2854         virNWFilterEntryItemFlags flags;
2855 
2856         VIR_WARNINGS_NO_CAST_ALIGN
2857         item = (nwItemDesc *)((char *)def + att[i].dataIdx);
2858         VIR_WARNINGS_RESET
2859 
2860         flags = item->flags;
2861         if ((flags & NWFILTER_ENTRY_ITEM_FLAG_EXISTS)) {
2862             if (!typeShown) {
2863                 virBufferAsprintf(buf, "<%s", type);
2864                 typeShown = true;
2865                 neverShown = false;
2866             }
2867 
2868             if ((flags & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG)) {
2869                 if (matchShown == MATCH_NONE) {
2870                     virBufferAddLit(buf, " match='no'");
2871                     matchShown = MATCH_NO;
2872                 } else if (matchShown == MATCH_YES) {
2873                     virBufferAddLit(buf, "/>\n");
2874                     typeShown = false;
2875                     matchShown = MATCH_NONE;
2876                     continue;
2877                 }
2878             } else {
2879                 if (matchShown == MATCH_NO) {
2880                     virBufferAddLit(buf, "/>\n");
2881                     typeShown = false;
2882                     matchShown = MATCH_NONE;
2883                     continue;
2884                 }
2885                 matchShown = MATCH_YES;
2886             }
2887 
2888             virBufferAsprintf(buf, " %s='",
2889                               att[i].name);
2890             if (att[i].formatter && !(flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
2891                if (!att[i].formatter(buf, def, item)) {
2892                   virReportError(VIR_ERR_INTERNAL_ERROR,
2893                                  _("formatter for %s %s reported error"),
2894                                  type,
2895                                  att[i].name);
2896                    return;
2897                }
2898             } else if ((flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
2899                 virBufferAddChar(buf, '$');
2900                 virNWFilterVarAccessPrint(item->varAccess, buf);
2901             } else {
2902                asHex = false;
2903 
2904                switch (item->datatype) {
2905 
2906                case DATATYPE_UINT8_HEX:
2907                    asHex = true;
2908                    G_GNUC_FALLTHROUGH;
2909                case DATATYPE_IPMASK:
2910                case DATATYPE_IPV6MASK:
2911                    /* display all masks in CIDR format */
2912                case DATATYPE_UINT8:
2913                    virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
2914                                      item->u.u8);
2915                break;
2916 
2917                case DATATYPE_UINT16_HEX:
2918                    asHex = true;
2919                    G_GNUC_FALLTHROUGH;
2920                case DATATYPE_UINT16:
2921                    virBufferAsprintf(buf, asHex ? "0x%x" : "%d",
2922                                      item->u.u16);
2923                break;
2924 
2925                case DATATYPE_UINT32_HEX:
2926                    asHex = true;
2927                    G_GNUC_FALLTHROUGH;
2928                case DATATYPE_UINT32:
2929                    virBufferAsprintf(buf, asHex ? "0x%x" : "%u",
2930                                      item->u.u32);
2931                break;
2932 
2933                case DATATYPE_IPADDR:
2934                case DATATYPE_IPV6ADDR:
2935                    virNWIPAddressFormat(buf,
2936                                         &item->u.ipaddr);
2937                break;
2938 
2939                case DATATYPE_MACMASK:
2940                case DATATYPE_MACADDR:
2941                    for (j = 0; j < 6; j++)
2942                        virBufferAsprintf(buf, "%02x%s",
2943                                          item->u.macaddr.addr[j],
2944                                          (j < 5) ? ":" : "");
2945                break;
2946 
2947                case DATATYPE_STRINGCOPY:
2948                    virBufferEscapeString(buf, "%s", item->u.string);
2949                break;
2950 
2951                case DATATYPE_BOOLEAN:
2952                    if (item->u.boolean)
2953                        virBufferAddLit(buf, "true");
2954                    else
2955                        virBufferAddLit(buf, "false");
2956                break;
2957 
2958                case DATATYPE_IPSETNAME:
2959                case DATATYPE_IPSETFLAGS:
2960                case DATATYPE_STRING:
2961                case DATATYPE_LAST:
2962                default:
2963                    virBufferAsprintf(buf,
2964                                      "UNSUPPORTED DATATYPE 0x%02x\n",
2965                                      att[i].datatype);
2966                }
2967             }
2968             virBufferAddLit(buf, "'");
2969         }
2970         i++;
2971     }
2972     if (typeShown)
2973        virBufferAddLit(buf, "/>\n");
2974 
2975     if (neverShown)
2976        virBufferAsprintf(buf,
2977                          "<%s/>\n", type);
2978 
2979     return;
2980 }
2981 
2982 
2983 static int
virNWFilterRuleDefFormat(virBuffer * buf,virNWFilterRuleDef * def)2984 virNWFilterRuleDefFormat(virBuffer *buf,
2985                          virNWFilterRuleDef *def)
2986 {
2987     size_t i;
2988     bool subelement = false;
2989 
2990     virBufferAsprintf(buf, "<rule action='%s' direction='%s' priority='%d'",
2991                       virNWFilterRuleActionTypeToString(def->action),
2992                       virNWFilterRuleDirectionTypeToString(def->tt),
2993                       def->priority);
2994 
2995     if ((def->flags & RULE_FLAG_NO_STATEMATCH))
2996         virBufferAddLit(buf, " statematch='false'");
2997 
2998     virBufferAdjustIndent(buf, 2);
2999     i = 0;
3000     while (virAttr[i].id) {
3001         if (virAttr[i].prtclType == def->prtclType) {
3002             if (!subelement)
3003                 virBufferAddLit(buf, ">\n");
3004             virNWFilterRuleDefDetailsFormat(buf,
3005                                             virAttr[i].id,
3006                                             virAttr[i].att,
3007                                             def);
3008             subelement = true;
3009             break;
3010         }
3011         i++;
3012     }
3013 
3014     virBufferAdjustIndent(buf, -2);
3015     if (subelement)
3016         virBufferAddLit(buf, "</rule>\n");
3017     else
3018         virBufferAddLit(buf, "/>\n");
3019     return 0;
3020 }
3021 
3022 
3023 static int
virNWFilterEntryFormat(virBuffer * buf,virNWFilterEntry * entry)3024 virNWFilterEntryFormat(virBuffer *buf,
3025                        virNWFilterEntry *entry)
3026 {
3027     if (entry->rule)
3028         return virNWFilterRuleDefFormat(buf, entry->rule);
3029     return virNWFilterFormatParamAttributes(buf, entry->include->params,
3030                                             entry->include->filterref);
3031 }
3032 
3033 
3034 char *
virNWFilterDefFormat(const virNWFilterDef * def)3035 virNWFilterDefFormat(const virNWFilterDef *def)
3036 {
3037     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
3038     char uuid[VIR_UUID_STRING_BUFLEN];
3039     size_t i;
3040 
3041     virBufferAsprintf(&buf, "<filter name='%s' chain='%s'",
3042                       def->name,
3043                       def->chainsuffix);
3044     if (def->chainPriority != 0)
3045         virBufferAsprintf(&buf, " priority='%d'",
3046                           def->chainPriority);
3047     virBufferAddLit(&buf, ">\n");
3048     virBufferAdjustIndent(&buf, 2);
3049 
3050     virUUIDFormat(def->uuid, uuid);
3051     virBufferAsprintf(&buf, "<uuid>%s</uuid>\n", uuid);
3052 
3053     for (i = 0; i < def->nentries; i++) {
3054         if (virNWFilterEntryFormat(&buf, def->filterEntries[i]) < 0)
3055             return NULL;
3056     }
3057 
3058     virBufferAdjustIndent(&buf, -2);
3059     virBufferAddLit(&buf, "</filter>\n");
3060 
3061     return virBufferContentAndReset(&buf);
3062 }
3063 
3064 static virNWFilterTriggerRebuildCallback rebuildCallback;
3065 static void *rebuildOpaque;
3066 
3067 int
virNWFilterConfLayerInit(virNWFilterTriggerRebuildCallback cb,void * opaque)3068 virNWFilterConfLayerInit(virNWFilterTriggerRebuildCallback cb,
3069                          void *opaque)
3070 {
3071     if (initialized)
3072         return -1;
3073 
3074     rebuildCallback = cb;
3075     rebuildOpaque = opaque;
3076 
3077     initialized = true;
3078 
3079     if (virRWLockInit(&updateLock) < 0)
3080         return -1;
3081 
3082     return 0;
3083 }
3084 
3085 
3086 void
virNWFilterConfLayerShutdown(void)3087 virNWFilterConfLayerShutdown(void)
3088 {
3089     if (!initialized)
3090         return;
3091 
3092     virRWLockDestroy(&updateLock);
3093 
3094     initialized = false;
3095     rebuildCallback = NULL;
3096     rebuildOpaque = NULL;
3097 }
3098 
3099 
3100 int
virNWFilterTriggerRebuild(void)3101 virNWFilterTriggerRebuild(void)
3102 {
3103     if (rebuildCallback)
3104         return rebuildCallback(rebuildOpaque);
3105     return 0;
3106 }
3107 
3108 
3109 bool
virNWFilterRuleIsProtocolIPv4(virNWFilterRuleDef * rule)3110 virNWFilterRuleIsProtocolIPv4(virNWFilterRuleDef *rule)
3111 {
3112     if (rule->prtclType >= VIR_NWFILTER_RULE_PROTOCOL_TCP &&
3113         rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_ALL)
3114         return true;
3115     return false;
3116 }
3117 
3118 
3119 bool
virNWFilterRuleIsProtocolIPv6(virNWFilterRuleDef * rule)3120 virNWFilterRuleIsProtocolIPv6(virNWFilterRuleDef *rule)
3121 {
3122     if (rule->prtclType >= VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6 &&
3123         rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6)
3124         return true;
3125     return false;
3126 }
3127 
3128 
3129 bool
virNWFilterRuleIsProtocolEthernet(virNWFilterRuleDef * rule)3130 virNWFilterRuleIsProtocolEthernet(virNWFilterRuleDef *rule)
3131 {
3132     if (rule->prtclType <= VIR_NWFILTER_RULE_PROTOCOL_IPV6)
3133         return true;
3134     return false;
3135 }
3136