From e90f2770ae44de238a227f884e806637a2b80403 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 3 Nov 2023 13:22:56 +1100 Subject: [PATCH] port_fwd: Move automatic port forwarding code to port_fwd.[ch] 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 Signed-off-by: Stefano Brivio --- 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 + * Author: David Gibson + */ + +#include +#include +#include +#include + +#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 945023c..c6cc020 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);