kerndat: create separate netns for has_nftables_concat check

There was a race window in kerndat_has_nftables_concat(void) when
two or more instances of CRIU are running in parallel.
One instance would create the table normally, and another would
fail when trying to create a table with the same name and wrongly
set kdat.has_nftables_concat = false, and will save it to kerndat
cache.

A separate netns is created to avoid table collisions.

v2: use call_in_child_process helper instead of fork

Co-developed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Zeyad Yasser <zeyady98@gmail.com>
This commit is contained in:
Zeyad Yasser 2021-08-23 20:59:11 +02:00 committed by Andrei Vagin
parent aa772bf286
commit d879220996

View file

@ -13,6 +13,7 @@
#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
#include <sys/prctl.h>
#include <sys/inotify.h>
#include <sched.h>
#if defined(CONFIG_HAS_NFTABLES_LIB_API_0) || defined(CONFIG_HAS_NFTABLES_LIB_API_1)
#include <nftables/libnftables.h>
@ -1150,32 +1151,56 @@ close:
return ret;
}
static int kerndat_has_nftables_concat(void)
{
#if defined(CONFIG_HAS_NFTABLES_LIB_API_0) || defined(CONFIG_HAS_NFTABLES_LIB_API_1)
static int __has_nftables_concat(void *arg)
{
bool *has = (bool *)arg;
struct nft_ctx *nft;
int ret = 0;
int ret = 1;
/*
* Create a separate network namespace to avoid
* collisions between two CRIU instances.
*/
if (unshare(CLONE_NEWNET)) {
pr_perror("Unable create a network namespace");
return 1;
}
nft = nft_ctx_new(NFT_CTX_DEFAULT);
if (!nft)
return -1;
return 1;
if (NFT_RUN_CMD(nft, "create table inet CRIU-kerndat-test")) {
ret = -1;
if (NFT_RUN_CMD(nft, "create table inet CRIU")) {
pr_err("Can't create nftables table\n");
goto nft_ctx_free_out;
}
if (NFT_RUN_CMD(nft, "add set inet CRIU-kerndat-test conn { type ipv4_addr . inet_service ;}"))
kdat.has_nftables_concat = false;
if (NFT_RUN_CMD(nft, "add set inet CRIU conn { type ipv4_addr . inet_service ;}"))
*has = false; /* kdat.has_nftables_concat = false */
else
kdat.has_nftables_concat = true;
*has = true; /* kdat.has_nftables_concat = true */
/* Clean up */
NFT_RUN_CMD(nft, "delete table inet CRIU-kerndat-test");
NFT_RUN_CMD(nft, "delete table inet CRIU");
ret = 0;
nft_ctx_free_out:
nft_ctx_free(nft);
return ret;
}
#endif
static int kerndat_has_nftables_concat(void)
{
#if defined(CONFIG_HAS_NFTABLES_LIB_API_0) || defined(CONFIG_HAS_NFTABLES_LIB_API_1)
bool has;
if (call_in_child_process(__has_nftables_concat, (void *)&has))
return -1;
kdat.has_nftables_concat = has;
return 0;
#else
pr_warn("CRIU was built without libnftables support\n");
kdat.has_nftables_concat = false;