From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: passt.top; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=fittiN50; dkim-atps=neutral Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by passt.top (Postfix) with ESMTPS id 32AA65A061A for ; Sun, 02 Aug 2026 15:23:53 +0200 (CEST) Received: by mail-wm1-x333.google.com with SMTP id 5b1f17b1804b1-496bb7cdf51so12374365e9.2 for ; Sun, 02 Aug 2026 06:23:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785677033; x=1786281833; darn=passt.top; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=RQOELr+yiWUiqn28h/Is38Xb9VkJqxugdbrajYnLohw=; b=fittiN50gDtYMikRdgZvpvfW17jYbyTsO7icVwHOxQjBdC1vG3jy+Yd9OcLnuPhXj/ mDlxLneQTBrAu4idej/iztce+iwmkptrXJ14kqTt7Y/FwlAZ3dc1L7ggCpvirszx3Ob5 RargrmNF/pEQAjwYloeYLe0bbB7UiUoNQVMjMk24wk9gBPzmbGGhatGPYoYLv/pdGw7Y FcYu09JEDmzcI33cbgFK6gtmh9aOt154zwUh/YnRe9o2COZWrqICjFG13roL2LwdwDF9 bfy1njBQk9KB7y1CAykjhbP6trUKn/CvbtMeQ274SNvCP08ygHfb/3oPHA59z5kCZy07 T98A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785677033; x=1786281833; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=RQOELr+yiWUiqn28h/Is38Xb9VkJqxugdbrajYnLohw=; b=tATKcFaRW/twKOrBJbgSL02exKOl5kKImkM3yOyBlosFBmhddQRaYuKtMZGr69mFnK 2FDVl2U1JEIfW5skFdpjOIxcX+hxF7p4+uPi9fx2KQqVfzkiBtRrePOPQ8iXvEVBGEAo +DqXJgvz0/ik/OqpQjPq6Dxoi2hMfAdANncC7dMHgzolNZWhHg3W3ct5DtFAN63Tqk+T Wc1r8gvlD0Jj70leON6c3mWDFbljSpgpW3KhDKQVZ2f15+3YYZ5g1GHH/47h8xpJkqyb Ap/ppTHzJWLTEAkG5ti7bxXeFaQIdn0dXhF0QzL2lOMc3znJDghjEPi3gZO7qXFJLh7X leIA== X-Gm-Message-State: AOJu0YwgNnF07Qe4E48wmP89ypWPkocBRrDLYp9EEAqmoUF5t8KoNQuO kS9TzeuRsg2tfejILkk5rWXdD4cLzzxSV3l6RCUwD21vfp4EqqH0I4G4XJWCqjPj X-Gm-Gg: AR+sD106tfqMCDvlN3mrF0/r6SU6BQzix5IKJG/9yd9Dr6mG09NXsrQJWn1/6WXpZaP wVaovSsENOQm3Xu2Q+r4GwETfv42Gx9CcxSOrzlQlJzVoNsTDBuxnocNmzhSt7CBwfnPVgAyzsU YY+FOVVqHpk1c9z0XGSuNDiMDzL8l/bWfkGh5aE1+1w+rHSQ5UBXYR3lUISIOcRRusmcPbFhyUo qeiguRv5bSq5RpHShlCRyXyv27b3oa7XsHejKxmpb0wU0M9afGjCFIChdLE9weoGqPyykSXhI05 kiXDFjBG3CN8XGUGfVsZwBRMA9r94Y9RUl0LpxFR06e+14YILsFHnJKXhipPVdOR+FPrIwTBRe9 fUuAvWh//f3RU30h+GRpbVOTIOOfB+yC3wK2kSzQCBo4Y3SQ9vm15RYf/GKUh0/kBQmDwXOCYAQ RKxwusqvk0kaAsT1Hj2wGE1mCY1DY/UGrtU9BHgj6fDn+3KKic5vcvpc1JcvuRM0yns93JMbaS/ DbshFMaOK2WMGkHwo6Qjpi5tn0PbFja9KIEut96nQtxOWs= X-Received: by 2002:a05:600c:a016:b0:495:573e:1c54 with SMTP id 5b1f17b1804b1-4980c6523bamr157047675e9.9.1785677032612; Sun, 02 Aug 2026 06:23:52 -0700 (PDT) Received: from localhost.localdomain ([196.137.15.128]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4980869106csm180745155e9.9.2026.08.02.06.23.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 02 Aug 2026 06:23:52 -0700 (PDT) From: Ammar Yasser To: passt-dev@passt.top Subject: [RFC v3 7/8] pasta: Implement pasta vhost TX (pasta->guest) prerequisites Date: Sun, 2 Aug 2026 13:21:54 +0000 Message-Id: <20260802132155.870796-8-aerosound161@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260802132155.870796-1-aerosound161@gmail.com> References: <20260802132155.870796-1-aerosound161@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: AIB5RUGPJG7ELABHSTE3DHPUOE2QRQ5N X-Message-ID-Hash: AIB5RUGPJG7ELABHSTE3DHPUOE2QRQ5N X-MailFrom: aerosound161@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: eperezma@redhat.com, Ammar Yasser X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: - tap_send_single was changed to directly call tap_send_frames_passt/pasta instead of relying on tap_send_frames because protocols that use tap_send_single won't use vhost acceleration, only tcp and udp will. The function was also moved lower in the file to by the time its called tap_send_frames_* functions are defined and a forward declaration isn't needed - extend udp_meta_t to instead of always carrying a tap_hdr, to carry a union of a tap_hdr and a virtio_net header because if vhost is used the will the latter type of header and then build an iov from this virtio net header in udp.c - create iov_from_virtio_net_hdr to build an iov from the vnet header - in the case that vhost is requested, set the first frame fragment to be a virtio_net header through calling iov_from_virtio_net_hdr - convert the tcp_payload_tap_hdr array to be an array of virtio_net header. in the case where we aren't using vhost, we will just be using the first 4 bytes of it - add a boolean argumen to tap_send_frames_pasta to determine if vhost will be used Signed-off-by: Ammar Yasser --- tap.c | 100 +++++++++++++++++++++++++++++++----------------------- tcp_buf.c | 39 +++++++++++++++++---- tcp_buf.h | 4 +-- udp.c | 28 ++++++++++++++- udp.h | 6 +++- 5 files changed, 125 insertions(+), 52 deletions(-) diff --git a/tap.c b/tap.c index 9d601a2..e177eef 100644 --- a/tap.c +++ b/tap.c @@ -124,45 +124,6 @@ unsigned long tap_l2_max_len(const struct ctx *c) return 0; /* Unreachable, for cppcheck's sake */ } -/** - * tap_send_single() - Send a single frame - * @c: Execution context - * @data: Packet buffer - * @l2len: Total L2 packet length - */ -void tap_send_single(const struct ctx *c, const void *data, size_t l2len) -{ - uint8_t padded[ETH_ZLEN] = { 0 }; - struct iovec iov[2]; - size_t iovcnt = 0; - uint32_t vnet_len; - - if (l2len < ETH_ZLEN) { - memcpy(padded, data, l2len); - data = padded; - l2len = ETH_ZLEN; - } - - vnet_len = htonl(l2len); - - switch (c->mode) { - case MODE_PASST: - iov[iovcnt] = IOV_OF_LVALUE(vnet_len); - iovcnt++; - /* fall through */ - case MODE_PASTA: - iov[iovcnt].iov_base = (void *)data; - iov[iovcnt].iov_len = l2len; - iovcnt++; - - tap_send_frames(c, iov, iovcnt, 1); - break; - case MODE_VU: - vu_send_single(c, data, l2len); - break; - } -} - /** * tap_push_l2h() - Build an L2 header for an inbound packet * @c: Execution context @@ -415,7 +376,7 @@ void tap_icmp6_send(const struct ctx *c, */ static size_t tap_send_frames_pasta(const struct ctx *c, const struct iovec *iov, - size_t bufs_per_frame, size_t nframes) + size_t bufs_per_frame, size_t nframes, bool vhost) { size_t nbufs = bufs_per_frame * nframes; size_t i; @@ -498,6 +459,59 @@ static size_t tap_send_frames_passt(const struct ctx *c, return i / bufs_per_frame; } +/** + * tap_send_single() - Send a single frame + * @c: Execution context + * @data: Packet buffer + * @l2len: Total L2 packet length + */ +void tap_send_single(const struct ctx *c, const void *data, size_t l2len) +{ + uint8_t padded[ETH_ZLEN] = { 0 }; + struct iovec iov[2]; + size_t iovcnt = 0; + int m = 0; + uint32_t vnet_len; + + if (l2len < ETH_ZLEN) { + memcpy(padded, data, l2len); + data = padded; + l2len = ETH_ZLEN; + } + + vnet_len = htonl(l2len); + switch (c->mode) { + case MODE_PASST: + /* create an iov for the length */ + iov[iovcnt] = IOV_OF_LVALUE(vnet_len); + iovcnt++; + /* create the data iov */ + iov[iovcnt].iov_base = (void *)data; + iov[iovcnt].iov_len = l2len; + iovcnt++; + m = tap_send_frames_passt(c, iov, iovcnt, 1); + break; + case MODE_PASTA: + /* don't create a length iov in the case of pasta */ + iov[iovcnt].iov_base = (void *)data; + iov[iovcnt].iov_len = l2len; + iovcnt++; + + m = tap_send_frames_pasta(c, iov, iovcnt, 1, false); + break; + case MODE_VU: + m = vu_send_single(c, data, l2len); + break; + } + + if (m < 1) + debug("tap: failed to send a single frame"); + + pcap_multiple(iov, iovcnt, m, + c->mode == MODE_PASST ? sizeof(uint32_t) : + (c->fd_vhost != -1) ? VNET_HLEN : 0); +} + /** * tap_send_frames() - Send out multiple prepared frames * @c: Execution context @@ -523,7 +537,8 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov, switch (c->mode) { case MODE_PASTA: - m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes); + /* use vhost in pasta sending only if the vhost setup was actually successful, indicated by a non-zero fd_vhost */ + m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes, ((c->fd_vhost != -1))); break; case MODE_PASST: m = tap_send_frames_passt(c, iov, bufs_per_frame, nframes); @@ -539,7 +554,8 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov, nframes - m, nframes); pcap_multiple(iov, bufs_per_frame, m, - c->mode == MODE_PASST ? sizeof(uint32_t) : 0); + c->mode == MODE_PASST ? sizeof(uint32_t) : + (c->fd_vhost != -1) ? VNET_HLEN : 0); return m; } diff --git a/tcp_buf.c b/tcp_buf.c index 4452337..080e4cb 100644 --- a/tcp_buf.c +++ b/tcp_buf.c @@ -21,6 +21,7 @@ #include #include +#include #include "util.h" #include "ip.h" @@ -38,7 +39,7 @@ /* Ethernet header for IPv4 and IPv6 frames */ struct ethhdr tcp_eth_hdr[TCP_FRAMES_MEM]; -struct tap_hdr tcp_payload_tap_hdr[TCP_FRAMES_MEM]; +struct virtio_net_hdr_mrg_rxbuf tcp_payload_tap_hdr[TCP_FRAMES_MEM]; /* IP headers for IPv4 and IPv6 */ struct iphdr tcp4_payload_ip[TCP_FRAMES_MEM]; @@ -71,6 +72,14 @@ void tcp_update_l2_buf(const unsigned char *eth_d) eth_update_mac(&tcp_eth_hdr[i], eth_d, NULL); } +static inline struct iovec iov_from_virtio_net_hdr(struct virtio_net_hdr_mrg_rxbuf *hdr) +{ + return (struct iovec){ + .iov_base = hdr, + .iov_len = sizeof(*hdr), + }; +} + /** * tcp_sock_iov_init() - Initialise scatter-gather L2 buffers for IPv4 sockets * @c: Execution context @@ -89,7 +98,14 @@ void tcp_sock_iov_init(const struct ctx *c) for (i = 0; i < TCP_FRAMES_MEM; i++) { struct iovec *iov = tcp_l2_iov[i]; - iov[TCP_IOV_TAP] = tap_hdr_iov(c, &tcp_payload_tap_hdr[i]); + /* If we are using pasta with vhost acceleration, the first entry in the tcp buffers + * should point to a virtio_net header, otherwise a tap header + */ + if ((c->fd_vhost != -1)) + iov[TCP_IOV_TAP] = iov_from_virtio_net_hdr(&tcp_payload_tap_hdr[i]); + else + iov[TCP_IOV_TAP] = tap_hdr_iov(c, (struct tap_hdr *)&tcp_payload_tap_hdr[i]); + iov[TCP_IOV_ETH].iov_len = sizeof(struct ethhdr); iov[TCP_IOV_PAYLOAD].iov_base = &tcp_payload[i]; iov[TCP_IOV_ETH_PAD].iov_base = eth_pad; @@ -135,8 +151,7 @@ void tcp_payload_flush(const struct ctx *c, const struct timespec *now) { size_t m; - m = tap_send_frames(c, &tcp_l2_iov[0][0], TCP_NUM_IOVS, - tcp_payload_used); + m = tap_send_frames(c, &tcp_l2_iov[0][0], TCP_NUM_IOVS, tcp_payload_used); if (m != tcp_payload_used) { tcp_revert_seq(c, &tcp_frame_conns[m], &tcp_l2_iov[m], tcp_payload_used - m, now); @@ -177,7 +192,7 @@ static void tcp_l2_buf_fill_headers(const struct ctx *c, { struct iov_tail tail = IOV_TAIL(&iov[TCP_IOV_PAYLOAD], 1, 0); struct tcphdr th_storage, *th = IOV_REMOVE_HEADER(&tail, th_storage); - struct tap_hdr *taph = iov[TCP_IOV_TAP].iov_base; + const struct flowside *tapside = TAPFLOW(conn); const struct in_addr *a4 = inany_v4(&tapside->oaddr); struct ethhdr *eh = iov[TCP_IOV_ETH].iov_base; @@ -192,7 +207,19 @@ static void tcp_l2_buf_fill_headers(const struct ctx *c, l2len = tcp_fill_headers(c, conn, eh, ip4h, ip6h, th, &tail, iov_tail_size(&tail), csum_flags, seq); - tap_hdr_update(taph, l2len); + + /* when in pasta mode, the length of the tap header iov is zero, so this + * l2len write doesn't do anything. But this function gets called from both + * pasta and passt. Make the l2len write in case we are in passt mode, denoted by + * fd_vhost being -1 since this field won't get initialized at all in passt. + */ + if (c->mode == MODE_PASST) { + iov[TCP_IOV_TAP].iov_len = sizeof(struct tap_hdr); + struct tap_hdr *taph = iov[TCP_IOV_TAP].iov_base; + tap_hdr_update(taph, l2len); + } else if (c->fd_vhost == -1) { /* pasta mode but without vhost */ + iov[TCP_IOV_TAP].iov_len = 0; + } } /** diff --git a/tcp_buf.h b/tcp_buf.h index c749038..da48cd0 100644 --- a/tcp_buf.h +++ b/tcp_buf.h @@ -9,7 +9,7 @@ #include "tcp_conn.h" #include "tcp_internal.h" -void tcp_sock_iov_init(); +void tcp_sock_iov_init(const struct ctx *c); void tcp_payload_flush(const struct ctx *c, const struct timespec *now); int tcp_buf_data_from_sock(const struct ctx *c, struct tcp_tap_conn *conn, uint32_t already_sent, const struct timespec *now); @@ -20,7 +20,7 @@ int tcp_buf_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags, #define TCP_FRAMES \ (c->mode == MODE_PASTA ? 1 : TCP_FRAMES_MEM) -extern struct tap_hdr tcp_payload_tap_hdr[TCP_FRAMES_MEM]; +extern struct virtio_net_hdr_mrg_rxbuf tcp_payload_tap_hdr[TCP_FRAMES_MEM]; extern struct ethhdr tcp_eth_hdr[TCP_FRAMES_MEM]; extern struct tcp_payload_t tcp_payload[TCP_FRAMES_MEM]; diff --git a/udp.c b/udp.c index d05ee66..5f90dce 100644 --- a/udp.c +++ b/udp.c @@ -103,6 +103,7 @@ #include #include #include +#include #include "checksum.h" #include "util.h" @@ -119,6 +120,18 @@ #include "udp_vu.h" #include "epoll_ctl.h" +/* UDP header and data for inbound messages */ +struct udp_payload_t udp_payload[UDP_MAX_FRAMES]; + +/* Ethernet headers for IPv4 and IPv6 frames */ +struct ethhdr udp_eth_hdr[UDP_MAX_FRAMES]; + +/* IOVs and msghdr arrays for receiving datagrams from sockets */ +struct iovec udp_iov_recv [UDP_MAX_FRAMES]; +struct mmsghdr udp_mh_recv [UDP_MAX_FRAMES]; + +/* Pre-cooked headers for UDP packets */ +struct udp_meta_t udp_meta[UDP_MAX_FRAMES]; #define UDP_TIMEOUT "/proc/sys/net/netfilter/nf_conntrack_udp_timeout" #define UDP_TIMEOUT_STREAM \ @@ -216,7 +229,20 @@ static void udp_iov_init_one(const struct ctx *c, size_t i) *siov = IOV_OF_LVALUE(payload->data); tiov[UDP_IOV_ETH] = IOV_OF_LVALUE(udp_eth_hdr[i]); - tiov[UDP_IOV_TAP] = tap_hdr_iov(c, &meta->taph); + /* if the vhost tap fd is initialized, this is a sign for us that + * we will be using virtio transport. make the iov that is supposed + * to point to a tap header point instead to a virtio_net_mrg_rxbuf + */ + if (c->fd_vhost != -1) { + struct iovec vnet_iov = { + .iov_base = (void *)(&meta->vnet_hdr), + .iov_len = sizeof(meta->vnet_hdr) + }; + tiov[UDP_IOV_TAP] = vnet_iov; + } else { + tiov[UDP_IOV_TAP] = tap_hdr_iov(c, &meta->taph); + } + tiov[UDP_IOV_PAYLOAD].iov_base = payload; tiov[UDP_IOV_ETH_PAD].iov_base = eth_pad; diff --git a/udp.h b/udp.h index b7a367c..3464b74 100644 --- a/udp.h +++ b/udp.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "tap_hdr.h" #include "fwd.h" @@ -49,7 +50,10 @@ extern struct mmsghdr udp_mh_recv [UDP_MAX_FRAMES]; struct udp_meta_t { struct ipv6hdr ip6h; struct iphdr ip4h; - struct tap_hdr taph; + union { + struct virtio_net_hdr_mrg_rxbuf vnet_hdr; + struct tap_hdr taph; +}; #ifdef __AVX2__ } __attribute__((aligned(32))); #else -- 2.34.1