Commit graph

89 commits

Author SHA1 Message Date
Stefano Brivio
1e49d194d0 passt, pasta: Introduce command-line options and port re-mapping
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-09-01 17:00:27 +02:00
Stefano Brivio
39ad062100 tcp: Introduce scatter-gather IO path from socket to tap
...similarly to what was done for UDP. Quick performance test with
32KiB buffers, host to VM:

$ iperf3 -c 192.0.2.2 -N
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  8.47 GBytes  7.27 Gbits/sec    0             sender
[  5]   0.00-10.00  sec  8.45 GBytes  7.26 Gbits/sec                  receiver

$ iperf3 -c 2a01:598:88ba:a056:271f:473a:c0d9:abc1
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  8.43 GBytes  7.24 Gbits/sec    0             sender
[  5]   0.00-10.00  sec  8.41 GBytes  7.22 Gbits/sec                  receiver

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-07-26 14:20:36 +02:00
Stefano Brivio
86b273150a tcp, udp: Allow binding ports in init namespace to both tap and loopback
Traffic with loopback source address will be forwarded to the direct
loopback connection in the namespace, and the tap interface is used
for the rest.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-07-26 14:10:29 +02:00
Stefano Brivio
49631a38a6 tcp, udp: Split IPv4 and IPv6 bound port sets
Allow to bind IPv4 and IPv6 ports to tap, namespace or init separately.

Port numbers of TCP ports that are bound in a namespace are also bound
for UDP for convenience (e.g. iperf3), and IPv4 ports are always bound
if the corresponding IPv6 port is bound (socket might not have the
IPV6_V6ONLY option set). This will also be configurable later.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-07-21 17:44:39 +02:00
Stefano Brivio
64a0ba3b27 udp: Introduce recvmmsg()/sendmmsg(), zero-copy path from socket
Packets are received directly onto pre-cooked, static buffers
for IPv4 (with partial checksum pre-calculation) and IPv6 frames,
with pre-filled Ethernet addresses and, partially, IP headers,
and sent out from the same buffers with sendmmsg(), for both
passt and pasta (non-local traffic only) modes.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-07-21 12:01:04 +02:00
Stefano Brivio
33482d5bf2 passt: Add PASTA mode, major rework
PASTA (Pack A Subtle Tap Abstraction) provides quasi-native host
connectivity to an otherwise disconnected, unprivileged network
and user namespace, similarly to slirp4netns. Given that the
implementation is largely overlapping with PASST, no separate binary
is built: 'pasta' (and 'passt4netns' for clarity) both link to
'passt', and the mode of operation is selected depending on how the
binary is invoked. Usage example:

	$ unshare -rUn
	# echo $$
	1871759

	$ ./pasta 1871759	# From another terminal

	# udhcpc -i pasta0 2>/dev/null
	# ping -c1 pasta.pizza
	PING pasta.pizza (64.190.62.111) 56(84) bytes of data.
	64 bytes from 64.190.62.111 (64.190.62.111): icmp_seq=1 ttl=255 time=34.6 ms

	--- pasta.pizza ping statistics ---
	1 packets transmitted, 1 received, 0% packet loss, time 0ms
	rtt min/avg/max/mdev = 34.575/34.575/34.575/0.000 ms
	# ping -c1 spaghetti.pizza
	PING spaghetti.pizza(2606:4700:3034::6815:147a (2606:4700:3034::6815:147a)) 56 data bytes
	64 bytes from 2606:4700:3034::6815:147a (2606:4700:3034::6815:147a): icmp_seq=1 ttl=255 time=29.0 ms

	--- spaghetti.pizza ping statistics ---
	1 packets transmitted, 1 received, 0% packet loss, time 0ms
	rtt min/avg/max/mdev = 28.967/28.967/28.967/0.000 ms

This entails a major rework, especially with regard to the storage of
tracked connections and to the semantics of epoll(7) references.

Indexing TCP and UDP bindings merely by socket proved to be
inflexible and unsuitable to handle different connection flows: pasta
also provides Layer-2 to Layer-2 socket mapping between init and a
separate namespace for local connections, using a pair of splice()
system calls for TCP, and a recvmmsg()/sendmmsg() pair for UDP local
bindings. For instance, building on the previous example:

	# ip link set dev lo up
	# iperf3 -s

	$ iperf3 -c ::1 -Z -w 32M -l 1024k -P2 | tail -n4
	[SUM]   0.00-10.00  sec  52.3 GBytes  44.9 Gbits/sec  283             sender
	[SUM]   0.00-10.43  sec  52.3 GBytes  43.1 Gbits/sec                  receiver

	iperf Done.

epoll(7) references now include a generic part in order to
demultiplex data to the relevant protocol handler, using 24
bits for the socket number, and an opaque portion reserved for
usage by the single protocol handlers, in order to track sockets
back to corresponding connections and bindings.

A number of fixes pertaining to TCP state machine and congestion
window handling are also included here.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-07-17 11:04:22 +02:00
Stefano Brivio
46b799c077 passt: When probing for an existing instance, also accept ENOENT on connect()
The most common case is actually that no other instance created a
socket with that name -- and that also means there is no other
instance.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-23 15:43:31 +02:00
Stefano Brivio
7ab1b2a97a util: On -DDEBUG, log to stderr with timestamps
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:22:09 +02:00
Stefano Brivio
84a62b79a2 passt: Also log to stderr, don't fork to background if not interactive
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:22:04 +02:00
Stefano Brivio
19d254bbbb passt: Add support for multiple instances in different network namespaces
...sharing the same filesystem. Instead of a fixed path for the UNIX
domain socket, passt now uses a path with a counter, probing for
existing instances, and picking the first free one.

The demo script is updated accordingly -- it can now be started several
times to create multiple namespaces with an instance of passt each,
with addressing reflecting separate subnets, and NDP proxying between
them.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:51 +02:00
Stefano Brivio
bd5aaaac7f tcp: Actually enforce MAX_CONNS limit
and, given that the connection table is indexed by socket number,
we also need to increase MAX_CONNS now as the ICMP implementation
needs 2^17 sockets, that will be opened before TCP connections are
accepted.

This needs to be changed later: the connection table should be
indexed by a translated number -- we're wasting 2^17 table entries
otherwise. Move initialisation of TCP listening sockets as last
per-protocol initialisation, this will make it easier.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:50 +02:00
Stefano Brivio
4adae47c40 passt: Close UNIX domain socket on failure before accepting new connections
The socket isn't necessarily closed, make sure we close it before
getting a new one from accept(), so that we don't mix it up with
protocol sockets numbering.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:49 +02:00
Stefano Brivio
17337a736f passt: Introduce packet capture implementation
With -DDEBUG, passt now saves guest-side traffic captures in
pcap format at /tmp/passt_<ISO8601 timestamp>.pcap. The timestamp
refers to time and date of start-up.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:48 +02:00
Stefano Brivio
9010054ea4 dhcp, ndp, dhcpv6: Support for multiple DNS servers, search list
Add support for a variable amount of DNS servers, including zero,
from /etc/resolv.conf, in DHCP, NDP and DHCPv6 implementations.

Introduce support for domain search list for DHCP (RFC 3397),
NDP (RFC 8106), and DHCPv6 (RFC 3646), also sourced from
/etc/resolv.conf.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:47 +02:00
Stefano Brivio
8754878fbf passt: With -DDEBUG, also print protocol number for unsupported protocols
...otherwise, we have no idea what's going on if we receive something
unexpected.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-21 11:14:46 +02:00
Stefano Brivio
0328e2a1f7 passt: Don't fork into background until the UNIX domain socket isn't listening
Once passt forks to background, it should be guaranteed that the UNIX
domain socket is available, otherwise, if qemu is started right after
it, it might fail to connect.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-10 13:15:52 +02:00
Stefano Brivio
b385ebaadf passt: Keep just two arrays to print context IPv4 and IPv6 addresses
Multiple arrays, one for each address, were needed with a single
fprintf(). Now that it's replaced by info(), we can have just one
for each protocol version.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-10 08:18:00 +02:00
Stefano Brivio
3c8af4819e passt: Don't use getprotobynumber() in debug build
With glibc, we can't reliably build a static binary with
getprotobynumber(), which is currently used with -DDEBUG.

Replace that with a small array of protocol strings.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-05-10 07:50:35 +02:00
Stefano Brivio
e07f539ae0 udp, passt: Introduce socket packet buffer, avoid getsockname() for UDP
This is in preparation for scatter-gather IO on the UDP receive path:
save a getsockname() syscall by setting a flag if we get the numbering
of all bound sockets in a strict sequence (expected, in practice) and
repurpose the tap buffer to be also a socket receive buffer, passing
it down to protocol handlers.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-30 14:52:18 +02:00
Stefano Brivio
605af213c5 udp: Connection tracking for ephemeral, local ports, and related fixes
As we support UDP forwarding for packets that are sent to local
ports, we actually need some kind of connection tracking for UDP.
While at it, this commit introduces a number of vaguely related fixes
for issues observed while trying this out. In detail:

- implement an explicit, albeit minimalistic, connection tracking
  for UDP, to allow usage of ephemeral ports by the guest and by
  the host at the same time, by binding them dynamically as needed,
  and to allow mapping address changes for packets with a loopback
  address as destination

- set the guest MAC address whenever we receive a packet from tap
  instead of waiting for an ARP request, and set it to broadcast on
  start, otherwise DHCPv6 might not work if all DHCPv6 requests time
  out before the guest starts talking IPv4

- split context IPv6 address into address we assign, global or site
  address seen on tap, and link-local address seen on tap, and make
  sure we use the addresses we've seen as destination (link-local
  choice depends on source address). Similarly, for IPv4, split into
  address we assign and address we observe, and use the address we
  observe as destination

- introduce a clock_gettime() syscall right after epoll_wait() wakes
  up, so that we can remove all the other ones and pass the current
  timestamp to tap and socket handlers -- this is additionally needed
  by UDP to time out bindings to ephemeral ports and mappings between
  loopback address and a local address

- rename sock_l4_add() to sock_l4(), no semantic changes intended

- include <arpa/inet.h> in passt.c before kernel headers so that we
  can use <netinet/in.h> macros to check IPv6 address types, and
  remove a duplicate <linux/ip.h> inclusion

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-29 17:15:26 +02:00
Stefano Brivio
db1fe773a3 tcp: Avoid SO_ACCEPTCONN getsockopt() by noting listening/data sockets numbers
...the rest is reshuffling existing macros to use the bits we need in
TCP code.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-29 17:15:26 +02:00
Stefano Brivio
38b50dba47 passt: Spare some syscalls, add some optimisations from profiling
Avoid a bunch of syscalls on forwarding paths by:

- storing minimum and maximum file descriptor numbers for each
  protocol, fall back to SO_PROTOCOL query only on overlaps

- allocating a larger receive buffer -- this can result in more
  coalesced packets than sendmmsg() can take (UIO_MAXIOV, i.e. 1024),
  so make sure we don't exceed that within a single call to protocol
  tap handlers

- nesting the handling loop in tap_handler() in the receive loop,
  so that we have better chances of filling our receive buffer in
  fewer calls

- skipping the recvfrom() in the UDP handler on EPOLLERR -- there's
  nothing to be done in that case

and while at it:

- restore the 20ms timer interval for periodic (TCP) events, I
  accidentally changed that to 100ms in an earlier commit

- attempt using SO_ZEROCOPY for UDP -- if it's not available,
  sendmmsg() will succeed anyway

- fix the handling of the status code from sendmmsg(), if it fails,
  we'll try to discard the first message, hence return 1 from the
  UDP handler

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-23 22:22:37 +02:00
Stefano Brivio
9ffb317cf9 passt: Don't unconditionally disable forking to background
...I left this there by mistake while debugging the debug
stuff.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-22 15:21:56 +02:00
Stefano Brivio
1f7cf04d34 passt: Introduce packet batching mechanism
Receive packets in batches from AF_UNIX, check if they can be sent
with a single syscall, and batch them up with sendmmsg() in case.

A bit rudimentary, currently only implemented for UDP, but it seems
to work.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-22 13:39:36 +02:00
Stefano Brivio
5f21a77737 passt: Print ports in debug messages only for protocols with ports
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-22 02:34:02 +02:00
Stefano Brivio
ad60ab1b37 passt: Always use INET_ADDRSTRLEN/INET6_ADDRSTRLEN for inet_ntop() buffers
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-21 17:19:11 +02:00
Stefano Brivio
0abec0eb60 passt: Make UNIX domain socket world-writable and world-readable
...save a chmod every time passt is started.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-13 22:37:40 +02:00
Stefano Brivio
4aa8e54a30 passt: Introduce a DHCPv6 server
This implementation, similarly to the IPv4 DHCP one, hands out a
single address, which is the same as the upstream address for the
host.

This avoids the need for address translation as long as the client
runs a DHCPv6 client. The NDP "Managed" flag is now set in Router
Advertisements.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-04-13 22:37:40 +02:00
Stefano Brivio
e653f9b3ed passt: Add libvirt patch for qemu UNIX socket domain back-end
...and mention it in the README.

While at it, remove useless escaping in the README, and fix
indentation in the syslog message with the qemu command line
example.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-21 00:08:42 +01:00
Stefano Brivio
35015ce72e passt: Initialise socket after getting addresses and routes
...otherwise, both IPv4 and IPv6 are considered disabled, and nothing
works anymore.

While at it, don't fork to background on debug builds, and log to
stderr too in that case.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-20 22:19:15 +01:00
Stefano Brivio
ef25cb39a9 passt: Set soft limit for number of open files to hard limit
Default value for /proc/sys/fs/nr_open is 2^20, which is more than
enough: set this hard limit as current (soft) limit on start, and
drop the 'ulimit -n' from the demo script.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-18 12:58:07 +01:00
Stefano Brivio
48ca38c606 passt: Run in background, add message logging with severities
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-18 12:58:07 +01:00
Stefano Brivio
8bfcc9208c passt: qemu patch for direct UNIX domain connection without the qrap wrapper
...and, while at it, a second patch to fail when connect() fails in turn
with EINVAL. These two patches haven't been sent upstream yet.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-18 12:58:07 +01:00
Stefano Brivio
1d807fc720 passt: Introduce ICMP echo proxy
It's nice to be able to confirm connectivity using ICMP or ICMPv6
echo requests, and "ping" sockets on Linux (IPPROTO_ICMP datagram)
allow us to do that without any special capability.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-18 12:58:03 +01:00
Stefano Brivio
d32edee60a passt: Use INET{,6}_ADDRSTRLEN instead of open coded sizeof
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-03-17 10:57:43 +01:00
Stefano Brivio
8bca388e8a passt: Assorted fixes from "fresh eyes" review
A bunch of fixes not worth single commits at this stage, notably:

- make buffer, length parameter ordering consistent in ARP, DHCP,
  NDP handlers

- strict checking of buffer, message and option length in DHCP
  handler (a malicious client could have easily crashed it)

- set up forwarding for IPv4 and IPv6, and masquerading with nft for
  IPv4, from demo script

- get rid of separate slow and fast timers, we don't save any
  overhead that way

- stricter checking of buffer lengths as passed to tap handlers

- proper dequeuing from qemu socket back-end: I accidentally trashed
  messages that were bundled up together in a single tap read
  operation -- the length header tells us what's the size of the next
  frame, but there's no apparent limit to the number of messages we
  get with one single receive

- rework some bits of the TCP state machine, now passive and active
  connection closes appear to be robust -- introduce a new
  FIN_WAIT_1_SOCK_FIN state indicating a FIN_WAIT_1 with a FIN flag
  from socket

- streamline TCP option parsing routine

- track TCP state changes to stderr (this is temporary, proper
  debugging and syslogging support pending)

- observe that multiplying a number by four might very well change
  its value, and this happens to be the case for the data offset
  from the TCP header as we check if it's the same as the total
  length to find out if it's a duplicated ACK segment

- recent estimates suggest that the duration of a millisecond is
  closer to a million nanoseconds than a thousand of them, this
  trend is now reflected into the timespec_diff_ms() convenience
  routine

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-02-21 11:55:49 +01:00
Stefano Brivio
105b916361 passt: New design and implementation with native Layer 4 sockets
This is a reimplementation, partially building on the earlier draft,
that uses L4 sockets (SOCK_DGRAM, SOCK_STREAM) instead of SOCK_RAW,
providing L4-L2 translation functionality without requiring any
security capability.

Conceptually, this follows the design presented at:
	https://gitlab.com/abologna/kubevirt-and-kvm/-/blob/master/Networking.md

The most significant novelty here comes from TCP and UDP translation
layers. In particular, the TCP state and translation logic follows
the intent of being minimalistic, without reimplementing a full TCP
stack in either direction, and synchronising as much as possible the
TCP dynamic and flows between guest and host kernel.

Another important introduction concerns addressing, port translation
and forwarding. The Layer 4 implementations now attempt to bind on
all unbound ports, in order to forward connections in a transparent
way.

While at it:
- the qemu 'tap' back-end can't be used as-is by qrap anymore,
  because of explicit checks now introduced in qemu to ensure that
  the corresponding file descriptor is actually a tap device. For
  this reason, qrap now operates on a 'socket' back-end type,
  accounting for and building the additional header reporting
  frame length

- provide a demo script that sets up namespaces, addresses and
  routes, and starts the daemon. A virtual machine started in the
  network namespace, wrapped by qrap, will now directly interface
  with passt and communicate using Layer 4 sockets provided by the
  host kernel.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-02-16 09:28:55 +01:00
Stefano Brivio
d02e059ddc passt: Add IPv6 and NDP support, further fixes for IPv4 CT
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-02-16 07:58:05 +01:00
Stefano Brivio
6709ade2bd merd: Rename to PASST
Plug A Simple Socket Transport.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2021-02-16 07:58:01 +01:00
Renamed from merd.c (Browse further)