From nobody Fri Apr 19 18:39:29 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=virtuozzo.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1540799185721903.0842541604072; Mon, 29 Oct 2018 00:46:25 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 44F3730CFADE; Mon, 29 Oct 2018 07:46:23 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D49A32619B; Mon, 29 Oct 2018 07:46:22 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 786B018005B2; Mon, 29 Oct 2018 07:46:22 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w9T7aEO4017847 for ; Mon, 29 Oct 2018 03:36:14 -0400 Received: by smtp.corp.redhat.com (Postfix) id A22E51A908; Mon, 29 Oct 2018 07:36:14 +0000 (UTC) Received: from mx1.redhat.com (ext-mx10.extmail.prod.ext.phx2.redhat.com [10.5.110.39]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9AECA1A916 for ; Mon, 29 Oct 2018 07:36:12 +0000 (UTC) Received: from relay.sw.ru (relay.sw.ru [185.231.240.75]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2C87AC7CBF for ; Mon, 29 Oct 2018 07:36:10 +0000 (UTC) Received: from [10.94.3.220] (helo=dim-vz7.qa.sw.ru) by relay.sw.ru with esmtp (Exim 4.90_1) (envelope-from ) id 1gH25n-0004lF-Bf for libvir-list@redhat.com; Mon, 29 Oct 2018 10:36:07 +0300 From: Nikolay Shirokovskiy To: libvir-list@redhat.com Date: Mon, 29 Oct 2018 10:35:03 +0300 Message-Id: <1540798504-836400-2-git-send-email-nshirokovskiy@virtuozzo.com> In-Reply-To: <1540798504-836400-1-git-send-email-nshirokovskiy@virtuozzo.com> References: <1540798504-836400-1-git-send-email-nshirokovskiy@virtuozzo.com> X-Greylist: Sender passed SPF test, ACL 238 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 29 Oct 2018 07:36:11 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 29 Oct 2018 07:36:11 +0000 (UTC) for IP:'185.231.240.75' DOMAIN:'relay.sw.ru' HELO:'relay.sw.ru' FROM:'nshirokovskiy@virtuozzo.com' RCPT:'' X-RedHat-Spam-Score: -0.001 (SPF_PASS) 185.231.240.75 relay.sw.ru 185.231.240.75 relay.sw.ru X-Scanned-By: MIMEDefang 2.78 on 10.5.110.39 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH v2 1/2] firewall: add dump function X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.47]); Mon, 29 Oct 2018 07:46:24 +0000 (UTC) Content-Type: text/plain; charset="utf-8" We are going to save this dump on applying rules so that on next applying we can compare dump of current rules with dump of previous rules. Which in turn will help us to decide wether to reinstantiate rules or not. Signed-off-by: Nikolay Shirokovskiy --- src/libvirt_private.syms | 1 + src/util/virfirewall.c | 111 +++++++++++++++++++++++++++++++++++++++++++= ++++ src/util/virfirewall.h | 3 ++ 3 files changed, 115 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 335210c..612ad41 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1892,6 +1892,7 @@ virFileCacheSetPriv; # util/virfirewall.h virFirewallAddRuleFull; virFirewallApply; +virFirewallDumpRules; virFirewallFree; virFirewallNew; virFirewallRemoveRule; diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c index c786d76..ed7f9e6 100644 --- a/src/util/virfirewall.c +++ b/src/util/virfirewall.c @@ -949,3 +949,114 @@ virFirewallApply(virFirewallPtr firewall) virMutexUnlock(&ruleLock); return ret; } + + +static void +virFirewallDumpRule(virBufferPtr buf, + const char *ifname, + virFirewallRulePtr rule) +{ + size_t i =3D 0; + size_t j, idx; + const char *concurrent =3D NULL; + + switch (rule->layer) { + case VIR_FIREWALL_LAYER_ETHERNET: + concurrent =3D "--concurrent"; + break; + + case VIR_FIREWALL_LAYER_IPV4: + case VIR_FIREWALL_LAYER_IPV6: + concurrent =3D "-w"; + break; + + case VIR_FIREWALL_LAYER_LAST: + break; + } + + /* don't dump concurrent option */ + if (concurrent && rule->argsLen > 0 && STREQ(rule->args[0], concurrent= )) + i++; + + /* calculate index of 'command' argument like -N or -A etc */ + idx =3D i; + if (i < rule->argsLen && STREQ(rule->args[i], "-t")) + idx +=3D 2; + + /* Whitelist commands that can go to dump. These are -N and -A. + * Commands D, L, X, F are used only for cleanup purpuses and do + * not present result rules list. -I is only used by + * iptablesCreateBaseChainsFW to [re]insert common non binding specific + * rules. + */ + if (idx >=3D rule->argsLen || + (STRNEQ(rule->args[idx], "-N") && STRNEQ(rule->args[idx], "-A"))) + return; + + for (j =3D i; j < rule->argsLen; j++) + if (strstr(rule->args[j], ifname)) + break; + + /* We also need to filter common non binding specific rules originated + * from iptablesCreateBaseChainsFW - these are -N for libvirt-in etc. = */ + if (j =3D=3D rule->argsLen) + return; + + switch (rule->layer) { + case VIR_FIREWALL_LAYER_ETHERNET: + virBufferAddLit(buf, "ebtables "); + break; + + case VIR_FIREWALL_LAYER_IPV4: + virBufferAddLit(buf, "iptables "); + break; + + case VIR_FIREWALL_LAYER_IPV6: + virBufferAddLit(buf, "ip6tables "); + break; + + case VIR_FIREWALL_LAYER_LAST: + break; + } + + /* dump default table explicitly */ + if (i < rule->argsLen && STRNEQ(rule->args[i], "-t")) + virBufferAddLit(buf, "-t filter "); + + for (; i < rule->argsLen; i++) { + virBufferAddStr(buf, rule->args[i]); + + if (i < rule->argsLen - 1) + virBufferAddLit(buf, " "); + } + + virBufferAddLit(buf, "\n"); +} + + +/* + * Dump rules for all transactions. Transaction rollbacks are not dumped. + * The order of rules in dump follows from order of transactions and + * order of rules in transactions. Only creation rules (-N, -A) are dumped. + * Example: + * + * ebtables -t nat -N libvirt-P-vme001c42fd388c + * ebtables -t nat -A libvirt-P-vme001c42fd388c -d 00:1c:42:fd:38:9d -j RE= TURN + * .. + */ +char* +virFirewallDumpRules(virFirewallPtr firewall, + const char *ifname) +{ + size_t i, j; + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + + if (!firewall || firewall->err) + return NULL; + + for (i =3D 0; i < firewall->ngroups; i++) + for (j =3D 0; j < firewall->groups[i]->naction; j++) + virFirewallDumpRule(&buf, ifname, firewall->groups[i]->action[= j]); + + return virBufferContentAndReset(&buf); +} diff --git a/src/util/virfirewall.h b/src/util/virfirewall.h index e024e88..bbbc561 100644 --- a/src/util/virfirewall.h +++ b/src/util/virfirewall.h @@ -115,6 +115,9 @@ void virFirewallStartRollback(virFirewallPtr firewall, =20 int virFirewallApply(virFirewallPtr firewall); =20 +char* virFirewallDumpRules(virFirewallPtr firewall, + const char *ifname); + void virFirewallSetLockOverride(bool avoid); =20 VIR_DEFINE_AUTOPTR_FUNC(virFirewall, virFirewallFree) --=20 1.8.3.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Fri Apr 19 18:39:29 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=virtuozzo.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1540799207497553.5508260823312; Mon, 29 Oct 2018 00:46:47 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 984D3308424E; Mon, 29 Oct 2018 07:46:42 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 600F226564; Mon, 29 Oct 2018 07:46:42 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 03D0F4CA94; Mon, 29 Oct 2018 07:46:42 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w9T7aFc8017853 for ; Mon, 29 Oct 2018 03:36:15 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1E8275D778; Mon, 29 Oct 2018 07:36:15 +0000 (UTC) Received: from mx1.redhat.com (ext-mx08.extmail.prod.ext.phx2.redhat.com [10.5.110.32]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 16C1E5D756 for ; Mon, 29 Oct 2018 07:36:13 +0000 (UTC) Received: from relay.sw.ru (relay.sw.ru [185.231.240.75]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 335E5C058CA4 for ; Mon, 29 Oct 2018 07:36:10 +0000 (UTC) Received: from [10.94.3.220] (helo=dim-vz7.qa.sw.ru) by relay.sw.ru with esmtp (Exim 4.90_1) (envelope-from ) id 1gH25n-0004lF-Rn for libvir-list@redhat.com; Mon, 29 Oct 2018 10:36:07 +0300 From: Nikolay Shirokovskiy To: libvir-list@redhat.com Date: Mon, 29 Oct 2018 10:35:04 +0300 Message-Id: <1540798504-836400-3-git-send-email-nshirokovskiy@virtuozzo.com> In-Reply-To: <1540798504-836400-1-git-send-email-nshirokovskiy@virtuozzo.com> References: <1540798504-836400-1-git-send-email-nshirokovskiy@virtuozzo.com> X-Greylist: Sender passed SPF test, ACL 238 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Mon, 29 Oct 2018 07:36:11 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Mon, 29 Oct 2018 07:36:11 +0000 (UTC) for IP:'185.231.240.75' DOMAIN:'relay.sw.ru' HELO:'relay.sw.ru' FROM:'nshirokovskiy@virtuozzo.com' RCPT:'' X-RedHat-Spam-Score: -0.001 (SPF_PASS) 185.231.240.75 relay.sw.ru 185.231.240.75 relay.sw.ru X-Scanned-By: MIMEDefang 2.78 on 10.5.110.32 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH v2 2/2] nwfilter: don't reinstantiate rules if there is no need to X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]); Mon, 29 Oct 2018 07:46:43 +0000 (UTC) Content-Type: text/plain; charset="utf-8" This patch adds caching to rules instantiating in nwfilter. This instantiat= ing can take much time as described in [1] so as we are lacking option to instantiate rules faster right now we can at least don't instantiate when we don't need to. And this is typical that between libvirtd restarts rules don= 't change in firewall so we don't need to reapply them. The most straightforward approach is just to compare rules to be instantiat= ed against rules in firewall but this comparison is complicated (see issues below). So let's instead dump both rules we are going to instantiate and ru= les after instantiation every time we apply rules. So next time we are going to instantiate rules we can check whether rules in firewall changed or not and whether we are going to reinstantiate same rules or different. A few words on dumping rules we are going to instantiate. After filling firewall obj with rules we have several transactions some of them can have rollbacks. Among all these rules those that are in rollbacks and those that= are aiming on preparation cleanup (-X, -F, -D and -L which helps to traverse tr= ee on cleanup) are not significant and ommited in dump. * Comparison issues * - different rules order - rules arguments order can differ. For example: in: -A libvirt-out -m physdev --physdev-is-bridged --physdev-out vme00= 1c42fd388c -g FO-vme001c42fd388c out: -A libvirt-out -m physdev --physdev-out vme001c42fd388c --physdev-= is-bridged -g FO-vme001c42fd388c - result rule can have extra arguments. For example (here it looks like jus= t explicit extension): in: -A FI-vme001c42fd388c -p tcp --dport 1000:3000 -j RETURN out: -A FI-vme001c42fd388c -p tcp -m tcp --dport 1000:3000 -j RETURN * References * [1] [RFC] Faster libvirtd restart with nwfilter rules https://www.redhat.com/archives/libvir-list/2018-September/msg01206.html Signed-off-by: Nikolay Shirokovskiy --- src/nwfilter/nwfilter_ebiptables_driver.c | 387 ++++++++++++++++++++++++++= +++- 1 file changed, 385 insertions(+), 2 deletions(-) diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfil= ter_ebiptables_driver.c index 5be1c9b..4cd6b54 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -96,6 +96,8 @@ static bool newMatchState; #define MATCH_PHYSDEV_OUT_FW "-m", "physdev", "--physdev-is-bridged", "--= physdev-out" #define MATCH_PHYSDEV_OUT_OLD_FW "-m", "physdev", "--physdev-out" =20 +#define EBIPTABLES_CACHE_DIR (LOCALSTATEDIR "/run/libvirt/nwfilter-rules") + static int ebtablesRemoveBasicRules(const char *ifname); static int ebiptablesDriverInit(bool privileged); static void ebiptablesDriverShutdown(void); @@ -150,6 +152,10 @@ static char chainprefixes_host_temp[3] =3D { 0 }; =20 +static virHashTablePtr ebiptables_cache_hits; + +static int ebiptablesTearNewRules(const char *ifname); + static int printVar(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, @@ -3393,6 +3399,339 @@ ebtablesGetSubChainInsts(virHashTablePtr chains, =20 } =20 + +static int +ebiptablesCacheApplyNew(const char *ifname, + const char *dumpDriver) +{ + char *fileFirewall =3D NULL; + char *fileDriver =3D NULL; + char *fileDriverTmp =3D NULL; + int ret =3D -1; + + /* for tests */ + if (!ebiptables_cache_hits) + return 0; + + if (virAsprintf(&fileFirewall, "%s/%s.firewall", + EBIPTABLES_CACHE_DIR, ifname) < 0) + return -1; + + if (virAsprintf(&fileDriver, "%s/%s.driver", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + if (unlink(fileFirewall) < 0 && errno !=3D ENOENT) { + virReportSystemError(errno, _("can not unlink file '%s'"), fileFir= ewall); + goto cleanup; + } + + if (unlink(fileDriver) < 0 && errno !=3D ENOENT) { + virReportSystemError(errno, _("can not unlink file '%s'"), fileDri= ver); + goto cleanup; + } + + ret =3D 0; + + if (!dumpDriver) + goto cleanup; + + if (virAsprintfQuiet(&fileDriverTmp, "%s/%s.driver.tmp", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + if (virFileWriteStr(fileDriverTmp, dumpDriver, 0600) < 0) { + unlink(fileDriverTmp); + goto cleanup; + } + + rename(fileDriverTmp, fileDriver); + + cleanup: + VIR_FREE(fileFirewall); + VIR_FREE(fileDriver); + VIR_FREE(fileDriverTmp); + + return ret; +} + + +struct ebiptablesDumpData { + char *dump; + const char *ifname; + virFirewallLayer layer; +}; + + +static int +ebiptablesAppendInstalledRules(virFirewallPtr fw ATTRIBUTE_UNUSED, + const char *const *lines, + void *opaque) +{ + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + struct ebiptablesDumpData *data =3D opaque; + + virBufferAdd(&buf, data->dump, -1); + + while (*lines) { + if (strstr(*lines, data->ifname)) { + /* iptables/ip6tables do not add binary and table in front of = rules + * thus let's do it ourselves */ + switch (data->layer) { + case VIR_FIREWALL_LAYER_IPV4: + if (!STRPREFIX(*lines, "iptables")) + virBufferAddLit(&buf, "iptables -t filter "); + break; + + case VIR_FIREWALL_LAYER_IPV6: + if (!STRPREFIX(*lines, "ip6tables")) + virBufferAddLit(&buf, "ip6tables -t filter "); + break; + + case VIR_FIREWALL_LAYER_ETHERNET: + case VIR_FIREWALL_LAYER_LAST: + break; + } + + virBufferAddStr(&buf, *lines); + virBufferAddLit(&buf, "\n"); + } + ++lines; + } + + data->dump =3D virBufferContentAndReset(&buf); + + return 0; +} + + +static char* +ebiptablesDumpIstalledRules(const char *ifname) +{ + virFirewallPtr fw; + struct ebiptablesDumpData data =3D { NULL, ifname, 0 }; + + /* Here we get fragile. We only check nat table in ebtables and filter + * table for both iptables and ip6tables because we don't use other ta= bles + * right now. But if we start to then we are in danger as we will miss + * changes in that tables on cache checks. On the other hand always ch= ecking + * all the tables is overkill and has significant performance impact if + * number of VMs is large. Do we need to add some protection here? */ + + fw =3D virFirewallNew(); + virFirewallStartTransaction(fw, 0); + data.layer =3D VIR_FIREWALL_LAYER_ETHERNET; + virFirewallAddRuleFull(fw, data.layer, + false, ebiptablesAppendInstalledRules, &data, + "-t", "nat", "-L", "--Lx", NULL); + virFirewallApply(fw); + virFirewallFree(fw); + + fw =3D virFirewallNew(); + virFirewallStartTransaction(fw, 0); + data.layer =3D VIR_FIREWALL_LAYER_IPV4; + virFirewallAddRuleFull(fw, data.layer, + false, ebiptablesAppendInstalledRules, &data, + "-S", NULL); + virFirewallApply(fw); + virFirewallFree(fw); + + fw =3D virFirewallNew(); + virFirewallStartTransaction(fw, 0); + data.layer =3D VIR_FIREWALL_LAYER_IPV6; + virFirewallAddRuleFull(fw, data.layer, + false, ebiptablesAppendInstalledRules, &data, + "-S", NULL); + virFirewallApply(fw); + virFirewallFree(fw); + + return data.dump; +} + + +static void +ebiptablesCacheTearOld(const char *ifname, + bool success) +{ + char *fileFirewall =3D NULL; + char *fileFirewallTmp =3D NULL; + char *fileDriver =3D NULL; + char *dump =3D NULL; + bool error =3D true; + + /* for tests */ + if (!ebiptables_cache_hits) + return; + + if (virAsprintfQuiet(&fileDriver, "%s/%s.driver", + EBIPTABLES_CACHE_DIR, ifname) < 0) + return; + + /* We failed to tear old rules thus let's not cache so we can + * redo rules instantiantion and hopefully tear successfully + * next time + */ + if (!success) { + unlink(fileDriver); + VIR_FREE(fileDriver); + return; + } + + /* When applying new rules we can fail to save driver rules. + * We need both file for cache purpose thus there is no sense + * to save firewall rules. + */ + if (access(fileDriver, F_OK) < 0) + return; + + if (virAsprintfQuiet(&fileFirewall, "%s/%s.firewall", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + if (virAsprintfQuiet(&fileFirewallTmp, "%s/%s.firewall.tmp", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + if (!(dump =3D ebiptablesDumpIstalledRules(ifname))) + goto cleanup; + + if (virFileWriteStr(fileFirewallTmp, dump, 0600) < 0) { + unlink(fileFirewallTmp); + goto cleanup; + } + + if (rename(fileFirewallTmp, fileFirewall) < 0) + goto cleanup; + + error =3D false; + + cleanup: + if (error) + unlink(fileDriver); + + VIR_FREE(fileFirewall); + VIR_FREE(fileFirewallTmp); + VIR_FREE(fileDriver); + VIR_FREE(dump); +} + + +static void +ebiptablesCacheTearNew(const char *ifname) +{ + char *fileDriver =3D NULL; + + /* for tests */ + if (!ebiptables_cache_hits) + return; + + if (virAsprintfQuiet(&fileDriver, "%s/%s.driver", + EBIPTABLES_CACHE_DIR, ifname) < 0) + return; + + unlink(fileDriver); + VIR_FREE(fileDriver); + + /* We lost cache files at this point. We could preserve + * old cache files and restore them here but tearing + * new rules is error path so this is not really important. + */ +} + + +static void +ebiptablesCacheAllTeardown(const char *ifname) +{ + char *fileFirewall =3D NULL; + char *fileDriver =3D NULL; + + /* for tests */ + if (!ebiptables_cache_hits) + return; + + if (virAsprintf(&fileFirewall, "%s/%s.firewall", + EBIPTABLES_CACHE_DIR, ifname) < 0) + return; + + if (virAsprintf(&fileDriver, "%s/%s.driver", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + unlink(fileFirewall); + unlink(fileDriver); + + cleanup: + VIR_FREE(fileFirewall); + VIR_FREE(fileDriver); +} + + +static bool +ebiptablesCacheIsHit(const char *ifname, + const char *dumpDriver) +{ + char *fileDriver =3D NULL; + char *fileFirewall =3D NULL; + char *dumpDriverCached =3D NULL; + char *dumpFirewallCached =3D NULL; + char *dumpFirewall =3D NULL; + bool ret =3D false; + + /* for tests */ + if (!ebiptables_cache_hits) + return false; + + /* just to be sure we don't have remnaints from previous operations */ + virHashRemoveEntry(ebiptables_cache_hits, ifname); + + if (!dumpDriver) + return false; + + if (virAsprintfQuiet(&fileDriver, "%s/%s.driver", + EBIPTABLES_CACHE_DIR, ifname) < 0) + return false; + + if (virAsprintfQuiet(&fileFirewall, "%s/%s.firewall", + EBIPTABLES_CACHE_DIR, ifname) < 0) + goto cleanup; + + if (virFileReadAllQuiet(fileDriver, 1024 * 1024, &dumpDriverCached) < = 0) { + if (errno =3D=3D EOVERFLOW) + VIR_WARN("nwfilter rules cache files exceeds limit"); + + goto cleanup; + } + + if (virFileReadAllQuiet(fileFirewall, 1024 * 1024, &dumpFirewallCached= ) < 0) { + if (errno =3D=3D EOVERFLOW) + VIR_WARN("nwfilter rules cache files exceeds limit"); + + goto cleanup; + } + + if (!(dumpFirewall =3D ebiptablesDumpIstalledRules(ifname))) + goto cleanup; + + if (STRNEQ(dumpFirewall, dumpFirewallCached) || + STRNEQ(dumpDriver, dumpDriverCached)) + goto cleanup; + + if (virHashAddEntry(ebiptables_cache_hits, ifname, (void *) 1) < 0) + goto cleanup; + + ret =3D true; + + cleanup: + VIR_FREE(fileDriver); + VIR_FREE(fileFirewall); + VIR_FREE(dumpFirewall); + VIR_FREE(dumpFirewallCached); + VIR_FREE(dumpDriverCached); + + return ret; +} + + static int ebiptablesApplyNewRules(const char *ifname, virNWFilterRuleInstPtr *rules, @@ -3408,6 +3747,7 @@ ebiptablesApplyNewRules(const char *ifname, char *errmsg =3D NULL; struct ebtablesSubChainInst **subchains =3D NULL; size_t nsubchains =3D 0; + char *dumpDriver =3D NULL; int ret =3D -1; =20 if (!chains_in_set || !chains_out_set) @@ -3592,8 +3932,20 @@ ebiptablesApplyNewRules(const char *ifname, ebtablesRemoveTmpRootChainFW(fw, true, ifname); ebtablesRemoveTmpRootChainFW(fw, false, ifname); =20 - if (virFirewallApply(fw) < 0) - goto cleanup; + dumpDriver =3D virFirewallDumpRules(fw, ifname); + + if (!ebiptablesCacheIsHit(ifname, dumpDriver)) { + if (virFirewallApply(fw) < 0) + goto cleanup; + + if (ebiptablesCacheApplyNew(ifname, dumpDriver) < 0) { + ebiptablesTearNewRules(ifname); + goto cleanup; + } + + } else { + VIR_DEBUG("Skip rules reinstantiating for %s", ifname); + } =20 ret =3D 0; =20 @@ -3604,6 +3956,7 @@ ebiptablesApplyNewRules(const char *ifname, virFirewallFree(fw); virHashFree(chains_in_set); virHashFree(chains_out_set); + VIR_FREE(dumpDriver); =20 VIR_FREE(errmsg); return ret; @@ -3633,12 +3986,20 @@ ebiptablesTearNewRules(const char *ifname) virFirewallPtr fw =3D virFirewallNew(); int ret =3D -1; =20 + if (virHashLookup(ebiptables_cache_hits, ifname)) { + virHashRemoveEntry(ebiptables_cache_hits, ifname); + return 0; + } + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS= ); =20 ebiptablesTearNewRulesFW(fw, ifname); =20 ret =3D virFirewallApply(fw); virFirewallFree(fw); + + ebiptablesCacheTearNew(ifname); + return ret; } =20 @@ -3648,6 +4009,11 @@ ebiptablesTearOldRules(const char *ifname) virFirewallPtr fw =3D virFirewallNew(); int ret =3D -1; =20 + if (virHashLookup(ebiptables_cache_hits, ifname)) { + virHashRemoveEntry(ebiptables_cache_hits, ifname); + return 0; + } + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS= ); =20 iptablesUnlinkRootChainsFW(fw, VIR_FIREWALL_LAYER_IPV4, ifname); @@ -3667,6 +4033,9 @@ ebiptablesTearOldRules(const char *ifname) =20 ret =3D virFirewallApply(fw); virFirewallFree(fw); + + ebiptablesCacheTearOld(ifname, ret =3D=3D 0); + return ret; } =20 @@ -3686,6 +4055,9 @@ ebiptablesAllTeardown(const char *ifname) virFirewallPtr fw =3D virFirewallNew(); int ret =3D -1; =20 + virHashRemoveEntry(ebiptables_cache_hits, ifname); + ebiptablesCacheAllTeardown(ifname); + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS= ); =20 ebiptablesTearNewRulesFW(fw, ifname); @@ -3826,6 +4198,15 @@ ebiptablesDriverInit(bool privileged) if (ebiptablesDriverProbeStateMatch() < 0) return -1; =20 + if (virFileMakePathWithMode(EBIPTABLES_CACHE_DIR, S_IRWXU) < 0) { + virReportSystemError(errno, _("cannot create cache directory '%s'"= ), + EBIPTABLES_CACHE_DIR); + return -1; + } + + if (!(ebiptables_cache_hits =3D virHashCreate(100, NULL))) + return -1; + ebiptables_driver.flags =3D TECHDRV_FLAG_INITIALIZED; =20 return 0; @@ -3835,5 +4216,7 @@ ebiptablesDriverInit(bool privileged) static void ebiptablesDriverShutdown(void) { + virHashFree(ebiptables_cache_hits); + ebiptables_driver.flags =3D 0; } --=20 1.8.3.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list