unix: sysctl -- Preserve max_dgram_qlen value

Submitted by Alexander Mikhalitsyn on Oct. 31, 2019, 9:10 a.m.

Details

Message ID 20191031091017.22312-1-alexander.mikhalitsyn@virtuozzo.com
State New
Series "unix: sysctl -- Preserve max_dgram_qlen value"
Headers show

Commit Message

Alexander Mikhalitsyn Oct. 31, 2019, 9:10 a.m.
The /proc/sys/net/unix/max_dgram_qlen is a per-net variable and
we already noticed that systemd inside a container may change its value
(for example it sets it to 512 by now instead of kernel's default
value 10), thus we need keep it inside image and restore then.

Based-on-patch-by: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Alexander Mikhalitsyn <alexander@mihalicyn.com>
Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
---
 criu/net.c                             | 102 ++++++++++++++++++++++++-
 images/netdev.proto                    |   1 +
 test/zdtm/static/Makefile              |   1 +
 test/zdtm/static/netns_sub_sysctl.c    |  62 +++++++++++++++
 test/zdtm/static/netns_sub_sysctl.desc |   5 ++
 5 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 test/zdtm/static/netns_sub_sysctl.c
 create mode 100644 test/zdtm/static/netns_sub_sysctl.desc

Patch hide | download patch | download mbox

diff --git a/criu/net.c b/criu/net.c
index fe9b51ad..843f74d7 100644
--- a/criu/net.c
+++ b/criu/net.c
@@ -210,6 +210,15 @@  char *devconfs6[] = {
 #define MAX_CONF_OPT_PATH IFNAMSIZ+60
 #define MAX_STR_CONF_LEN 200
 
+static char *unix_conf_entries[] = {
+	"max_dgram_qlen",
+};
+
+#define CONF_UNIX_BASE		"net/unix"
+#define CONF_UNIX_FMT		CONF_UNIX_BASE"/%s"
+#define MAX_CONF_UNIX_OPT_PATH	32
+#define MAX_CONF_UNIX_PATH	(sizeof(CONF_UNIX_FMT) + MAX_CONF_UNIX_OPT_PATH - 2)
+
 static int net_conf_op(char *tgt, SysctlEntry **conf, int n, int op, char *proto,
 		struct sysctl_req *req, char (*path)[MAX_CONF_OPT_PATH], int size,
 		char **devconfs, SysctlEntry **def_conf)
@@ -339,6 +348,72 @@  static int ipv6_conf_op(char *tgt, SysctlEntry **conf, int n, int op, SysctlEntr
 			devconfs6, def_conf);
 }
 
+static int unix_conf_op(SysctlEntry ***rconf, size_t *n, int op)
+{
+	int i, ret = -1, flags = op == CTL_READ ? CTL_FLAGS_OPTIONAL : 0;
+	char path[ARRAY_SIZE(unix_conf_entries)][MAX_CONF_UNIX_PATH] = { };
+	struct sysctl_req req[ARRAY_SIZE(unix_conf_entries)] = { };
+	SysctlEntry **conf = *rconf;
+
+	if (*n != ARRAY_SIZE(unix_conf_entries)) {
+		pr_err("unix: Unexpected entries in config (%u %u)\n",
+		       (unsigned)*n, (unsigned)ARRAY_SIZE(unix_conf_entries));
+		return -EINVAL;
+	}
+
+	if (opts.weak_sysctls)
+		flags = CTL_FLAGS_OPTIONAL;
+
+	for (i = 0; i < *n; i++) {
+		snprintf(path[i], MAX_CONF_UNIX_PATH, CONF_UNIX_FMT,
+			 unix_conf_entries[i]);
+		req[i].name = path[i];
+		req[i].flags = flags;
+
+		switch (conf[i]->type) {
+		case SYSCTL_TYPE__CTL_32:
+			req[i].type = CTL_32;
+			req[i].arg = &conf[i]->iarg;
+			break;
+		default:
+			pr_err("unix: Unknown config type %d\n",
+			       (unsigned)conf[i]->type);
+			return -1;
+		}
+	}
+
+	ret = sysctl_op(req, *n, op, CLONE_NEWNET);
+	if (ret < 0) {
+		pr_err("unix: Failed to %s %s/<confs>\n",
+		       (op == CTL_READ) ? "read" : "write",
+		       CONF_UNIX_BASE);
+		return -1;
+	}
+
+	if (op == CTL_READ) {
+		bool has_entries = false;
+
+		for (i = 0; i < *n; i++) {
+			if (req[i].flags & CTL_FLAGS_HAS) {
+				conf[i]->has_iarg = true;
+				if (!has_entries)
+					has_entries = true;
+			}
+		}
+
+		/*
+		 * Zap the whole section of data.
+		 * Unix conf is optional.
+		 */
+		if (!has_entries) {
+			*n = 0;
+			*rconf = NULL;
+		}
+	}
+
+	return 0;
+}
+
 /*
  * I case if some entry is missing in
  * the kernel, simply write DEVCONFS_UNUSED
@@ -1824,6 +1899,8 @@  static int dump_netns_conf(struct ns_id *ns, struct cr_imgset *fds)
 	int ret = -1;
 	int i;
 	NetnsEntry netns = NETNS_ENTRY__INIT;
+	SysctlEntry *unix_confs = NULL;
+	size_t sizex = ARRAY_SIZE(unix_conf_entries);
 	SysctlEntry *def_confs4 = NULL, *all_confs4 = NULL;
 	int size4 = ARRAY_SIZE(devconfs4);
 	SysctlEntry *def_confs6 = NULL, *all_confs6 = NULL;
@@ -1840,7 +1917,8 @@  static int dump_netns_conf(struct ns_id *ns, struct cr_imgset *fds)
 	o_buf = buf = xmalloc(
 			i * (sizeof(NetnsId*) + sizeof(NetnsId)) +
 			size4 * (sizeof(SysctlEntry*) + sizeof(SysctlEntry)) * 2 +
-			size6 * (sizeof(SysctlEntry*) + sizeof(SysctlEntry)) * 2
+			size6 * (sizeof(SysctlEntry*) + sizeof(SysctlEntry)) * 2 +
+			sizex * (sizeof(SysctlEntry*) + sizeof(SysctlEntry))
 		     );
 	if (!buf)
 		goto out;
@@ -1896,6 +1974,16 @@  static int dump_netns_conf(struct ns_id *ns, struct cr_imgset *fds)
 		}
 	}
 
+	netns.n_unix_conf = sizex;
+	netns.unix_conf = xptr_pull_s(&buf, sizex * sizeof(SysctlEntry*));
+	unix_confs = xptr_pull_s(&buf, sizex * sizeof(SysctlEntry));
+
+	for (i = 0; i < sizex; i++) {
+		sysctl_entry__init(&unix_confs[i]);
+		netns.unix_conf[i] = &unix_confs[i];
+		netns.unix_conf[i]->type = SYSCTL_TYPE__CTL_32;
+	}
+
 	ret = ipv4_conf_op("default", netns.def_conf4, size4, CTL_READ, NULL);
 	if (ret < 0)
 		goto err_free;
@@ -1910,6 +1998,12 @@  static int dump_netns_conf(struct ns_id *ns, struct cr_imgset *fds)
 	if (ret < 0)
 		goto err_free;
 
+	ret = unix_conf_op(&netns.unix_conf, &netns.n_unix_conf, CTL_READ);
+	if (ret < 0)
+		goto err_free;
+
+
+
 	ret = pb_write_one(img_from_set(fds, CR_FD_NETNS), &netns, PB_NETNS);
 err_free:
 	xfree(o_buf);
@@ -2148,6 +2242,12 @@  static int restore_netns_conf(struct ns_id *ns)
 		ret = ipv6_conf_op("default", (netns)->def_conf6, (netns)->n_def_conf6, CTL_WRITE, NULL);
 	}
 
+	if ((netns)->unix_conf) {
+		ret = unix_conf_op(&(netns)->unix_conf, &(netns)->n_unix_conf, CTL_WRITE);
+		if (ret)
+			goto out;
+	}
+
 	ns->net.netns = netns;
 out:
 	return ret;
diff --git a/images/netdev.proto b/images/netdev.proto
index 476a92ce..ae9c9953 100644
--- a/images/netdev.proto
+++ b/images/netdev.proto
@@ -71,4 +71,5 @@  message netns_entry {
 
 	repeated netns_id nsids		= 7;
 	optional string	ext_key		= 8;
+	repeated sysctl_entry unix_conf	= 9;
 }
diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index a38482f4..887bf4ac 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -207,6 +207,7 @@  TST_NOFILE	:=				\
 		pipe03				\
 		netns_sub			\
 		netns_sub_veth			\
+		netns_sub_sysctl	\
 		unlink_multiple_largefiles	\
 		config_inotify_irmap		\
 		thp_disable			\
diff --git a/test/zdtm/static/netns_sub_sysctl.c b/test/zdtm/static/netns_sub_sysctl.c
new file mode 100644
index 00000000..ea84cd30
--- /dev/null
+++ b/test/zdtm/static/netns_sub_sysctl.c
@@ -0,0 +1,62 @@ 
+#include <sched.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc	= "Check dump and restore a net.unix.max_dgram_qlen sysctl parameter in subns";
+
+#define CONF_UNIX_PARAM "/proc/sys/net/unix/max_dgram_qlen"
+
+int main(int argc, char **argv)
+{
+	char cmd[128];
+	FILE *fp;
+	int ret, test_max_dgram_qlen = 321, max_dgram_qlen = 0;
+	test_init(argc, argv);
+
+	if (unshare(CLONE_NEWNET)) {
+		perror("unshare");
+		return 1;
+	}
+
+	sprintf(cmd, "echo %d > %s", test_max_dgram_qlen, CONF_UNIX_PARAM);
+	if (system(cmd)) {
+		pr_perror("Can't change %s", CONF_UNIX_PARAM);
+		return -1;
+	}
+
+	fp = fopen(CONF_UNIX_PARAM, "r+");
+	if (fp == NULL) {
+		pr_perror("fopen");
+		return -1;
+	}
+
+	ret = fscanf(fp, "%d", &max_dgram_qlen);
+	if (ret != 1) {
+		pr_perror("fscanf");
+		fclose(fp);
+		return -1;
+	}
+
+	test_daemon();
+	test_waitsig();
+
+	if (test_max_dgram_qlen != max_dgram_qlen) {
+		fail();
+		return 1;
+	}
+
+	pass();
+	return 0;
+}
diff --git a/test/zdtm/static/netns_sub_sysctl.desc b/test/zdtm/static/netns_sub_sysctl.desc
new file mode 100644
index 00000000..e13a65b6
--- /dev/null
+++ b/test/zdtm/static/netns_sub_sysctl.desc
@@ -0,0 +1,5 @@ 
+{
+    'deps': ['/bin/sh', '/sbin/ip|/bin/ip'],
+    'flavor': 'ns',
+    'flags': 'suid'
+}