public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Nikolay Edigaryev <edigaryev@gmail.com>
To: passt-dev@passt.top
Cc: Nikolay Edigaryev <edigaryev@gmail.com>
Subject: [PATCH] passt: introduce --fd-is-tap to allow passing TAP file descriptor
Date: Fri, 15 Sep 2023 18:21:52 +0400	[thread overview]
Message-ID: <20230915142152.73499-1-edigaryev@gmail.com> (raw)

Problem: I have a Cloud Hypervisor virtual machine that needs both
(1) an internet access without fiddling with iptables/Netfilter and
(2) VM <-> host access (to be able to provision this VM over SSH)
without dealing with passt port forwarding it doesn't seem to be
possible to map the whole IP address, yet the users expect an IP
instead of IP:port combination.

Requirement #1 is why I've choosen passt and it's pretty much
satisfied (thank you for this great piece of software!).

Requirement #2 implies some kind of bridge interface on the host
with one TAP interface for the VM and the other for the passt.

However, only pasta can accept TAP interface FD's in it's -F/--fd,
which is OK, but it also configures unneeded namespacing, which in
turn results in unneeded complexity and performance overhead due
to the need of involving veth pairs to break away from the pasta
namespace to the host for the requirement #2 to be satisfied.

I've also considered proxying the UNIX domain socket communication
to/from a TAP interface in my own Golang code, but it incurs
significant performance overhead.

On the other hand passt seems to already can do everything I need,
it just needs some guidance on which type of FD it's dealing with.

Solution: introduce --fd-is-tap command-line flag to tell passt
which type of FD it's being passed to and force it to use appropriate
system calls and offset calculation.

This patch also clarifies the -F/--fd description for pasta to note
that we're expecting a TAP device and not a UNIX domain socket.
---
 README.md |  4 ++++
 conf.c    | 14 +++++++++++++-
 passt.c   |  2 ++
 passt.h   |  1 +
 tap.c     |  8 +++++---
 tap.h     |  4 ++--
 6 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 6d00313..a78288f 100644
--- a/README.md
+++ b/README.md
@@ -381,6 +381,10 @@ descriptor that's already opened.
 This approach, compared to using a _tap_ device, doesn't require any security
 capabilities, as we don't need to create any interface.
 
+However, if you already have a _tap_ device opened by other means, you can
+specify `--fd-is-tap` command-line option and _passt_ will treat the file
+descriptor passed in `-F`/`--fd` option as a pre-opened TAP device.
+
 _pasta_ runs out of the box with any recent (post-3.8) Linux kernel.
 
 ## Services
diff --git a/conf.c b/conf.c
index 0ad6e23..d622fdf 100644
--- a/conf.c
+++ b/conf.c
@@ -803,7 +803,12 @@ static void print_usage(const char *name, int status)
 		     UNIX_SOCK_PATH, 1);
 	}
 
-	info(   "  -F, --fd FD		Use FD as pre-opened connected socket");
+	if (strstr(name, "pasta")) {
+		info(   "  -F, --fd FD		Use FD as pre-opened TAP device");
+	} else {
+		info(   "  -F, --fd FD		Use FD as pre-opened and connected UNIX domain socket");
+		info(   "  --fd-is-tap		Treat FD as pre-opened TAP device instead of connected UNIX domain socket");
+	}
 	info(   "  -p, --pcap FILE	Log tap-facing traffic to pcap file");
 	info(   "  -P, --pid FILE	Write own PID to the given file");
 	info(   "  -m, --mtu MTU	Assign MTU via DHCP/NDP");
@@ -1232,6 +1237,7 @@ void conf(struct ctx *c, int argc, char **argv)
 		{"config-net",	no_argument,		NULL,		17 },
 		{"no-copy-routes", no_argument,		NULL,		18 },
 		{"no-copy-addrs", no_argument,		NULL,		19 },
+		{"fd-is-tap",	no_argument,		NULL,		20 },
 		{ 0 },
 	};
 	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
@@ -1411,6 +1417,12 @@ void conf(struct ctx *c, int argc, char **argv)
 			warn("--no-copy-addrs will be dropped soon");
 			c->no_copy_addrs = copy_addrs_opt = true;
 			break;
+		case 20:
+			if (c->mode != MODE_PASST)
+				die("--fd-is-tap is for passt mode only");
+
+			c->fd_tap_is_socket = false;
+			break;
 		case 'd':
 			if (c->debug)
 				die("Multiple --debug options given");
diff --git a/passt.c b/passt.c
index 8ddd9b3..b7276ff 100644
--- a/passt.c
+++ b/passt.c
@@ -195,9 +195,11 @@ int main(int argc, char **argv)
 		}
 
 		c.mode = MODE_PASTA;
+		c.fd_tap_is_socket = false;
 		log_name = "pasta";
 	} else if (strstr(name, "passt")) {
 		c.mode = MODE_PASST;
+		c.fd_tap_is_socket = true;
 		log_name = "passt";
 	} else {
 		exit(EXIT_FAILURE);
diff --git a/passt.h b/passt.h
index 282bd1a..2079cd0 100644
--- a/passt.h
+++ b/passt.h
@@ -264,6 +264,7 @@ struct ctx {
 	int epollfd;
 	int fd_tap_listen;
 	int fd_tap;
+	bool fd_tap_is_socket;
 	unsigned char mac[ETH_ALEN];
 	unsigned char mac_guest[ETH_ALEN];
 
diff --git a/tap.c b/tap.c
index 93db989..12b66ca 100644
--- a/tap.c
+++ b/tap.c
@@ -76,7 +76,7 @@ int tap_send(const struct ctx *c, const void *data, size_t len)
 {
 	pcap(data, len);
 
-	if (c->mode == MODE_PASST) {
+	if (c->fd_tap_is_socket) {
 		int flags = MSG_NOSIGNAL | MSG_DONTWAIT;
 		uint32_t vnet_len = htonl(len);
 
@@ -421,7 +421,7 @@ void tap_send_frames(struct ctx *c, const struct iovec *iov, size_t n)
 	if (!n)
 		return;
 
-	if (c->mode == MODE_PASST)
+	if (c->fd_tap_is_socket)
 		m = tap_send_frames_passt(c, iov, n);
 	else
 		m = tap_send_frames_pasta(c, iov, n);
@@ -1176,6 +1176,7 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
 	}
 
 	c->fd_tap = accept4(c->fd_tap_listen, NULL, NULL, 0);
+	c->fd_tap_is_socket = true;
 
 	if (!getsockopt(c->fd_tap, SOL_SOCKET, SO_PEERCRED, &ucred, &len))
 		info("accepted connection from PID %i", ucred.pid);
@@ -1225,6 +1226,7 @@ static int tap_ns_tun(void *arg)
 		die("Tap device opened but no network interface found");
 
 	c->fd_tap = fd;
+	c->fd_tap_is_socket = false;
 
 	return 0;
 }
@@ -1273,7 +1275,7 @@ void tap_sock_init(struct ctx *c)
 
 		ASSERT(c->one_off);
 		ref.fd = c->fd_tap;
-		if (c->mode == MODE_PASST)
+		if (c->fd_tap_is_socket)
 			ref.type = EPOLL_TYPE_TAP_PASST;
 		else
 			ref.type = EPOLL_TYPE_TAP_PASTA;
diff --git a/tap.h b/tap.h
index 021fb7c..3626e49 100644
--- a/tap.h
+++ b/tap.h
@@ -20,7 +20,7 @@ struct tap_hdr {
 
 static inline size_t tap_hdr_len_(const struct ctx *c)
 {
-	if (c->mode == MODE_PASST)
+	if (c->fd_tap_is_socket)
 		return sizeof(struct tap_hdr);
 	else
 		return sizeof(struct ethhdr);
@@ -52,7 +52,7 @@ static inline void *tap_iov_base(const struct ctx *c, struct tap_hdr *taph)
 static inline size_t tap_iov_len(const struct ctx *c, struct tap_hdr *taph,
 				 size_t plen)
 {
-	if (c->mode == MODE_PASST)
+	if (c->fd_tap_is_socket)
 		taph->vnet_len = htonl(plen + sizeof(taph->eh));
 	return plen + tap_hdr_len_(c);
 }
-- 
@@ -20,7 +20,7 @@ struct tap_hdr {
 
 static inline size_t tap_hdr_len_(const struct ctx *c)
 {
-	if (c->mode == MODE_PASST)
+	if (c->fd_tap_is_socket)
 		return sizeof(struct tap_hdr);
 	else
 		return sizeof(struct ethhdr);
@@ -52,7 +52,7 @@ static inline void *tap_iov_base(const struct ctx *c, struct tap_hdr *taph)
 static inline size_t tap_iov_len(const struct ctx *c, struct tap_hdr *taph,
 				 size_t plen)
 {
-	if (c->mode == MODE_PASST)
+	if (c->fd_tap_is_socket)
 		taph->vnet_len = htonl(plen + sizeof(taph->eh));
 	return plen + tap_hdr_len_(c);
 }
-- 
2.39.2 (Apple Git-144)


             reply	other threads:[~2023-09-15 14:21 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-15 14:21 Nikolay Edigaryev [this message]
2023-09-16 12:34 ` [PATCH] passt: introduce --fd-is-tap to allow passing TAP file descriptor Stefano Brivio
2023-09-18  2:23   ` David Gibson
2023-09-18 13:37   ` [PATCH] passt: introduce --tap-fd " Nikolay Edigaryev
2023-09-19  0:45     ` David Gibson
2023-09-18 13:44   ` [PATCH] passt: introduce --fd-is-tap " Nikolay Edigaryev

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=20230915142152.73499-1-edigaryev@gmail.com \
    --to=edigaryev@gmail.com \
    --cc=passt-dev@passt.top \
    /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).