2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-31 22:35:33 +00:00

net: skip iptables dump if it has nft backend and nft dump is supported

On modern Linux distributions iptables binaries using new nftables
API. We dump iptables rules using "iptables-save", and nftables
rules using libnftables API. This breaks network unlock on modern systems
because technically, we dump rules (including network lock rules) two times.

There is another problem - on host we can have modern distribution, but
in Docker container we can use iptables with netfilter (legacy) API.
So, in this case this legacy rules will be skipped.

This patch handles all of that cases. It tries to find iptables legacy and
dump legacy rules by using appropriate iptables binaries, dump nftables
rules by using libnftables.

Fixes #1435

Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
This commit is contained in:
Alexander Mikhalitsyn
2021-04-11 18:56:54 +03:00
committed by Andrei Vagin
parent e26949cfed
commit d8821d9a8f
4 changed files with 157 additions and 5 deletions

View File

@@ -1201,6 +1201,44 @@ static int check_compat_cr(void)
return -1;
}
static int check_nftables_cr(void)
{
#if defined(CONFIG_HAS_NFTABLES_LIB_API_0) || defined(CONFIG_HAS_NFTABLES_LIB_API_1)
return 0;
#else
pr_warn("CRIU was built without nftables support - nftables rules will "
"not be preserved during C/R\n");
return -1;
#endif
}
static int check_ipt_legacy(void)
{
char *ipt_legacy_bin;
char *ip6t_legacy_bin;
ipt_legacy_bin = get_legacy_iptables_bin(false);
if (!ipt_legacy_bin) {
pr_warn("Couldn't find iptables version which is using iptables legacy API\n");
return -1;
}
pr_info("iptables cmd: %s\n", ipt_legacy_bin);
if (!kdat.ipv6)
return 0;
ip6t_legacy_bin = get_legacy_iptables_bin(true);
if (!ip6t_legacy_bin) {
pr_warn("Couldn't find ip6tables version which is using iptables legacy API\n");
return -1;
}
pr_info("ip6tables cmd: %s\n", ip6t_legacy_bin);
return 0;
}
static int check_uffd(void)
{
if (!kdat.has_uffd) {
@@ -1511,6 +1549,8 @@ static struct feature_list feature_list[] = {
{ "external_net_ns", check_external_net_ns},
{ "clone3_set_tid", check_clone3_set_tid},
{ "newifindex", check_newifindex},
{ "nftables", check_nftables_cr },
{ "has_ipt_legacy", check_ipt_legacy },
{ NULL, NULL },
};

View File

@@ -383,4 +383,6 @@ static inline void print_stack_trace(pid_t pid) {}
extern int mount_detached_fs(const char *fsname);
extern char *get_legacy_iptables_bin(bool ipv6);
#endif /* __CR_UTIL_H__ */

View File

@@ -2065,14 +2065,36 @@ static inline int dump_rule(struct cr_imgset *fds)
static inline int dump_iptables(struct cr_imgset *fds)
{
struct cr_img *img;
char *iptables_cmd = "iptables-save";
char *ip6tables_cmd = "ip6tables-save";
img = img_from_set(fds, CR_FD_IPTABLES);
if (run_iptables_tool("iptables-save", -1, img_raw_fd(img)))
return -1;
/*
* Let's skip iptables dump if we have nftables support compiled in,
* and iptables backend is nft to prevent duplicate dumps.
*/
#if defined(CONFIG_HAS_NFTABLES_LIB_API_0) || defined(CONFIG_HAS_NFTABLES_LIB_API_1)
iptables_cmd = get_legacy_iptables_bin(false);
if (kdat.ipv6) {
if (kdat.ipv6)
ip6tables_cmd = get_legacy_iptables_bin(true);
#endif
if (!iptables_cmd) {
pr_info("skipping iptables dump - no legacy version present\n");
} else {
img = img_from_set(fds, CR_FD_IPTABLES);
if (run_iptables_tool(iptables_cmd, -1, img_raw_fd(img)))
return -1;
}
if (!kdat.ipv6)
return 0;
if (!ip6tables_cmd) {
pr_info("skipping ip6tables dump - no legacy version present\n");
} else {
img = img_from_set(fds, CR_FD_IP6TABLES);
if (run_iptables_tool("ip6tables-save", -1, img_raw_fd(img)))
if (run_iptables_tool(ip6tables_cmd, -1, img_raw_fd(img)))
return -1;
}

View File

@@ -1495,4 +1495,92 @@ int cut_path_ending(char *path, char *ending)
path[ending_pos - 1] = 0;
return 0;
}
static int is_iptables_nft(char *bin)
{
int pfd[2] = { -1, -1 }, ret = -1;
char *cmd[] = { bin, "-V", NULL };
char buf[100];
if (pipe(pfd) < 0) {
pr_perror("Unable to create pipe");
goto err;
}
ret = cr_system(-1, pfd[1], -1, cmd[0], cmd, 0);
if (ret) {
pr_err("%s -V failed\n", cmd[0]);
goto err;
}
close_safe(&pfd[1]);
ret = read(pfd[0], buf, sizeof(buf) - 1);
if (ret < 0) {
pr_perror("Unable to read %s -V output", cmd[0]);
goto err;
}
buf[ret] = '\0';
ret = 0;
if (strstr(buf, "nf_tables")) {
pr_info("iptables has nft backend: %s\n", buf);
ret = 1;
}
err:
close_safe(&pfd[1]);
close_safe(&pfd[0]);
return ret;
}
char *get_legacy_iptables_bin(bool ipv6)
{
static char iptables_bin[2][32];
/* 0 - means we don't know yet,
* -1 - not present,
* 1 - present.
*/
static int iptables_present[2] = { 0, 0 };
char bins[2][2][32] = {
{
"iptables-save",
"iptables-legacy-save"
},
{
"ip6tables-save",
"ip6tables-legacy-save"
}
};
int ret;
if (iptables_present[ipv6] == -1)
return NULL;
if (iptables_present[ipv6] == 1)
return iptables_bin[ipv6];
memcpy(iptables_bin[ipv6], bins[ipv6][0], strlen(bins[ipv6][0]) + 1);
ret = is_iptables_nft(iptables_bin[ipv6]);
/*
* iptables on host uses nft backend (or not installed),
* let's try iptables-legacy
*/
if (ret < 0 || ret == 1) {
memcpy(iptables_bin[ipv6], bins[ipv6][1],
strlen(bins[ipv6][1]) + 1);
ret = is_iptables_nft(iptables_bin[ipv6]);
if (ret < 0 || ret == 1) {
iptables_present[ipv6] = -1;
return NULL;
}
}
/* we can come here with iptables-save or iptables-legacy-save */
iptables_present[ipv6] = 1;
return iptables_bin[ipv6];
}