public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: Stefano Brivio <sbrivio@redhat.com>, passt-dev@passt.top
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH 2/9] port_fwd: Move automatic port forwarding code to port_fwd.[ch]
Date: Thu,  5 Oct 2023 14:44:38 +1100	[thread overview]
Message-ID: <20231005034445.2015303-3-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20231005034445.2015303-1-david@gibson.dropbear.id.au>

The implementation of scanning /proc files to do automatic port forwarding
is a bit awkwardly split between procfs_scan_listen() in util.c,
get_bound_ports() and related functions in conf.c and the initial setup
code in conf().

Consolidate all of this into port_fwd.h, which already has some related
definitions, and a new port_fwd.c.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile   |   2 +-
 conf.c     |  85 +------------------------
 conf.h     |   1 -
 port_fwd.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 port_fwd.h |   3 +
 tcp.c      |   1 -
 util.c     |  65 +------------------
 util.h     |   2 -
 8 files changed, 185 insertions(+), 153 deletions(-)
 create mode 100644 port_fwd.c

diff --git a/Makefile b/Makefile
index 941086a..0324fdd 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,7 @@ FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS)
 
 PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c icmp.c igmp.c \
 	isolation.c lineread.c log.c mld.c ndp.c netlink.c packet.c passt.c \
-	pasta.c pcap.c tap.c tcp.c tcp_splice.c udp.c util.c
+	pasta.c pcap.c port_fwd.c tap.c tcp.c tcp_splice.c udp.c util.c
 QRAP_SRCS = qrap.c
 SRCS = $(PASST_SRCS) $(QRAP_SRCS)
 
diff --git a/conf.c b/conf.c
index 4d37af1..d3e6eb2 100644
--- a/conf.c
+++ b/conf.c
@@ -44,72 +44,6 @@
 #include "isolation.h"
 #include "log.h"
 
-/**
- * get_bound_ports() - Get maps of ports with bound sockets
- * @c:		Execution context
- * @ns:		If set, set bitmaps for ports to tap/ns -- to init otherwise
- * @proto:	Protocol number (IPPROTO_TCP or IPPROTO_UDP)
- */
-void get_bound_ports(struct ctx *c, int ns, uint8_t proto)
-{
-	uint8_t *udp_map, *udp_excl, *tcp_map, *tcp_excl;
-
-	if (ns) {
-		udp_map = c->udp.fwd_in.f.map;
-		udp_excl = c->udp.fwd_out.f.map;
-		tcp_map = c->tcp.fwd_in.map;
-		tcp_excl = c->tcp.fwd_out.map;
-	} else {
-		udp_map = c->udp.fwd_out.f.map;
-		udp_excl = c->udp.fwd_in.f.map;
-		tcp_map = c->tcp.fwd_out.map;
-		tcp_excl = c->tcp.fwd_in.map;
-	}
-
-	if (proto == IPPROTO_UDP) {
-		memset(udp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(c, IPPROTO_UDP, V4, ns, udp_map, udp_excl);
-		procfs_scan_listen(c, IPPROTO_UDP, V6, ns, udp_map, udp_excl);
-
-		procfs_scan_listen(c, IPPROTO_TCP, V4, ns, udp_map, udp_excl);
-		procfs_scan_listen(c, IPPROTO_TCP, V6, ns, udp_map, udp_excl);
-	} else if (proto == IPPROTO_TCP) {
-		memset(tcp_map, 0, PORT_BITMAP_SIZE);
-		procfs_scan_listen(c, IPPROTO_TCP, V4, ns, tcp_map, tcp_excl);
-		procfs_scan_listen(c, IPPROTO_TCP, V6, ns, tcp_map, tcp_excl);
-	}
-}
-
-/**
- * struct get_bound_ports_ns_arg - Arguments for get_bound_ports_ns()
- * @c:		Execution context
- * @proto:	Protocol number (IPPROTO_TCP or IPPROTO_UDP)
- */
-struct get_bound_ports_ns_arg {
-	struct ctx *c;
-	uint8_t proto;
-};
-
-/**
- * get_bound_ports_ns() - Get maps of ports in namespace with bound sockets
- * @arg:	See struct get_bound_ports_ns_arg
- *
- * Return: 0
- */
-static int get_bound_ports_ns(void *arg)
-{
-	struct get_bound_ports_ns_arg *a = (struct get_bound_ports_ns_arg *)arg;
-	struct ctx *c = a->c;
-
-	if (!c->pasta_netns_fd)
-		return 0;
-
-	ns_enter(c);
-	get_bound_ports(c, 1, a->proto);
-
-	return 0;
-}
-
 /**
  * next_chunk - Return the next piece of a string delimited by a character
  * @s:		String to search
@@ -1235,7 +1169,6 @@ void conf(struct ctx *c, int argc, char **argv)
 		{"no-copy-addrs", no_argument,		NULL,		19 },
 		{ 0 },
 	};
-	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
 	char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 };
 	bool copy_addrs_opt = false, copy_routes_opt = false;
 	enum port_fwd_mode fwd_default = FWD_NONE;
@@ -1814,23 +1747,7 @@ void conf(struct ctx *c, int argc, char **argv)
 	if (!c->udp.fwd_out.f.mode)
 		c->udp.fwd_out.f.mode = fwd_default;
 
-	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;
-	c->proc_net_udp[V4][0] = c->proc_net_udp[V4][1] = -1;
-	c->proc_net_udp[V6][0] = c->proc_net_udp[V6][1] = -1;
-
-	if (c->tcp.fwd_in.mode == FWD_AUTO) {
-		ns_ports_arg.proto = IPPROTO_TCP;
-		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
-	}
-	if (c->udp.fwd_in.f.mode == FWD_AUTO) {
-		ns_ports_arg.proto = IPPROTO_UDP;
-		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
-	}
-	if (c->tcp.fwd_out.mode == FWD_AUTO)
-		get_bound_ports(c, 0, IPPROTO_TCP);
-	if (c->udp.fwd_out.f.mode == FWD_AUTO)
-		get_bound_ports(c, 0, IPPROTO_UDP);
+	port_fwd_init(c);
 
 	if (!c->quiet)
 		conf_print(c);
diff --git a/conf.h b/conf.h
index ce7b8c5..9d2143d 100644
--- a/conf.h
+++ b/conf.h
@@ -7,6 +7,5 @@
 #define CONF_H
 
 void conf(struct ctx *c, int argc, char **argv);
-void get_bound_ports(struct ctx *c, int ns, uint8_t proto);
 
 #endif /* CONF_H */
diff --git a/port_fwd.c b/port_fwd.c
new file mode 100644
index 0000000..136a450
--- /dev/null
+++ b/port_fwd.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ *  for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ *  for network namespace/tap device mode
+ *
+ * port_fwd.c - Port forwarding helpers
+ *
+ * Copyright Red Hat
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+
+#include "util.h"
+#include "port_fwd.h"
+#include "passt.h"
+#include "lineread.h"
+
+/**
+ * procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
+ * @proto:	IPPROTO_TCP or IPPROTO_UDP
+ * @ip_version:	IP version, V4 or V6
+ * @ns:		Use saved file descriptors for namespace if set
+ * @map:	Bitmap where numbers of ports in listening state will be set
+ * @exclude:	Bitmap of ports to exclude from setting (and clear)
+ *
+ * #syscalls:pasta lseek
+ * #syscalls:pasta ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
+ */
+static void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version,
+			       int ns, uint8_t *map, const uint8_t *exclude)
+{
+	char *path, *line;
+	struct lineread lr;
+	unsigned long port;
+	unsigned int state;
+	int *fd;
+
+	if (proto == IPPROTO_TCP) {
+		fd = &c->proc_net_tcp[ip_version][ns];
+		if (ip_version == V4)
+			path = "/proc/net/tcp";
+		else
+			path = "/proc/net/tcp6";
+	} else {
+		fd = &c->proc_net_udp[ip_version][ns];
+		if (ip_version == V4)
+			path = "/proc/net/udp";
+		else
+			path = "/proc/net/udp6";
+	}
+
+	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) {
+		return;
+	}
+
+	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 */
+		if (sscanf(line, "%*u: %*x:%lx %*x:%*x %x", &port, &state) != 2)
+			continue;
+
+		/* See enum in kernel's include/net/tcp_states.h */
+		if ((proto == IPPROTO_TCP && state != 0x0a) ||
+		    (proto == IPPROTO_UDP && state != 0x07))
+			continue;
+
+		if (bitmap_isset(exclude, port))
+			bitmap_clear(map, port);
+		else
+			bitmap_set(map, port);
+	}
+}
+
+/**
+ * get_bound_ports() - Get maps of ports with bound sockets
+ * @c:		Execution context
+ * @ns:		If set, set bitmaps for ports to tap/ns -- to init otherwise
+ * @proto:	Protocol number (IPPROTO_TCP or IPPROTO_UDP)
+ */
+void get_bound_ports(struct ctx *c, int ns, uint8_t proto)
+{
+	uint8_t *udp_map, *udp_excl, *tcp_map, *tcp_excl;
+
+	if (ns) {
+		udp_map = c->udp.fwd_in.f.map;
+		udp_excl = c->udp.fwd_out.f.map;
+		tcp_map = c->tcp.fwd_in.map;
+		tcp_excl = c->tcp.fwd_out.map;
+	} else {
+		udp_map = c->udp.fwd_out.f.map;
+		udp_excl = c->udp.fwd_in.f.map;
+		tcp_map = c->tcp.fwd_out.map;
+		tcp_excl = c->tcp.fwd_in.map;
+	}
+
+	if (proto == IPPROTO_UDP) {
+		memset(udp_map, 0, PORT_BITMAP_SIZE);
+		procfs_scan_listen(c, IPPROTO_UDP, V4, ns, udp_map, udp_excl);
+		procfs_scan_listen(c, IPPROTO_UDP, V6, ns, udp_map, udp_excl);
+
+		procfs_scan_listen(c, IPPROTO_TCP, V4, ns, udp_map, udp_excl);
+		procfs_scan_listen(c, IPPROTO_TCP, V6, ns, udp_map, udp_excl);
+	} else if (proto == IPPROTO_TCP) {
+		memset(tcp_map, 0, PORT_BITMAP_SIZE);
+		procfs_scan_listen(c, IPPROTO_TCP, V4, ns, tcp_map, tcp_excl);
+		procfs_scan_listen(c, IPPROTO_TCP, V6, ns, tcp_map, tcp_excl);
+	}
+}
+
+/**
+ * struct get_bound_ports_ns_arg - Arguments for get_bound_ports_ns()
+ * @c:		Execution context
+ * @proto:	Protocol number (IPPROTO_TCP or IPPROTO_UDP)
+ */
+struct get_bound_ports_ns_arg {
+	struct ctx *c;
+	uint8_t proto;
+};
+
+/**
+ * get_bound_ports_ns() - Get maps of ports in namespace with bound sockets
+ * @arg:	See struct get_bound_ports_ns_arg
+ *
+ * Return: 0
+ */
+static int get_bound_ports_ns(void *arg)
+{
+	struct get_bound_ports_ns_arg *a = (struct get_bound_ports_ns_arg *)arg;
+	struct ctx *c = a->c;
+
+	if (!c->pasta_netns_fd)
+		return 0;
+
+	ns_enter(c);
+	get_bound_ports(c, 1, a->proto);
+
+	return 0;
+}
+
+/**
+ * port_fwd_init() - Initial setup for port forwarding
+ * @c:		Execution context
+ */
+void port_fwd_init(struct ctx *c)
+{
+	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
+
+	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;
+	c->proc_net_udp[V4][0] = c->proc_net_udp[V4][1] = -1;
+	c->proc_net_udp[V6][0] = c->proc_net_udp[V6][1] = -1;
+
+	if (c->tcp.fwd_in.mode == FWD_AUTO) {
+		ns_ports_arg.proto = IPPROTO_TCP;
+		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
+	}
+	if (c->udp.fwd_in.f.mode == FWD_AUTO) {
+		ns_ports_arg.proto = IPPROTO_UDP;
+		NS_CALL(get_bound_ports_ns, &ns_ports_arg);
+	}
+	if (c->tcp.fwd_out.mode == FWD_AUTO)
+		get_bound_ports(c, 0, IPPROTO_TCP);
+	if (c->udp.fwd_out.f.mode == FWD_AUTO)
+		get_bound_ports(c, 0, IPPROTO_UDP);
+}
diff --git a/port_fwd.h b/port_fwd.h
index 6ed3f14..ad8ed1f 100644
--- a/port_fwd.h
+++ b/port_fwd.h
@@ -31,4 +31,7 @@ struct port_fwd {
 	in_port_t delta[NUM_PORTS];
 };
 
+void get_bound_ports(struct ctx *c, int ns, uint8_t proto);
+void port_fwd_init(struct ctx *c);
+
 #endif /* PORT_FWD_H */
diff --git a/tcp.c b/tcp.c
index a9a6f2a..a2418ae 100644
--- a/tcp.c
+++ b/tcp.c
@@ -298,7 +298,6 @@
 #include "tap.h"
 #include "siphash.h"
 #include "pcap.h"
-#include "conf.h"
 #include "tcp_splice.h"
 #include "log.h"
 #include "inany.h"
diff --git a/util.c b/util.c
index 1f35382..a8f3b35 100644
--- a/util.c
+++ b/util.c
@@ -28,7 +28,6 @@
 #include "util.h"
 #include "passt.h"
 #include "packet.h"
-#include "lineread.h"
 #include "log.h"
 
 #define IPV6_NH_OPT(nh)							\
@@ -326,69 +325,7 @@ int bitmap_isset(const uint8_t *map, int bit)
 	return !!(*word & BITMAP_BIT(bit));
 }
 
-/**
- * procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
- * @proto:	IPPROTO_TCP or IPPROTO_UDP
- * @ip_version:	IP version, V4 or V6
- * @ns:		Use saved file descriptors for namespace if set
- * @map:	Bitmap where numbers of ports in listening state will be set
- * @exclude:	Bitmap of ports to exclude from setting (and clear)
- *
- * #syscalls:pasta lseek
- * #syscalls:pasta ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
- */
-void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version, int ns,
-			uint8_t *map, const uint8_t *exclude)
-{
-	char *path, *line;
-	struct lineread lr;
-	unsigned long port;
-	unsigned int state;
-	int *fd;
-
-	if (proto == IPPROTO_TCP) {
-		fd = &c->proc_net_tcp[ip_version][ns];
-		if (ip_version == V4)
-			path = "/proc/net/tcp";
-		else
-			path = "/proc/net/tcp6";
-	} else {
-		fd = &c->proc_net_udp[ip_version][ns];
-		if (ip_version == V4)
-			path = "/proc/net/udp";
-		else
-			path = "/proc/net/udp6";
-	}
-
-	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) {
-		return;
-	}
-
-	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 */
-		if (sscanf(line, "%*u: %*x:%lx %*x:%*x %x", &port, &state) != 2)
-			continue;
-
-		/* See enum in kernel's include/net/tcp_states.h */
-		if ((proto == IPPROTO_TCP && state != 0x0a) ||
-		    (proto == IPPROTO_UDP && state != 0x07))
-			continue;
-
-		if (bitmap_isset(exclude, port))
-			bitmap_clear(map, port);
-		else
-			bitmap_set(map, port);
-	}
-}
-
-/**
+/*
  * ns_enter() - Enter configured user (unless already joined) and network ns
  * @c:		Execution context
  *
diff --git a/util.h b/util.h
index 60d6872..9effc54 100644
--- a/util.h
+++ b/util.h
@@ -217,8 +217,6 @@ void bitmap_set(uint8_t *map, int bit);
 void bitmap_clear(uint8_t *map, int bit);
 int bitmap_isset(const uint8_t *map, int bit);
 char *line_read(char *buf, size_t len, int fd);
-void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version, int ns,
-			uint8_t *map, const uint8_t *exclude);
 void ns_enter(const struct ctx *c);
 bool ns_is_init(void);
 void write_pidfile(int fd, pid_t pid);
-- 
@@ -217,8 +217,6 @@ void bitmap_set(uint8_t *map, int bit);
 void bitmap_clear(uint8_t *map, int bit);
 int bitmap_isset(const uint8_t *map, int bit);
 char *line_read(char *buf, size_t len, int fd);
-void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version, int ns,
-			uint8_t *map, const uint8_t *exclude);
 void ns_enter(const struct ctx *c);
 bool ns_is_init(void);
 void write_pidfile(int fd, pid_t pid);
-- 
2.41.0


  parent reply	other threads:[~2023-10-05  3:44 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-05  3:44 [PATCH 0/9] Clean ups to automatic port forwarding David Gibson
2023-10-05  3:44 ` [PATCH 1/9] conf: Cleaner initialisation of default forwarding modes David Gibson
2023-10-05  3:44 ` David Gibson [this message]
2023-10-05  3:44 ` [PATCH 3/9] port_fwd: Better parameterise procfs_scan_listen() David Gibson
2023-11-02 18:07   ` Stefano Brivio
2023-11-03  0:16     ` David Gibson
2023-10-05  3:44 ` [PATCH 4/9] util: Add open_in_ns() helper David Gibson
2023-11-02 18:07   ` Stefano Brivio
2023-11-03  0:20     ` David Gibson
2023-10-05  3:44 ` [PATCH 5/9] port_fwd: Pre-open /proc/net/* files rather than on-demand David Gibson
2023-10-05  3:44 ` [PATCH 6/9] port_fwd: Don't NS_CALL get_bound_ports() David Gibson
2023-10-05  3:44 ` [PATCH 7/9] port_fwd: Split TCP and UDP cases for get_bound_ports() David Gibson
2023-10-05  3:44 ` [PATCH 8/9] port_fwd: Move port scanning /proc fds into struct port_fwd David Gibson
2023-10-05  3:44 ` [PATCH 9/9] port_fwd: Simplify get_bound_ports_*() to port_fwd_scan_*() David Gibson

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=20231005034445.2015303-3-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).