bae676a44e
lseek() is declared in unistd.h, and stdio.h provides sscanf(). Include these two headers in port_fwd.c. SIGCHLD, even if used exclusively for clone(), is defined in signal.h: add the include to util.h, as NS_CALL needs it. Reported-by: lemmi <lemmi@nerd2nerd.org> Link: https://github.com/void-linux/void-packages/actions/runs/6999782606/job/19039526604#step:7:57 Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
155 lines
4.8 KiB
C
155 lines
4.8 KiB
C
// 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 <unistd.h>
|
|
#include <stdio.h>
|
|
|
|
#include "util.h"
|
|
#include "port_fwd.h"
|
|
#include "passt.h"
|
|
#include "lineread.h"
|
|
|
|
/* See enum in kernel's include/net/tcp_states.h */
|
|
#define UDP_LISTEN 0x07
|
|
#define TCP_LISTEN 0x0a
|
|
|
|
/**
|
|
* procfs_scan_listen() - Set bits for listening TCP or UDP sockets from procfs
|
|
* @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)
|
|
*
|
|
* #syscalls:pasta lseek
|
|
* #syscalls:pasta ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
|
|
*/
|
|
static void procfs_scan_listen(int fd, unsigned int lstate,
|
|
uint8_t *map, const uint8_t *exclude)
|
|
{
|
|
struct lineread lr;
|
|
unsigned long port;
|
|
unsigned int state;
|
|
char *line;
|
|
|
|
if (fd < 0)
|
|
return;
|
|
|
|
if (lseek(fd, 0, SEEK_SET)) {
|
|
warn("lseek() failed on /proc/net file: %s", strerror(errno));
|
|
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;
|
|
|
|
if (state != lstate)
|
|
continue;
|
|
|
|
if (bitmap_isset(exclude, port))
|
|
bitmap_clear(map, port);
|
|
else
|
|
bitmap_set(map, port);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* port_fwd_scan_tcp() - Scan /proc to update TCP forwarding map
|
|
* @fwd: Forwarding information to update
|
|
* @rev: Forwarding information for the reverse direction
|
|
*/
|
|
void port_fwd_scan_tcp(struct port_fwd *fwd, const struct port_fwd *rev)
|
|
{
|
|
memset(fwd->map, 0, PORT_BITMAP_SIZE);
|
|
procfs_scan_listen(fwd->scan4, TCP_LISTEN, fwd->map, rev->map);
|
|
procfs_scan_listen(fwd->scan6, TCP_LISTEN, fwd->map, rev->map);
|
|
}
|
|
|
|
/**
|
|
* port_fwd_scan_tcp() - Scan /proc to update TCP forwarding map
|
|
* @fwd: Forwarding information to update
|
|
* @rev: Forwarding information for the reverse direction
|
|
* @tcp_fwd: Corresponding TCP forwarding information
|
|
* @tcp_rev: TCP forwarding information for the reverse direction
|
|
*/
|
|
void port_fwd_scan_udp(struct port_fwd *fwd, const struct port_fwd *rev,
|
|
const struct port_fwd *tcp_fwd,
|
|
const struct port_fwd *tcp_rev)
|
|
{
|
|
uint8_t exclude[PORT_BITMAP_SIZE];
|
|
|
|
bitmap_or(exclude, PORT_BITMAP_SIZE, rev->map, tcp_rev->map);
|
|
|
|
memset(fwd->map, 0, PORT_BITMAP_SIZE);
|
|
procfs_scan_listen(fwd->scan4, UDP_LISTEN, fwd->map, exclude);
|
|
procfs_scan_listen(fwd->scan6, UDP_LISTEN, fwd->map, exclude);
|
|
|
|
/* Also forward UDP ports with the same numbers as bound TCP ports.
|
|
* This is useful for a handful of protocols (e.g. iperf3) where a TCP
|
|
* control port is used to set up transfers on a corresponding UDP
|
|
* port.
|
|
*
|
|
* This means we need to skip numbers of TCP ports bound on the other
|
|
* side, too. Otherwise, we would detect corresponding UDP ports as
|
|
* bound and try to forward them from the opposite side, but it's
|
|
* already us handling them.
|
|
*/
|
|
procfs_scan_listen(tcp_fwd->scan4, TCP_LISTEN, fwd->map, exclude);
|
|
procfs_scan_listen(tcp_fwd->scan6, TCP_LISTEN, fwd->map, exclude);
|
|
}
|
|
|
|
/**
|
|
* port_fwd_init() - Initial setup for port forwarding
|
|
* @c: Execution context
|
|
*/
|
|
void port_fwd_init(struct ctx *c)
|
|
{
|
|
const int flags = O_RDONLY | O_CLOEXEC;
|
|
|
|
c->tcp.fwd_in.scan4 = c->tcp.fwd_in.scan6 = -1;
|
|
c->tcp.fwd_out.scan4 = c->tcp.fwd_out.scan6 = -1;
|
|
c->udp.fwd_in.f.scan4 = c->udp.fwd_in.f.scan6 = -1;
|
|
c->udp.fwd_out.f.scan4 = c->udp.fwd_out.f.scan6 = -1;
|
|
|
|
if (c->tcp.fwd_in.mode == FWD_AUTO) {
|
|
c->tcp.fwd_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
|
|
c->tcp.fwd_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
|
|
port_fwd_scan_tcp(&c->tcp.fwd_in, &c->tcp.fwd_out);
|
|
}
|
|
if (c->udp.fwd_in.f.mode == FWD_AUTO) {
|
|
c->udp.fwd_in.f.scan4 = open_in_ns(c, "/proc/net/udp", flags);
|
|
c->udp.fwd_in.f.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
|
|
port_fwd_scan_udp(&c->udp.fwd_in.f, &c->udp.fwd_out.f,
|
|
&c->tcp.fwd_in, &c->tcp.fwd_out);
|
|
}
|
|
if (c->tcp.fwd_out.mode == FWD_AUTO) {
|
|
c->tcp.fwd_out.scan4 = open("/proc/net/tcp", flags);
|
|
c->tcp.fwd_out.scan6 = open("/proc/net/tcp6", flags);
|
|
port_fwd_scan_tcp(&c->tcp.fwd_out, &c->tcp.fwd_in);
|
|
}
|
|
if (c->udp.fwd_out.f.mode == FWD_AUTO) {
|
|
c->udp.fwd_out.f.scan4 = open("/proc/net/udp", flags);
|
|
c->udp.fwd_out.f.scan6 = open("/proc/net/udp6", flags);
|
|
port_fwd_scan_udp(&c->udp.fwd_out.f, &c->udp.fwd_in.f,
|
|
&c->tcp.fwd_out, &c->tcp.fwd_in);
|
|
}
|
|
}
|