public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH v2 5/9] port_fwd: Pre-open /proc/net/* files rather than on-demand
Date: Fri,  3 Nov 2023 13:22:59 +1100	[thread overview]
Message-ID: <20231103022303.968638-6-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20231103022303.968638-1-david@gibson.dropbear.id.au>

procfs_scan_listen() can either use an already opened fd for a /proc/net
file, or it will open it.  So, effectively it will open the file on the
first call, then re-use the fd in subsequent calls.  However, it's not
possible to open the /proc/net files after we isolate our filesystem in
isolate_prefork().  That means that for each /proc/net file we must call
procfs_scan_listen() at least once before isolate_prefork(), or it won't
work afterwards.

That happens to be the case, but it's a pretty fragile requirement.  To
make this more robust, instead always pre-open the /proc files we will need
in get_bounds_port_init() and have procfs_scan_listen() just use those.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 port_fwd.c | 42 ++++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/port_fwd.c b/port_fwd.c
index 49b60b1..045ad7a 100644
--- a/port_fwd.c
+++ b/port_fwd.c
@@ -29,8 +29,7 @@
 
 /**
  * procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
- * @fd:		Pointer to fd for relevant /proc/net file
- * @path:	Path to /proc/net file to open (if fd is -1)
+ * @fd:		fd for relevant /proc/net file
  * @lstate:	Code for listening state to scan for
  * @map:	Bitmap where numbers of ports in listening state will be set
  * @exclude:	Bitmap of ports to exclude from setting (and clear)
@@ -38,7 +37,7 @@
  * #syscalls:pasta lseek
  * #syscalls:pasta ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
  */
-static void procfs_scan_listen(int *fd, const char *path, unsigned int lstate,
+static void procfs_scan_listen(int fd, unsigned int lstate,
 			       uint8_t *map, const uint8_t *exclude)
 {
 	struct lineread lr;
@@ -46,16 +45,12 @@ static void procfs_scan_listen(int *fd, const char *path, unsigned int lstate,
 	unsigned int state;
 	char *line;
 
-	if (*fd != -1) {
-		if (lseek(*fd, 0, SEEK_SET)) {
-			warn("lseek() failed on %s: %s", path, strerror(errno));
-			return;
-		}
-	} else if ((*fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+	if (lseek(fd, 0, SEEK_SET)) {
+		warn("lseek() failed on /proc/net file: %s", strerror(errno));
 		return;
 	}
 
-	lineread_init(&lr, *fd);
+	lineread_init(&lr, fd);
 	lineread_get(&lr, &line); /* throw away header */
 	while (lineread_get(&lr, &line) > 0) {
 		/* NOLINTNEXTLINE(cert-err34-c): != 2 if conversion fails */
@@ -96,20 +91,20 @@ void get_bound_ports(struct ctx *c, int ns, uint8_t proto)
 
 	if (proto == IPPROTO_UDP) {
 		memset(udp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(&c->proc_net_udp[V4][ns], "/proc/net/udp",
+		procfs_scan_listen(c->proc_net_udp[V4][ns],
 				   UDP_LISTEN, udp_map, udp_excl);
-		procfs_scan_listen(&c->proc_net_udp[V6][ns], "/proc/net/udp6",
+		procfs_scan_listen(c->proc_net_udp[V6][ns],
 				   UDP_LISTEN, udp_map, udp_excl);
 
-		procfs_scan_listen(&c->proc_net_tcp[V4][ns], "/proc/net/tcp",
+		procfs_scan_listen(c->proc_net_tcp[V4][ns],
 				   TCP_LISTEN, udp_map, udp_excl);
-		procfs_scan_listen(&c->proc_net_tcp[V6][ns], "/proc/net/tcp6",
+		procfs_scan_listen(c->proc_net_tcp[V6][ns],
 				   TCP_LISTEN, udp_map, udp_excl);
 	} else if (proto == IPPROTO_TCP) {
 		memset(tcp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(&c->proc_net_tcp[V4][ns], "/proc/net/tcp",
+		procfs_scan_listen(c->proc_net_tcp[V4][ns],
 				   TCP_LISTEN, tcp_map, tcp_excl);
-		procfs_scan_listen(&c->proc_net_tcp[V6][ns], "/proc/net/tcp6",
+		procfs_scan_listen(c->proc_net_tcp[V6][ns],
 				   TCP_LISTEN, tcp_map, tcp_excl);
 	}
 }
@@ -151,6 +146,7 @@ static int get_bound_ports_ns(void *arg)
 void port_fwd_init(struct ctx *c)
 {
 	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
+	const int flags = O_RDONLY | O_CLOEXEC;
 
 	c->proc_net_tcp[V4][0] = c->proc_net_tcp[V4][1] = -1;
 	c->proc_net_tcp[V6][0] = c->proc_net_tcp[V6][1] = -1;
@@ -158,15 +154,25 @@ void port_fwd_init(struct ctx *c)
 	c->proc_net_udp[V6][0] = c->proc_net_udp[V6][1] = -1;
 
 	if (c->tcp.fwd_in.mode == FWD_AUTO) {
+		c->proc_net_tcp[V4][1] = open_in_ns(c, "/proc/net/tcp", flags);
+		c->proc_net_tcp[V6][1] = open_in_ns(c, "/proc/net/tcp6", flags);
 		ns_ports_arg.proto = IPPROTO_TCP;
 		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
 	}
 	if (c->udp.fwd_in.f.mode == FWD_AUTO) {
+		c->proc_net_udp[V4][1] = open_in_ns(c, "/proc/net/udp", flags);
+		c->proc_net_udp[V6][1] = open_in_ns(c, "/proc/net/udp6", flags);
 		ns_ports_arg.proto = IPPROTO_UDP;
 		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
 	}
-	if (c->tcp.fwd_out.mode == FWD_AUTO)
+	if (c->tcp.fwd_out.mode == FWD_AUTO) {
+		c->proc_net_tcp[V4][0] = open("/proc/net/tcp", flags);
+		c->proc_net_tcp[V6][0] = open("/proc/net/tcp6", flags);
 		get_bound_ports(c, 0, IPPROTO_TCP);
-	if (c->udp.fwd_out.f.mode == FWD_AUTO)
+	}
+	if (c->udp.fwd_out.f.mode == FWD_AUTO) {
+		c->proc_net_udp[V4][0] = open("/proc/net/udp", flags);
+		c->proc_net_udp[V6][0] = open("/proc/net/udp6", flags);
 		get_bound_ports(c, 0, IPPROTO_UDP);
+	}
 }
-- 
@@ -29,8 +29,7 @@
 
 /**
  * procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
- * @fd:		Pointer to fd for relevant /proc/net file
- * @path:	Path to /proc/net file to open (if fd is -1)
+ * @fd:		fd for relevant /proc/net file
  * @lstate:	Code for listening state to scan for
  * @map:	Bitmap where numbers of ports in listening state will be set
  * @exclude:	Bitmap of ports to exclude from setting (and clear)
@@ -38,7 +37,7 @@
  * #syscalls:pasta lseek
  * #syscalls:pasta ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
  */
-static void procfs_scan_listen(int *fd, const char *path, unsigned int lstate,
+static void procfs_scan_listen(int fd, unsigned int lstate,
 			       uint8_t *map, const uint8_t *exclude)
 {
 	struct lineread lr;
@@ -46,16 +45,12 @@ static void procfs_scan_listen(int *fd, const char *path, unsigned int lstate,
 	unsigned int state;
 	char *line;
 
-	if (*fd != -1) {
-		if (lseek(*fd, 0, SEEK_SET)) {
-			warn("lseek() failed on %s: %s", path, strerror(errno));
-			return;
-		}
-	} else if ((*fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+	if (lseek(fd, 0, SEEK_SET)) {
+		warn("lseek() failed on /proc/net file: %s", strerror(errno));
 		return;
 	}
 
-	lineread_init(&lr, *fd);
+	lineread_init(&lr, fd);
 	lineread_get(&lr, &line); /* throw away header */
 	while (lineread_get(&lr, &line) > 0) {
 		/* NOLINTNEXTLINE(cert-err34-c): != 2 if conversion fails */
@@ -96,20 +91,20 @@ void get_bound_ports(struct ctx *c, int ns, uint8_t proto)
 
 	if (proto == IPPROTO_UDP) {
 		memset(udp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(&c->proc_net_udp[V4][ns], "/proc/net/udp",
+		procfs_scan_listen(c->proc_net_udp[V4][ns],
 				   UDP_LISTEN, udp_map, udp_excl);
-		procfs_scan_listen(&c->proc_net_udp[V6][ns], "/proc/net/udp6",
+		procfs_scan_listen(c->proc_net_udp[V6][ns],
 				   UDP_LISTEN, udp_map, udp_excl);
 
-		procfs_scan_listen(&c->proc_net_tcp[V4][ns], "/proc/net/tcp",
+		procfs_scan_listen(c->proc_net_tcp[V4][ns],
 				   TCP_LISTEN, udp_map, udp_excl);
-		procfs_scan_listen(&c->proc_net_tcp[V6][ns], "/proc/net/tcp6",
+		procfs_scan_listen(c->proc_net_tcp[V6][ns],
 				   TCP_LISTEN, udp_map, udp_excl);
 	} else if (proto == IPPROTO_TCP) {
 		memset(tcp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(&c->proc_net_tcp[V4][ns], "/proc/net/tcp",
+		procfs_scan_listen(c->proc_net_tcp[V4][ns],
 				   TCP_LISTEN, tcp_map, tcp_excl);
-		procfs_scan_listen(&c->proc_net_tcp[V6][ns], "/proc/net/tcp6",
+		procfs_scan_listen(c->proc_net_tcp[V6][ns],
 				   TCP_LISTEN, tcp_map, tcp_excl);
 	}
 }
@@ -151,6 +146,7 @@ static int get_bound_ports_ns(void *arg)
 void port_fwd_init(struct ctx *c)
 {
 	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
+	const int flags = O_RDONLY | O_CLOEXEC;
 
 	c->proc_net_tcp[V4][0] = c->proc_net_tcp[V4][1] = -1;
 	c->proc_net_tcp[V6][0] = c->proc_net_tcp[V6][1] = -1;
@@ -158,15 +154,25 @@ void port_fwd_init(struct ctx *c)
 	c->proc_net_udp[V6][0] = c->proc_net_udp[V6][1] = -1;
 
 	if (c->tcp.fwd_in.mode == FWD_AUTO) {
+		c->proc_net_tcp[V4][1] = open_in_ns(c, "/proc/net/tcp", flags);
+		c->proc_net_tcp[V6][1] = open_in_ns(c, "/proc/net/tcp6", flags);
 		ns_ports_arg.proto = IPPROTO_TCP;
 		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
 	}
 	if (c->udp.fwd_in.f.mode == FWD_AUTO) {
+		c->proc_net_udp[V4][1] = open_in_ns(c, "/proc/net/udp", flags);
+		c->proc_net_udp[V6][1] = open_in_ns(c, "/proc/net/udp6", flags);
 		ns_ports_arg.proto = IPPROTO_UDP;
 		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
 	}
-	if (c->tcp.fwd_out.mode == FWD_AUTO)
+	if (c->tcp.fwd_out.mode == FWD_AUTO) {
+		c->proc_net_tcp[V4][0] = open("/proc/net/tcp", flags);
+		c->proc_net_tcp[V6][0] = open("/proc/net/tcp6", flags);
 		get_bound_ports(c, 0, IPPROTO_TCP);
-	if (c->udp.fwd_out.f.mode == FWD_AUTO)
+	}
+	if (c->udp.fwd_out.f.mode == FWD_AUTO) {
+		c->proc_net_udp[V4][0] = open("/proc/net/udp", flags);
+		c->proc_net_udp[V6][0] = open("/proc/net/udp6", flags);
 		get_bound_ports(c, 0, IPPROTO_UDP);
+	}
 }
-- 
2.41.0


  parent reply	other threads:[~2023-11-03  2:23 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-03  2:22 [PATCH v2 0/9] Clean ups to automatic port forwarding David Gibson
2023-11-03  2:22 ` [PATCH v2 1/9] conf: Cleaner initialisation of default forwarding modes David Gibson
2023-11-03  2:22 ` [PATCH v2 2/9] port_fwd: Move automatic port forwarding code to port_fwd.[ch] David Gibson
2023-11-03  2:22 ` [PATCH v2 3/9] port_fwd: Better parameterise procfs_scan_listen() David Gibson
2023-11-03  2:22 ` [PATCH v2 4/9] util: Add open_in_ns() helper David Gibson
2023-11-03  2:22 ` David Gibson [this message]
2023-11-03  2:23 ` [PATCH v2 6/9] port_fwd: Don't NS_CALL get_bound_ports() David Gibson
2023-11-03  2:23 ` [PATCH v2 7/9] port_fwd: Split TCP and UDP cases for get_bound_ports() David Gibson
2023-11-03  2:23 ` [PATCH v2 8/9] port_fwd: Move port scanning /proc fds into struct port_fwd David Gibson
2023-11-03  2:23 ` [PATCH v2 9/9] port_fwd: Simplify get_bound_ports_*() to port_fwd_scan_*() David Gibson
2023-11-07 12:44 ` [PATCH v2 0/9] Clean ups to automatic port forwarding Stefano Brivio

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231103022303.968638-6-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).