aboutgitcodebugslistschat
path: root/tap.c
diff options
context:
space:
mode:
Diffstat (limited to 'tap.c')
-rw-r--r--tap.c540
1 files changed, 347 insertions, 193 deletions
diff --git a/tap.c b/tap.c
index 87be3a6..6db5d88 100644
--- a/tap.c
+++ b/tap.c
@@ -56,17 +56,73 @@
#include "netlink.h"
#include "pasta.h"
#include "packet.h"
+#include "repair.h"
#include "tap.h"
#include "log.h"
+#include "vhost_user.h"
+#include "vu_common.h"
+
+/* Maximum allowed frame lengths (including L2 header) */
+
+/* Verify that an L2 frame length limit is large enough to contain the header,
+ * but small enough to fit in the packet pool
+ */
+#define CHECK_FRAME_LEN(len) \
+ static_assert((len) >= ETH_HLEN && (len) <= PACKET_MAX_LEN, \
+ #len " has bad value")
+
+CHECK_FRAME_LEN(L2_MAX_LEN_PASTA);
+CHECK_FRAME_LEN(L2_MAX_LEN_PASST);
+CHECK_FRAME_LEN(L2_MAX_LEN_VU);
+
+/* We try size the packet pools so that we can use a single batch for the entire
+ * packet buffer. This might be exceeded for vhost-user, though, which uses its
+ * own buffers rather than pkt_buf.
+ *
+ * This is just a tuning parameter, the code will work with slightly more
+ * overhead if it's incorrect. So, we estimate based on the minimum practical
+ * frame size - an empty UDP datagram - rather than the minimum theoretical
+ * frame size.
+ *
+ * FIXME: Profile to work out how big this actually needs to be to amortise
+ * per-batch syscall overheads
+ */
+#define TAP_MSGS_IP4 \
+ DIV_ROUND_UP(sizeof(pkt_buf), \
+ ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))
+#define TAP_MSGS_IP6 \
+ DIV_ROUND_UP(sizeof(pkt_buf), \
+ ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct udphdr))
/* IPv4 (plus ARP) and IPv6 message batches from tap/guest to IP handlers */
-static PACKET_POOL_NOINIT(pool_tap4, TAP_MSGS, pkt_buf);
-static PACKET_POOL_NOINIT(pool_tap6, TAP_MSGS, pkt_buf);
+static PACKET_POOL_NOINIT(pool_tap4, TAP_MSGS_IP4, pkt_buf);
+static PACKET_POOL_NOINIT(pool_tap6, TAP_MSGS_IP6, pkt_buf);
#define TAP_SEQS 128 /* Different L4 tuples in one batch */
#define FRAGMENT_MSG_RATE 10 /* # seconds between fragment warnings */
/**
+ * tap_l2_max_len() - Maximum frame size (including L2 header) for current mode
+ * @c: Execution context
+ */
+unsigned long tap_l2_max_len(const struct ctx *c)
+{
+ /* NOLINTBEGIN(bugprone-branch-clone): values can be the same */
+ switch (c->mode) {
+ case MODE_PASST:
+ return L2_MAX_LEN_PASST;
+ case MODE_PASTA:
+ return L2_MAX_LEN_PASTA;
+ case MODE_VU:
+ return L2_MAX_LEN_VU;
+ }
+ /* NOLINTEND(bugprone-branch-clone) */
+ ASSERT(0);
+
+ return 0; /* Unreachable, for cppcheck's sake */
+}
+
+/**
* tap_send_single() - Send a single frame
* @c: Execution context
* @data: Packet buffer
@@ -78,16 +134,22 @@ void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
struct iovec iov[2];
size_t iovcnt = 0;
- if (c->mode == MODE_PASST) {
+ switch (c->mode) {
+ case MODE_PASST:
iov[iovcnt] = IOV_OF_LVALUE(vnet_len);
iovcnt++;
- }
-
- iov[iovcnt].iov_base = (void *)data;
- iov[iovcnt].iov_len = l2len;
- 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);
+ tap_send_frames(c, iov, iovcnt, 1);
+ break;
+ case MODE_VU:
+ vu_send_single(c, data, l2len);
+ break;
+ }
}
/**
@@ -113,13 +175,13 @@ const struct in6_addr *tap_ip6_daddr(const struct ctx *c,
*
* Return: pointer at which to write the packet's payload
*/
-static void *tap_push_l2h(const struct ctx *c, void *buf, uint16_t proto)
+void *tap_push_l2h(const struct ctx *c, void *buf, uint16_t proto)
{
struct ethhdr *eh = (struct ethhdr *)buf;
/* TODO: ARP table lookup */
- memcpy(eh->h_dest, c->mac_guest, ETH_ALEN);
- memcpy(eh->h_source, c->mac, ETH_ALEN);
+ memcpy(eh->h_dest, c->guest_mac, ETH_ALEN);
+ memcpy(eh->h_source, c->our_tap_mac, ETH_ALEN);
eh->h_proto = ntohs(proto);
return eh + 1;
}
@@ -134,8 +196,8 @@ static void *tap_push_l2h(const struct ctx *c, void *buf, uint16_t proto)
*
* Return: pointer at which to write the packet's payload
*/
-static void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
- struct in_addr dst, size_t l4len, uint8_t proto)
+void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
+ struct in_addr dst, size_t l4len, uint8_t proto)
{
uint16_t l3len = l4len + sizeof(*ip4h);
@@ -144,17 +206,17 @@ static void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
ip4h->tos = 0;
ip4h->tot_len = htons(l3len);
ip4h->id = 0;
- ip4h->frag_off = 0;
+ ip4h->frag_off = htons(IP_DF);
ip4h->ttl = 255;
ip4h->protocol = proto;
ip4h->saddr = src.s_addr;
ip4h->daddr = dst.s_addr;
ip4h->check = csum_ip4_header(l3len, proto, src, dst);
- return ip4h + 1;
+ return (char *)ip4h + sizeof(*ip4h);
}
/**
- * tap_udp4_send() - Send UDP over IPv4 packet
+ * tap_push_uh4() - Build UDPv4 header with checksum
* @c: Execution context
* @src: IPv4 source address
* @sport: UDP source port
@@ -162,6 +224,36 @@ static void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
* @dport: UDP destination port
* @in: UDP payload contents (not including UDP header)
* @dlen: UDP payload length (not including UDP header)
+ *
+ * Return: pointer at which to write the packet's payload
+ */
+void *tap_push_uh4(struct udphdr *uh, struct in_addr src, in_port_t sport,
+ struct in_addr dst, in_port_t dport,
+ const void *in, size_t dlen)
+{
+ size_t l4len = dlen + sizeof(struct udphdr);
+ const struct iovec iov = {
+ .iov_base = (void *)in,
+ .iov_len = dlen
+ };
+ struct iov_tail payload = IOV_TAIL(&iov, 1, 0);
+
+ uh->source = htons(sport);
+ uh->dest = htons(dport);
+ uh->len = htons(l4len);
+ csum_udp4(uh, src, dst, &payload);
+ return (char *)uh + sizeof(*uh);
+}
+
+/**
+ * tap_udp4_send() - Send UDP over IPv4 packet
+ * @c: Execution context
+ * @src: IPv4 source address
+ * @sport: UDP source port
+ * @dst: IPv4 destination address
+ * @dport: UDP destination port
+ * @in: UDP payload contents (not including UDP header)
+ * @dlen: UDP payload length (not including UDP header)
*/
void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport,
struct in_addr dst, in_port_t dport,
@@ -171,14 +263,9 @@ void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport,
char buf[USHRT_MAX];
struct iphdr *ip4h = tap_push_l2h(c, buf, ETH_P_IP);
struct udphdr *uh = tap_push_ip4h(ip4h, src, dst, l4len, IPPROTO_UDP);
- char *data = (char *)(uh + 1);
+ char *data = tap_push_uh4(uh, src, sport, dst, dport, in, dlen);
- uh->source = htons(sport);
- uh->dest = htons(dport);
- uh->len = htons(l4len);
- csum_udp4(uh, src, dst, in, dlen);
memcpy(data, in, dlen);
-
tap_send_single(c, buf, dlen + (data - buf));
}
@@ -215,10 +302,9 @@ void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst,
*
* Return: pointer at which to write the packet's payload
*/
-static void *tap_push_ip6h(struct ipv6hdr *ip6h,
- const struct in6_addr *src,
- const struct in6_addr *dst,
- size_t l4len, uint8_t proto, uint32_t flow)
+void *tap_push_ip6h(struct ipv6hdr *ip6h,
+ const struct in6_addr *src, const struct in6_addr *dst,
+ size_t l4len, uint8_t proto, uint32_t flow)
{
ip6h->payload_len = htons(l4len);
ip6h->priority = 0;
@@ -227,14 +313,12 @@ static void *tap_push_ip6h(struct ipv6hdr *ip6h,
ip6h->hop_limit = 255;
ip6h->saddr = *src;
ip6h->daddr = *dst;
- ip6h->flow_lbl[0] = (flow >> 16) & 0xf;
- ip6h->flow_lbl[1] = (flow >> 8) & 0xff;
- ip6h->flow_lbl[2] = (flow >> 0) & 0xff;
- return ip6h + 1;
+ ip6_set_flow_lbl(ip6h, flow);
+ return (char *)ip6h + sizeof(*ip6h);
}
/**
- * tap_udp6_send() - Send UDP over IPv6 packet
+ * tap_push_uh6() - Build UDPv6 header with checksum
* @c: Execution context
* @src: IPv6 source address
* @sport: UDP source port
@@ -243,25 +327,52 @@ static void *tap_push_ip6h(struct ipv6hdr *ip6h,
* @flow: Flow label
* @in: UDP payload contents (not including UDP header)
* @dlen: UDP payload length (not including UDP header)
+ *
+ * Return: pointer at which to write the packet's payload
+ */
+void *tap_push_uh6(struct udphdr *uh,
+ const struct in6_addr *src, in_port_t sport,
+ const struct in6_addr *dst, in_port_t dport,
+ void *in, size_t dlen)
+{
+ size_t l4len = dlen + sizeof(struct udphdr);
+ const struct iovec iov = {
+ .iov_base = in,
+ .iov_len = dlen
+ };
+ struct iov_tail payload = IOV_TAIL(&iov, 1, 0);
+
+ uh->source = htons(sport);
+ uh->dest = htons(dport);
+ uh->len = htons(l4len);
+ csum_udp6(uh, src, dst, &payload);
+ return (char *)uh + sizeof(*uh);
+}
+
+/**
+ * tap_udp6_send() - Send UDP over IPv6 packet
+ * @c: Execution context
+ * @src: IPv6 source address
+ * @sport: UDP source port
+ * @dst: IPv6 destination address
+ * @dport: UDP destination port
+ * @flow: Flow label
+ * @in: UDP payload contents (not including UDP header)
+ * @dlen: UDP payload length (not including UDP header)
*/
void tap_udp6_send(const struct ctx *c,
const struct in6_addr *src, in_port_t sport,
const struct in6_addr *dst, in_port_t dport,
- uint32_t flow, const void *in, size_t dlen)
+ uint32_t flow, void *in, size_t dlen)
{
size_t l4len = dlen + sizeof(struct udphdr);
char buf[USHRT_MAX];
struct ipv6hdr *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6);
struct udphdr *uh = tap_push_ip6h(ip6h, src, dst,
l4len, IPPROTO_UDP, flow);
- char *data = (char *)(uh + 1);
+ char *data = tap_push_uh6(uh, src, sport, dst, dport, in, dlen);
- uh->source = htons(sport);
- uh->dest = htons(dport);
- uh->len = htons(l4len);
- csum_udp6(uh, src, dst, in, dlen);
memcpy(data, in, dlen);
-
tap_send_single(c, buf, dlen + (data - buf));
}
@@ -406,10 +517,18 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov,
if (!nframes)
return 0;
- if (c->mode == MODE_PASTA)
+ switch (c->mode) {
+ case MODE_PASTA:
m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes);
- else
+ break;
+ case MODE_PASST:
m = tap_send_frames_passt(c, iov, bufs_per_frame, nframes);
+ break;
+ case MODE_VU:
+ /* fall through */
+ default:
+ ASSERT(0);
+ }
if (m < nframes)
debug("tap: failed to send %zu frames of %zu",
@@ -442,6 +561,7 @@ PACKET_POOL_DECL(pool_l4, UIO_MAXIOV, pkt_buf);
* struct l4_seq4_t - Message sequence for one protocol handler call, IPv4
* @msgs: Count of messages in sequence
* @protocol: Protocol number
+ * @ttl: Time to live
* @source: Source port
* @dest: Destination port
* @saddr: Source address
@@ -450,6 +570,7 @@ PACKET_POOL_DECL(pool_l4, UIO_MAXIOV, pkt_buf);
*/
static struct tap4_l4_t {
uint8_t protocol;
+ uint8_t ttl;
uint16_t source;
uint16_t dest;
@@ -464,14 +585,17 @@ static struct tap4_l4_t {
* struct l4_seq6_t - Message sequence for one protocol handler call, IPv6
* @msgs: Count of messages in sequence
* @protocol: Protocol number
+ * @flow_lbl: IPv6 flow label
* @source: Source port
* @dest: Destination port
* @saddr: Source address
* @daddr: Destination address
+ * @hop_limit: Hop limit
* @msg: Array of messages that can be handled in a single call
*/
static struct tap6_l4_t {
uint8_t protocol;
+ uint32_t flow_lbl :20;
uint16_t source;
uint16_t dest;
@@ -479,6 +603,8 @@ static struct tap6_l4_t {
struct in6_addr saddr;
struct in6_addr daddr;
+ uint8_t hop_limit;
+
struct pool_l4_t p;
} tap6_l4[TAP_SEQS /* Arbitrary: TAP_MSGS in theory, so limit in users */];
@@ -667,7 +793,8 @@ resume:
#define L4_MATCH(iph, uh, seq) \
((seq)->protocol == (iph)->protocol && \
(seq)->source == (uh)->source && (seq)->dest == (uh)->dest && \
- (seq)->saddr.s_addr == (iph)->saddr && (seq)->daddr.s_addr == (iph)->daddr)
+ (seq)->saddr.s_addr == (iph)->saddr && \
+ (seq)->daddr.s_addr == (iph)->daddr && (seq)->ttl == (iph)->ttl)
#define L4_SET(iph, uh, seq) \
do { \
@@ -676,6 +803,7 @@ resume:
(seq)->dest = (uh)->dest; \
(seq)->saddr.s_addr = (iph)->saddr; \
(seq)->daddr.s_addr = (iph)->daddr; \
+ (seq)->ttl = (iph)->ttl; \
} while (0)
if (seq && L4_MATCH(iph, uh, seq) && seq->p.count < UIO_MAXIOV)
@@ -717,14 +845,14 @@ append:
for (k = 0; k < p->count; )
k += tcp_tap_handler(c, PIF_TAP, AF_INET,
&seq->saddr, &seq->daddr,
- p, k, now);
+ 0, p, k, now);
} else if (seq->protocol == IPPROTO_UDP) {
if (c->no_udp)
continue;
for (k = 0; k < p->count; )
k += udp_tap_handler(c, PIF_TAP, AF_INET,
&seq->saddr, &seq->daddr,
- p, k, now);
+ seq->ttl, p, k, now);
}
}
@@ -795,6 +923,9 @@ resume:
if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) {
c->ip6.addr_seen = *saddr;
}
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr))
+ c->ip6.addr = *saddr;
} else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){
c->ip6.addr_seen = *saddr;
}
@@ -842,16 +973,20 @@ resume:
((seq)->protocol == (proto) && \
(seq)->source == (uh)->source && \
(seq)->dest == (uh)->dest && \
+ (seq)->flow_lbl == ip6_get_flow_lbl(ip6h) && \
IN6_ARE_ADDR_EQUAL(&(seq)->saddr, saddr) && \
- IN6_ARE_ADDR_EQUAL(&(seq)->daddr, daddr))
+ IN6_ARE_ADDR_EQUAL(&(seq)->daddr, daddr) && \
+ (seq)->hop_limit == (ip6h)->hop_limit)
#define L4_SET(ip6h, proto, uh, seq) \
do { \
(seq)->protocol = (proto); \
(seq)->source = (uh)->source; \
(seq)->dest = (uh)->dest; \
+ (seq)->flow_lbl = ip6_get_flow_lbl(ip6h); \
(seq)->saddr = *saddr; \
(seq)->daddr = *daddr; \
+ (seq)->hop_limit = (ip6h)->hop_limit; \
} while (0)
if (seq && L4_MATCH(ip6h, proto, uh, seq) &&
@@ -895,14 +1030,14 @@ append:
for (k = 0; k < p->count; )
k += tcp_tap_handler(c, PIF_TAP, AF_INET6,
&seq->saddr, &seq->daddr,
- p, k, now);
+ seq->flow_lbl, p, k, now);
} else if (seq->protocol == IPPROTO_UDP) {
if (c->no_udp)
continue;
for (k = 0; k < p->count; )
k += udp_tap_handler(c, PIF_TAP, AF_INET6,
&seq->saddr, &seq->daddr,
- p, k, now);
+ seq->hop_limit, p, k, now);
}
}
@@ -937,8 +1072,10 @@ void tap_handler(struct ctx *c, const struct timespec *now)
* @c: Execution context
* @l2len: Total L2 packet length
* @p: Packet buffer
+ * @now: Current timestamp
*/
-void tap_add_packet(struct ctx *c, ssize_t l2len, char *p)
+void tap_add_packet(struct ctx *c, ssize_t l2len, char *p,
+ const struct timespec *now)
{
const struct ethhdr *eh;
@@ -946,17 +1083,25 @@ void tap_add_packet(struct ctx *c, ssize_t l2len, char *p)
eh = (struct ethhdr *)p;
- if (memcmp(c->mac_guest, eh->h_source, ETH_ALEN)) {
- memcpy(c->mac_guest, eh->h_source, ETH_ALEN);
- proto_update_l2_buf(c->mac_guest, NULL);
+ if (memcmp(c->guest_mac, eh->h_source, ETH_ALEN)) {
+ memcpy(c->guest_mac, eh->h_source, ETH_ALEN);
+ proto_update_l2_buf(c->guest_mac, NULL);
}
switch (ntohs(eh->h_proto)) {
case ETH_P_ARP:
case ETH_P_IP:
+ if (pool_full(pool_tap4)) {
+ tap4_handler(c, pool_tap4, now);
+ pool_flush(pool_tap4);
+ }
packet_add(pool_tap4, l2len, p);
break;
case ETH_P_IPV6:
+ if (pool_full(pool_tap6)) {
+ tap6_handler(c, pool_tap6, now);
+ pool_flush(pool_tap6);
+ }
packet_add(pool_tap6, l2len, p);
break;
default:
@@ -968,38 +1113,33 @@ void tap_add_packet(struct ctx *c, ssize_t l2len, char *p)
* tap_sock_reset() - Handle closing or failure of connect AF_UNIX socket
* @c: Execution context
*/
-static void tap_sock_reset(struct ctx *c)
+void tap_sock_reset(struct ctx *c)
{
info("Client connection closed%s", c->one_off ? ", exiting" : "");
if (c->one_off)
- exit(EXIT_SUCCESS);
+ _exit(EXIT_SUCCESS);
/* Close the connected socket, wait for a new connection */
- epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_tap, NULL);
+ epoll_del(c, c->fd_tap);
close(c->fd_tap);
c->fd_tap = -1;
+ if (c->mode == MODE_VU)
+ vu_cleanup(c->vdev);
}
/**
- * tap_handler_passt() - Packet handler for AF_UNIX file descriptor
+ * tap_passt_input() - Handler for new data on the socket to qemu
* @c: Execution context
- * @events: epoll events
* @now: Current timestamp
*/
-void tap_handler_passt(struct ctx *c, uint32_t events,
- const struct timespec *now)
+static void tap_passt_input(struct ctx *c, const struct timespec *now)
{
static const char *partial_frame;
static ssize_t partial_len = 0;
ssize_t n;
char *p;
- if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
- tap_sock_reset(c);
- return;
- }
-
tap_flush_pools();
if (partial_len) {
@@ -1010,10 +1150,13 @@ void tap_handler_passt(struct ctx *c, uint32_t events,
memmove(pkt_buf, partial_frame, partial_len);
}
- n = recv(c->fd_tap, pkt_buf + partial_len, TAP_BUF_BYTES - partial_len,
- MSG_DONTWAIT);
+ do {
+ n = recv(c->fd_tap, pkt_buf + partial_len,
+ sizeof(pkt_buf) - partial_len, MSG_DONTWAIT);
+ } while ((n < 0) && errno == EINTR);
+
if (n < 0) {
- if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
err_perror("Receive error on guest connection, reset");
tap_sock_reset(c);
}
@@ -1026,7 +1169,7 @@ void tap_handler_passt(struct ctx *c, uint32_t events,
while (n >= (ssize_t)sizeof(uint32_t)) {
uint32_t l2len = ntohl_unaligned(p);
- if (l2len < sizeof(struct ethhdr) || l2len > ETH_MAX_MTU) {
+ if (l2len < sizeof(struct ethhdr) || l2len > L2_MAX_LEN_PASST) {
err("Bad frame size from guest, resetting connection");
tap_sock_reset(c);
return;
@@ -1039,7 +1182,7 @@ void tap_handler_passt(struct ctx *c, uint32_t events,
p += sizeof(uint32_t);
n -= sizeof(uint32_t);
- tap_add_packet(c, l2len, p);
+ tap_add_packet(c, l2len, p, now);
p += l2len;
n -= l2len;
@@ -1052,121 +1195,110 @@ void tap_handler_passt(struct ctx *c, uint32_t events,
}
/**
- * tap_handler_pasta() - Packet handler for /dev/net/tun file descriptor
+ * tap_handler_passt() - Event handler for AF_UNIX file descriptor
* @c: Execution context
* @events: epoll events
* @now: Current timestamp
*/
-void tap_handler_pasta(struct ctx *c, uint32_t events,
+void tap_handler_passt(struct ctx *c, uint32_t events,
const struct timespec *now)
{
- ssize_t n, len;
- int ret;
+ if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+ tap_sock_reset(c);
+ return;
+ }
- if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
- die("Disconnect event on /dev/net/tun device, exiting");
+ if (events & EPOLLIN)
+ tap_passt_input(c, now);
+}
-redo:
- n = 0;
+/**
+ * tap_pasta_input() - Handler for new data on the socket to hypervisor
+ * @c: Execution context
+ * @now: Current timestamp
+ */
+static void tap_pasta_input(struct ctx *c, const struct timespec *now)
+{
+ ssize_t n, len;
tap_flush_pools();
-restart:
- while ((len = read(c->fd_tap, pkt_buf + n, TAP_BUF_BYTES - n)) > 0) {
- if (len < (ssize_t)sizeof(struct ethhdr) ||
- len > (ssize_t)ETH_MAX_MTU) {
- n += len;
- continue;
- }
+ for (n = 0;
+ n <= (ssize_t)(sizeof(pkt_buf) - L2_MAX_LEN_PASTA);
+ n += len) {
+ len = read(c->fd_tap, pkt_buf + n, L2_MAX_LEN_PASTA);
+ if (len == 0) {
+ die("EOF on tap device, exiting");
+ } else if (len < 0) {
+ if (errno == EINTR) {
+ len = 0;
+ continue;
+ }
- tap_add_packet(c, len, pkt_buf + n);
+ if (errno == EAGAIN && errno == EWOULDBLOCK)
+ break; /* all done for now */
- if ((n += len) == TAP_BUF_BYTES)
- break;
- }
+ die("Error on tap device, exiting");
+ }
- if (len < 0 && errno == EINTR)
- goto restart;
+ /* Ignore frames of bad length */
+ if (len < (ssize_t)sizeof(struct ethhdr) ||
+ len > (ssize_t)L2_MAX_LEN_PASTA)
+ continue;
- ret = errno;
+ tap_add_packet(c, len, pkt_buf + n, now);
+ }
tap_handler(c, now);
-
- if (len > 0 || ret == EAGAIN)
- return;
-
- if (n == TAP_BUF_BYTES)
- goto redo;
-
- die("Error on tap device, exiting");
}
/**
- * tap_sock_unix_open() - Create and bind AF_UNIX socket
- * @sock_path: Socket path. If empty, set on return (UNIX_SOCK_PATH as prefix)
- *
- * Return: socket descriptor on success, won't return on failure
+ * tap_handler_pasta() - Packet handler for /dev/net/tun file descriptor
+ * @c: Execution context
+ * @events: epoll events
+ * @now: Current timestamp
*/
-int tap_sock_unix_open(char *sock_path)
+void tap_handler_pasta(struct ctx *c, uint32_t events,
+ const struct timespec *now)
{
- int fd = socket(AF_UNIX, SOCK_STREAM, 0);
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
- int i;
-
- if (fd < 0)
- die_perror("Failed to open UNIX domain socket");
-
- for (i = 1; i < UNIX_SOCK_MAX; i++) {
- char *path = addr.sun_path;
- int ex, ret;
-
- if (*sock_path)
- memcpy(path, sock_path, UNIX_PATH_MAX);
- else
- snprintf(path, UNIX_PATH_MAX - 1, UNIX_SOCK_PATH, i);
-
- ex = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
- if (ex < 0)
- die_perror("Failed to check for UNIX domain conflicts");
-
- ret = connect(ex, (const struct sockaddr *)&addr, sizeof(addr));
- if (!ret || (errno != ENOENT && errno != ECONNREFUSED &&
- errno != EACCES)) {
- if (*sock_path)
- die("Socket path %s already in use", path);
-
- close(ex);
- continue;
- }
- close(ex);
+ if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
+ die("Disconnect event on /dev/net/tun device, exiting");
- unlink(path);
- ret = bind(fd, (const struct sockaddr *)&addr, sizeof(addr));
- if (*sock_path && ret)
- die_perror("Failed to bind UNIX domain socket");
+ if (events & EPOLLIN)
+ tap_pasta_input(c, now);
+}
- if (!ret)
- break;
+/**
+ * tap_backend_show_hints() - Give help information to start QEMU
+ * @c: Execution context
+ */
+static void tap_backend_show_hints(struct ctx *c)
+{
+ switch (c->mode) {
+ case MODE_PASTA:
+ /* No hints */
+ break;
+ case MODE_PASST:
+ info("\nYou can now start qemu (>= 7.2, with commit 13c6be96618c):");
+ info(" kvm ... -device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=%s",
+ c->sock_path);
+ info("or qrap, for earlier qemu versions:");
+ info(" ./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio");
+ break;
+ case MODE_VU:
+ info("You can start qemu with:");
+ info(" kvm ... -chardev socket,id=chr0,path=%s -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=$RAMSIZE -numa node,memdev=memfd0\n",
+ c->sock_path);
+ break;
}
-
- if (i == UNIX_SOCK_MAX)
- die_perror("Failed to bind UNIX domain socket");
-
- info("UNIX domain socket bound at %s", addr.sun_path);
- if (!*sock_path)
- memcpy(sock_path, addr.sun_path, UNIX_PATH_MAX);
-
- return fd;
}
/**
* tap_sock_unix_init() - Start listening for connections on AF_UNIX socket
* @c: Execution context
*/
-static void tap_sock_unix_init(struct ctx *c)
+static void tap_sock_unix_init(const struct ctx *c)
{
union epoll_ref ref = { .type = EPOLL_TYPE_TAP_LISTEN };
struct epoll_event ev = { 0 };
@@ -1177,12 +1309,33 @@ static void tap_sock_unix_init(struct ctx *c)
ev.events = EPOLLIN | EPOLLET;
ev.data.u64 = ref.u64;
epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap_listen, &ev);
+}
- info("\nYou can now start qemu (>= 7.2, with commit 13c6be96618c):");
- info(" kvm ... -device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=%s",
- c->sock_path);
- info("or qrap, for earlier qemu versions:");
- info(" ./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio");
+/**
+ * tap_start_connection() - start a new connection
+ * @c: Execution context
+ */
+static void tap_start_connection(const struct ctx *c)
+{
+ struct epoll_event ev = { 0 };
+ union epoll_ref ref = { 0 };
+
+ ref.fd = c->fd_tap;
+ switch (c->mode) {
+ case MODE_PASST:
+ ref.type = EPOLL_TYPE_TAP_PASST;
+ break;
+ case MODE_PASTA:
+ ref.type = EPOLL_TYPE_TAP_PASTA;
+ break;
+ case MODE_VU:
+ ref.type = EPOLL_TYPE_VHOST_CMD;
+ break;
+ }
+
+ ev.events = EPOLLIN | EPOLLRDHUP;
+ ev.data.u64 = ref.u64;
+ epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
}
/**
@@ -1192,8 +1345,6 @@ static void tap_sock_unix_init(struct ctx *c)
*/
void tap_listen_handler(struct ctx *c, uint32_t events)
{
- union epoll_ref ref = { .type = EPOLL_TYPE_TAP_PASST };
- struct epoll_event ev = { 0 };
int v = INT_MAX / 2;
struct ucred ucred;
socklen_t len;
@@ -1232,10 +1383,7 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
setsockopt(c->fd_tap, SOL_SOCKET, SO_SNDBUF, &v, sizeof(v)))
trace("tap: failed to set SO_SNDBUF to %i", v);
- ref.fd = c->fd_tap;
- ev.events = EPOLLIN | EPOLLRDHUP;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
+ tap_start_connection(c);
}
/**
@@ -1261,7 +1409,7 @@ static int tap_ns_tun(void *arg)
if (fd < 0)
die_perror("Failed to open() /dev/net/tun");
- rc = ioctl(fd, TUNSETIFF, &ifr);
+ rc = ioctl(fd, (int)TUNSETIFF, &ifr);
if (rc < 0)
die_perror("TUNSETIFF ioctl on /dev/net/tun failed");
@@ -1279,64 +1427,70 @@ static int tap_ns_tun(void *arg)
*/
static void tap_sock_tun_init(struct ctx *c)
{
- union epoll_ref ref = { .type = EPOLL_TYPE_TAP_PASTA };
- struct epoll_event ev = { 0 };
-
NS_CALL(tap_ns_tun, c);
if (c->fd_tap == -1)
die("Failed to set up tap device in namespace");
pasta_ns_conf(c);
- ref.fd = c->fd_tap;
- ev.events = EPOLLIN | EPOLLRDHUP;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
+ tap_start_connection(c);
}
/**
- * tap_sock_init() - Create and set up AF_UNIX socket or tuntap file descriptor
- * @c: Execution context
+ * tap_sock_update_pool() - Set the buffer base and size for the pool of packets
+ * @base: Buffer base
+ * @size Buffer size
*/
-void tap_sock_init(struct ctx *c)
+void tap_sock_update_pool(void *base, size_t size)
{
- size_t sz = sizeof(pkt_buf);
int i;
- pool_tap4_storage = PACKET_INIT(pool_tap4, TAP_MSGS, pkt_buf, sz);
- pool_tap6_storage = PACKET_INIT(pool_tap6, TAP_MSGS, pkt_buf, sz);
+ pool_tap4_storage = PACKET_INIT(pool_tap4, TAP_MSGS_IP4, base, size);
+ pool_tap6_storage = PACKET_INIT(pool_tap6, TAP_MSGS_IP6, base, size);
for (i = 0; i < TAP_SEQS; i++) {
- tap4_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, pkt_buf, sz);
- tap6_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, pkt_buf, sz);
+ tap4_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, base, size);
+ tap6_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, base, size);
}
+}
- if (c->fd_tap != -1) { /* Passed as --fd */
- struct epoll_event ev = { 0 };
- union epoll_ref ref;
+/**
+ * tap_backend_init() - Create and set up AF_UNIX socket or
+ * tuntap file descriptor
+ * @c: Execution context
+ */
+void tap_backend_init(struct ctx *c)
+{
+ if (c->mode == MODE_VU) {
+ tap_sock_update_pool(NULL, 0);
+ vu_init(c);
+ } else {
+ tap_sock_update_pool(pkt_buf, sizeof(pkt_buf));
+ }
+ if (c->fd_tap != -1) { /* Passed as --fd */
ASSERT(c->one_off);
- ref.fd = c->fd_tap;
- if (c->mode == MODE_PASST)
- ref.type = EPOLL_TYPE_TAP_PASST;
- else
- ref.type = EPOLL_TYPE_TAP_PASTA;
-
- ev.events = EPOLLIN | EPOLLRDHUP;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
+ tap_start_connection(c);
return;
}
- if (c->mode == MODE_PASTA) {
+ switch (c->mode) {
+ case MODE_PASTA:
tap_sock_tun_init(c);
- } else {
+ break;
+ case MODE_VU:
+ repair_sock_init(c);
+ /* fall through */
+ case MODE_PASST:
tap_sock_unix_init(c);
/* In passt mode, we don't know the guest's MAC address until it
* sends us packets. Use the broadcast address so that our
* first packets will reach it.
*/
- memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
+ memset(&c->guest_mac, 0xff, sizeof(c->guest_mac));
+ break;
}
+
+ tap_backend_show_hints(c);
}