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(ðHdr->dataSrcMACAddr)) {
363 if (printDataType(vars,
364 macaddr, sizeof(macaddr),
365 ðHdr->dataSrcMACAddr) < 0)
366 return -1;
367
368 virFirewallRuleAddArgList(fw, fwrule,
369 reverse ? "-d" : "-s",
370 NULL);
371 if (ENTRY_WANT_NEG_SIGN(ðHdr->dataSrcMACAddr))
372 virFirewallRuleAddArg(fw, fwrule, "!");
373
374 if (HAS_ENTRY_ITEM(ðHdr->dataSrcMACMask)) {
375 if (printDataType(vars,
376 macmask, sizeof(macmask),
377 ðHdr->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(ðHdr->dataDstMACAddr)) {
388 if (printDataType(vars,
389 macaddr, sizeof(macaddr),
390 ðHdr->dataDstMACAddr) < 0)
391 return -1;
392
393 virFirewallRuleAddArgList(fw, fwrule,
394 reverse ? "-s" : "-d",
395 NULL);
396 if (ENTRY_WANT_NEG_SIGN(ðHdr->dataDstMACAddr))
397 virFirewallRuleAddArg(fw, fwrule, "!");
398
399 if (HAS_ENTRY_ITEM(ðHdr->dataDstMACMask)) {
400 if (printDataType(vars,
401 macmask, sizeof(macmask),
402 ðHdr->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