mirror of
https://passt.top/passt
synced 2025-05-04 09:20:45 +02:00
Introduce passt-repair
A privileged helper to set/clear TCP_REPAIR on sockets on behalf of passt. Not used yet. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
e894d9ae82
commit
8c24301462
10 changed files with 342 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
/passt.avx2
|
||||
/pasta
|
||||
/pasta.avx2
|
||||
/passt-repair
|
||||
/qrap
|
||||
/pasta.1
|
||||
/seccomp.h
|
||||
|
|
17
Makefile
17
Makefile
|
@ -42,9 +42,10 @@ PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c flow.c fwd.c \
|
|||
tcp_buf.c tcp_splice.c tcp_vu.c udp.c udp_flow.c udp_vu.c util.c \
|
||||
vhost_user.c virtio.c vu_common.c
|
||||
QRAP_SRCS = qrap.c
|
||||
SRCS = $(PASST_SRCS) $(QRAP_SRCS)
|
||||
PASST_REPAIR_SRCS = passt-repair.c
|
||||
SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS)
|
||||
|
||||
MANPAGES = passt.1 pasta.1 qrap.1
|
||||
MANPAGES = passt.1 pasta.1 qrap.1 passt-repair.1
|
||||
|
||||
PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h flow.h fwd.h \
|
||||
flow_table.h icmp.h icmp_flow.h inany.h iov.h ip.h isolation.h \
|
||||
|
@ -72,9 +73,9 @@ mandir ?= $(datarootdir)/man
|
|||
man1dir ?= $(mandir)/man1
|
||||
|
||||
ifeq ($(TARGET_ARCH),x86_64)
|
||||
BIN := passt passt.avx2 pasta pasta.avx2 qrap
|
||||
BIN := passt passt.avx2 pasta pasta.avx2 qrap passt-repair
|
||||
else
|
||||
BIN := passt pasta qrap
|
||||
BIN := passt pasta qrap passt-repair
|
||||
endif
|
||||
|
||||
all: $(BIN) $(MANPAGES) docs
|
||||
|
@ -83,7 +84,10 @@ static: FLAGS += -static -DGLIBC_NO_STATIC_NSS
|
|||
static: clean all
|
||||
|
||||
seccomp.h: seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
|
||||
@ EXTRA_SYSCALLS="$(EXTRA_SYSCALLS)" ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
|
||||
@ EXTRA_SYSCALLS="$(EXTRA_SYSCALLS)" ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh seccomp.h $(PASST_SRCS) $(PASST_HEADERS)
|
||||
|
||||
seccomp_repair.h: seccomp.sh $(PASST_REPAIR_SRCS)
|
||||
@ ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh seccomp_repair.h $(PASST_REPAIR_SRCS)
|
||||
|
||||
passt: $(PASST_SRCS) $(HEADERS)
|
||||
$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PASST_SRCS) -o passt $(LDFLAGS)
|
||||
|
@ -101,6 +105,9 @@ pasta.avx2 pasta.1 pasta: pasta%: passt%
|
|||
qrap: $(QRAP_SRCS) passt.h
|
||||
$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) -DARCH=\"$(TARGET_ARCH)\" $(QRAP_SRCS) -o qrap $(LDFLAGS)
|
||||
|
||||
passt-repair: $(PASST_REPAIR_SRCS) seccomp_repair.h
|
||||
$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PASST_REPAIR_SRCS) -o passt-repair $(LDFLAGS)
|
||||
|
||||
valgrind: EXTRA_SYSCALLS += rt_sigprocmask rt_sigtimedwait rt_sigaction \
|
||||
rt_sigreturn getpid gettid kill clock_gettime mmap \
|
||||
mmap2 munmap open unlink gettimeofday futex statx \
|
||||
|
|
29
contrib/apparmor/usr.bin.passt-repair
Normal file
29
contrib/apparmor/usr.bin.passt-repair
Normal file
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# contrib/apparmor/usr.bin.passt-repair - AppArmor profile for passt-repair(1)
|
||||
#
|
||||
# Copyright (c) 2025 Red Hat GmbH
|
||||
# Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
abi <abi/3.0>,
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
profile passt-repair /usr/bin/passt-repair {
|
||||
#include <abstractions/base>
|
||||
/** rw, # passt's ".repair" socket might be anywhere
|
||||
unix (connect, receive, send) type=stream,
|
||||
|
||||
capability dac_override, # connect to passt's socket as root
|
||||
capability net_admin, # currently needed for TCP_REPAIR socket option
|
||||
capability net_raw, # what TCP_REPAIR should require instead
|
||||
|
||||
network unix stream, # connect and use UNIX domain socket
|
||||
network inet stream, # use TCP sockets
|
||||
}
|
|
@ -108,9 +108,11 @@ fi
|
|||
%{_bindir}/passt
|
||||
%{_bindir}/pasta
|
||||
%{_bindir}/qrap
|
||||
%{_bindir}/passt-repair
|
||||
%{_mandir}/man1/passt.1*
|
||||
%{_mandir}/man1/pasta.1*
|
||||
%{_mandir}/man1/qrap.1*
|
||||
%{_mandir}/man1/passt-repair.1*
|
||||
%ifarch x86_64
|
||||
%{_bindir}/passt.avx2
|
||||
%{_mandir}/man1/passt.avx2.1*
|
||||
|
|
11
contrib/selinux/passt-repair.fc
Normal file
11
contrib/selinux/passt-repair.fc
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# PASST - Plug A Simple Socket Transport
|
||||
# for qemu/UNIX domain socket mode
|
||||
#
|
||||
# contrib/selinux/passt-repair.fc - SELinux: File Context for passt-repair
|
||||
#
|
||||
# Copyright (c) 2025 Red Hat GmbH
|
||||
# Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
/usr/bin/passt-repair system_u:object_r:passt_repair_exec_t:s0
|
58
contrib/selinux/passt-repair.te
Normal file
58
contrib/selinux/passt-repair.te
Normal file
|
@ -0,0 +1,58 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# PASST - Plug A Simple Socket Transport
|
||||
# for qemu/UNIX domain socket mode
|
||||
#
|
||||
# contrib/selinux/passt-repair.te - SELinux: Type Enforcement for passt-repair
|
||||
#
|
||||
# Copyright (c) 2025 Red Hat GmbH
|
||||
# Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
policy_module(passt-repair, 0.1)
|
||||
|
||||
require {
|
||||
type unconfined_t;
|
||||
type passt_t;
|
||||
role unconfined_r;
|
||||
class process transition;
|
||||
|
||||
class file { read execute execute_no_trans entrypoint open map };
|
||||
class capability { dac_override net_admin net_raw };
|
||||
class chr_file { append open getattr read write ioctl };
|
||||
|
||||
class unix_stream_socket { create connect sendto };
|
||||
class sock_file { read write };
|
||||
|
||||
class tcp_socket { read setopt write };
|
||||
|
||||
type console_device_t;
|
||||
type user_devpts_t;
|
||||
type user_tmp_t;
|
||||
}
|
||||
|
||||
type passt_repair_t;
|
||||
domain_type(passt_repair_t);
|
||||
type passt_repair_exec_t;
|
||||
files_type(passt_repair_exec_t);
|
||||
|
||||
role unconfined_r types passt_repair_t;
|
||||
|
||||
allow passt_repair_t passt_repair_exec_t:file { read execute execute_no_trans entrypoint open map };
|
||||
type_transition unconfined_t passt_repair_exec_t:process passt_repair_t;
|
||||
allow unconfined_t passt_repair_t:process transition;
|
||||
|
||||
allow passt_repair_t self:capability { dac_override net_admin net_raw };
|
||||
|
||||
allow passt_repair_t console_device_t:chr_file { append open getattr read write ioctl };
|
||||
allow passt_repair_t user_devpts_t:chr_file { append open getattr read write ioctl };
|
||||
|
||||
allow passt_repair_t unconfined_t:unix_stream_socket { connectto read write };
|
||||
allow passt_repair_t passt_t:unix_stream_socket { connectto read write };
|
||||
allow passt_repair_t user_tmp_t:unix_stream_socket { connectto read write };
|
||||
|
||||
allow passt_repair_t unconfined_t:sock_file { read write };
|
||||
allow passt_repair_t passt_t:sock_file { read write };
|
||||
allow passt_repair_t user_tmp_t:sock_file { read write };
|
||||
|
||||
allow passt_repair_t unconfined_t:tcp_socket { read setopt write };
|
||||
allow passt_repair_t passt_t:tcp_socket { read setopt write };
|
|
@ -56,6 +56,7 @@ cd ..
|
|||
make pkgs
|
||||
scp passt passt.avx2 passt.1 qrap qrap.1 "${USER_HOST}:${BIN}"
|
||||
scp pasta pasta.avx2 pasta.1 "${USER_HOST}:${BIN}"
|
||||
scp passt-repair passt-repair.1 "${USER_HOST}:${BIN}"
|
||||
|
||||
ssh "${USER_HOST}" "rm -f ${BIN}/*.deb"
|
||||
ssh "${USER_HOST}" "rm -f ${BIN}/*.rpm"
|
||||
|
|
70
passt-repair.1
Normal file
70
passt-repair.1
Normal file
|
@ -0,0 +1,70 @@
|
|||
.\" SPDX-License-Identifier: GPL-2.0-or-later
|
||||
.\" Copyright (c) 2025 Red Hat GmbH
|
||||
.\" Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
.TH passt-repair 1
|
||||
|
||||
.SH NAME
|
||||
.B passt-repair
|
||||
\- Helper setting TCP_REPAIR socket options for \fBpasst\fR(1)
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B passt-repair
|
||||
\fIPATH\fR
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
.B passt-repair
|
||||
is a privileged helper setting and clearing repair mode on TCP sockets on behalf
|
||||
of \fBpasst\fR(1), as instructed via single-byte commands over a UNIX domain
|
||||
socket, specified by \fIPATH\fR.
|
||||
|
||||
It can be used to migrate TCP connections between guests without granting
|
||||
additional capabilities to \fBpasst\fR(1) itself: to migrate TCP connections,
|
||||
\fBpasst\fR(1) leverages repair mode, which needs the \fBCAP_NET_ADMIN\fR
|
||||
capability (see \fBcapabilities\fR(7)) to be set or cleared.
|
||||
|
||||
.SH PROTOCOL
|
||||
|
||||
\fBpasst-repair\fR(1) connects to \fBpasst\fR(1) using the socket specified via
|
||||
\fI--repair-path\fR option in \fBpasst\fR(1) itself. By default, the name is the
|
||||
same as the UNIX domain socket used for guest communication, suffixed by
|
||||
\fI.repair\fR.
|
||||
|
||||
The messages consist of one 8-bit signed integer that can be \fITCP_REPAIR_ON\fR
|
||||
(1), \fITCP_REPAIR_OFF\fR (2), or \fITCP_REPAIR_OFF_WP\fR (-1), as defined by
|
||||
the Linux kernel user API, and one to SCM_MAX_FD (253) sockets as SCM_RIGHTS
|
||||
(see \fBunix\fR(7)) ancillary message, sent by the server, \fBpasst\fR(1).
|
||||
|
||||
The client, \fBpasst-repair\fR(1), replies with the same byte (and no ancillary
|
||||
message) to indicate success, and closes the connection on failure.
|
||||
|
||||
The server closes the connection on error or completion.
|
||||
|
||||
.SH NOTES
|
||||
|
||||
\fBpasst-repair\fR(1) can be granted the \fBCAP_NET_ADMIN\fR capability
|
||||
(preferred, as it limits privileges to the strictly necessary ones), or it can
|
||||
be run as root.
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Stefano Brivio <sbrivio@redhat.com>.
|
||||
|
||||
.SH REPORTING BUGS
|
||||
|
||||
Please report issues on the bug tracker at https://bugs.passt.top/, or
|
||||
send a message to the passt-user@passt.top mailing list, see
|
||||
https://lists.passt.top/.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright (c) 2025 Red Hat GmbH.
|
||||
|
||||
\fBpasst-repair\fR is free software: you can redistribute them and/or modify
|
||||
them under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 2 of the License, or (at your option) any
|
||||
later version.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
\fBpasst\fR(1), \fBqemu\fR(1), \fBcapabilities\fR(7), \fBunix\fR(7).
|
154
passt-repair.c
Normal file
154
passt-repair.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
// 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
|
||||
*
|
||||
* passt-repair.c - Privileged helper to set/clear TCP_REPAIR on sockets
|
||||
*
|
||||
* Copyright (c) 2025 Red Hat GmbH
|
||||
* Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
*
|
||||
* Connect to passt via UNIX domain socket, receive sockets via SCM_RIGHTS along
|
||||
* with byte commands mapping to TCP_REPAIR values, and switch repair mode on or
|
||||
* off. Reply by echoing the command. Exit on EOF.
|
||||
*/
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <linux/audit.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h>
|
||||
|
||||
#include "seccomp_repair.h"
|
||||
|
||||
#define SCM_MAX_FD 253 /* From Linux kernel (include/net/scm.h), not in UAPI */
|
||||
|
||||
/**
|
||||
* main() - Entry point and whole program with loop
|
||||
* @argc: Argument count, must be 2
|
||||
* @argv: Argument: path of UNIX domain socket to connect to
|
||||
*
|
||||
* Return: 0 on success (EOF), 1 on error, 2 on usage error
|
||||
*
|
||||
* #syscalls:repair connect setsockopt write exit_group
|
||||
* #syscalls:repair socket s390x:socketcall i686:socketcall
|
||||
* #syscalls:repair recvfrom recvmsg arm:recv ppc64le:recv
|
||||
* #syscalls:repair sendto sendmsg arm:send ppc64le:send
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char buf[CMSG_SPACE(sizeof(int) * SCM_MAX_FD)]
|
||||
__attribute__ ((aligned(__alignof__(struct cmsghdr))));
|
||||
struct sockaddr_un a = { AF_UNIX, "" };
|
||||
int fds[SCM_MAX_FD], s, ret, i, n;
|
||||
struct sock_fprog prog;
|
||||
int8_t cmd = INT8_MAX;
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
|
||||
prctl(PR_SET_DUMPABLE, 0);
|
||||
|
||||
prog.len = (unsigned short)sizeof(filter_repair) /
|
||||
sizeof(filter_repair[0]);
|
||||
prog.filter = filter_repair;
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
|
||||
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
|
||||
fprintf(stderr, "Failed to apply seccomp filter");
|
||||
return 1;
|
||||
}
|
||||
|
||||
iov = (struct iovec){ &cmd, sizeof(cmd) };
|
||||
msg = (struct msghdr){ NULL, 0, &iov, 1, buf, sizeof(buf), 0 };
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s PATH\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
ret = snprintf(a.sun_path, sizeof(a.sun_path), "%s", argv[1]);
|
||||
if (ret <= 0 || ret >= (int)sizeof(a.sun_path)) {
|
||||
fprintf(stderr, "Invalid socket path: %s\n", argv[1]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
perror("Failed to create AF_UNIX socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (connect(s, (struct sockaddr *)&a, sizeof(a))) {
|
||||
fprintf(stderr, "Failed to connect to %s: %s\n", argv[1],
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
loop:
|
||||
ret = recvmsg(s, &msg, 0);
|
||||
if (ret < 0) {
|
||||
perror("Failed to receive message");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ret) /* Done */
|
||||
return 0;
|
||||
|
||||
if (!cmsg ||
|
||||
cmsg->cmsg_len < CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_len > CMSG_LEN(sizeof(int) * SCM_MAX_FD) ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr, "No/bad ancillary data from peer\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
n = cmsg->cmsg_len / CMSG_LEN(sizeof(int));
|
||||
memcpy(fds, CMSG_DATA(cmsg), sizeof(int) * n);
|
||||
|
||||
if (cmd != TCP_REPAIR_ON && cmd != TCP_REPAIR_OFF &&
|
||||
cmd != TCP_REPAIR_OFF_NO_WP) {
|
||||
fprintf(stderr, "Unsupported command 0x%04x\n", cmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
int o = cmd;
|
||||
|
||||
if (setsockopt(fds[i], SOL_TCP, TCP_REPAIR, &o, sizeof(o))) {
|
||||
fprintf(stderr,
|
||||
"Setting TCP_REPAIR to %i on socket %i: %s", o,
|
||||
fds[i], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Close _our_ copy */
|
||||
close(fds[i]);
|
||||
|
||||
/* Confirm setting by echoing the command back */
|
||||
if (send(s, &cmd, sizeof(cmd), 0) < 0) {
|
||||
fprintf(stderr, "Reply to command %i: %s\n",
|
||||
o, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
goto loop;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -14,8 +14,10 @@
|
|||
# Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
TMP="$(mktemp)"
|
||||
IN="$@"
|
||||
OUT="$(mktemp)"
|
||||
OUT_FINAL="${1}"
|
||||
shift
|
||||
IN="$@"
|
||||
|
||||
[ -z "${ARCH}" ] && ARCH="$(uname -m)"
|
||||
[ -z "${CC}" ] && CC="cc"
|
||||
|
@ -268,4 +270,4 @@ for __p in ${__profiles}; do
|
|||
gen_profile "${__p}" ${__calls}
|
||||
done
|
||||
|
||||
mv "${OUT}" seccomp.h
|
||||
mv "${OUT}" "${OUT_FINAL}"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue