1 /*
2  * nwfilter_ebiptables_driver.c: driver for ebtables/iptables on tap devices
3  *
4  * Copyright (C) 2011-2014 Red Hat, Inc.
5  * Copyright (C) 2010-2012 IBM Corp.
6  * Copyright (C) 2010-2012 Stefan Berger
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library.  If not, see
20  * <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <config.h>
24 
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <sys/utsname.h>
28 
29 #include "internal.h"
30 
31 #include "virbuffer.h"
32 #include "viralloc.h"
33 #include "virlog.h"
34 #include "virerror.h"
35 #include "domain_conf.h"
36 #include "nwfilter_conf.h"
37 #include "nwfilter_driver.h"
38 #include "nwfilter_gentech_driver.h"
39 #include "nwfilter_ebiptables_driver.h"
40 #include "virfile.h"
41 #include "vircommand.h"
42 #include "configmake.h"
43 #include "virstring.h"
44 #include "virfirewall.h"
45 #include "virutil.h"
46 
47 #define VIR_FROM_THIS VIR_FROM_NWFILTER
48 
49 VIR_LOG_INIT("nwfilter.nwfilter_ebiptables_driver");
50 
51 #define EBTABLES_CHAIN_INCOMING "PREROUTING"
52 #define EBTABLES_CHAIN_OUTGOING "POSTROUTING"
53 
54 #define CHAINPREFIX_HOST_IN       'I'
55 #define CHAINPREFIX_HOST_OUT      'O'
56 #define CHAINPREFIX_HOST_IN_TEMP  'J'
57 #define CHAINPREFIX_HOST_OUT_TEMP 'P'
58 
59 
60 #define PROC_BRIDGE_NF_CALL_IPTABLES \
61         "/proc/sys/net/bridge/bridge-nf-call-iptables"
62 #define PROC_BRIDGE_NF_CALL_IP6TABLES \
63         "/proc/sys/net/bridge/bridge-nf-call-ip6tables"
64 
65 #define BRIDGE_NF_CALL_ALERT_INTERVAL  10 /* seconds */
66 
67 /*
68  * --ctdir original vs. --ctdir reply's meaning was inverted in netfilter
69  * at some point (Linux 2.6.39)
70  */
71 enum ctdirStatus {
72     CTDIR_STATUS_UNKNOWN    = 0,
73     CTDIR_STATUS_CORRECTED  = 1,
74     CTDIR_STATUS_OLD        = 2,
75 };
76 static enum ctdirStatus iptables_ctdir_corrected;
77 
78 #define PRINT_ROOT_CHAIN(buf, prefix, ifname) \
79     g_snprintf(buf, sizeof(buf), "libvirt-%c-%s", prefix, ifname)
80 #define PRINT_CHAIN(buf, prefix, ifname, suffix) \
81     g_snprintf(buf, sizeof(buf), "%c-%s-%s", prefix, ifname, suffix)
82 
83 #define VIRT_IN_CHAIN      "libvirt-in"
84 #define VIRT_OUT_CHAIN     "libvirt-out"
85 #define VIRT_IN_POST_CHAIN "libvirt-in-post"
86 #define HOST_IN_CHAIN      "libvirt-host-in"
87 
88 #define PRINT_IPT_ROOT_CHAIN(buf, prefix, ifname) \
89     g_snprintf(buf, sizeof(buf), "%c%c-%s", prefix[0], prefix[1], ifname)
90 
91 static bool newMatchState;
92 
93 #define MATCH_PHYSDEV_IN_FW   "-m", "physdev", "--physdev-in"
94 #define MATCH_PHYSDEV_OUT_FW  "-m", "physdev", "--physdev-is-bridged", "--physdev-out"
95 #define MATCH_PHYSDEV_OUT_OLD_FW  "-m", "physdev", "--physdev-out"
96 
97 static int ebtablesRemoveBasicRules(const char *ifname);
98 static int ebiptablesDriverInit(bool privileged);
99 static void ebiptablesDriverShutdown(void);
100 static int ebtablesCleanAll(const char *ifname);
101 static int ebiptablesAllTeardown(const char *ifname);
102 
103 struct ushort_map {
104     unsigned short attr;
105     const char *val;
106 };
107 
108 
109 enum l3_proto_idx {
110     L3_PROTO_IPV4_IDX = 0,
111     L3_PROTO_IPV6_IDX,
112     L3_PROTO_ARP_IDX,
113     L3_PROTO_RARP_IDX,
114     L2_PROTO_MAC_IDX,
115     L2_PROTO_VLAN_IDX,
116     L2_PROTO_STP_IDX,
117     L3_PROTO_LAST_IDX
118 };
119 
120 #define USHORTMAP_ENTRY_IDX(IDX, ATT, VAL) [IDX] = { .attr = ATT, .val = VAL }
121 
122 /* A lookup table for translating ethernet protocol IDs to human readable
123  * strings. None of the human readable strings must be found as a prefix
124  * in another entry here (example 'ab' would be found in 'abc') to allow
125  * for prefix matching.
126  */
127 static const struct ushort_map l3_protocols[] = {
128     USHORTMAP_ENTRY_IDX(L3_PROTO_IPV4_IDX, ETHERTYPE_IP,     "ipv4"),
129     USHORTMAP_ENTRY_IDX(L3_PROTO_IPV6_IDX, ETHERTYPE_IPV6,   "ipv6"),
130     USHORTMAP_ENTRY_IDX(L3_PROTO_ARP_IDX,  ETHERTYPE_ARP,    "arp"),
131     USHORTMAP_ENTRY_IDX(L3_PROTO_RARP_IDX, ETHERTYPE_REVARP, "rarp"),
132     USHORTMAP_ENTRY_IDX(L2_PROTO_VLAN_IDX, ETHERTYPE_VLAN,   "vlan"),
133     USHORTMAP_ENTRY_IDX(L2_PROTO_STP_IDX,  0,                "stp"),
134     USHORTMAP_ENTRY_IDX(L2_PROTO_MAC_IDX,  0,                "mac"),
135     USHORTMAP_ENTRY_IDX(L3_PROTO_LAST_IDX, 0,                NULL),
136 };
137 
138 
139 static char chainprefixes_host[3] = {
140     CHAINPREFIX_HOST_IN,
141     CHAINPREFIX_HOST_OUT,
142     0
143 };
144 
145 static char chainprefixes_host_temp[3] = {
146     CHAINPREFIX_HOST_IN_TEMP,
147     CHAINPREFIX_HOST_OUT_TEMP,
148     0
149 };
150 
151 static int
printVar(virNWFilterVarCombIter * vars,char * buf,int bufsize,nwItemDesc * item,bool * done)152 printVar(virNWFilterVarCombIter *vars,
153          char *buf, int bufsize,
154          nwItemDesc *item,
155          bool *done)
156 {
157     *done = false;
158 
159     if ((item->flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
160         const char *val;
161 
162         val = virNWFilterVarCombIterGetVarValue(vars, item->varAccess);
163         if (!val) {
164             /* error has been reported */
165             return -1;
166         }
167 
168         if (virStrcpy(buf, val, bufsize) < 0) {
169             const char *varName;
170 
171             varName = virNWFilterVarAccessGetVarName(item->varAccess);
172             virReportError(VIR_ERR_INTERNAL_ERROR,
173                            _("Buffer too small to print variable "
174                              "'%s' into"), varName);
175             return -1;
176         }
177 
178         *done = true;
179     }
180     return 0;
181 }
182 
183 
184 static int
_printDataType(virNWFilterVarCombIter * vars,char * buf,int bufsize,nwItemDesc * item,bool asHex,bool directionIn)185 _printDataType(virNWFilterVarCombIter *vars,
186                char *buf, int bufsize,
187                nwItemDesc *item,
188                bool asHex, bool directionIn)
189 {
190     bool done;
191     g_autofree char *data = NULL;
192     uint8_t ctr;
193     g_auto(virBuffer) vb = VIR_BUFFER_INITIALIZER;
194     g_autofree char *flags = NULL;
195 
196     if (printVar(vars, buf, bufsize, item, &done) < 0)
197         return -1;
198 
199     if (done)
200         return 0;
201 
202     switch (item->datatype) {
203     case DATATYPE_IPADDR:
204         data = virSocketAddrFormat(&item->u.ipaddr);
205         if (!data)
206             return -1;
207         if (g_snprintf(buf, bufsize, "%s", data) >= bufsize) {
208             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
209                            _("buffer too small for IP address"));
210             return -1;
211         }
212     break;
213 
214     case DATATYPE_IPV6ADDR:
215         data = virSocketAddrFormat(&item->u.ipaddr);
216         if (!data)
217             return -1;
218 
219         if (g_snprintf(buf, bufsize, "%s", data) >= bufsize) {
220             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
221                            _("buffer too small for IPv6 address"));
222             return -1;
223         }
224     break;
225 
226     case DATATYPE_MACADDR:
227     case DATATYPE_MACMASK:
228         if (bufsize < VIR_MAC_STRING_BUFLEN) {
229             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
230                            _("Buffer too small for MAC address"));
231             return -1;
232         }
233 
234         virMacAddrFormat(&item->u.macaddr, buf);
235     break;
236 
237     case DATATYPE_IPV6MASK:
238     case DATATYPE_IPMASK:
239         if (g_snprintf(buf, bufsize, "%d",
240                        item->u.u8) >= bufsize) {
241             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
242                            _("Buffer too small for uint8 type"));
243             return -1;
244         }
245     break;
246 
247     case DATATYPE_UINT32:
248     case DATATYPE_UINT32_HEX:
249         if (g_snprintf(buf, bufsize, asHex ? "0x%x" : "%u",
250                        item->u.u32) >= bufsize) {
251             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
252                            _("Buffer too small for uint32 type"));
253             return -1;
254         }
255     break;
256 
257     case DATATYPE_UINT16:
258     case DATATYPE_UINT16_HEX:
259         if (g_snprintf(buf, bufsize, asHex ? "0x%x" : "%d",
260                        item->u.u16) >= bufsize) {
261             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
262                            _("Buffer too small for uint16 type"));
263             return -1;
264         }
265     break;
266 
267     case DATATYPE_UINT8:
268     case DATATYPE_UINT8_HEX:
269         if (g_snprintf(buf, bufsize, asHex ? "0x%x" : "%d",
270                        item->u.u8) >= bufsize) {
271             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
272                            _("Buffer too small for uint8 type"));
273             return -1;
274         }
275     break;
276 
277     case DATATYPE_IPSETNAME:
278         if (virStrcpy(buf, item->u.ipset.setname, bufsize) < 0) {
279             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
280                            _("Buffer to small for ipset name"));
281             return -1;
282         }
283     break;
284 
285     case DATATYPE_IPSETFLAGS:
286         for (ctr = 0; ctr < item->u.ipset.numFlags; ctr++) {
287             if (ctr != 0)
288                 virBufferAddLit(&vb, ",");
289             if ((item->u.ipset.flags & (1 << ctr))) {
290                 if (directionIn)
291                     virBufferAddLit(&vb, "dst");
292                 else
293                     virBufferAddLit(&vb, "src");
294             } else {
295                 if (directionIn)
296                     virBufferAddLit(&vb, "src");
297                 else
298                     virBufferAddLit(&vb, "dst");
299             }
300         }
301 
302         flags = virBufferContentAndReset(&vb);
303 
304         if (virStrcpy(buf, flags, bufsize) < 0) {
305             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
306                            _("Buffer too small for IPSETFLAGS type"));
307             return -1;
308         }
309     break;
310 
311     case DATATYPE_STRING:
312     case DATATYPE_STRINGCOPY:
313     case DATATYPE_BOOLEAN:
314         virReportError(VIR_ERR_INTERNAL_ERROR,
315                        _("Cannot print data type %x"), item->datatype);
316         return -1;
317     case DATATYPE_LAST:
318     default:
319         virReportEnumRangeError(virNWFilterAttrDataType, item->datatype);
320         return -1;
321     }
322 
323     return 0;
324 }
325 
326 
327 static int
printDataType(virNWFilterVarCombIter * vars,char * buf,int bufsize,nwItemDesc * item)328 printDataType(virNWFilterVarCombIter *vars,
329               char *buf, int bufsize,
330               nwItemDesc *item)
331 {
332     return _printDataType(vars, buf, bufsize, item, 0, 0);
333 }
334 
335 static int
printDataTypeDirection(virNWFilterVarCombIter * vars,char * buf,int bufsize,nwItemDesc * item,bool directionIn)336 printDataTypeDirection(virNWFilterVarCombIter *vars,
337                        char *buf, int bufsize,
338                        nwItemDesc *item, bool directionIn)
339 {
340     return _printDataType(vars, buf, bufsize, item, 0, directionIn);
341 }
342 
343 static int
printDataTypeAsHex(virNWFilterVarCombIter * vars,char * buf,int bufsize,nwItemDesc * item)344 printDataTypeAsHex(virNWFilterVarCombIter *vars,
345                    char *buf, int bufsize,
346                    nwItemDesc *item)
347 {
348     return _printDataType(vars, buf, bufsize, item, 1, 0);
349 }
350 
351 
352 static int
ebtablesHandleEthHdr(virFirewall * fw,virFirewallRule * fwrule,virNWFilterVarCombIter * vars,ethHdrDataDef * ethHdr,bool reverse)353 ebtablesHandleEthHdr(virFirewall *fw,
354                      virFirewallRule *fwrule,
355                      virNWFilterVarCombIter *vars,
356                      ethHdrDataDef *ethHdr,
357                      bool reverse)
358 {
359     char macaddr[VIR_MAC_STRING_BUFLEN];
360     char macmask[VIR_MAC_STRING_BUFLEN];
361 
362     if (HAS_ENTRY_ITEM(&ethHdr->dataSrcMACAddr)) {
363         if (printDataType(vars,
364                           macaddr, sizeof(macaddr),
365                           &ethHdr->dataSrcMACAddr) < 0)
366             return -1;
367 
368         virFirewallRuleAddArgList(fw, fwrule,
369                                   reverse ? "-d" : "-s",
370                                   NULL);
371         if (ENTRY_WANT_NEG_SIGN(&ethHdr->dataSrcMACAddr))
372             virFirewallRuleAddArg(fw, fwrule, "!");
373 
374         if (HAS_ENTRY_ITEM(&ethHdr->dataSrcMACMask)) {
375             if (printDataType(vars,
376                               macmask, sizeof(macmask),
377                               &ethHdr->dataSrcMACMask) < 0)
378                 return -1;
379 
380             virFirewallRuleAddArgFormat(fw, fwrule,
381                                         "%s/%s", macaddr, macmask);
382         } else {
383             virFirewallRuleAddArg(fw, fwrule, macaddr);
384         }
385     }
386 
387     if (HAS_ENTRY_ITEM(&ethHdr->dataDstMACAddr)) {
388         if (printDataType(vars,
389                           macaddr, sizeof(macaddr),
390                           &ethHdr->dataDstMACAddr) < 0)
391             return -1;
392 
393         virFirewallRuleAddArgList(fw, fwrule,
394                                   reverse ? "-s" : "-d",
395                                   NULL);
396         if (ENTRY_WANT_NEG_SIGN(&ethHdr->dataDstMACAddr))
397             virFirewallRuleAddArg(fw, fwrule, "!");
398 
399         if (HAS_ENTRY_ITEM(&ethHdr->dataDstMACMask)) {
400             if (printDataType(vars,
401                               macmask, sizeof(macmask),
402                               &ethHdr->dataDstMACMask) < 0)
403                 return -1;
404 
405             virFirewallRuleAddArgFormat(fw, fwrule,
406                                         "%s/%s", macaddr, macmask);
407         } else {
408             virFirewallRuleAddArg(fw, fwrule, macaddr);
409         }
410     }
411 
412     return 0;
413 }
414 
415 
416 /************************ iptables support ************************/
417 
418 
419 static void
iptablesCreateBaseChainsFW(virFirewall * fw,virFirewallLayer layer)420 iptablesCreateBaseChainsFW(virFirewall *fw,
421                            virFirewallLayer layer)
422 {
423     virFirewallAddRuleFull(fw, layer,
424                            true, NULL, NULL,
425                            "-N", VIRT_IN_CHAIN, NULL);
426     virFirewallAddRuleFull(fw, layer,
427                            true, NULL, NULL,
428                            "-N", VIRT_OUT_CHAIN, NULL);
429     virFirewallAddRuleFull(fw, layer,
430                            true, NULL, NULL,
431                            "-N", VIRT_IN_POST_CHAIN, NULL);
432     virFirewallAddRuleFull(fw, layer,
433                            true, NULL, NULL,
434                            "-N", HOST_IN_CHAIN, NULL);
435     virFirewallAddRuleFull(fw, layer,
436                            true, NULL, NULL,
437                            "-D", "FORWARD", "-j", VIRT_IN_CHAIN, NULL);
438     virFirewallAddRuleFull(fw, layer,
439                            true, NULL, NULL,
440                            "-D", "FORWARD", "-j", VIRT_OUT_CHAIN, NULL);
441     virFirewallAddRuleFull(fw, layer,
442                            true, NULL, NULL,
443                            "-D", "FORWARD", "-j", VIRT_IN_POST_CHAIN, NULL);
444     virFirewallAddRuleFull(fw, layer,
445                            true, NULL, NULL,
446                            "-D", "INPUT", "-j", HOST_IN_CHAIN, NULL);
447     virFirewallAddRule(fw, layer,
448                        "-I", "FORWARD", "1", "-j", VIRT_IN_CHAIN, NULL);
449     virFirewallAddRule(fw, layer,
450                        "-I", "FORWARD", "2", "-j", VIRT_OUT_CHAIN, NULL);
451     virFirewallAddRule(fw, layer,
452                        "-I", "FORWARD", "3", "-j", VIRT_IN_POST_CHAIN, NULL);
453     virFirewallAddRule(fw, layer,
454                        "-I", "INPUT", "1", "-j", HOST_IN_CHAIN, NULL);
455 }
456 
457 
458 static void
iptablesCreateTmpRootChainFW(virFirewall * fw,virFirewallLayer layer,char prefix,bool incoming,const char * ifname)459 iptablesCreateTmpRootChainFW(virFirewall *fw,
460                              virFirewallLayer layer,
461                              char prefix,
462                              bool incoming, const char *ifname)
463 {
464     char chain[MAX_CHAINNAME_LENGTH];
465     char chainPrefix[2] = {
466        prefix,
467        incoming ? CHAINPREFIX_HOST_IN_TEMP
468                 : CHAINPREFIX_HOST_OUT_TEMP
469     };
470 
471     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
472 
473     virFirewallAddRule(fw, layer,
474                        "-N", chain, NULL);
475 }
476 
477 
478 static void
iptablesCreateTmpRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)479 iptablesCreateTmpRootChainsFW(virFirewall *fw,
480                               virFirewallLayer layer,
481                               const char *ifname)
482 {
483     iptablesCreateTmpRootChainFW(fw, layer, 'F', false, ifname);
484     iptablesCreateTmpRootChainFW(fw, layer, 'F', true, ifname);
485     iptablesCreateTmpRootChainFW(fw, layer, 'H', true, ifname);
486 }
487 
488 
489 static void
_iptablesRemoveRootChainFW(virFirewall * fw,virFirewallLayer layer,char prefix,bool incoming,const char * ifname,int isTempChain)490 _iptablesRemoveRootChainFW(virFirewall *fw,
491                            virFirewallLayer layer,
492                            char prefix,
493                            bool incoming, const char *ifname,
494                            int isTempChain)
495 {
496     char chain[MAX_CHAINNAME_LENGTH];
497     char chainPrefix[2] = {
498         prefix,
499     };
500 
501     if (isTempChain)
502         chainPrefix[1] = incoming ? CHAINPREFIX_HOST_IN_TEMP
503                                   : CHAINPREFIX_HOST_OUT_TEMP;
504     else
505         chainPrefix[1] = incoming ? CHAINPREFIX_HOST_IN
506                                   : CHAINPREFIX_HOST_OUT;
507 
508     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
509 
510     virFirewallAddRuleFull(fw, layer,
511                            true, NULL, NULL,
512                            "-F", chain, NULL);
513     virFirewallAddRuleFull(fw, layer,
514                            true, NULL, NULL,
515                            "-X", chain, NULL);
516 }
517 
518 
519 static void
iptablesRemoveRootChainFW(virFirewall * fw,virFirewallLayer layer,char prefix,bool incoming,const char * ifname)520 iptablesRemoveRootChainFW(virFirewall *fw,
521                           virFirewallLayer layer,
522                           char prefix,
523                           bool incoming,
524                           const char *ifname)
525 {
526     _iptablesRemoveRootChainFW(fw, layer, prefix, incoming, ifname, false);
527 }
528 
529 
530 static void
iptablesRemoveTmpRootChainFW(virFirewall * fw,virFirewallLayer layer,char prefix,bool incoming,const char * ifname)531 iptablesRemoveTmpRootChainFW(virFirewall *fw,
532                              virFirewallLayer layer,
533                              char prefix,
534                              bool incoming,
535                              const char *ifname)
536 {
537     _iptablesRemoveRootChainFW(fw, layer, prefix,
538                                incoming, ifname, 1);
539 }
540 
541 
542 static void
iptablesRemoveTmpRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)543 iptablesRemoveTmpRootChainsFW(virFirewall *fw,
544                               virFirewallLayer layer,
545                               const char *ifname)
546 {
547     iptablesRemoveTmpRootChainFW(fw, layer, 'F', false, ifname);
548     iptablesRemoveTmpRootChainFW(fw, layer, 'F', true, ifname);
549     iptablesRemoveTmpRootChainFW(fw, layer, 'H', true, ifname);
550 }
551 
552 
553 static void
iptablesRemoveRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)554 iptablesRemoveRootChainsFW(virFirewall *fw,
555                            virFirewallLayer layer,
556                            const char *ifname)
557 {
558     iptablesRemoveRootChainFW(fw, layer, 'F', false, ifname);
559     iptablesRemoveRootChainFW(fw, layer, 'F', true, ifname);
560     iptablesRemoveRootChainFW(fw, layer, 'H', true, ifname);
561 }
562 
563 
564 static void
iptablesLinkTmpRootChainFW(virFirewall * fw,virFirewallLayer layer,const char * basechain,char prefix,bool incoming,const char * ifname)565 iptablesLinkTmpRootChainFW(virFirewall *fw,
566                            virFirewallLayer layer,
567                            const char *basechain,
568                            char prefix,
569                            bool incoming, const char *ifname)
570 {
571     char chain[MAX_CHAINNAME_LENGTH];
572     char chainPrefix[2] = {
573         prefix,
574         incoming ? CHAINPREFIX_HOST_IN_TEMP
575                  : CHAINPREFIX_HOST_OUT_TEMP
576     };
577 
578     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
579 
580     if (incoming)
581         virFirewallAddRule(fw, layer,
582                            "-A", basechain,
583                            MATCH_PHYSDEV_IN_FW,
584                            ifname,
585                            "-g", chain, NULL);
586     else
587         virFirewallAddRule(fw, layer,
588                            "-A", basechain,
589                            MATCH_PHYSDEV_OUT_FW,
590                            ifname,
591                            "-g", chain, NULL);
592 }
593 
594 
595 static void
iptablesLinkTmpRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)596 iptablesLinkTmpRootChainsFW(virFirewall *fw,
597                             virFirewallLayer layer,
598                             const char *ifname)
599 {
600     iptablesLinkTmpRootChainFW(fw, layer, VIRT_OUT_CHAIN, 'F', false, ifname);
601     iptablesLinkTmpRootChainFW(fw, layer, VIRT_IN_CHAIN,  'F', true, ifname);
602     iptablesLinkTmpRootChainFW(fw, layer, HOST_IN_CHAIN,  'H', true, ifname);
603 }
604 
605 
606 static void
iptablesSetupVirtInPostFW(virFirewall * fw G_GNUC_UNUSED,virFirewallLayer layer G_GNUC_UNUSED,const char * ifname G_GNUC_UNUSED)607 iptablesSetupVirtInPostFW(virFirewall *fw G_GNUC_UNUSED,
608                           virFirewallLayer layer G_GNUC_UNUSED,
609                           const char *ifname G_GNUC_UNUSED)
610 {
611     virFirewallAddRuleFull(fw, layer,
612                            true, NULL, NULL,
613                            "-D", VIRT_IN_POST_CHAIN,
614                            MATCH_PHYSDEV_IN_FW,
615                            ifname, "-j", "ACCEPT", NULL);
616     virFirewallAddRule(fw, layer,
617                        "-A", VIRT_IN_POST_CHAIN,
618                        MATCH_PHYSDEV_IN_FW,
619                        ifname, "-j", "ACCEPT", NULL);
620 }
621 
622 
623 static void
iptablesClearVirtInPostFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)624 iptablesClearVirtInPostFW(virFirewall *fw,
625                           virFirewallLayer layer,
626                           const char *ifname)
627 {
628     virFirewallAddRuleFull(fw, layer,
629                            true, NULL, NULL,
630                            "-D", VIRT_IN_POST_CHAIN,
631                            MATCH_PHYSDEV_IN_FW,
632                            ifname, "-j", "ACCEPT", NULL);
633 }
634 
635 
636 static void
_iptablesUnlinkRootChainFW(virFirewall * fw,virFirewallLayer layer,const char * basechain,char prefix,bool incoming,const char * ifname,int isTempChain)637 _iptablesUnlinkRootChainFW(virFirewall *fw,
638                            virFirewallLayer layer,
639                            const char *basechain,
640                            char prefix,
641                            bool incoming, const char *ifname,
642                            int isTempChain)
643 {
644     char chain[MAX_CHAINNAME_LENGTH];
645     char chainPrefix[2] = {
646         prefix,
647     };
648     if (isTempChain)
649         chainPrefix[1] = incoming ? CHAINPREFIX_HOST_IN_TEMP
650                                   : CHAINPREFIX_HOST_OUT_TEMP;
651     else
652         chainPrefix[1] = incoming ? CHAINPREFIX_HOST_IN
653                                   : CHAINPREFIX_HOST_OUT;
654 
655     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
656 
657     if (incoming)
658         virFirewallAddRuleFull(fw, layer,
659                                true, NULL, NULL,
660                                "-D", basechain,
661                                MATCH_PHYSDEV_IN_FW, ifname,
662                                "-g", chain,
663                                NULL);
664     else
665         virFirewallAddRuleFull(fw, layer,
666                                true, NULL, NULL,
667                                "-D", basechain,
668                                MATCH_PHYSDEV_OUT_FW, ifname,
669                                "-g", chain,
670                                NULL);
671 
672     /*
673      * Previous versions of libvirt may have created a rule
674      * with the --physdev-is-bridged missing. Remove this one
675      * as well.
676      */
677     if (!incoming)
678         virFirewallAddRuleFull(fw, layer,
679                                true, NULL, NULL,
680                                "-D", basechain,
681                                MATCH_PHYSDEV_OUT_OLD_FW, ifname,
682                                "-g", chain,
683                                NULL);
684 }
685 
686 
687 static void
iptablesUnlinkRootChainFW(virFirewall * fw,virFirewallLayer layer,const char * basechain,char prefix,bool incoming,const char * ifname)688 iptablesUnlinkRootChainFW(virFirewall *fw,
689                           virFirewallLayer layer,
690                           const char *basechain,
691                           char prefix,
692                           bool incoming, const char *ifname)
693 {
694     _iptablesUnlinkRootChainFW(fw, layer,
695                                basechain, prefix, incoming, ifname, false);
696 }
697 
698 
699 static void
iptablesUnlinkTmpRootChainFW(virFirewall * fw,virFirewallLayer layer,const char * basechain,char prefix,bool incoming,const char * ifname)700 iptablesUnlinkTmpRootChainFW(virFirewall *fw,
701                              virFirewallLayer layer,
702                              const char *basechain,
703                              char prefix,
704                              bool incoming, const char *ifname)
705 {
706     _iptablesUnlinkRootChainFW(fw, layer,
707                                basechain, prefix, incoming, ifname, 1);
708 }
709 
710 
711 static void
iptablesUnlinkRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)712 iptablesUnlinkRootChainsFW(virFirewall *fw,
713                            virFirewallLayer layer,
714                            const char *ifname)
715 {
716     iptablesUnlinkRootChainFW(fw, layer, VIRT_OUT_CHAIN, 'F', false, ifname);
717     iptablesUnlinkRootChainFW(fw, layer, VIRT_IN_CHAIN,  'F', true, ifname);
718     iptablesUnlinkRootChainFW(fw, layer, HOST_IN_CHAIN,  'H', true, ifname);
719 }
720 
721 
722 static void
iptablesUnlinkTmpRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)723 iptablesUnlinkTmpRootChainsFW(virFirewall *fw,
724                               virFirewallLayer layer,
725                               const char *ifname)
726 {
727     iptablesUnlinkTmpRootChainFW(fw, layer, VIRT_OUT_CHAIN, 'F', false, ifname);
728     iptablesUnlinkTmpRootChainFW(fw, layer, VIRT_IN_CHAIN,  'F', true, ifname);
729     iptablesUnlinkTmpRootChainFW(fw, layer, HOST_IN_CHAIN,  'H', true, ifname);
730 }
731 
732 
733 static void
iptablesRenameTmpRootChainFW(virFirewall * fw,virFirewallLayer layer,char prefix,bool incoming,const char * ifname)734 iptablesRenameTmpRootChainFW(virFirewall *fw,
735                              virFirewallLayer layer,
736                              char prefix,
737                              bool incoming,
738                              const char *ifname)
739 {
740     char tmpchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
741     char tmpChainPrefix[2] = {
742         prefix,
743         incoming ? CHAINPREFIX_HOST_IN_TEMP
744                  : CHAINPREFIX_HOST_OUT_TEMP
745     };
746     char chainPrefix[2] = {
747         prefix,
748         incoming ? CHAINPREFIX_HOST_IN
749                  : CHAINPREFIX_HOST_OUT
750     };
751 
752     PRINT_IPT_ROOT_CHAIN(tmpchain, tmpChainPrefix, ifname);
753     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
754 
755     virFirewallAddRule(fw, layer,
756                        "-E", tmpchain, chain, NULL);
757 }
758 
759 
760 static void
iptablesRenameTmpRootChainsFW(virFirewall * fw,virFirewallLayer layer,const char * ifname)761 iptablesRenameTmpRootChainsFW(virFirewall *fw,
762                               virFirewallLayer layer,
763                               const char *ifname)
764 {
765     iptablesRenameTmpRootChainFW(fw, layer, 'F', false, ifname);
766     iptablesRenameTmpRootChainFW(fw, layer, 'F', true, ifname);
767     iptablesRenameTmpRootChainFW(fw, layer, 'H', true, ifname);
768 }
769 
770 
771 static int
iptablesHandleSrcMacAddr(virFirewall * fw,virFirewallRule * fwrule,virNWFilterVarCombIter * vars,nwItemDesc * srcMacAddr,bool directionIn,bool * srcmacskipped)772 iptablesHandleSrcMacAddr(virFirewall *fw,
773                          virFirewallRule *fwrule,
774                          virNWFilterVarCombIter *vars,
775                          nwItemDesc *srcMacAddr,
776                          bool directionIn,
777                          bool *srcmacskipped)
778 {
779     char macaddr[VIR_MAC_STRING_BUFLEN];
780 
781     *srcmacskipped = false;
782 
783     if (HAS_ENTRY_ITEM(srcMacAddr)) {
784         if (directionIn) {
785             *srcmacskipped = true;
786             return 0;
787         }
788 
789         if (printDataType(vars,
790                           macaddr, sizeof(macaddr),
791                           srcMacAddr) < 0)
792             return -1;
793 
794         virFirewallRuleAddArgList(fw, fwrule,
795                                   "-m", "mac",
796                                   NULL);
797         if (ENTRY_WANT_NEG_SIGN(srcMacAddr))
798             virFirewallRuleAddArg(fw, fwrule, "!");
799         virFirewallRuleAddArgList(fw, fwrule,
800                                   "--mac-source",
801                                   macaddr,
802                                   NULL);
803     }
804 
805     return 0;
806 }
807 
808 
809 static int
iptablesHandleIPHdr(virFirewall * fw,virFirewallRule * fwrule,virNWFilterVarCombIter * vars,ipHdrDataDef * ipHdr,bool directionIn,bool * skipRule,bool * skipMatch)810 iptablesHandleIPHdr(virFirewall *fw,
811                     virFirewallRule *fwrule,
812                     virNWFilterVarCombIter *vars,
813                     ipHdrDataDef *ipHdr,
814                     bool directionIn,
815                     bool *skipRule, bool *skipMatch)
816 {
817     char ipaddr[INET6_ADDRSTRLEN];
818     char ipaddralt[INET6_ADDRSTRLEN];
819     char number[VIR_INT64_STR_BUFLEN];
820     const char *src = "--source";
821     const char *dst = "--destination";
822     const char *srcrange = "--src-range";
823     const char *dstrange = "--dst-range";
824 
825     if (directionIn) {
826         src = "--destination";
827         dst = "--source";
828         srcrange = "--dst-range";
829         dstrange = "--src-range";
830     }
831 
832     if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPAddr)) {
833         if (printDataType(vars,
834                           ipaddr, sizeof(ipaddr),
835                           &ipHdr->dataSrcIPAddr) < 0)
836             return -1;
837 
838         if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataSrcIPAddr))
839             virFirewallRuleAddArg(fw, fwrule, "!");
840         virFirewallRuleAddArg(fw, fwrule, src);
841 
842         if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPMask)) {
843 
844             if (printDataType(vars,
845                               number, sizeof(number),
846                               &ipHdr->dataSrcIPMask) < 0)
847                 return -1;
848 
849             virFirewallRuleAddArgFormat(fw, fwrule,
850                                         "%s/%s", ipaddr, number);
851         } else {
852             virFirewallRuleAddArg(fw, fwrule, ipaddr);
853         }
854     } else if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPFrom)) {
855         if (printDataType(vars,
856                           ipaddr, sizeof(ipaddr),
857                           &ipHdr->dataSrcIPFrom) < 0)
858             return -1;
859 
860         virFirewallRuleAddArgList(fw, fwrule,
861                                   "-m", "iprange",
862                                   NULL);
863         if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataSrcIPFrom))
864             virFirewallRuleAddArg(fw, fwrule, "!");
865         virFirewallRuleAddArg(fw, fwrule, srcrange);
866 
867         if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPTo)) {
868 
869             if (printDataType(vars,
870                               ipaddralt, sizeof(ipaddralt),
871                               &ipHdr->dataSrcIPTo) < 0)
872                 return -1;
873 
874             virFirewallRuleAddArgFormat(fw, fwrule,
875                                         "%s-%s", ipaddr, ipaddralt);
876         } else {
877             virFirewallRuleAddArg(fw, fwrule, ipaddr);
878         }
879     }
880 
881     if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPAddr)) {
882         if (printDataType(vars,
883                           ipaddr, sizeof(ipaddr),
884                           &ipHdr->dataDstIPAddr) < 0)
885            return -1;
886 
887         if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataDstIPAddr))
888             virFirewallRuleAddArg(fw, fwrule, "!");
889         virFirewallRuleAddArg(fw, fwrule, dst);
890 
891         if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPMask)) {
892             if (printDataType(vars,
893                               number, sizeof(number),
894                               &ipHdr->dataDstIPMask) < 0)
895                 return -1;
896 
897             virFirewallRuleAddArgFormat(fw, fwrule,
898                                         "%s/%s", ipaddr, number);
899         } else {
900             virFirewallRuleAddArg(fw, fwrule, ipaddr);
901         }
902     } else if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPFrom)) {
903         if (printDataType(vars,
904                           ipaddr, sizeof(ipaddr),
905                           &ipHdr->dataDstIPFrom) < 0)
906             return -1;
907 
908         virFirewallRuleAddArgList(fw, fwrule,
909                                   "-m", "iprange",
910                                   NULL);
911         if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataDstIPFrom))
912             virFirewallRuleAddArg(fw, fwrule, "!");
913         virFirewallRuleAddArg(fw, fwrule, dstrange);
914 
915         if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPTo)) {
916             if (printDataType(vars,
917                               ipaddralt, sizeof(ipaddralt),
918                               &ipHdr->dataDstIPTo) < 0)
919                 return -1;
920 
921             virFirewallRuleAddArgFormat(fw, fwrule,
922                                         "%s-%s", ipaddr, ipaddralt);
923         } else {
924             virFirewallRuleAddArg(fw, fwrule, ipaddr);
925         }
926     }
927 
928     if (HAS_ENTRY_ITEM(&ipHdr->dataDSCP)) {
929         if (printDataType(vars,
930                           number, sizeof(number),
931                           &ipHdr->dataDSCP) < 0)
932            return -1;
933 
934         virFirewallRuleAddArgList(fw, fwrule,
935                                   "-m", "dscp",
936                                   NULL);
937         if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataDSCP))
938             virFirewallRuleAddArg(fw, fwrule, "!");
939         virFirewallRuleAddArgList(fw, fwrule,
940                                   "--dscp", number,
941                                   NULL);
942     }
943 
944     if (HAS_ENTRY_ITEM(&ipHdr->dataConnlimitAbove)) {
945         if (directionIn) {
946             /* only support for limit in outgoing dir. */
947             *skipRule = true;
948         } else {
949             *skipMatch = true;
950         }
951     }
952 
953     return 0;
954 }
955 
956 
957 static int
iptablesHandleIPHdrAfterStateMatch(virFirewall * fw,virFirewallRule * fwrule,virNWFilterVarCombIter * vars,ipHdrDataDef * ipHdr,bool directionIn)958 iptablesHandleIPHdrAfterStateMatch(virFirewall *fw,
959                                    virFirewallRule *fwrule,
960                                    virNWFilterVarCombIter *vars,
961                                    ipHdrDataDef *ipHdr,
962                                    bool directionIn)
963 {
964     char number[VIR_INT64_STR_BUFLEN];
965     char str[MAX_IPSET_NAME_LENGTH];
966 
967     if (HAS_ENTRY_ITEM(&ipHdr->dataIPSet) &&
968         HAS_ENTRY_ITEM(&ipHdr->dataIPSetFlags)) {
969 
970         if (printDataType(vars,
971                           str, sizeof(str),
972                           &ipHdr->dataIPSet) < 0)
973             return -1;
974 
975         virFirewallRuleAddArgList(fw, fwrule,
976                                   "-m", "set",
977                                   "--match-set", str,
978                                   NULL);
979 
980         if (printDataTypeDirection(vars,
981                                    str, sizeof(str),
982                                    &ipHdr->dataIPSetFlags, directionIn) < 0)
983             return -1;
984 
985         virFirewallRuleAddArg(fw, fwrule, str);
986     }
987 
988     if (HAS_ENTRY_ITEM(&ipHdr->dataConnlimitAbove)) {
989         if (!directionIn) {
990             if (printDataType(vars,
991                               number, sizeof(number),
992                               &ipHdr->dataConnlimitAbove) < 0)
993                return -1;
994 
995             /* place connlimit after potential -m state --state ...
996                since this is the most useful order */
997             virFirewallRuleAddArgList(fw, fwrule,
998                                       "-m", "connlimit",
999                                       NULL);
1000             if (ENTRY_WANT_NEG_SIGN(&ipHdr->dataConnlimitAbove))
1001                 virFirewallRuleAddArg(fw, fwrule, "!");
1002             virFirewallRuleAddArgList(fw, fwrule,
1003                                       "--connlimit-above", number,
1004                                       NULL);
1005         }
1006     }
1007 
1008     if (HAS_ENTRY_ITEM(&ipHdr->dataComment)) {
1009         /* keep comments behind everything else -- they are packet eval.
1010            no-ops */
1011         virFirewallRuleAddArgList(fw, fwrule,
1012                                   "-m", "comment",
1013                                   "--comment", ipHdr->dataComment.u.string,
1014                                   NULL);
1015     }
1016 
1017     return 0;
1018 }
1019 
1020 
1021 static int
iptablesHandlePortData(virFirewall * fw,virFirewallRule * fwrule,virNWFilterVarCombIter * vars,portDataDef * portData,bool directionIn)1022 iptablesHandlePortData(virFirewall *fw,
1023                        virFirewallRule *fwrule,
1024                        virNWFilterVarCombIter *vars,
1025                        portDataDef *portData,
1026                        bool directionIn)
1027 {
1028     char portstr[20];
1029     char portstralt[20];
1030     const char *sport = "--sport";
1031     const char *dport = "--dport";
1032     if (directionIn) {
1033         sport = "--dport";
1034         dport = "--sport";
1035     }
1036 
1037     if (HAS_ENTRY_ITEM(&portData->dataSrcPortStart)) {
1038         if (printDataType(vars,
1039                           portstr, sizeof(portstr),
1040                           &portData->dataSrcPortStart) < 0)
1041             return -1;
1042 
1043         if (ENTRY_WANT_NEG_SIGN(&portData->dataSrcPortStart))
1044             virFirewallRuleAddArg(fw, fwrule, "!");
1045         virFirewallRuleAddArg(fw, fwrule, sport);
1046 
1047         if (HAS_ENTRY_ITEM(&portData->dataSrcPortEnd)) {
1048             if (printDataType(vars,
1049                               portstralt, sizeof(portstralt),
1050                               &portData->dataSrcPortEnd) < 0)
1051                 return -1;
1052 
1053             virFirewallRuleAddArgFormat(fw, fwrule,
1054                                         "%s:%s", portstr, portstralt);
1055         } else {
1056             virFirewallRuleAddArg(fw, fwrule, portstr);
1057         }
1058     }
1059 
1060     if (HAS_ENTRY_ITEM(&portData->dataDstPortStart)) {
1061         if (printDataType(vars,
1062                           portstr, sizeof(portstr),
1063                           &portData->dataDstPortStart) < 0)
1064             return -1;
1065 
1066         if (ENTRY_WANT_NEG_SIGN(&portData->dataDstPortStart))
1067             virFirewallRuleAddArg(fw, fwrule, "!");
1068         virFirewallRuleAddArg(fw, fwrule, dport);
1069 
1070         if (HAS_ENTRY_ITEM(&portData->dataDstPortEnd)) {
1071             if (printDataType(vars,
1072                               portstralt, sizeof(portstralt),
1073                               &portData->dataDstPortEnd) < 0)
1074                 return -1;
1075 
1076             virFirewallRuleAddArgFormat(fw, fwrule,
1077                                         "%s:%s", portstr, portstralt);
1078         } else {
1079             virFirewallRuleAddArg(fw, fwrule, portstr);
1080         }
1081     }
1082 
1083     return 0;
1084 }
1085 
1086 
1087 static void
iptablesEnforceDirection(virFirewall * fw,virFirewallRule * fwrule,bool directionIn,virNWFilterRuleDef * rule)1088 iptablesEnforceDirection(virFirewall *fw,
1089                          virFirewallRule *fwrule,
1090                          bool directionIn,
1091                          virNWFilterRuleDef *rule)
1092 {
1093     switch (iptables_ctdir_corrected) {
1094     case CTDIR_STATUS_UNKNOWN:
1095         /* could not be determined or s.th. is seriously wrong */
1096         return;
1097     case CTDIR_STATUS_CORRECTED:
1098         directionIn = !directionIn;
1099         break;
1100     case CTDIR_STATUS_OLD:
1101         break;
1102     }
1103 
1104     if (rule->tt != VIR_NWFILTER_RULE_DIRECTION_INOUT)
1105         virFirewallRuleAddArgList(fw, fwrule,
1106                                   "-m", "conntrack",
1107                                   "--ctdir",
1108                                   (directionIn ?
1109                                    "Original" :
1110                                    "Reply"),
1111                                   NULL);
1112 }
1113 
1114 
1115 /*
1116  * _iptablesCreateRuleInstance:
1117  * @fw: the firewall ruleset instance
1118  * @layer: the firewall layer
1119  * @chainPrefix : The prefix to put in front of the name of the chain
1120  * @rule: The rule of the filter to convert
1121  * @ifname : The name of the interface to apply the rule to
1122  * @vars : A map containing the variables to resolve
1123  * @match : optional string for state match
1124  * @accept_target : where to jump to on accepted traffic, i.e., "RETURN"
1125  *    "ACCEPT"
1126  * @maySkipICMP : whether this rule may under certain circumstances skip
1127  *           the ICMP rule from being created
1128  *
1129  * Convert a single rule into its representation for later instantiation
1130  *
1131  * Returns 0 in case of success with the result stored in the data structure
1132  * pointed to by res, != 0 otherwise.
1133  */
1134 static int
_iptablesCreateRuleInstance(virFirewall * fw,virFirewallLayer layer,bool directionIn,const char * chainPrefix,virNWFilterRuleDef * rule,const char * ifname,virNWFilterVarCombIter * vars,const char * match,bool defMatch,const char * accept_target,bool maySkipICMP)1135 _iptablesCreateRuleInstance(virFirewall *fw,
1136                             virFirewallLayer layer,
1137                             bool directionIn,
1138                             const char *chainPrefix,
1139                             virNWFilterRuleDef *rule,
1140                             const char *ifname,
1141                             virNWFilterVarCombIter *vars,
1142                             const char *match, bool defMatch,
1143                             const char *accept_target,
1144                             bool maySkipICMP)
1145 {
1146     char chain[MAX_CHAINNAME_LENGTH];
1147     char number[VIR_INT64_STR_BUFLEN];
1148     char numberalt[VIR_INT64_STR_BUFLEN];
1149     const char *target;
1150     bool srcMacSkipped = false;
1151     bool skipRule = false;
1152     bool skipMatch = false;
1153     bool hasICMPType = false;
1154     virFirewallRule *fwrule;
1155     size_t fwruleargs;
1156 
1157     PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname);
1158 
1159     switch ((int)rule->prtclType) {
1160     case VIR_NWFILTER_RULE_PROTOCOL_TCP:
1161     case VIR_NWFILTER_RULE_PROTOCOL_TCPoIPV6:
1162         fwrule = virFirewallAddRule(fw, layer,
1163                                     "-A", chain,
1164                                     "-p", "tcp",
1165                                     NULL);
1166 
1167         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1168 
1169         if (iptablesHandleSrcMacAddr(fw, fwrule,
1170                                      vars,
1171                                      &rule->p.tcpHdrFilter.dataSrcMACAddr,
1172                                      directionIn,
1173                                      &srcMacSkipped) < 0)
1174             return -1;
1175 
1176         if (iptablesHandleIPHdr(fw, fwrule,
1177                                 vars,
1178                                 &rule->p.tcpHdrFilter.ipHdr,
1179                                 directionIn,
1180                                 &skipRule, &skipMatch) < 0)
1181             return -1;
1182 
1183         if (HAS_ENTRY_ITEM(&rule->p.tcpHdrFilter.dataTCPFlags)) {
1184             g_autofree char *mask = NULL;
1185             g_autofree char *flags = NULL;
1186             if (ENTRY_WANT_NEG_SIGN(&rule->p.tcpHdrFilter.dataTCPFlags))
1187                 virFirewallRuleAddArg(fw, fwrule, "!");
1188             virFirewallRuleAddArg(fw, fwrule, "--tcp-flags");
1189 
1190             if (!(mask = virNWFilterPrintTCPFlags(rule->p.tcpHdrFilter.dataTCPFlags.u.tcpFlags.mask)))
1191                 return -1;
1192             virFirewallRuleAddArg(fw, fwrule, mask);
1193 
1194             if (!(flags = virNWFilterPrintTCPFlags(rule->p.tcpHdrFilter.dataTCPFlags.u.tcpFlags.flags)))
1195                 return -1;
1196             virFirewallRuleAddArg(fw, fwrule, flags);
1197         }
1198 
1199         if (iptablesHandlePortData(fw, fwrule,
1200                                    vars,
1201                                    &rule->p.tcpHdrFilter.portData,
1202                                    directionIn) < 0)
1203             return -1;
1204 
1205         if (HAS_ENTRY_ITEM(&rule->p.tcpHdrFilter.dataTCPOption)) {
1206             if (printDataType(vars,
1207                               number, sizeof(number),
1208                               &rule->p.tcpHdrFilter.dataTCPOption) < 0)
1209                 return -1;
1210 
1211             if (ENTRY_WANT_NEG_SIGN(&rule->p.tcpHdrFilter.dataTCPOption))
1212                 virFirewallRuleAddArg(fw, fwrule, "!");
1213             virFirewallRuleAddArgList(fw, fwrule,
1214                                       "--tcp-option", number, NULL);
1215         }
1216 
1217     break;
1218 
1219     case VIR_NWFILTER_RULE_PROTOCOL_UDP:
1220     case VIR_NWFILTER_RULE_PROTOCOL_UDPoIPV6:
1221         fwrule = virFirewallAddRule(fw, layer,
1222                                     "-A", chain,
1223                                     "-p", "udp",
1224                                     NULL);
1225 
1226         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1227 
1228         if (iptablesHandleSrcMacAddr(fw, fwrule,
1229                                      vars,
1230                                      &rule->p.udpHdrFilter.dataSrcMACAddr,
1231                                      directionIn,
1232                                      &srcMacSkipped) < 0)
1233             return -1;
1234 
1235         if (iptablesHandleIPHdr(fw, fwrule,
1236                                 vars,
1237                                 &rule->p.udpHdrFilter.ipHdr,
1238                                 directionIn,
1239                                 &skipRule, &skipMatch) < 0)
1240             return -1;
1241 
1242         if (iptablesHandlePortData(fw, fwrule,
1243                                    vars,
1244                                    &rule->p.udpHdrFilter.portData,
1245                                    directionIn) < 0)
1246             return -1;
1247     break;
1248 
1249     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITE:
1250     case VIR_NWFILTER_RULE_PROTOCOL_UDPLITEoIPV6:
1251         fwrule = virFirewallAddRule(fw, layer,
1252                                     "-A", chain,
1253                                     "-p", "udplite",
1254                                     NULL);
1255 
1256         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1257 
1258         if (iptablesHandleSrcMacAddr(fw, fwrule,
1259                                      vars,
1260                                      &rule->p.udpliteHdrFilter.dataSrcMACAddr,
1261                                      directionIn,
1262                                      &srcMacSkipped) < 0)
1263             return -1;
1264 
1265         if (iptablesHandleIPHdr(fw, fwrule,
1266                                 vars,
1267                                 &rule->p.udpliteHdrFilter.ipHdr,
1268                                 directionIn,
1269                                 &skipRule, &skipMatch) < 0)
1270             return -1;
1271 
1272     break;
1273 
1274     case VIR_NWFILTER_RULE_PROTOCOL_ESP:
1275     case VIR_NWFILTER_RULE_PROTOCOL_ESPoIPV6:
1276         fwrule = virFirewallAddRule(fw, layer,
1277                                     "-A", chain,
1278                                     "-p", "esp",
1279                                     NULL);
1280 
1281         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1282 
1283         if (iptablesHandleSrcMacAddr(fw, fwrule,
1284                                      vars,
1285                                      &rule->p.espHdrFilter.dataSrcMACAddr,
1286                                      directionIn,
1287                                      &srcMacSkipped) < 0)
1288             return -1;
1289 
1290         if (iptablesHandleIPHdr(fw, fwrule,
1291                                 vars,
1292                                 &rule->p.espHdrFilter.ipHdr,
1293                                 directionIn,
1294                                 &skipRule, &skipMatch) < 0)
1295             return -1;
1296 
1297     break;
1298 
1299     case VIR_NWFILTER_RULE_PROTOCOL_AH:
1300     case VIR_NWFILTER_RULE_PROTOCOL_AHoIPV6:
1301         fwrule = virFirewallAddRule(fw, layer,
1302                                     "-A", chain,
1303                                     "-p", "ah",
1304                                     NULL);
1305 
1306         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1307 
1308         if (iptablesHandleSrcMacAddr(fw, fwrule,
1309                                      vars,
1310                                      &rule->p.ahHdrFilter.dataSrcMACAddr,
1311                                      directionIn,
1312                                      &srcMacSkipped) < 0)
1313             return -1;
1314 
1315         if (iptablesHandleIPHdr(fw, fwrule,
1316                                 vars,
1317                                 &rule->p.ahHdrFilter.ipHdr,
1318                                 directionIn,
1319                                 &skipRule, &skipMatch) < 0)
1320             return -1;
1321 
1322     break;
1323 
1324     case VIR_NWFILTER_RULE_PROTOCOL_SCTP:
1325     case VIR_NWFILTER_RULE_PROTOCOL_SCTPoIPV6:
1326         fwrule = virFirewallAddRule(fw, layer,
1327                                     "-A", chain,
1328                                     "-p", "sctp",
1329                                     NULL);
1330 
1331         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1332 
1333         if (iptablesHandleSrcMacAddr(fw, fwrule,
1334                                      vars,
1335                                      &rule->p.sctpHdrFilter.dataSrcMACAddr,
1336                                      directionIn,
1337                                      &srcMacSkipped) < 0)
1338             return -1;
1339 
1340         if (iptablesHandleIPHdr(fw, fwrule,
1341                                 vars,
1342                                 &rule->p.sctpHdrFilter.ipHdr,
1343                                 directionIn,
1344                                 &skipRule, &skipMatch) < 0)
1345             return -1;
1346 
1347         if (iptablesHandlePortData(fw, fwrule,
1348                                    vars,
1349                                    &rule->p.sctpHdrFilter.portData,
1350                                    directionIn) < 0)
1351             return -1;
1352     break;
1353 
1354     case VIR_NWFILTER_RULE_PROTOCOL_ICMP:
1355     case VIR_NWFILTER_RULE_PROTOCOL_ICMPV6:
1356         fwrule = virFirewallAddRule(fw, layer,
1357                                     "-A", chain,
1358                                     NULL);
1359 
1360         if (rule->prtclType == VIR_NWFILTER_RULE_PROTOCOL_ICMP)
1361             virFirewallRuleAddArgList(fw, fwrule,
1362                                       "-p", "icmp", NULL);
1363         else
1364             virFirewallRuleAddArgList(fw, fwrule,
1365                                       "-p", "icmpv6", NULL);
1366 
1367         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1368 
1369         if (iptablesHandleSrcMacAddr(fw, fwrule,
1370                                      vars,
1371                                      &rule->p.icmpHdrFilter.dataSrcMACAddr,
1372                                      directionIn,
1373                                      &srcMacSkipped) < 0)
1374             return -1;
1375 
1376         if (iptablesHandleIPHdr(fw, fwrule,
1377                                 vars,
1378                                 &rule->p.icmpHdrFilter.ipHdr,
1379                                 directionIn,
1380                                 &skipRule, &skipMatch) < 0)
1381             return -1;
1382 
1383         if (HAS_ENTRY_ITEM(&rule->p.icmpHdrFilter.dataICMPType)) {
1384             const char *parm;
1385 
1386             hasICMPType = true;
1387 
1388             if (maySkipICMP) {
1389                 virFirewallRemoveRule(fw, fwrule);
1390                 return 0;
1391             }
1392 
1393             if (rule->prtclType == VIR_NWFILTER_RULE_PROTOCOL_ICMP)
1394                 parm = "--icmp-type";
1395             else
1396                 parm = "--icmpv6-type";
1397 
1398             if (printDataType(vars,
1399                               number, sizeof(number),
1400                               &rule->p.icmpHdrFilter.dataICMPType) < 0)
1401                 return -1;
1402 
1403             if (ENTRY_WANT_NEG_SIGN(&rule->p.icmpHdrFilter.dataICMPType))
1404                 virFirewallRuleAddArg(fw, fwrule, "!");
1405             virFirewallRuleAddArg(fw, fwrule, parm);
1406 
1407             if (HAS_ENTRY_ITEM(&rule->p.icmpHdrFilter.dataICMPCode)) {
1408                 if (printDataType(vars,
1409                                   numberalt, sizeof(numberalt),
1410                                   &rule->p.icmpHdrFilter.dataICMPCode) < 0)
1411                     return -1;
1412 
1413                 virFirewallRuleAddArgFormat(fw, fwrule,
1414                                             "%s/%s", number, numberalt);
1415             } else {
1416                 virFirewallRuleAddArg(fw, fwrule, number);
1417             }
1418         }
1419     break;
1420 
1421     case VIR_NWFILTER_RULE_PROTOCOL_IGMP:
1422         fwrule = virFirewallAddRule(fw, layer,
1423                                     "-A", chain,
1424                                     "-p", "igmp",
1425                                     NULL);
1426 
1427         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1428 
1429         if (iptablesHandleSrcMacAddr(fw, fwrule,
1430                                      vars,
1431                                      &rule->p.igmpHdrFilter.dataSrcMACAddr,
1432                                      directionIn,
1433                                      &srcMacSkipped) < 0)
1434             return -1;
1435 
1436         if (iptablesHandleIPHdr(fw, fwrule,
1437                                 vars,
1438                                 &rule->p.igmpHdrFilter.ipHdr,
1439                                 directionIn,
1440                                 &skipRule, &skipMatch) < 0)
1441             return -1;
1442 
1443     break;
1444 
1445     case VIR_NWFILTER_RULE_PROTOCOL_ALL:
1446     case VIR_NWFILTER_RULE_PROTOCOL_ALLoIPV6:
1447         fwrule = virFirewallAddRule(fw, layer,
1448                                     "-A", chain,
1449                                     "-p", "all",
1450                                     NULL);
1451 
1452         fwruleargs = virFirewallRuleGetArgCount(fwrule);
1453 
1454         if (iptablesHandleSrcMacAddr(fw, fwrule,
1455                                      vars,
1456                                      &rule->p.allHdrFilter.dataSrcMACAddr,
1457                                      directionIn,
1458                                      &srcMacSkipped) < 0)
1459             return -1;
1460 
1461         if (iptablesHandleIPHdr(fw, fwrule,
1462                                 vars,
1463                                 &rule->p.allHdrFilter.ipHdr,
1464                                 directionIn,
1465                                 &skipRule, &skipMatch) < 0)
1466             return -1;
1467 
1468     break;
1469 
1470     default:
1471         virReportError(VIR_ERR_INTERNAL_ERROR,
1472                        _("Unexpected protocol %d"),
1473                        rule->prtclType);
1474         return -1;
1475     }
1476 
1477     if ((srcMacSkipped &&
1478          fwruleargs == virFirewallRuleGetArgCount(fwrule)) ||
1479         skipRule) {
1480         virFirewallRemoveRule(fw, fwrule);
1481         return 0;
1482     }
1483 
1484     if (rule->action == VIR_NWFILTER_RULE_ACTION_ACCEPT) {
1485         target = accept_target;
1486     } else {
1487         target = virNWFilterJumpTargetTypeToString(rule->action);
1488         skipMatch = defMatch;
1489     }
1490 
1491     if (match && !skipMatch) {
1492         if (newMatchState)
1493             virFirewallRuleAddArgList(fw, fwrule,
1494                                       "-m", "conntrack",
1495                                       "--ctstate", match,
1496                                       NULL);
1497         else
1498             virFirewallRuleAddArgList(fw, fwrule,
1499                                       "-m", "state",
1500                                       "--state", match,
1501                                       NULL);
1502     }
1503 
1504     if (defMatch && match != NULL && !skipMatch && !hasICMPType)
1505         iptablesEnforceDirection(fw, fwrule,
1506                                  directionIn,
1507                                  rule);
1508 
1509     if (iptablesHandleIPHdrAfterStateMatch(fw, fwrule,
1510                                            vars,
1511                                            &rule->p.allHdrFilter.ipHdr,
1512                                            directionIn) < 0)
1513         return -1;
1514 
1515     virFirewallRuleAddArgList(fw, fwrule,
1516                               "-j", target, NULL);
1517 
1518     return 0;
1519 }
1520 
1521 
1522 static int
printStateMatchFlags(int32_t flags,char ** bufptr)1523 printStateMatchFlags(int32_t flags, char **bufptr)
1524 {
1525     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
1526     virNWFilterPrintStateMatchFlags(&buf,
1527                                     "",
1528                                     flags,
1529                                     false);
1530     *bufptr = virBufferContentAndReset(&buf);
1531     return 0;
1532 }
1533 
1534 static int
iptablesCreateRuleInstanceStateCtrl(virFirewall * fw,virFirewallLayer layer,virNWFilterRuleDef * rule,const char * ifname,virNWFilterVarCombIter * vars)1535 iptablesCreateRuleInstanceStateCtrl(virFirewall *fw,
1536                                     virFirewallLayer layer,
1537                                     virNWFilterRuleDef *rule,
1538                                     const char *ifname,
1539                                     virNWFilterVarCombIter *vars)
1540 {
1541     int rc = 0;
1542     bool directionIn = false;
1543     char chainPrefix[2];
1544     bool maySkipICMP, inout = false;
1545     g_autofree char *matchState1 = NULL;
1546     g_autofree char *matchState2 = NULL;
1547     g_autofree char *matchState3 = NULL;
1548     bool create;
1549 
1550     if ((rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN) ||
1551         (rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT)) {
1552         directionIn = true;
1553         inout = (rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT);
1554     }
1555 
1556     chainPrefix[0] = 'F';
1557 
1558     maySkipICMP = directionIn || inout;
1559 
1560     create = true;
1561 
1562     if (directionIn && !inout) {
1563         if ((rule->flags & IPTABLES_STATE_FLAGS))
1564             create = false;
1565     }
1566 
1567     if (create && (rule->flags & IPTABLES_STATE_FLAGS)) {
1568         if (printStateMatchFlags(rule->flags, &matchState1) < 0)
1569             return -1;
1570     }
1571 
1572     chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP;
1573     if (create) {
1574         rc = _iptablesCreateRuleInstance(fw,
1575                                          layer,
1576                                          directionIn,
1577                                          chainPrefix,
1578                                          rule,
1579                                          ifname,
1580                                          vars,
1581                                          matchState1, false,
1582                                          "RETURN",
1583                                          maySkipICMP);
1584 
1585         if (rc < 0)
1586             return rc;
1587     }
1588 
1589     maySkipICMP = !directionIn || inout;
1590     create = true;
1591 
1592     if (!directionIn) {
1593         if ((rule->flags & IPTABLES_STATE_FLAGS))
1594             create = false;
1595     }
1596 
1597     if (create && (rule->flags & IPTABLES_STATE_FLAGS)) {
1598         if (printStateMatchFlags(rule->flags, &matchState2) < 0)
1599             return -1;
1600     }
1601 
1602     chainPrefix[1] = CHAINPREFIX_HOST_OUT_TEMP;
1603     if (create) {
1604         rc = _iptablesCreateRuleInstance(fw,
1605                                          layer,
1606                                          !directionIn,
1607                                          chainPrefix,
1608                                          rule,
1609                                          ifname,
1610                                          vars,
1611                                          matchState2, false,
1612                                          "ACCEPT",
1613                                          maySkipICMP);
1614         if (rc < 0)
1615             return rc;
1616     }
1617 
1618     maySkipICMP = directionIn;
1619 
1620     create = true;
1621 
1622     if (directionIn && !inout) {
1623         if ((rule->flags & IPTABLES_STATE_FLAGS))
1624             create = false;
1625     } else {
1626         if ((rule->flags & IPTABLES_STATE_FLAGS)) {
1627             if (printStateMatchFlags(rule->flags, &matchState3) < 0)
1628                 return -1;
1629         }
1630     }
1631 
1632     if (create) {
1633         chainPrefix[0] = 'H';
1634         chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP;
1635         rc = _iptablesCreateRuleInstance(fw,
1636                                          layer,
1637                                          directionIn,
1638                                          chainPrefix,
1639                                          rule,
1640                                          ifname,
1641                                          vars,
1642                                          matchState3, false,
1643                                          "RETURN",
1644                                          maySkipICMP);
1645     }
1646 
1647     return rc;
1648 }
1649 
1650 
1651 static int
iptablesCreateRuleInstance(virFirewall * fw,virFirewallLayer layer,virNWFilterRuleDef * rule,const char * ifname,virNWFilterVarCombIter * vars)1652 iptablesCreateRuleInstance(virFirewall *fw,
1653                            virFirewallLayer layer,
1654                            virNWFilterRuleDef *rule,
1655                            const char *ifname,
1656                            virNWFilterVarCombIter *vars)
1657 {
1658     int rc;
1659     bool directionIn = false;
1660     char chainPrefix[2];
1661     bool needState = true;
1662     bool maySkipICMP, inout = false;
1663     const char *matchState;
1664 
1665     if (!(rule->flags & RULE_FLAG_NO_STATEMATCH) &&
1666          (rule->flags & IPTABLES_STATE_FLAGS)) {
1667         return iptablesCreateRuleInstanceStateCtrl(fw,
1668                                                    layer,
1669                                                    rule,
1670                                                    ifname,
1671                                                    vars);
1672     }
1673 
1674     if ((rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN) ||
1675         (rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT)) {
1676         directionIn = true;
1677         inout = (rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT);
1678         if (inout)
1679             needState = false;
1680     }
1681 
1682     if ((rule->flags & RULE_FLAG_NO_STATEMATCH))
1683         needState = false;
1684 
1685     chainPrefix[0] = 'F';
1686 
1687     maySkipICMP = directionIn || inout;
1688 
1689     if (needState)
1690         matchState = directionIn ? "ESTABLISHED" : "NEW,ESTABLISHED";
1691     else
1692         matchState = NULL;
1693 
1694     chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP;
1695     rc = _iptablesCreateRuleInstance(fw,
1696                                      layer,
1697                                      directionIn,
1698                                      chainPrefix,
1699                                      rule,
1700                                      ifname,
1701                                      vars,
1702                                      matchState, true,
1703                                      "RETURN",
1704                                      maySkipICMP);
1705     if (rc < 0)
1706         return rc;
1707 
1708 
1709     maySkipICMP = !directionIn || inout;
1710     if (needState)
1711         matchState = directionIn ?  "NEW,ESTABLISHED" : "ESTABLISHED";
1712     else
1713         matchState = NULL;
1714 
1715     chainPrefix[1] = CHAINPREFIX_HOST_OUT_TEMP;
1716     rc = _iptablesCreateRuleInstance(fw,
1717                                      layer,
1718                                      !directionIn,
1719                                      chainPrefix,
1720                                      rule,
1721                                      ifname,
1722                                      vars,
1723                                      matchState, true,
1724                                      "ACCEPT",
1725                                      maySkipICMP);
1726     if (rc < 0)
1727         return rc;
1728 
1729     maySkipICMP = directionIn;
1730     if (needState)
1731         matchState = directionIn ? "ESTABLISHED" : "NEW,ESTABLISHED";
1732     else
1733         matchState = NULL;
1734 
1735     chainPrefix[0] = 'H';
1736     chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP;
1737     rc = _iptablesCreateRuleInstance(fw,
1738                                      layer,
1739                                      directionIn,
1740                                      chainPrefix,
1741                                      rule,
1742                                      ifname,
1743                                      vars,
1744                                      matchState, true,
1745                                      "RETURN",
1746                                      maySkipICMP);
1747 
1748     return rc;
1749 }
1750 
1751 
1752 
1753 
1754 /*
1755  * ebtablesCreateRuleInstance:
1756  * @fw: the firewall ruleset to add to
1757  * @chainPrefix : The prefix to put in front of the name of the chain
1758  * @chainSuffix: The suffix to put on the end of the name of the chain
1759  * @rule: The rule of the filter to convert
1760  * @ifname : The name of the interface to apply the rule to
1761  * @vars : A map containing the variables to resolve
1762  * @reverse : Whether to reverse src and dst attributes
1763  *
1764  * Convert a single rule into its representation for later instantiation
1765  *
1766  * Returns 0 in case of success with the result stored in the data structure
1767  * pointed to by res, != 0 otherwise.
1768  */
1769 static int
ebtablesCreateRuleInstance(virFirewall * fw,char chainPrefix,const char * chainSuffix,virNWFilterRuleDef * rule,const char * ifname,virNWFilterVarCombIter * vars,bool reverse)1770 ebtablesCreateRuleInstance(virFirewall *fw,
1771                            char chainPrefix,
1772                            const char *chainSuffix,
1773                            virNWFilterRuleDef *rule,
1774                            const char *ifname,
1775                            virNWFilterVarCombIter *vars,
1776                            bool reverse)
1777 {
1778     char macaddr[VIR_MAC_STRING_BUFLEN];
1779     char ipaddr[INET_ADDRSTRLEN];
1780     char ipmask[INET_ADDRSTRLEN];
1781     char ipv6addr[INET6_ADDRSTRLEN];
1782     char number[VIR_INT64_STR_BUFLEN];
1783     char numberalt[VIR_INT64_STR_BUFLEN];
1784     char field[VIR_INT64_STR_BUFLEN];
1785     char fieldalt[VIR_INT64_STR_BUFLEN];
1786     char chain[MAX_CHAINNAME_LENGTH];
1787     const char *target;
1788     bool hasMask = false;
1789     virFirewallRule *fwrule;
1790 
1791     if (STREQ(chainSuffix,
1792               virNWFilterChainSuffixTypeToString(
1793                   VIR_NWFILTER_CHAINSUFFIX_ROOT)))
1794         PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
1795     else
1796         PRINT_CHAIN(chain, chainPrefix, ifname,
1797                     chainSuffix);
1798 
1799 #define INST_ITEM(STRUCT, ITEM, CLI) \
1800         if (HAS_ENTRY_ITEM(&rule->p.STRUCT.ITEM)) { \
1801             if (printDataType(vars, \
1802                               field, sizeof(field), \
1803                               &rule->p.STRUCT.ITEM) < 0) \
1804                 return -1; \
1805             virFirewallRuleAddArg(fw, fwrule, CLI); \
1806             if (ENTRY_WANT_NEG_SIGN(&rule->p.STRUCT.ITEM)) \
1807                 virFirewallRuleAddArg(fw, fwrule, "!"); \
1808             virFirewallRuleAddArg(fw, fwrule, field); \
1809         }
1810 
1811 #define INST_ITEM_2PARMS(STRUCT, ITEM, ITEM_HI, CLI, SEP) \
1812         if (HAS_ENTRY_ITEM(&rule->p.STRUCT.ITEM)) { \
1813             if (printDataType(vars, \
1814                               field, sizeof(field), \
1815                               &rule->p.STRUCT.ITEM) < 0) \
1816                 return -1; \
1817             virFirewallRuleAddArg(fw, fwrule, CLI); \
1818             if (ENTRY_WANT_NEG_SIGN(&rule->p.STRUCT.ITEM)) \
1819                 virFirewallRuleAddArg(fw, fwrule, "!"); \
1820             if (HAS_ENTRY_ITEM(&rule->p.STRUCT.ITEM_HI)) { \
1821                 if (printDataType(vars, \
1822                                   fieldalt, sizeof(fieldalt), \
1823                                   &rule->p.STRUCT.ITEM_HI) < 0) \
1824                     return -1; \
1825                 virFirewallRuleAddArgFormat(fw, fwrule, \
1826                                             "%s%s%s", field, SEP, fieldalt); \
1827             } else  { \
1828                 virFirewallRuleAddArg(fw, fwrule, field); \
1829             } \
1830         }
1831 #define INST_ITEM_RANGE(S, I, I_HI, C) \
1832     INST_ITEM_2PARMS(S, I, I_HI, C, ":")
1833 #define INST_ITEM_MASK(S, I, MASK, C) \
1834     INST_ITEM_2PARMS(S, I, MASK, C, "/")
1835 
1836     switch ((int)rule->prtclType) {
1837     case VIR_NWFILTER_RULE_PROTOCOL_MAC:
1838         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
1839                                     "-t", "nat",
1840                                     "-A", chain, NULL);
1841 
1842         if (ebtablesHandleEthHdr(fw, fwrule,
1843                                  vars,
1844                                  &rule->p.ethHdrFilter.ethHdr,
1845                                  reverse) < 0)
1846             return -1;
1847 
1848         if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataProtocolID)) {
1849             if (printDataTypeAsHex(vars,
1850                                    number, sizeof(number),
1851                                    &rule->p.ethHdrFilter.dataProtocolID) < 0)
1852                 return -1;
1853             virFirewallRuleAddArg(fw, fwrule, "-p");
1854             if (ENTRY_WANT_NEG_SIGN(&rule->p.ethHdrFilter.dataProtocolID))
1855                 virFirewallRuleAddArg(fw, fwrule, "!");
1856             virFirewallRuleAddArg(fw, fwrule, number);
1857         }
1858         break;
1859 
1860     case VIR_NWFILTER_RULE_PROTOCOL_VLAN:
1861         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
1862                                     "-t", "nat", "-A", chain, NULL);
1863 
1864         if (ebtablesHandleEthHdr(fw, fwrule,
1865                                  vars,
1866                                  &rule->p.vlanHdrFilter.ethHdr,
1867                                  reverse) < 0)
1868             return -1;
1869 
1870         virFirewallRuleAddArgList(fw, fwrule,
1871                                   "-p", "0x8100", NULL);
1872 
1873         INST_ITEM(vlanHdrFilter, dataVlanID, "--vlan-id")
1874         INST_ITEM(vlanHdrFilter, dataVlanEncap, "--vlan-encap")
1875         break;
1876 
1877     case VIR_NWFILTER_RULE_PROTOCOL_STP:
1878         /* cannot handle inout direction with srcmask set in reverse dir.
1879            since this clashes with -d below... */
1880         if (reverse &&
1881             HAS_ENTRY_ITEM(&rule->p.stpHdrFilter.ethHdr.dataSrcMACAddr)) {
1882             virReportError(VIR_ERR_INTERNAL_ERROR,
1883                            _("STP filtering in %s direction with "
1884                              "source MAC address set is not supported"),
1885                            virNWFilterRuleDirectionTypeToString(
1886                                VIR_NWFILTER_RULE_DIRECTION_INOUT));
1887             return -1;
1888         }
1889 
1890         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
1891                                     "-t", "nat", "-A", chain, NULL);
1892 
1893         if (ebtablesHandleEthHdr(fw, fwrule,
1894                                  vars,
1895                                  &rule->p.stpHdrFilter.ethHdr,
1896                                  reverse) < 0)
1897             return -1;
1898 
1899         virFirewallRuleAddArgList(fw, fwrule,
1900                                   "-d",  NWFILTER_MAC_BGA, NULL);
1901 
1902         INST_ITEM(stpHdrFilter, dataType, "--stp-type")
1903         INST_ITEM(stpHdrFilter, dataFlags, "--stp-flags")
1904         INST_ITEM_RANGE(stpHdrFilter, dataRootPri, dataRootPriHi,
1905                         "--stp-root-pri");
1906         INST_ITEM_MASK(stpHdrFilter, dataRootAddr, dataRootAddrMask,
1907                        "--stp-root-addr");
1908         INST_ITEM_RANGE(stpHdrFilter, dataRootCost, dataRootCostHi,
1909                         "--stp-root-cost");
1910         INST_ITEM_RANGE(stpHdrFilter, dataSndrPrio, dataSndrPrioHi,
1911                         "--stp-sender-prio");
1912         INST_ITEM_MASK(stpHdrFilter, dataSndrAddr, dataSndrAddrMask,
1913                        "--stp-sender-addr");
1914         INST_ITEM_RANGE(stpHdrFilter, dataPort, dataPortHi, "--stp-port");
1915         INST_ITEM_RANGE(stpHdrFilter, dataAge, dataAgeHi, "--stp-msg-age");
1916         INST_ITEM_RANGE(stpHdrFilter, dataMaxAge, dataMaxAgeHi,
1917                         "--stp-max-age");
1918         INST_ITEM_RANGE(stpHdrFilter, dataHelloTime, dataHelloTimeHi,
1919                         "--stp-hello-time");
1920         INST_ITEM_RANGE(stpHdrFilter, dataFwdDelay, dataFwdDelayHi,
1921                         "--stp-forward-delay");
1922         break;
1923 
1924     case VIR_NWFILTER_RULE_PROTOCOL_ARP:
1925     case VIR_NWFILTER_RULE_PROTOCOL_RARP:
1926         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
1927                                     "-t", "nat", "-A", chain, NULL);
1928 
1929         if (ebtablesHandleEthHdr(fw, fwrule,
1930                                  vars,
1931                                  &rule->p.arpHdrFilter.ethHdr,
1932                                  reverse) < 0)
1933             return -1;
1934 
1935         virFirewallRuleAddArg(fw, fwrule, "-p");
1936         virFirewallRuleAddArgFormat(fw, fwrule, "0x%x",
1937                                     (rule->prtclType == VIR_NWFILTER_RULE_PROTOCOL_ARP)
1938                                     ? l3_protocols[L3_PROTO_ARP_IDX].attr
1939                                     : l3_protocols[L3_PROTO_RARP_IDX].attr);
1940 
1941         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataHWType)) {
1942             if (printDataType(vars,
1943                               number, sizeof(number),
1944                               &rule->p.arpHdrFilter.dataHWType) < 0)
1945                 return -1;
1946             virFirewallRuleAddArg(fw, fwrule, "--arp-htype");
1947             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataHWType))
1948                 virFirewallRuleAddArg(fw, fwrule, "!");
1949             virFirewallRuleAddArg(fw, fwrule, number);
1950         }
1951 
1952         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataOpcode)) {
1953             if (printDataType(vars,
1954                               number, sizeof(number),
1955                               &rule->p.arpHdrFilter.dataOpcode) < 0)
1956                 return -1;
1957             virFirewallRuleAddArg(fw, fwrule, "--arp-opcode");
1958             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataOpcode))
1959                 virFirewallRuleAddArg(fw, fwrule, "!");
1960             virFirewallRuleAddArg(fw, fwrule, number);
1961         }
1962 
1963         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataProtocolType)) {
1964             if (printDataTypeAsHex(vars,
1965                                    number, sizeof(number),
1966                                    &rule->p.arpHdrFilter.dataProtocolType) < 0)
1967                 return -1;
1968             virFirewallRuleAddArg(fw, fwrule, "--arp-ptype");
1969             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataProtocolType))
1970                 virFirewallRuleAddArg(fw, fwrule, "!");
1971             virFirewallRuleAddArg(fw, fwrule, number);
1972         }
1973 
1974         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPSrcIPAddr)) {
1975             if (printDataType(vars,
1976                               ipaddr, sizeof(ipaddr),
1977                               &rule->p.arpHdrFilter.dataARPSrcIPAddr) < 0)
1978                 return -1;
1979 
1980             if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPSrcIPMask)) {
1981                 if (printDataType(vars,
1982                                   ipmask, sizeof(ipmask),
1983                                   &rule->p.arpHdrFilter.dataARPSrcIPMask) < 0)
1984                     return -1;
1985                 hasMask = true;
1986             }
1987 
1988             virFirewallRuleAddArg(fw, fwrule,
1989                                   reverse ? "--arp-ip-dst" : "--arp-ip-src");
1990             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataARPSrcIPAddr))
1991                 virFirewallRuleAddArg(fw, fwrule, "!");
1992             virFirewallRuleAddArgFormat(fw, fwrule,
1993                                         "%s/%s", ipaddr, hasMask ? ipmask : "32");
1994         }
1995 
1996         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPDstIPAddr)) {
1997             if (printDataType(vars,
1998                               ipaddr, sizeof(ipaddr),
1999                               &rule->p.arpHdrFilter.dataARPDstIPAddr) < 0)
2000                 return -1;
2001 
2002             if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPDstIPMask)) {
2003                 if (printDataType(vars,
2004                                   ipmask, sizeof(ipmask),
2005                                   &rule->p.arpHdrFilter.dataARPDstIPMask) < 0)
2006                     return -1;
2007                 hasMask = true;
2008             }
2009 
2010             virFirewallRuleAddArg(fw, fwrule,
2011                                   reverse ? "--arp-ip-src" : "--arp-ip-dst");
2012             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataARPDstIPAddr))
2013                 virFirewallRuleAddArg(fw, fwrule, "!");
2014             virFirewallRuleAddArgFormat(fw, fwrule,
2015                                         "%s/%s", ipaddr, hasMask ? ipmask : "32");
2016         }
2017 
2018         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPSrcMACAddr)) {
2019             if (printDataType(vars,
2020                               macaddr, sizeof(macaddr),
2021                               &rule->p.arpHdrFilter.dataARPSrcMACAddr) < 0)
2022                 return -1;
2023 
2024             virFirewallRuleAddArg(fw, fwrule,
2025                                   reverse ? "--arp-mac-dst" : "--arp-mac-src");
2026             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataARPSrcMACAddr))
2027                 virFirewallRuleAddArg(fw, fwrule, "!");
2028             virFirewallRuleAddArg(fw, fwrule, macaddr);
2029         }
2030 
2031         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPDstMACAddr)) {
2032             if (printDataType(vars,
2033                               macaddr, sizeof(macaddr),
2034                               &rule->p.arpHdrFilter.dataARPDstMACAddr) < 0)
2035                 return -1;
2036 
2037             virFirewallRuleAddArg(fw, fwrule,
2038                                   reverse ? "--arp-mac-src" : "--arp-mac-dst");
2039             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataARPDstMACAddr))
2040                 virFirewallRuleAddArg(fw, fwrule, "!");
2041             virFirewallRuleAddArg(fw, fwrule, macaddr);
2042         }
2043 
2044         if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataGratuitousARP) &&
2045             rule->p.arpHdrFilter.dataGratuitousARP.u.boolean) {
2046             if (ENTRY_WANT_NEG_SIGN(&rule->p.arpHdrFilter.dataGratuitousARP))
2047                 virFirewallRuleAddArg(fw, fwrule, "!");
2048             virFirewallRuleAddArg(fw, fwrule, "--arp-gratuitous");
2049         }
2050         break;
2051 
2052     case VIR_NWFILTER_RULE_PROTOCOL_IP:
2053         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2054                                     "-t", "nat", "-A", chain, NULL);
2055 
2056         if (ebtablesHandleEthHdr(fw, fwrule,
2057                                  vars,
2058                                  &rule->p.ipHdrFilter.ethHdr,
2059                                  reverse) < 0)
2060             return -1;
2061 
2062         virFirewallRuleAddArgList(fw, fwrule,
2063                                   "-p", "ipv4", NULL);
2064 
2065         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr)) {
2066             if (printDataType(vars,
2067                               ipaddr, sizeof(ipaddr),
2068                               &rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr) < 0)
2069                 return -1;
2070 
2071             virFirewallRuleAddArg(fw, fwrule,
2072                                   reverse ? "--ip-destination" : "--ip-source");
2073             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr))
2074                 virFirewallRuleAddArg(fw, fwrule, "!");
2075 
2076             if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcIPMask)) {
2077                 if (printDataType(vars,
2078                                   number, sizeof(number),
2079                                   &rule->p.ipHdrFilter.ipHdr.dataSrcIPMask) < 0)
2080                     return -1;
2081                 virFirewallRuleAddArgFormat(fw, fwrule,
2082                                             "%s/%s", ipaddr, number);
2083             } else {
2084                 virFirewallRuleAddArg(fw, fwrule, ipaddr);
2085             }
2086         }
2087 
2088         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstIPAddr)) {
2089 
2090             if (printDataType(vars,
2091                               ipaddr, sizeof(ipaddr),
2092                               &rule->p.ipHdrFilter.ipHdr.dataDstIPAddr) < 0)
2093                 return -1;
2094 
2095             virFirewallRuleAddArg(fw, fwrule,
2096                                   reverse ? "--ip-source" : "--ip-destination");
2097             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataDstIPAddr))
2098                 virFirewallRuleAddArg(fw, fwrule, "!");
2099 
2100             if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstIPMask)) {
2101                 if (printDataType(vars,
2102                                   number, sizeof(number),
2103                                   &rule->p.ipHdrFilter.ipHdr.dataDstIPMask) < 0)
2104                     return -1;
2105                 virFirewallRuleAddArgFormat(fw, fwrule,
2106                                             "%s/%s", ipaddr, number);
2107             } else {
2108                 virFirewallRuleAddArg(fw, fwrule, ipaddr);
2109             }
2110         }
2111 
2112         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataProtocolID)) {
2113             if (printDataType(vars,
2114                               number, sizeof(number),
2115                               &rule->p.ipHdrFilter.ipHdr.dataProtocolID) < 0)
2116                 return -1;
2117 
2118             virFirewallRuleAddArg(fw, fwrule, "--ip-protocol");
2119             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataProtocolID))
2120                 virFirewallRuleAddArg(fw, fwrule, "!");
2121             virFirewallRuleAddArg(fw, fwrule, number);
2122         }
2123 
2124         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortStart)) {
2125             if (printDataType(vars,
2126                               number, sizeof(number),
2127                               &rule->p.ipHdrFilter.portData.dataSrcPortStart) < 0)
2128                 return -1;
2129 
2130             virFirewallRuleAddArg(fw, fwrule,
2131                                   reverse ? "--ip-destination-port" : "--ip-source-port");
2132             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataSrcPortStart))
2133                 virFirewallRuleAddArg(fw, fwrule, "!");
2134 
2135             if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortEnd)) {
2136                 if (printDataType(vars,
2137                                   numberalt, sizeof(numberalt),
2138                                   &rule->p.ipHdrFilter.portData.dataSrcPortEnd) < 0)
2139                     return -1;
2140 
2141                 virFirewallRuleAddArgFormat(fw, fwrule,
2142                                             "%s:%s", number, numberalt);
2143             } else {
2144                 virFirewallRuleAddArg(fw, fwrule, number);
2145             }
2146         }
2147 
2148         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortStart)) {
2149             if (printDataType(vars,
2150                               number, sizeof(number),
2151                               &rule->p.ipHdrFilter.portData.dataDstPortStart) < 0)
2152                 return -1;
2153 
2154             virFirewallRuleAddArg(fw, fwrule,
2155                                   reverse ? "--ip-source-port" : "--ip-destination-port");
2156             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataDstPortStart))
2157                 virFirewallRuleAddArg(fw, fwrule, "!");
2158 
2159             if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortEnd)) {
2160                 if (printDataType(vars,
2161                                   numberalt, sizeof(numberalt),
2162                                   &rule->p.ipHdrFilter.portData.dataDstPortEnd) < 0)
2163                     return -1;
2164 
2165                 virFirewallRuleAddArgFormat(fw, fwrule,
2166                                             "%s:%s", number, numberalt);
2167             } else {
2168                 virFirewallRuleAddArg(fw, fwrule, number);
2169             }
2170         }
2171 
2172         if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDSCP)) {
2173             if (printDataTypeAsHex(vars,
2174                                    number, sizeof(number),
2175                                    &rule->p.ipHdrFilter.ipHdr.dataDSCP) < 0)
2176                 return -1;
2177 
2178             virFirewallRuleAddArg(fw, fwrule, "--ip-tos");
2179             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataDSCP))
2180                 virFirewallRuleAddArg(fw, fwrule, "!");
2181             virFirewallRuleAddArg(fw, fwrule, number);
2182         }
2183         break;
2184 
2185     case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
2186         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2187                                     "-t", "nat", "-A", chain, NULL);
2188 
2189         if (ebtablesHandleEthHdr(fw, fwrule,
2190                                  vars,
2191                                  &rule->p.ipv6HdrFilter.ethHdr,
2192                                  reverse) < 0)
2193             return -1;
2194 
2195         virFirewallRuleAddArgList(fw, fwrule,
2196                                   "-p", "ipv6", NULL);
2197 
2198         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr)) {
2199             if (printDataType(vars,
2200                               ipv6addr, sizeof(ipv6addr),
2201                               &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr) < 0)
2202                 return -1;
2203 
2204             virFirewallRuleAddArg(fw, fwrule,
2205                                   reverse ? "--ip6-destination" : "--ip6-source");
2206             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr))
2207                 virFirewallRuleAddArg(fw, fwrule, "!");
2208 
2209             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask)) {
2210                 if (printDataType(vars,
2211                                   number, sizeof(number),
2212                                   &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask) < 0)
2213                     return -1;
2214                 virFirewallRuleAddArgFormat(fw, fwrule,
2215                                             "%s/%s", ipv6addr, number);
2216             } else {
2217                 virFirewallRuleAddArg(fw, fwrule, ipv6addr);
2218             }
2219         }
2220 
2221         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr)) {
2222 
2223             if (printDataType(vars,
2224                               ipv6addr, sizeof(ipv6addr),
2225                               &rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr) < 0)
2226                 return -1;
2227 
2228             virFirewallRuleAddArg(fw, fwrule,
2229                                   reverse ? "--ip6-source" : "--ip6-destination");
2230             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr))
2231                 virFirewallRuleAddArg(fw, fwrule, "!");
2232 
2233             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask)) {
2234                 if (printDataType(vars,
2235                                   number, sizeof(number),
2236                                   &rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask) < 0)
2237                     return -1;
2238                 virFirewallRuleAddArgFormat(fw, fwrule,
2239                                             "%s/%s", ipv6addr, number);
2240             } else {
2241                 virFirewallRuleAddArg(fw, fwrule, ipv6addr);
2242             }
2243         }
2244 
2245         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID)) {
2246             if (printDataType(vars,
2247                               number, sizeof(number),
2248                               &rule->p.ipv6HdrFilter.ipHdr.dataProtocolID) < 0)
2249                 return -1;
2250 
2251             virFirewallRuleAddArg(fw, fwrule, "--ip6-protocol");
2252             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID))
2253                 virFirewallRuleAddArg(fw, fwrule, "!");
2254             virFirewallRuleAddArg(fw, fwrule, number);
2255         }
2256 
2257         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart)) {
2258 
2259             if (printDataType(vars,
2260                               number, sizeof(number),
2261                               &rule->p.ipv6HdrFilter.portData.dataSrcPortStart) < 0)
2262                 return -1;
2263 
2264             virFirewallRuleAddArg(fw, fwrule,
2265                                   reverse ? "--ip6-destination-port" : "--ip6-source-port");
2266             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart))
2267                 virFirewallRuleAddArg(fw, fwrule, "!");
2268 
2269             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortEnd)) {
2270                 if (printDataType(vars,
2271                                   numberalt, sizeof(numberalt),
2272                                   &rule->p.ipv6HdrFilter.portData.dataSrcPortEnd) < 0)
2273                     return -1;
2274 
2275                 virFirewallRuleAddArgFormat(fw, fwrule,
2276                                             "%s:%s", number, numberalt);
2277             } else {
2278                 virFirewallRuleAddArg(fw, fwrule, number);
2279             }
2280         }
2281 
2282         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortStart)) {
2283 
2284             if (printDataType(vars,
2285                               number, sizeof(number),
2286                               &rule->p.ipv6HdrFilter.portData.dataDstPortStart) < 0)
2287                 return -1;
2288 
2289             virFirewallRuleAddArg(fw, fwrule,
2290                                   reverse ? "--ip6-source-port" : "--ip6-destination-port");
2291             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataDstPortStart))
2292                 virFirewallRuleAddArg(fw, fwrule, "!");
2293 
2294             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortEnd)) {
2295                 if (printDataType(vars,
2296                                   numberalt, sizeof(numberalt),
2297                                   &rule->p.ipv6HdrFilter.portData.dataDstPortEnd) < 0)
2298                     return -1;
2299 
2300                 virFirewallRuleAddArgFormat(fw, fwrule,
2301                                             "%s:%s", number, numberalt);
2302             } else {
2303                 virFirewallRuleAddArg(fw, fwrule, number);
2304             }
2305         }
2306 
2307         if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeStart)  ||
2308             HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeEnd) ||
2309             HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeStart) ||
2310             HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeEnd)) {
2311             bool lo = false;
2312             g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
2313             g_autofree char *r = NULL;
2314 
2315             virFirewallRuleAddArg(fw, fwrule,
2316                                   "--ip6-icmp-type");
2317 
2318             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeStart)) {
2319                 if (printDataType(vars,
2320                                   number, sizeof(number),
2321                                   &rule->p.ipv6HdrFilter.dataICMPTypeStart) < 0)
2322                     return -1;
2323                 lo = true;
2324             } else {
2325                 ignore_value(virStrcpyStatic(number, "0"));
2326             }
2327 
2328             virBufferStrcat(&buf, number, ":", NULL);
2329 
2330             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeEnd)) {
2331                 if (printDataType(vars,
2332                                   numberalt, sizeof(numberalt),
2333                                   &rule->p.ipv6HdrFilter.dataICMPTypeEnd) < 0)
2334                     return -1;
2335             } else {
2336                 if (lo)
2337                     ignore_value(virStrcpyStatic(numberalt, number));
2338                 else
2339                     ignore_value(virStrcpyStatic(numberalt, "255"));
2340             }
2341 
2342             virBufferStrcat(&buf, numberalt, "/", NULL);
2343 
2344             lo = false;
2345 
2346             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeStart)) {
2347                 if (printDataType(vars,
2348                                   number, sizeof(number),
2349                                   &rule->p.ipv6HdrFilter.dataICMPCodeStart) < 0)
2350                     return -1;
2351                 lo = true;
2352             } else {
2353                 ignore_value(virStrcpyStatic(number, "0"));
2354             }
2355 
2356             virBufferStrcat(&buf, number, ":", NULL);
2357 
2358             if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeEnd)) {
2359                 if (printDataType(vars,
2360                                   numberalt, sizeof(numberalt),
2361                                   &rule->p.ipv6HdrFilter.dataICMPCodeEnd) < 0)
2362                     return -1;
2363             } else {
2364                 if (lo)
2365                     ignore_value(virStrcpyStatic(numberalt, number));
2366                 else
2367                     ignore_value(virStrcpyStatic(numberalt, "255"));
2368             }
2369 
2370             virBufferStrcat(&buf, numberalt, NULL);
2371 
2372             if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.dataICMPTypeStart))
2373                 virFirewallRuleAddArg(fw, fwrule, "!");
2374 
2375             r = virBufferContentAndReset(&buf);
2376 
2377             virFirewallRuleAddArg(fw, fwrule, r);
2378         }
2379         break;
2380 
2381     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
2382         fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2383                                     "-t", "nat", "-A", chain, NULL);
2384         break;
2385 
2386     default:
2387         virReportError(VIR_ERR_INTERNAL_ERROR,
2388                        _("Unexpected rule protocol %d"),
2389                        rule->prtclType);
2390         return -1;
2391     }
2392 
2393     switch (rule->action) {
2394     case VIR_NWFILTER_RULE_ACTION_REJECT:
2395         /* REJECT not supported */
2396         target = virNWFilterJumpTargetTypeToString(
2397                                      VIR_NWFILTER_RULE_ACTION_DROP);
2398         break;
2399     default:
2400         target = virNWFilterJumpTargetTypeToString(rule->action);
2401     }
2402 
2403     virFirewallRuleAddArgList(fw, fwrule,
2404                               "-j", target, NULL);
2405 
2406 #undef INST_ITEM_RANGE
2407 #undef INST_ITEM_MASK
2408 #undef INST_ITEM_2PARMS
2409 #undef INST_ITEM
2410 
2411     return 0;
2412 }
2413 
2414 
2415 /*
2416  * ebiptablesCreateRuleInstance:
2417  * @chainPriority : The priority of the chain
2418  * @chainSuffix: The suffix to put on the end of the name of the chain
2419  * @rule: The rule of the filter to convert
2420  * @ifname : The name of the interface to apply the rule to
2421  * @vars : A map containing the variables to resolve
2422  * @res : The data structure to store the result(s) into
2423  *
2424  * Convert a single rule into its representation for later instantiation
2425  *
2426  * Returns 0 in case of success with the result stored in the data structure
2427  * pointed to by res, -1 otherwise
2428  */
2429 static int
ebiptablesCreateRuleInstance(virFirewall * fw,const char * chainSuffix,virNWFilterRuleDef * rule,const char * ifname,virNWFilterVarCombIter * vars)2430 ebiptablesCreateRuleInstance(virFirewall *fw,
2431                              const char *chainSuffix,
2432                              virNWFilterRuleDef *rule,
2433                              const char *ifname,
2434                              virNWFilterVarCombIter *vars)
2435 {
2436     if (virNWFilterRuleIsProtocolEthernet(rule)) {
2437         if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_OUT ||
2438             rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
2439             if (ebtablesCreateRuleInstance(fw,
2440                                            CHAINPREFIX_HOST_IN_TEMP,
2441                                            chainSuffix,
2442                                            rule,
2443                                            ifname,
2444                                            vars,
2445                                            rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) < 0)
2446                 return -1;
2447         }
2448 
2449         if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN ||
2450             rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
2451             if (ebtablesCreateRuleInstance(fw,
2452                                            CHAINPREFIX_HOST_OUT_TEMP,
2453                                            chainSuffix,
2454                                            rule,
2455                                            ifname,
2456                                            vars,
2457                                            false) < 0)
2458                 return -1;
2459         }
2460     } else {
2461         virFirewallLayer layer;
2462         if (virNWFilterRuleIsProtocolIPv6(rule)) {
2463             layer = VIR_FIREWALL_LAYER_IPV6;
2464         } else if (virNWFilterRuleIsProtocolIPv4(rule)) {
2465             layer = VIR_FIREWALL_LAYER_IPV4;
2466         } else {
2467             virReportError(VIR_ERR_OPERATION_FAILED,
2468                            "%s", _("unexpected protocol type"));
2469             return -1;
2470         }
2471 
2472         if (iptablesCreateRuleInstance(fw,
2473                                        layer,
2474                                        rule,
2475                                        ifname,
2476                                        vars) < 0)
2477             return -1;
2478     }
2479 
2480     return 0;
2481 }
2482 
2483 
2484 static void
ebtablesCreateTmpRootChainFW(virFirewall * fw,int incoming,const char * ifname)2485 ebtablesCreateTmpRootChainFW(virFirewall *fw,
2486                              int incoming, const char *ifname)
2487 {
2488     char chain[MAX_CHAINNAME_LENGTH];
2489     char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
2490                                   : CHAINPREFIX_HOST_OUT_TEMP;
2491 
2492     PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2493 
2494     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2495                        "-t", "nat", "-N", chain, NULL);
2496 }
2497 
2498 
2499 static void
ebtablesLinkTmpRootChainFW(virFirewall * fw,int incoming,const char * ifname)2500 ebtablesLinkTmpRootChainFW(virFirewall *fw,
2501                            int incoming, const char *ifname)
2502 {
2503     char chain[MAX_CHAINNAME_LENGTH];
2504     char chainPrefix = incoming ? CHAINPREFIX_HOST_IN_TEMP
2505                                 : CHAINPREFIX_HOST_OUT_TEMP;
2506 
2507     PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2508 
2509     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2510                        "-t", "nat", "-A",
2511                        incoming ? EBTABLES_CHAIN_INCOMING : EBTABLES_CHAIN_OUTGOING,
2512                        incoming ? "-i" : "-o",
2513                        ifname, "-j", chain, NULL);
2514 }
2515 
2516 
2517 static void
_ebtablesRemoveRootChainFW(virFirewall * fw,bool incoming,const char * ifname,int isTempChain)2518 _ebtablesRemoveRootChainFW(virFirewall *fw,
2519                            bool incoming, const char *ifname,
2520                            int isTempChain)
2521 {
2522     char chain[MAX_CHAINNAME_LENGTH];
2523     char chainPrefix;
2524     if (isTempChain)
2525         chainPrefix = incoming ? CHAINPREFIX_HOST_IN_TEMP
2526                                : CHAINPREFIX_HOST_OUT_TEMP;
2527     else
2528         chainPrefix = incoming ? CHAINPREFIX_HOST_IN
2529                                : CHAINPREFIX_HOST_OUT;
2530 
2531     PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2532 
2533     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2534                            true, NULL, NULL,
2535                            "-t", "nat", "-F", chain, NULL);
2536     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2537                            true, NULL, NULL,
2538                            "-t", "nat", "-X", chain, NULL);
2539 }
2540 
2541 
2542 static void
ebtablesRemoveRootChainFW(virFirewall * fw,bool incoming,const char * ifname)2543 ebtablesRemoveRootChainFW(virFirewall *fw,
2544                           bool incoming, const char *ifname)
2545 {
2546     _ebtablesRemoveRootChainFW(fw, incoming, ifname, false);
2547 }
2548 
2549 
2550 static void
ebtablesRemoveTmpRootChainFW(virFirewall * fw,bool incoming,const char * ifname)2551 ebtablesRemoveTmpRootChainFW(virFirewall *fw,
2552                              bool incoming, const char *ifname)
2553 {
2554     _ebtablesRemoveRootChainFW(fw, incoming, ifname, 1);
2555 }
2556 
2557 
2558 static void
_ebtablesUnlinkRootChainFW(virFirewall * fw,bool incoming,const char * ifname,int isTempChain)2559 _ebtablesUnlinkRootChainFW(virFirewall *fw,
2560                            bool incoming, const char *ifname,
2561                            int isTempChain)
2562 {
2563     char chain[MAX_CHAINNAME_LENGTH];
2564     char chainPrefix;
2565 
2566     if (isTempChain) {
2567         chainPrefix = incoming ? CHAINPREFIX_HOST_IN_TEMP
2568                                : CHAINPREFIX_HOST_OUT_TEMP;
2569     } else {
2570         chainPrefix = incoming ? CHAINPREFIX_HOST_IN
2571                                : CHAINPREFIX_HOST_OUT;
2572     }
2573 
2574     PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2575 
2576     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2577                            true, NULL, NULL,
2578                            "-t", "nat", "-D",
2579                            incoming ? EBTABLES_CHAIN_INCOMING : EBTABLES_CHAIN_OUTGOING,
2580                            incoming ? "-i" : "-o",
2581                            ifname, "-j", chain, NULL);
2582 }
2583 
2584 
2585 static void
ebtablesUnlinkRootChainFW(virFirewall * fw,bool incoming,const char * ifname)2586 ebtablesUnlinkRootChainFW(virFirewall *fw,
2587                           bool incoming, const char *ifname)
2588 {
2589     _ebtablesUnlinkRootChainFW(fw, incoming, ifname, false);
2590 }
2591 
2592 
2593 static void
ebtablesUnlinkTmpRootChainFW(virFirewall * fw,int incoming,const char * ifname)2594 ebtablesUnlinkTmpRootChainFW(virFirewall *fw,
2595                              int incoming, const char *ifname)
2596 {
2597     _ebtablesUnlinkRootChainFW(fw, incoming, ifname, 1);
2598 }
2599 
2600 static void
ebtablesCreateTmpSubChainFW(virFirewall * fw,bool incoming,const char * ifname,enum l3_proto_idx protoidx,const char * filtername)2601 ebtablesCreateTmpSubChainFW(virFirewall *fw,
2602                             bool incoming,
2603                             const char *ifname,
2604                             enum l3_proto_idx protoidx,
2605                             const char *filtername)
2606 {
2607     char rootchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
2608     char chainPrefix = incoming ? CHAINPREFIX_HOST_IN_TEMP
2609                                 : CHAINPREFIX_HOST_OUT_TEMP;
2610     virFirewallRule *fwrule;
2611 
2612     PRINT_ROOT_CHAIN(rootchain, chainPrefix, ifname);
2613     PRINT_CHAIN(chain, chainPrefix, ifname,
2614                 (filtername) ? filtername : l3_protocols[protoidx].val);
2615 
2616     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2617                            true, NULL, NULL,
2618                            "-t", "nat", "-F", chain, NULL);
2619     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2620                            true, NULL, NULL,
2621                            "-t", "nat", "-X", chain, NULL);
2622     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2623                        "-t", "nat", "-N", chain, NULL);
2624 
2625     fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2626                                 "-t", "nat", "-A", rootchain, NULL);
2627 
2628     switch ((int)protoidx) {
2629     case L2_PROTO_MAC_IDX:
2630         break;
2631     case L2_PROTO_STP_IDX:
2632         virFirewallRuleAddArgList(fw, fwrule,
2633                                   "-d", NWFILTER_MAC_BGA, NULL);
2634         break;
2635     default:
2636         virFirewallRuleAddArg(fw, fwrule, "-p");
2637         virFirewallRuleAddArgFormat(fw, fwrule,
2638                                     "0x%04x",
2639                                     l3_protocols[protoidx].attr);
2640         break;
2641     }
2642 
2643     virFirewallRuleAddArgList(fw, fwrule,
2644                               "-j", chain, NULL);
2645 }
2646 
2647 
2648 static int
ebtablesRemoveSubChainsQuery(virFirewall * fw,virFirewallLayer layer,const char * const * lines,void * opaque)2649 ebtablesRemoveSubChainsQuery(virFirewall *fw,
2650                              virFirewallLayer layer,
2651                              const char *const *lines,
2652                              void *opaque)
2653 {
2654     size_t i, j;
2655     const char *chainprefixes = opaque;
2656 
2657     for (i = 0; lines[i] != NULL; i++) {
2658         char *tmp = strstr(lines[i], "-j ");
2659 
2660         VIR_DEBUG("Considering '%s'", lines[i]);
2661 
2662         if (!tmp)
2663             continue;
2664         tmp = tmp + 3;
2665         for (j = 0; chainprefixes[j]; j++) {
2666             if (tmp[0] == chainprefixes[j] &&
2667                 tmp[1] == '-') {
2668                 VIR_DEBUG("Processing chain '%s'", tmp);
2669                 virFirewallAddRuleFull(fw, layer,
2670                                        false, ebtablesRemoveSubChainsQuery,
2671                                        (void *)chainprefixes,
2672                                         "-t", "nat", "-L", tmp, NULL);
2673                 virFirewallAddRuleFull(fw, layer,
2674                                        true, NULL, NULL,
2675                                        "-t", "nat", "-F", tmp, NULL);
2676                 virFirewallAddRuleFull(fw, layer,
2677                                        true, NULL, NULL,
2678                                        "-t", "nat", "-X", tmp, NULL);
2679             }
2680         }
2681     }
2682 
2683     return 0;
2684 }
2685 
2686 
2687 static void
_ebtablesRemoveSubChainsFW(virFirewall * fw,const char * ifname,const char * chainprefixes)2688 _ebtablesRemoveSubChainsFW(virFirewall *fw,
2689                            const char *ifname,
2690                            const char *chainprefixes)
2691 {
2692     char rootchain[MAX_CHAINNAME_LENGTH];
2693     size_t i;
2694 
2695     for (i = 0; chainprefixes[i] != 0; i++) {
2696         PRINT_ROOT_CHAIN(rootchain, chainprefixes[i], ifname);
2697         virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2698                                false, ebtablesRemoveSubChainsQuery,
2699                                (void *)chainprefixes,
2700                                "-t", "nat", "-L", rootchain, NULL);
2701     }
2702 }
2703 
2704 static void
ebtablesRemoveSubChainsFW(virFirewall * fw,const char * ifname)2705 ebtablesRemoveSubChainsFW(virFirewall *fw,
2706                           const char *ifname)
2707 {
2708     _ebtablesRemoveSubChainsFW(fw, ifname, chainprefixes_host);
2709 }
2710 
2711 
2712 static void
ebtablesRemoveTmpSubChainsFW(virFirewall * fw,const char * ifname)2713 ebtablesRemoveTmpSubChainsFW(virFirewall *fw,
2714                              const char *ifname)
2715 {
2716     _ebtablesRemoveSubChainsFW(fw, ifname, chainprefixes_host_temp);
2717 }
2718 
2719 static void
ebtablesRenameTmpSubChainFW(virFirewall * fw,int incoming,const char * ifname,const char * protocol)2720 ebtablesRenameTmpSubChainFW(virFirewall *fw,
2721                             int incoming,
2722                             const char *ifname,
2723                             const char *protocol)
2724 {
2725     char tmpchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
2726     char tmpChainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
2727                                      : CHAINPREFIX_HOST_OUT_TEMP;
2728     char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN
2729                                   : CHAINPREFIX_HOST_OUT;
2730 
2731     if (protocol) {
2732         PRINT_CHAIN(tmpchain, tmpChainPrefix, ifname, protocol);
2733         PRINT_CHAIN(chain, chainPrefix, ifname, protocol);
2734     } else {
2735         PRINT_ROOT_CHAIN(tmpchain, tmpChainPrefix, ifname);
2736         PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2737     }
2738 
2739     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2740                        "-t", "nat", "-E", tmpchain, chain, NULL);
2741 }
2742 
2743 static void
ebtablesRenameTmpRootChainFW(virFirewall * fw,bool incoming,const char * ifname)2744 ebtablesRenameTmpRootChainFW(virFirewall *fw,
2745                              bool incoming,
2746                              const char *ifname)
2747 {
2748     ebtablesRenameTmpSubChainFW(fw, incoming, ifname, NULL);
2749 }
2750 
2751 
2752 static int
ebtablesRenameTmpSubAndRootChainsQuery(virFirewall * fw,virFirewallLayer layer,const char * const * lines,void * opaque G_GNUC_UNUSED)2753 ebtablesRenameTmpSubAndRootChainsQuery(virFirewall *fw,
2754                                        virFirewallLayer layer,
2755                                        const char *const *lines,
2756                                        void *opaque G_GNUC_UNUSED)
2757 {
2758     size_t i;
2759     char newchain[MAX_CHAINNAME_LENGTH];
2760 
2761     for (i = 0; lines[i] != NULL; i++) {
2762         char *tmp = strstr(lines[i], "-j ");
2763 
2764         VIR_DEBUG("Considering '%s'", lines[i]);
2765 
2766         if (!tmp)
2767             continue;
2768         tmp = tmp + 3;
2769         if (tmp[0] != CHAINPREFIX_HOST_IN_TEMP &&
2770             tmp[0] != CHAINPREFIX_HOST_OUT_TEMP)
2771             continue;
2772         if (tmp[1] != '-')
2773             continue;
2774 
2775         ignore_value(virStrcpyStatic(newchain, tmp));
2776         if (newchain[0] == CHAINPREFIX_HOST_IN_TEMP)
2777             newchain[0] = CHAINPREFIX_HOST_IN;
2778         else
2779             newchain[0] = CHAINPREFIX_HOST_OUT;
2780         VIR_DEBUG("Renaming chain '%s' to '%s'", tmp, newchain);
2781         virFirewallAddRuleFull(fw, layer,
2782                                false, ebtablesRenameTmpSubAndRootChainsQuery,
2783                                NULL,
2784                                "-t", "nat", "-L", tmp, NULL);
2785         virFirewallAddRuleFull(fw, layer,
2786                                true, NULL, NULL,
2787                                "-t", "nat", "-F", newchain, NULL);
2788         virFirewallAddRuleFull(fw, layer,
2789                                true, NULL, NULL,
2790                                "-t", "nat", "-X", newchain, NULL);
2791         virFirewallAddRule(fw, layer,
2792                            "-t", "nat", "-E", tmp, newchain, NULL);
2793     }
2794 
2795     return 0;
2796 }
2797 
2798 
2799 static void
ebtablesRenameTmpSubAndRootChainsFW(virFirewall * fw,const char * ifname)2800 ebtablesRenameTmpSubAndRootChainsFW(virFirewall *fw,
2801                                     const char *ifname)
2802 {
2803     char rootchain[MAX_CHAINNAME_LENGTH];
2804     size_t i;
2805     char chains[3] = {
2806         CHAINPREFIX_HOST_IN_TEMP,
2807         CHAINPREFIX_HOST_OUT_TEMP,
2808         0
2809     };
2810     for (i = 0; chains[i] != 0; i++) {
2811         PRINT_ROOT_CHAIN(rootchain, chains[i], ifname);
2812         virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_ETHERNET,
2813                                false, ebtablesRenameTmpSubAndRootChainsQuery,
2814                                NULL,
2815                                "-t", "nat", "-L", rootchain, NULL);
2816     }
2817 
2818     ebtablesRenameTmpRootChainFW(fw, true, ifname);
2819     ebtablesRenameTmpRootChainFW(fw, false, ifname);
2820 }
2821 
2822 
2823 /**
2824  * ebiptablesCanApplyBasicRules
2825  *
2826  * Determine whether this driver can apply the basic rules, meaning
2827  * run ebtablesApplyBasicRules and ebtablesApplyDHCPOnlyRules.
2828  * In case of this driver we need the ebtables tool available.
2829  */
2830 static int
ebiptablesCanApplyBasicRules(void)2831 ebiptablesCanApplyBasicRules(void)
2832 {
2833     return true;
2834 }
2835 
2836 /**
2837  * ebtablesApplyBasicRules
2838  *
2839  * @ifname: name of the backend-interface to which to apply the rules
2840  * @macaddr: MAC address the VM is using in packets sent through the
2841  *    interface
2842  *
2843  * Returns 0 on success, -1 on failure with the rules removed
2844  *
2845  * Apply basic filtering rules on the given interface
2846  * - filtering for MAC address spoofing
2847  * - allowing IPv4 & ARP traffic
2848  */
2849 static int
ebtablesApplyBasicRules(const char * ifname,const virMacAddr * macaddr)2850 ebtablesApplyBasicRules(const char *ifname,
2851                         const virMacAddr *macaddr)
2852 {
2853     g_autoptr(virFirewall) fw = virFirewallNew();
2854     char chain[MAX_CHAINNAME_LENGTH];
2855     char chainPrefix = CHAINPREFIX_HOST_IN_TEMP;
2856     char macaddr_str[VIR_MAC_STRING_BUFLEN];
2857 
2858     virMacAddrFormat(macaddr, macaddr_str);
2859 
2860     if (ebiptablesAllTeardown(ifname) < 0)
2861         return -1;
2862 
2863     virFirewallStartTransaction(fw, 0);
2864 
2865     ebtablesCreateTmpRootChainFW(fw, true, ifname);
2866 
2867     PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
2868     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2869                        "-t", "nat", "-A", chain,
2870                        "-s", "!", macaddr_str,
2871                        "-j", "DROP", NULL);
2872     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2873                        "-t", "nat", "-A", chain,
2874                        "-p", "IPv4",
2875                        "-j", "ACCEPT", NULL);
2876     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2877                        "-t", "nat", "-A", chain,
2878                        "-p", "ARP",
2879                        "-j", "ACCEPT", NULL);
2880     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2881                        "-t", "nat", "-A", chain,
2882                        "-j", "DROP", NULL);
2883 
2884     ebtablesLinkTmpRootChainFW(fw, true, ifname);
2885     ebtablesRenameTmpRootChainFW(fw, true, ifname);
2886 
2887     if (virFirewallApply(fw) < 0)
2888         goto error;
2889 
2890     return 0;
2891 
2892  error:
2893     ebtablesCleanAll(ifname);
2894     return -1;
2895 }
2896 
2897 
2898 /**
2899  * ebtablesApplyDHCPOnlyRules
2900  *
2901  * @ifname: name of the backend-interface to which to apply the rules
2902  * @macaddr: MAC address the VM is using in packets sent through the
2903  *    interface
2904  * @dhcpsrvrs: The DHCP server(s) from which the VM may receive traffic
2905  *    from; may be NULL
2906  * @leaveTemporary: Whether to leave the table names with their temporary
2907  *    names (true) or also perform the renaming to their final names as
2908  *    part of this call (false)
2909  *
2910  * Returns 0 on success, -1 on failure with the rules removed
2911  *
2912  * Apply filtering rules so that the VM can only send and receive
2913  * DHCP traffic and nothing else.
2914  */
2915 static int
ebtablesApplyDHCPOnlyRules(const char * ifname,const virMacAddr * macaddr,virNWFilterVarValue * dhcpsrvrs,bool leaveTemporary)2916 ebtablesApplyDHCPOnlyRules(const char *ifname,
2917                            const virMacAddr *macaddr,
2918                            virNWFilterVarValue *dhcpsrvrs,
2919                            bool leaveTemporary)
2920 {
2921     char chain_in [MAX_CHAINNAME_LENGTH],
2922          chain_out[MAX_CHAINNAME_LENGTH];
2923     char macaddr_str[VIR_MAC_STRING_BUFLEN];
2924     unsigned int idx = 0;
2925     unsigned int num_dhcpsrvrs;
2926     g_autoptr(virFirewall) fw = virFirewallNew();
2927 
2928     virMacAddrFormat(macaddr, macaddr_str);
2929 
2930     if (ebiptablesAllTeardown(ifname) < 0)
2931         return -1;
2932 
2933     virFirewallStartTransaction(fw, 0);
2934 
2935     ebtablesCreateTmpRootChainFW(fw, true, ifname);
2936     ebtablesCreateTmpRootChainFW(fw, false, ifname);
2937 
2938     PRINT_ROOT_CHAIN(chain_in, CHAINPREFIX_HOST_IN_TEMP, ifname);
2939     PRINT_ROOT_CHAIN(chain_out, CHAINPREFIX_HOST_OUT_TEMP, ifname);
2940 
2941     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2942                        "-t", "nat", "-A", chain_in,
2943                        "-s", macaddr_str,
2944                        "-p", "ipv4", "--ip-protocol", "udp",
2945                        "--ip-sport", "68", "--ip-dport", "67",
2946                        "-j", "ACCEPT", NULL);
2947 
2948     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2949                        "-t", "nat", "-A", chain_in,
2950                        "-j", "DROP", NULL);
2951 
2952     num_dhcpsrvrs = (dhcpsrvrs != NULL)
2953                     ? virNWFilterVarValueGetCardinality(dhcpsrvrs)
2954                     : 0;
2955 
2956     while (true) {
2957         const char *dhcpserver = NULL;
2958         int ctr;
2959 
2960         if (idx < num_dhcpsrvrs)
2961             dhcpserver = virNWFilterVarValueGetNthValue(dhcpsrvrs, idx);
2962 
2963         /*
2964          * create two rules allowing response to MAC address of VM
2965          * or to broadcast MAC address
2966          */
2967         for (ctr = 0; ctr < 2; ctr++) {
2968             if (dhcpserver)
2969                 virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2970                                    "-t", "nat", "-A", chain_out,
2971                                    "-d", (ctr == 0) ? macaddr_str : "ff:ff:ff:ff:ff:ff",
2972                                    "-p", "ipv4", "--ip-protocol", "udp",
2973                                    "--ip-src", dhcpserver,
2974                                    "--ip-sport", "67", "--ip-dport", "68",
2975                                    "-j", "ACCEPT", NULL);
2976             else
2977                 virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2978                                    "-t", "nat", "-A", chain_out,
2979                                    "-d", (ctr == 0) ? macaddr_str : "ff:ff:ff:ff:ff:ff",
2980                                    "-p", "ipv4", "--ip-protocol", "udp",
2981                                    "--ip-sport", "67", "--ip-dport", "68",
2982                                    "-j", "ACCEPT", NULL);
2983         }
2984 
2985         idx++;
2986 
2987         if (idx >= num_dhcpsrvrs)
2988             break;
2989     }
2990 
2991     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
2992                        "-t", "nat", "-A", chain_out,
2993                        "-j", "DROP", NULL);
2994 
2995     ebtablesLinkTmpRootChainFW(fw, true, ifname);
2996     ebtablesLinkTmpRootChainFW(fw, false, ifname);
2997 
2998     if (!leaveTemporary) {
2999         ebtablesRenameTmpRootChainFW(fw, true, ifname);
3000         ebtablesRenameTmpRootChainFW(fw, false, ifname);
3001     }
3002 
3003     if (virFirewallApply(fw) < 0)
3004         goto error;
3005 
3006     return 0;
3007 
3008  error:
3009     ebtablesCleanAll(ifname);
3010     return -1;
3011 }
3012 
3013 
3014 /**
3015  * ebtablesApplyDropAllRules
3016  *
3017  * @ifname: name of the backend-interface to which to apply the rules
3018  *
3019  * Returns 0 on success, -1 on failure with the rules removed
3020  *
3021  * Apply filtering rules so that the VM cannot receive or send traffic.
3022  */
3023 static int
ebtablesApplyDropAllRules(const char * ifname)3024 ebtablesApplyDropAllRules(const char *ifname)
3025 {
3026     char chain_in [MAX_CHAINNAME_LENGTH],
3027          chain_out[MAX_CHAINNAME_LENGTH];
3028     g_autoptr(virFirewall) fw = virFirewallNew();
3029 
3030     if (ebiptablesAllTeardown(ifname) < 0)
3031         return -1;
3032 
3033     virFirewallStartTransaction(fw, 0);
3034 
3035     ebtablesCreateTmpRootChainFW(fw, true, ifname);
3036     ebtablesCreateTmpRootChainFW(fw, false, ifname);
3037 
3038     PRINT_ROOT_CHAIN(chain_in, CHAINPREFIX_HOST_IN_TEMP, ifname);
3039     PRINT_ROOT_CHAIN(chain_out, CHAINPREFIX_HOST_OUT_TEMP, ifname);
3040 
3041     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
3042                        "-t", "nat", "-A", chain_in,
3043                        "-j", "DROP", NULL);
3044 
3045     virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET,
3046                        "-t", "nat", "-A", chain_out,
3047                        "-j", "DROP", NULL);
3048 
3049     ebtablesLinkTmpRootChainFW(fw, true, ifname);
3050     ebtablesLinkTmpRootChainFW(fw, false, ifname);
3051     ebtablesRenameTmpRootChainFW(fw, true, ifname);
3052     ebtablesRenameTmpRootChainFW(fw, false, ifname);
3053 
3054     if (virFirewallApply(fw) < 0)
3055         goto error;
3056 
3057     return 0;
3058 
3059  error:
3060     ebtablesCleanAll(ifname);
3061     return -1;
3062 }
3063 
3064 
3065 static int
ebtablesRemoveBasicRules(const char * ifname)3066 ebtablesRemoveBasicRules(const char *ifname)
3067 {
3068     return ebtablesCleanAll(ifname);
3069 }
3070 
3071 
3072 static int
ebtablesCleanAll(const char * ifname)3073 ebtablesCleanAll(const char *ifname)
3074 {
3075     g_autoptr(virFirewall) fw = virFirewallNew();
3076 
3077     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
3078 
3079     ebtablesUnlinkRootChainFW(fw, true, ifname);
3080     ebtablesUnlinkRootChainFW(fw, false, ifname);
3081     ebtablesRemoveSubChainsFW(fw, ifname);
3082     ebtablesRemoveRootChainFW(fw, true, ifname);
3083     ebtablesRemoveRootChainFW(fw, false, ifname);
3084 
3085     ebtablesUnlinkTmpRootChainFW(fw, true, ifname);
3086     ebtablesUnlinkTmpRootChainFW(fw, false, ifname);
3087     ebtablesRemoveTmpSubChainsFW(fw, ifname);
3088     ebtablesRemoveTmpRootChainFW(fw, true, ifname);
3089     ebtablesRemoveTmpRootChainFW(fw, false, ifname);
3090 
3091     return virFirewallApply(fw);
3092 }
3093 
3094 
3095 static int
virNWFilterRuleInstSort(const void * a,const void * b)3096 virNWFilterRuleInstSort(const void *a, const void *b)
3097 {
3098     const virNWFilterRuleInst *insta = a;
3099     const virNWFilterRuleInst *instb = b;
3100     const char *root = virNWFilterChainSuffixTypeToString(
3101                                      VIR_NWFILTER_CHAINSUFFIX_ROOT);
3102     bool root_a = STREQ(insta->chainSuffix, root);
3103     bool root_b = STREQ(instb->chainSuffix, root);
3104 
3105     /* ensure root chain commands appear before all others since
3106        we will need them to create the child chains */
3107     if (root_a) {
3108         if (!root_b)
3109             return -1; /* a before b */
3110     } else if (root_b) {
3111         return 1; /* b before a */
3112     }
3113 
3114     /* priorities are limited to range [-1000, 1000] */
3115     return insta->priority - instb->priority;
3116 }
3117 
3118 
3119 static int
virNWFilterRuleInstSortPtr(const void * a,const void * b)3120 virNWFilterRuleInstSortPtr(const void *a, const void *b)
3121 {
3122     virNWFilterRuleInst * const *insta = a;
3123     virNWFilterRuleInst * const *instb = b;
3124     return virNWFilterRuleInstSort(*insta, *instb);
3125 }
3126 
3127 
3128 static int
ebiptablesFilterOrderSort(const void * va,const void * vb)3129 ebiptablesFilterOrderSort(const void *va,
3130                           const void *vb)
3131 {
3132     const virHashKeyValuePair *a = va;
3133     const virHashKeyValuePair *b = vb;
3134 
3135     /* elements' values has been limited to range [-1000, 1000] */
3136     return *(virNWFilterChainPriority *)a->value -
3137            *(virNWFilterChainPriority *)b->value;
3138 }
3139 
3140 
3141 static void
iptablesCheckBridgeNFCallEnabled(bool isIPv6)3142 iptablesCheckBridgeNFCallEnabled(bool isIPv6)
3143 {
3144     static time_t lastReport, lastReportIPv6;
3145     const char *pathname = NULL;
3146     char buffer[1];
3147     time_t now = time(NULL);
3148 
3149     if (isIPv6 &&
3150         (now - lastReportIPv6) > BRIDGE_NF_CALL_ALERT_INTERVAL) {
3151         pathname = PROC_BRIDGE_NF_CALL_IP6TABLES;
3152     } else if (now - lastReport > BRIDGE_NF_CALL_ALERT_INTERVAL) {
3153         pathname = PROC_BRIDGE_NF_CALL_IPTABLES;
3154     }
3155 
3156     if (pathname) {
3157         int fd = open(pathname, O_RDONLY);
3158         if (fd >= 0) {
3159             if (read(fd, buffer, 1) == 1) {
3160                 if (buffer[0] == '0') {
3161                     char msg[256];
3162                     g_snprintf(msg, sizeof(msg),
3163                                _("To enable ip%stables filtering for the VM do "
3164                                 "'echo 1 > %s'"),
3165                                isIPv6 ? "6" : "",
3166                                pathname);
3167                     VIR_WARN("%s", msg);
3168                     if (isIPv6)
3169                         lastReportIPv6 = now;
3170                     else
3171                         lastReport = now;
3172                 }
3173             }
3174             VIR_FORCE_CLOSE(fd);
3175         }
3176     }
3177 }
3178 
3179 /*
3180  * Given a filtername determine the protocol it is used for evaluating
3181  * We do prefix-matching to determine the protocol.
3182  */
3183 static enum l3_proto_idx
ebtablesGetProtoIdxByFiltername(const char * filtername)3184 ebtablesGetProtoIdxByFiltername(const char *filtername)
3185 {
3186     enum l3_proto_idx idx;
3187 
3188     for (idx = 0; idx < L3_PROTO_LAST_IDX; idx++) {
3189         if (STRPREFIX(filtername, l3_protocols[idx].val))
3190             return idx;
3191     }
3192 
3193     return -1;
3194 }
3195 
3196 
3197 static int
iptablesRuleInstCommand(virFirewall * fw,const char * ifname,virNWFilterRuleInst * rule)3198 iptablesRuleInstCommand(virFirewall *fw,
3199                         const char *ifname,
3200                         virNWFilterRuleInst *rule)
3201 {
3202     virNWFilterVarCombIter *vciter;
3203     virNWFilterVarCombIter *tmp;
3204     int ret = -1;
3205 
3206     /* rule->vars holds all the variables names that this rule will access.
3207      * iterate over all combinations of the variables' values and instantiate
3208      * the filtering rule with each combination.
3209      */
3210     tmp = vciter = virNWFilterVarCombIterCreate(rule->vars,
3211                                                 rule->def->varAccess,
3212                                                 rule->def->nVarAccess);
3213     if (!vciter)
3214         return -1;
3215 
3216     do {
3217         if (ebiptablesCreateRuleInstance(fw,
3218                                          rule->chainSuffix,
3219                                          rule->def,
3220                                          ifname,
3221                                          tmp) < 0)
3222             goto cleanup;
3223         tmp = virNWFilterVarCombIterNext(tmp);
3224     } while (tmp != NULL);
3225 
3226     ret = 0;
3227  cleanup:
3228     virNWFilterVarCombIterFree(vciter);
3229     return ret;
3230 }
3231 
3232 
3233 static int
ebtablesRuleInstCommand(virFirewall * fw,const char * ifname,virNWFilterRuleInst * rule)3234 ebtablesRuleInstCommand(virFirewall *fw,
3235                         const char *ifname,
3236                         virNWFilterRuleInst *rule)
3237 {
3238     virNWFilterVarCombIter *vciter;
3239     virNWFilterVarCombIter *tmp;
3240     int ret = -1;
3241 
3242     /* rule->vars holds all the variables names that this rule will access.
3243      * iterate over all combinations of the variables' values and instantiate
3244      * the filtering rule with each combination.
3245      */
3246     tmp = vciter = virNWFilterVarCombIterCreate(rule->vars,
3247                                                 rule->def->varAccess,
3248                                                 rule->def->nVarAccess);
3249     if (!vciter)
3250         return -1;
3251 
3252     do {
3253         if (ebiptablesCreateRuleInstance(fw,
3254                                          rule->chainSuffix,
3255                                          rule->def,
3256                                          ifname,
3257                                          tmp) < 0)
3258             goto cleanup;
3259         tmp = virNWFilterVarCombIterNext(tmp);
3260     } while (tmp != NULL);
3261 
3262     ret = 0;
3263  cleanup:
3264     virNWFilterVarCombIterFree(vciter);
3265     return ret;
3266 }
3267 
3268 typedef struct _ebtablesSubChainInst ebtablesSubChainInst;
3269 struct _ebtablesSubChainInst {
3270     virNWFilterChainPriority priority;
3271     bool incoming;
3272     enum l3_proto_idx protoidx;
3273     const char *filtername;
3274 };
3275 
3276 
3277 static int
ebtablesSubChainInstSort(const void * a,const void * b)3278 ebtablesSubChainInstSort(const void *a, const void *b)
3279 {
3280     const ebtablesSubChainInst **insta = (const ebtablesSubChainInst **)a;
3281     const ebtablesSubChainInst **instb = (const ebtablesSubChainInst **)b;
3282 
3283     /* priorities are limited to range [-1000, 1000] */
3284     return (*insta)->priority - (*instb)->priority;
3285 }
3286 
3287 
3288 static int
ebtablesGetSubChainInsts(GHashTable * chains,bool incoming,ebtablesSubChainInst *** insts,size_t * ninsts)3289 ebtablesGetSubChainInsts(GHashTable *chains,
3290                          bool incoming,
3291                          ebtablesSubChainInst ***insts,
3292                          size_t *ninsts)
3293 {
3294     g_autofree virHashKeyValuePair *filter_names = NULL;
3295     size_t nfilter_names;
3296     size_t i;
3297 
3298     filter_names = virHashGetItems(chains, &nfilter_names, false);
3299     if (filter_names == NULL)
3300         return -1;
3301 
3302     qsort(filter_names, nfilter_names, sizeof(*filter_names), ebiptablesFilterOrderSort);
3303 
3304     for (i = 0; filter_names[i].key; i++) {
3305         g_autofree ebtablesSubChainInst *inst = NULL;
3306         enum l3_proto_idx idx = ebtablesGetProtoIdxByFiltername(
3307                                   filter_names[i].key);
3308 
3309         if ((int)idx < 0)
3310             continue;
3311 
3312         inst = g_new0(ebtablesSubChainInst, 1);
3313         inst->priority = *(const virNWFilterChainPriority *)filter_names[i].value;
3314         inst->incoming = incoming;
3315         inst->protoidx = idx;
3316         inst->filtername = filter_names[i].key;
3317 
3318         VIR_APPEND_ELEMENT(*insts, *ninsts, inst);
3319     }
3320 
3321     return 0;
3322 }
3323 
3324 static int
ebiptablesApplyNewRules(const char * ifname,virNWFilterRuleInst ** rules,size_t nrules)3325 ebiptablesApplyNewRules(const char *ifname,
3326                         virNWFilterRuleInst **rules,
3327                         size_t nrules)
3328 {
3329     size_t i, j;
3330     g_autoptr(virFirewall) fw = virFirewallNew();
3331     g_autoptr(GHashTable) chains_in_set  = virHashNew(NULL);
3332     g_autoptr(GHashTable) chains_out_set = virHashNew(NULL);
3333     bool haveEbtables = false;
3334     bool haveIptables = false;
3335     bool haveIp6tables = false;
3336     g_autofree ebtablesSubChainInst **subchains = NULL;
3337     size_t nsubchains = 0;
3338     int ret = -1;
3339 
3340     if (nrules)
3341         qsort(rules, nrules, sizeof(rules[0]),
3342               virNWFilterRuleInstSortPtr);
3343 
3344     /* cleanup whatever may exist */
3345     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
3346     ebtablesUnlinkTmpRootChainFW(fw, true, ifname);
3347     ebtablesUnlinkTmpRootChainFW(fw, false, ifname);
3348     ebtablesRemoveTmpSubChainsFW(fw, ifname);
3349     ebtablesRemoveTmpRootChainFW(fw, true, ifname);
3350     ebtablesRemoveTmpRootChainFW(fw, false, ifname);
3351 
3352     virFirewallStartTransaction(fw, 0);
3353 
3354     /* walk the list of rules and increase the priority
3355      * of rules in case the chain priority is of higher value;
3356      * this preserves the order of the rules and ensures that
3357      * the chain will be created before the chain's rules
3358      * are created; don't adjust rules in the root chain
3359      * example: a rule of priority -510 will be adjusted to
3360      * priority -500 and the chain with priority -500 will
3361      * then be created before it.
3362      */
3363     for (i = 0; i < nrules; i++) {
3364         if (rules[i]->chainPriority > rules[i]->priority &&
3365             !strstr("root", rules[i]->chainSuffix)) {
3366 
3367              rules[i]->priority = rules[i]->chainPriority;
3368         }
3369     }
3370 
3371     for (i = 0; i < nrules; i++) {
3372         if (virNWFilterRuleIsProtocolEthernet(rules[i]->def)) {
3373             haveEbtables = true;
3374         } else {
3375             if (virNWFilterRuleIsProtocolIPv4(rules[i]->def))
3376                 haveIptables = true;
3377             else if (virNWFilterRuleIsProtocolIPv6(rules[i]->def))
3378                 haveIp6tables = true;
3379         }
3380     }
3381     /* process ebtables commands; interleave commands from filters with
3382        commands for creating and connecting ebtables chains */
3383     if (haveEbtables) {
3384 
3385         /* scan the rules to see which chains need to be created */
3386         for (i = 0; i < nrules; i++) {
3387             if (virNWFilterRuleIsProtocolEthernet(rules[i]->def)) {
3388                 const char *name = rules[i]->chainSuffix;
3389                 if (rules[i]->def->tt == VIR_NWFILTER_RULE_DIRECTION_OUT ||
3390                     rules[i]->def->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
3391                     if (virHashUpdateEntry(chains_in_set, name,
3392                                            &rules[i]->chainPriority) < 0)
3393                         goto cleanup;
3394                 }
3395                 if (rules[i]->def->tt == VIR_NWFILTER_RULE_DIRECTION_IN ||
3396                     rules[i]->def->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
3397                     if (virHashUpdateEntry(chains_out_set, name,
3398                                            &rules[i]->chainPriority) < 0)
3399                         goto cleanup;
3400                 }
3401             }
3402         }
3403 
3404         /* create needed chains */
3405         if (virHashSize(chains_in_set) > 0) {
3406             ebtablesCreateTmpRootChainFW(fw, true, ifname);
3407             if (ebtablesGetSubChainInsts(chains_in_set,
3408                                          true,
3409                                          &subchains,
3410                                          &nsubchains) < 0)
3411                 goto cleanup;
3412         }
3413         if (virHashSize(chains_out_set) > 0) {
3414             ebtablesCreateTmpRootChainFW(fw, false, ifname);
3415             if (ebtablesGetSubChainInsts(chains_out_set,
3416                                          false,
3417                                          &subchains,
3418                                          &nsubchains) < 0)
3419                 goto cleanup;
3420         }
3421 
3422         if (nsubchains > 0)
3423             qsort(subchains, nsubchains, sizeof(subchains[0]),
3424                   ebtablesSubChainInstSort);
3425 
3426         for (i = 0, j = 0; i < nrules; i++) {
3427             if (virNWFilterRuleIsProtocolEthernet(rules[i]->def)) {
3428                 while (j < nsubchains &&
3429                        subchains[j]->priority <= rules[i]->priority) {
3430                     ebtablesCreateTmpSubChainFW(fw,
3431                                                 subchains[j]->incoming,
3432                                                 ifname,
3433                                                 subchains[j]->protoidx,
3434                                                 subchains[j]->filtername);
3435                     j++;
3436                 }
3437                 if (ebtablesRuleInstCommand(fw,
3438                                             ifname,
3439                                             rules[i]) < 0)
3440                     goto cleanup;
3441             }
3442         }
3443         while (j < nsubchains) {
3444             ebtablesCreateTmpSubChainFW(fw,
3445                                         subchains[j]->incoming,
3446                                         ifname,
3447                                         subchains[j]->protoidx,
3448                                         subchains[j]->filtername);
3449             j++;
3450         }
3451     }
3452 
3453     if (haveIptables) {
3454         iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3455         iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3456 
3457         iptablesCreateBaseChainsFW(fw, VIR_FIREWALL_LAYER_IPV4);
3458         iptablesCreateTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3459 
3460         iptablesLinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3461         iptablesSetupVirtInPostFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3462 
3463         for (i = 0; i < nrules; i++) {
3464             if (virNWFilterRuleIsProtocolIPv4(rules[i]->def)) {
3465                 if (iptablesRuleInstCommand(fw,
3466                                             ifname,
3467                                             rules[i]) < 0)
3468                     goto cleanup;
3469             }
3470         }
3471 
3472         iptablesCheckBridgeNFCallEnabled(false);
3473     }
3474 
3475     if (haveIp6tables) {
3476         iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3477         iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3478 
3479         iptablesCreateBaseChainsFW(fw, VIR_FIREWALL_LAYER_IPV6);
3480         iptablesCreateTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3481 
3482         iptablesLinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3483         iptablesSetupVirtInPostFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3484 
3485         for (i = 0; i < nrules; i++) {
3486             if (virNWFilterRuleIsProtocolIPv6(rules[i]->def)) {
3487                 if (iptablesRuleInstCommand(fw,
3488                                             ifname,
3489                                             rules[i]) < 0)
3490                     goto cleanup;
3491             }
3492         }
3493 
3494         iptablesCheckBridgeNFCallEnabled(true);
3495     }
3496 
3497     if (virHashSize(chains_in_set) != 0)
3498         ebtablesLinkTmpRootChainFW(fw, true, ifname);
3499     if (virHashSize(chains_out_set) != 0)
3500         ebtablesLinkTmpRootChainFW(fw, false, ifname);
3501 
3502     virFirewallStartRollback(fw, 0);
3503     ebtablesUnlinkTmpRootChainFW(fw, true, ifname);
3504     ebtablesUnlinkTmpRootChainFW(fw, false, ifname);
3505     if (haveIp6tables) {
3506         iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3507         iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3508     }
3509 
3510     if (haveIptables) {
3511         iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3512         iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3513     }
3514 
3515     ebtablesRemoveTmpSubChainsFW(fw, ifname);
3516     ebtablesRemoveTmpRootChainFW(fw, true, ifname);
3517     ebtablesRemoveTmpRootChainFW(fw, false, ifname);
3518 
3519     if (virFirewallApply(fw) < 0)
3520         goto cleanup;
3521 
3522     ret = 0;
3523 
3524  cleanup:
3525     for (i = 0; i < nsubchains; i++)
3526         g_free(subchains[i]);
3527 
3528     return ret;
3529 }
3530 
3531 
3532 static void
ebiptablesTearNewRulesFW(virFirewall * fw,const char * ifname)3533 ebiptablesTearNewRulesFW(virFirewall *fw, const char *ifname)
3534 {
3535     iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3536     iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3537 
3538     iptablesUnlinkTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3539     iptablesRemoveTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3540 
3541     ebtablesUnlinkTmpRootChainFW(fw, true, ifname);
3542     ebtablesUnlinkTmpRootChainFW(fw, false, ifname);
3543     ebtablesRemoveTmpSubChainsFW(fw, ifname);
3544     ebtablesRemoveTmpRootChainFW(fw, true, ifname);
3545     ebtablesRemoveTmpRootChainFW(fw, false, ifname);
3546 }
3547 
3548 
3549 static int
ebiptablesTearNewRules(const char * ifname)3550 ebiptablesTearNewRules(const char *ifname)
3551 {
3552     g_autoptr(virFirewall) fw = virFirewallNew();
3553 
3554     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
3555 
3556     ebiptablesTearNewRulesFW(fw, ifname);
3557 
3558     return virFirewallApply(fw);
3559 }
3560 
3561 static int
ebiptablesTearOldRules(const char * ifname)3562 ebiptablesTearOldRules(const char *ifname)
3563 {
3564     g_autoptr(virFirewall) fw = virFirewallNew();
3565 
3566     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
3567 
3568     iptablesUnlinkRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3569     iptablesRemoveRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3570     iptablesRenameTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3571 
3572     iptablesUnlinkRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3573     iptablesRemoveRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3574     iptablesRenameTmpRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3575 
3576     ebtablesUnlinkRootChainFW(fw, true, ifname);
3577     ebtablesUnlinkRootChainFW(fw, false, ifname);
3578     ebtablesRemoveSubChainsFW(fw, ifname);
3579     ebtablesRemoveRootChainFW(fw, true, ifname);
3580     ebtablesRemoveRootChainFW(fw, false, ifname);
3581     ebtablesRenameTmpSubAndRootChainsFW(fw, ifname);
3582 
3583     return virFirewallApply(fw);
3584 }
3585 
3586 
3587 /**
3588  * ebiptablesAllTeardown:
3589  * @ifname : the name of the interface to which the rules apply
3590  *
3591  * Unconditionally remove all possible user defined tables and rules
3592  * that were created for the given interface (ifname).
3593  *
3594  * Returns 0 on success, -1 on OOM
3595  */
3596 static int
ebiptablesAllTeardown(const char * ifname)3597 ebiptablesAllTeardown(const char *ifname)
3598 {
3599     g_autoptr(virFirewall) fw = virFirewallNew();
3600 
3601     virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
3602 
3603     ebiptablesTearNewRulesFW(fw, ifname);
3604 
3605     iptablesUnlinkRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3606     iptablesClearVirtInPostFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3607     iptablesRemoveRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname);
3608 
3609     iptablesUnlinkRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3610     iptablesClearVirtInPostFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3611     iptablesRemoveRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV6, ifname);
3612 
3613     ebtablesUnlinkRootChainFW(fw, true, ifname);
3614     ebtablesUnlinkRootChainFW(fw, false, ifname);
3615 
3616     ebtablesRemoveSubChainsFW(fw, ifname);
3617 
3618     ebtablesRemoveRootChainFW(fw, true, ifname);
3619     ebtablesRemoveRootChainFW(fw, false, ifname);
3620 
3621     return virFirewallApply(fw);
3622 }
3623 
3624 
3625 virNWFilterTechDriver ebiptables_driver = {
3626     .name = EBIPTABLES_DRIVER_ID,
3627     .flags = 0,
3628 
3629     .init     = ebiptablesDriverInit,
3630     .shutdown = ebiptablesDriverShutdown,
3631 
3632     .applyNewRules       = ebiptablesApplyNewRules,
3633     .tearNewRules        = ebiptablesTearNewRules,
3634     .tearOldRules        = ebiptablesTearOldRules,
3635     .allTeardown         = ebiptablesAllTeardown,
3636 
3637     .canApplyBasicRules  = ebiptablesCanApplyBasicRules,
3638     .applyBasicRules     = ebtablesApplyBasicRules,
3639     .applyDHCPOnlyRules  = ebtablesApplyDHCPOnlyRules,
3640     .applyDropAllRules   = ebtablesApplyDropAllRules,
3641     .removeBasicRules    = ebtablesRemoveBasicRules,
3642 };
3643 
3644 static void
ebiptablesDriverProbeCtdir(void)3645 ebiptablesDriverProbeCtdir(void)
3646 {
3647     struct utsname utsname;
3648     unsigned long thisversion;
3649 
3650     iptables_ctdir_corrected = CTDIR_STATUS_UNKNOWN;
3651 
3652     if (uname(&utsname) < 0) {
3653         VIR_ERROR(_("Call to utsname failed: %d"), errno);
3654         return;
3655     }
3656 
3657     /* following Linux lxr, the logic was inverted in 2.6.39 */
3658     if (virParseVersionString(utsname.release, &thisversion, true) < 0) {
3659         VIR_ERROR(_("Could not determine kernel version from string %s"),
3660                   utsname.release);
3661         return;
3662     }
3663 
3664     if (thisversion >= 2 * 1000000 + 6 * 1000 + 39)
3665         iptables_ctdir_corrected = CTDIR_STATUS_CORRECTED;
3666     else
3667         iptables_ctdir_corrected = CTDIR_STATUS_OLD;
3668 }
3669 
3670 
3671 static int
ebiptablesDriverProbeStateMatchQuery(virFirewall * fw G_GNUC_UNUSED,virFirewallLayer layer G_GNUC_UNUSED,const char * const * lines,void * opaque)3672 ebiptablesDriverProbeStateMatchQuery(virFirewall *fw G_GNUC_UNUSED,
3673                                      virFirewallLayer layer G_GNUC_UNUSED,
3674                                      const char *const *lines,
3675                                      void *opaque)
3676 {
3677     unsigned long *version = opaque;
3678     char *tmp;
3679 
3680     if (!lines || !lines[0]) {
3681         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
3682                        _("No output from iptables --version"));
3683         return -1;
3684     }
3685 
3686     /*
3687      * we expect output in the format
3688      * 'iptables v1.4.16'
3689      */
3690     if (!(tmp = strchr(lines[0], 'v')) ||
3691         virParseVersionString(tmp + 1, version, true) < 0) {
3692         virReportError(VIR_ERR_INTERNAL_ERROR,
3693                        _("Cannot parse version string '%s'"),
3694                        lines[0]);
3695         return -1;
3696     }
3697 
3698     return 0;
3699 }
3700 
3701 
3702 static int
ebiptablesDriverProbeStateMatch(void)3703 ebiptablesDriverProbeStateMatch(void)
3704 {
3705     unsigned long version;
3706     g_autoptr(virFirewall) fw = virFirewallNew();
3707 
3708     virFirewallStartTransaction(fw, 0);
3709     virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_IPV4,
3710                            false, ebiptablesDriverProbeStateMatchQuery, &version,
3711                            "--version", NULL);
3712 
3713     if (virFirewallApply(fw) < 0)
3714         return -1;
3715 
3716     /*
3717      * since version 1.4.16 '-m state --state ...' will be converted to
3718      * '-m conntrack --ctstate ...'
3719      */
3720     if (version >= 1 * 1000000 + 4 * 1000 + 16)
3721         newMatchState = true;
3722 
3723     return 0;
3724 }
3725 
3726 static int
ebiptablesDriverInit(bool privileged)3727 ebiptablesDriverInit(bool privileged)
3728 {
3729     if (!privileged)
3730         return 0;
3731 
3732     ebiptablesDriverProbeCtdir();
3733     if (ebiptablesDriverProbeStateMatch() < 0)
3734         return -1;
3735 
3736     ebiptables_driver.flags = TECHDRV_FLAG_INITIALIZED;
3737 
3738     return 0;
3739 }
3740 
3741 
3742 static void
ebiptablesDriverShutdown(void)3743 ebiptablesDriverShutdown(void)
3744 {
3745     ebiptables_driver.flags = 0;
3746 }
3747