public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
cb5274aa1d8131d4757b2bbddfc89041e7fdd2d1 blob 6269 bytes (raw)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
 
// SPDX-License-Identifier: GPL-2.0-or-later
/* udp_vu.c - UDP L2 vhost-user management functions
 *
 * Copyright Red Hat
 * Author: Laurent Vivier <lvivier@redhat.com>
 */

#include <unistd.h>
#include <assert.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/uio.h>
#include <linux/virtio_net.h>

#include "checksum.h"
#include "util.h"
#include "ip.h"
#include "siphash.h"
#include "inany.h"
#include "passt.h"
#include "pcap.h"
#include "log.h"
#include "vhost_user.h"
#include "udp_internal.h"
#include "flow.h"
#include "flow_table.h"
#include "udp_flow.h"
#include "udp_vu.h"
#include "vu_common.h"

static struct iovec     iov_vu		[VIRTQUEUE_MAX_SIZE];
static struct vu_virtq_element	elem		[VIRTQUEUE_MAX_SIZE];

/**
 * udp_vu_hdrlen() - Sum size of all headers, from UDP to virtio-net
 * @v6:		Set for IPv6 packet
 *
 * Return: total size of virtio-net, Ethernet, IP, and UDP headers
 */
static size_t udp_vu_hdrlen(bool v6)
{
	size_t hdrlen;

	hdrlen = VNET_HLEN + sizeof(struct ethhdr) + sizeof(struct udphdr);

	if (v6)
		hdrlen += sizeof(struct ipv6hdr);
	else
		hdrlen += sizeof(struct iphdr);

	return hdrlen;
}

/**
 * udp_vu_sock_recv() - Receive datagrams from socket into vhost-user buffers
 * @iov:	IO vector for the frame (modified on output)
 * @cnt:	Number of IO vector entries (in/out)
 * @s:		Socket to receive from
 * @v6:		Set for IPv6 connections
 *
 * Return: size of received data, -1 on error
 */
static ssize_t udp_vu_sock_recv(struct iovec *iov, size_t *cnt, int s, bool v6)
{
	struct iovec msg_iov[*cnt];
	struct msghdr msg = { 0 };
	struct iov_tail payload;
	size_t hdrlen;
	ssize_t dlen;

	/* compute L2 header length */
	hdrlen = udp_vu_hdrlen(v6);

	payload = IOV_TAIL(iov, *cnt, hdrlen);

	msg.msg_iov = msg_iov;
	msg.msg_iovlen = iov_tail_clone(msg.msg_iov, payload.cnt, &payload);

	/* read data from the socket */
	dlen = recvmsg(s, &msg, 0);
	if (dlen < 0)
		return -1;

	/* Pad short frames to ETH_ZLEN */
	if (ETH_ZLEN + VNET_HLEN > dlen + hdrlen) {
		iov_memset(iov, *cnt, dlen + hdrlen, 0,
			   ETH_ZLEN + VNET_HLEN - (dlen + hdrlen));
	}
	*cnt = iov_truncate(iov, *cnt, dlen + hdrlen);

	return dlen;
}

/**
 * udp_vu_prepare() - Prepare the packet header
 * @c:		Execution context
 * @data:	IO vector tail for the frame
 * @toside:	Address information for one side of the flow
 *
 * Return: Layer-4 length
 */
static size_t udp_vu_prepare(const struct ctx *c, const struct iov_tail *data,
			     const struct flowside *toside)
{
	struct iov_tail current = *data;
	struct ethhdr *eh, eh_storage;
	struct udphdr *uh, uh_storage;
	size_t l4len;

	/* ethernet header */
	eh = IOV_REMOVE_HEADER(&current, eh_storage);

	memcpy(eh->h_dest, c->guest_mac, sizeof(eh->h_dest));
	memcpy(eh->h_source, c->our_tap_mac, sizeof(eh->h_source));

	/* initialize header */
	if (inany_v4(&toside->eaddr) && inany_v4(&toside->oaddr)) {
		struct iphdr *iph, iph_storage;

		eh->h_proto = htons(ETH_P_IP);

		iph = IOV_REMOVE_HEADER(&current, iph_storage);
		*iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_UDP);

		uh = IOV_REMOVE_HEADER(&current, uh_storage);
		l4len = udp_update_hdr4(iph, uh, &current, toside, true);

		current = *data;
		IOV_PUT_HEADER(&current, eh);
		IOV_PUT_HEADER(&current, iph);
		IOV_PUT_HEADER(&current, uh);
	} else {
		struct ipv6hdr *ip6h, ip6h_storage;

		eh->h_proto = htons(ETH_P_IPV6);

		ip6h = IOV_REMOVE_HEADER(&current, ip6h_storage);
		*ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_UDP);

		uh = IOV_REMOVE_HEADER(&current, uh_storage);
		l4len = udp_update_hdr6(ip6h, uh, &current, toside, true);

		current = *data;
		IOV_PUT_HEADER(&current, eh);
		IOV_PUT_HEADER(&current, ip6h);
		IOV_PUT_HEADER(&current, uh);
	}

	return l4len;
}

/**
 * udp_vu_csum() - Calculate and set checksum for a UDP packet
 * @toside:	Address information for one side of the flow
 * @data:	IO vector tail for the frame
 */
static void udp_vu_csum(const struct flowside *toside,
			const struct iov_tail *data)
{
	const struct in_addr *src4 = inany_v4(&toside->oaddr);
	const struct in_addr *dst4 = inany_v4(&toside->eaddr);
	struct iov_tail payload = *data;
	struct udphdr *uh, uh_storage;
	bool ipv4 = src4 && dst4;
	int hdrlen = sizeof(struct ethhdr) +
		     (ipv4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr));

	iov_drop_header(&payload, hdrlen);
	uh = IOV_REMOVE_HEADER(&payload, uh_storage);

	if (ipv4)
		csum_udp4(uh, *src4, *dst4, &payload);
	else
		csum_udp6(uh, &toside->oaddr.a6, &toside->eaddr.a6, &payload);
}

/**
 * udp_vu_sock_to_tap() - Forward datagrams from socket to tap
 * @c:		Execution context
 * @s:		Socket to read data from
 * @n:		Maximum number of datagrams to forward
 * @tosidx:	Flow & side to forward data from @s to
 */
void udp_vu_sock_to_tap(const struct ctx *c, int s, int n, flow_sidx_t tosidx)
{
	const struct flowside *toside = flowside_at_sidx(tosidx);
	bool v6 = !(inany_v4(&toside->eaddr) && inany_v4(&toside->oaddr));
	struct vu_dev *vdev = c->vdev;
	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
	int i;

	ASSERT(!c->no_udp);

	if (!vu_queue_enabled(vq) || !vu_queue_started(vq)) {
		struct msghdr msg = { 0 };

		debug("Got UDP packet, but RX virtqueue not usable yet");

		for (i = 0; i < n; i++) {
			if (recvmsg(s, &msg, MSG_DONTWAIT) < 0)
				debug_perror("Failed to discard datagram");
		}

		return;
	}

	for (i = 0; i < n; i++) {
		int elem_cnt, elem_used;
		size_t iov_cnt;
		ssize_t dlen;

		vu_init_elem(elem, iov_vu, ARRAY_SIZE(elem), 1);

		elem_cnt = vu_collect(vdev, vq, elem, ARRAY_SIZE(elem),
				      IP_MAX_MTU + ETH_HLEN + VNET_HLEN, NULL);
		if (elem_cnt == 0)
			break;

		iov_cnt = elem_cnt;
		dlen = udp_vu_sock_recv(iov_vu, &iov_cnt, s, v6);
		if (dlen < 0) {
			vu_queue_rewind(vq, elem_cnt);
			break;
		}
		elem_used = iov_cnt;

		/* release unused buffers */
		vu_queue_rewind(vq, elem_cnt - elem_used);
		if (iov_cnt > 0) {
			struct iov_tail data = IOV_TAIL(iov_vu, iov_cnt, 0);
			vu_set_vnethdr(&data, elem_used);
			udp_vu_prepare(c, &data, toside);
			if (*c->pcap) {
				udp_vu_csum(toside, &data);
				pcap_iov(data.iov, data.cnt, data.off);
			}
			vu_flush(vdev, vq, elem, elem_used);
		}
	}
}
debug log:

solving cb5274aa1d81 ...
found cb5274aa1d81 in https://archives.passt.top/passt-dev/20260309094744.1907754-12-lvivier@redhat.com/
found 414750ff742a in https://archives.passt.top/passt-dev/20260309094744.1907754-10-lvivier@redhat.com/
found a21a03dbf23e in https://archives.passt.top/passt-dev/20260309094744.1907754-9-lvivier@redhat.com/
found 2a5d3f822bf6 in https://archives.passt.top/passt-dev/20260309094744.1907754-8-lvivier@redhat.com/
found 27ae93de4420 in https://archives.passt.top/passt-dev/20260309094744.1907754-6-lvivier@redhat.com/
found a39254776099 in https://archives.passt.top/passt-dev/20260309094744.1907754-5-lvivier@redhat.com/
found 439f2cb399b7 in https://archives.passt.top/passt-dev/20260309094744.1907754-4-lvivier@redhat.com/
found ef9d26118eaf in https://archives.passt.top/passt-dev/20260309094744.1907754-3-lvivier@redhat.com/
found 5effca777e0a in https://archives.passt.top/passt-dev/20260305075701.2653212-1-lvivier@redhat.com/ ||
	https://archives.passt.top/passt-dev/20260305125648.3720714-1-lvivier@redhat.com/ ||
	https://archives.passt.top/passt-dev/20260309094744.1907754-2-lvivier@redhat.com/ ||
	https://archives.passt.top/passt-dev/20260306115119.1589020-1-lvivier@redhat.com/
found 3520f89e5671 in https://passt.top/passt
preparing index
index prepared:
100644 3520f89e56717371757aa480ad4a3c83df59b483	udp_vu.c

applying [1/12] https://archives.passt.top/passt-dev/20260305075701.2653212-1-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 3520f89e5671..5effca777e0a 100644

Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.

skipping https://archives.passt.top/passt-dev/20260305125648.3720714-1-lvivier@redhat.com/ for 5effca777e0a
skipping https://archives.passt.top/passt-dev/20260309094744.1907754-2-lvivier@redhat.com/ for 5effca777e0a
skipping https://archives.passt.top/passt-dev/20260306115119.1589020-1-lvivier@redhat.com/ for 5effca777e0a
index at:
100644 5effca777e0ab432bbf5502ec18143de317ac4e5	udp_vu.c

applying [2/12] https://archives.passt.top/passt-dev/20260309094744.1907754-3-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 5effca777e0a..ef9d26118eaf 100644


applying [3/12] https://archives.passt.top/passt-dev/20260309094744.1907754-4-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index ef9d26118eaf..439f2cb399b7 100644


applying [4/12] https://archives.passt.top/passt-dev/20260309094744.1907754-5-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 439f2cb399b7..a39254776099 100644


applying [5/12] https://archives.passt.top/passt-dev/20260309094744.1907754-6-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index a39254776099..27ae93de4420 100644


applying [6/12] https://archives.passt.top/passt-dev/20260309094744.1907754-8-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 27ae93de4420..2a5d3f822bf6 100644


applying [7/12] https://archives.passt.top/passt-dev/20260309094744.1907754-9-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 2a5d3f822bf6..a21a03dbf23e 100644


applying [8/12] https://archives.passt.top/passt-dev/20260309094744.1907754-10-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index a21a03dbf23e..414750ff742a 100644


applying [9/12] https://archives.passt.top/passt-dev/20260309094744.1907754-12-lvivier@redhat.com/
diff --git a/udp_vu.c b/udp_vu.c
index 414750ff742a..cb5274aa1d81 100644

Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.
Checking patch udp_vu.c...
Applied patch udp_vu.c cleanly.

index at:
100644 cb5274aa1d8131d4757b2bbddfc89041e7fdd2d1	udp_vu.c

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).