aboutgitcodebugslistschat
path: root/tap.c
diff options
context:
space:
mode:
Diffstat (limited to 'tap.c')
-rw-r--r--tap.c823
1 files changed, 395 insertions, 428 deletions
diff --git a/tap.c b/tap.c
index 3c4a222..386f0bc 100644
--- a/tap.c
+++ b/tap.c
@@ -59,6 +59,7 @@
#include "tap.h"
#include "log.h"
#include "vhost_user.h"
+#include "vu_common.h"
/* IPv4 (plus ARP) and IPv6 message batches from tap/guest to IP handlers */
static PACKET_POOL_NOINIT(pool_tap4, TAP_MSGS, pkt_buf);
@@ -68,42 +69,33 @@ static PACKET_POOL_NOINIT(pool_tap6, TAP_MSGS, pkt_buf);
#define FRAGMENT_MSG_RATE 10 /* # seconds between fragment warnings */
/**
- * tap_send() - Send frame, with qemu socket header if needed
+ * tap_send_single() - Send a single frame
* @c: Execution context
* @data: Packet buffer
- * @len: Total L2 packet length
- *
- * Return: return code from send() or write()
+ * @l2len: Total L2 packet length
*/
-int tap_send(const struct ctx *c, const void *data, size_t len)
+void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
{
- int flags = MSG_NOSIGNAL | MSG_DONTWAIT;
- uint32_t vnet_len = htonl(len);
-
- pcap(data, len);
+ uint32_t vnet_len = htonl(l2len);
+ struct iovec iov[2];
+ size_t iovcnt = 0;
switch (c->mode) {
case MODE_PASST:
- if (send(c->fd_tap, &vnet_len, 4, flags) < 0)
- return -1;
- return send(c->fd_tap, data, len, flags);
+ iov[iovcnt] = IOV_OF_LVALUE(vnet_len);
+ iovcnt++;
+ /* fall through */
case MODE_PASTA:
- return write(c->fd_tap, (char *)data, len);
+ iov[iovcnt].iov_base = (void *)data;
+ iov[iovcnt].iov_len = l2len;
+ iovcnt++;
+
+ tap_send_frames(c, iov, iovcnt, 1);
+ break;
case MODE_VU:
- return vu_send(c, data, len);
+ vu_send_single(c, data, l2len);
+ break;
}
- return 0;
-}
-
-/**
- * tap_ip4_daddr() - Normal IPv4 destination address for inbound packets
- * @c: Execution context
- *
- * Return: IPv4 address, network order
- */
-struct in_addr tap_ip4_daddr(const struct ctx *c)
-{
- return c->ip4.addr_seen;
}
/**
@@ -134,8 +126,8 @@ static 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;
}
@@ -143,29 +135,29 @@ static void *tap_push_l2h(const struct ctx *c, void *buf, uint16_t proto)
/**
* tap_push_ip4h() - Build IPv4 header for inbound packet, with checksum
* @c: Execution context
- * @src: IPv4 source address, network order
- * @dst: IPv4 destination address, network order
- * @len: L4 payload length
+ * @src: IPv4 source address
+ * @dst: IPv4 destination address
+ * @l4len: IPv4 payload length
* @proto: L4 protocol number
*
* Return: pointer at which to write the packet's payload
*/
-static void *tap_push_ip4h(char *buf, struct in_addr src, struct in_addr dst,
- size_t len, uint8_t proto)
+static void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
+ struct in_addr dst, size_t l4len, uint8_t proto)
{
- struct iphdr *ip4h = (struct iphdr *)buf;
+ uint16_t l3len = l4len + sizeof(*ip4h);
ip4h->version = 4;
ip4h->ihl = sizeof(struct iphdr) / 4;
ip4h->tos = 0;
- ip4h->tot_len = htons(len + sizeof(*ip4h));
+ ip4h->tot_len = htons(l3len);
ip4h->id = 0;
ip4h->frag_off = 0;
ip4h->ttl = 255;
ip4h->protocol = proto;
ip4h->saddr = src.s_addr;
ip4h->daddr = dst.s_addr;
- ip4h->check = csum_ip4_header(ip4h->tot_len, proto, src, dst);
+ ip4h->check = csum_ip4_header(l3len, proto, src, dst);
return ip4h + 1;
}
@@ -177,27 +169,29 @@ static void *tap_push_ip4h(char *buf, struct in_addr src, struct in_addr dst,
* @dst: IPv4 destination address
* @dport: UDP destination port
* @in: UDP payload contents (not including UDP header)
- * @len: UDP payload length (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,
- const void *in, size_t len)
+ const void *in, size_t dlen)
{
- size_t udplen = len + sizeof(struct udphdr);
+ size_t l4len = dlen + sizeof(struct udphdr);
char buf[USHRT_MAX];
- void *ip4h = tap_push_l2h(c, buf, ETH_P_IP);
- void *uhp = tap_push_ip4h(ip4h, src, dst, udplen, IPPROTO_UDP);
- struct udphdr *uh = (struct udphdr *)uhp;
+ 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);
+ const struct iovec iov = {
+ .iov_base = (void *)in,
+ .iov_len = dlen
+ };
uh->source = htons(sport);
uh->dest = htons(dport);
- uh->len = htons(udplen);
- csum_udp4(uh, src, dst, in, len);
- memcpy(data, in, len);
+ uh->len = htons(l4len);
+ csum_udp4(uh, src, dst, &iov, 1, 0);
+ memcpy(data, in, dlen);
- if (tap_send(c, buf, len + (data - buf)) < 0)
- debug("tap: failed to send %zu bytes (IPv4)", len);
+ tap_send_single(c, buf, dlen + (data - buf));
}
/**
@@ -206,21 +200,20 @@ void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport,
* @src: IPv4 source address
* @dst: IPv4 destination address
* @in: ICMP packet, including ICMP header
- * @len: ICMP packet length, including ICMP header
+ * @l4len: ICMP packet length, including ICMP header
*/
void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst,
- const void *in, size_t len)
+ const void *in, size_t l4len)
{
char buf[USHRT_MAX];
- void *ip4h = tap_push_l2h(c, buf, ETH_P_IP);
- char *data = tap_push_ip4h(ip4h, src, dst, len, IPPROTO_ICMP);
- struct icmphdr *icmp4h = (struct icmphdr *)data;
+ struct iphdr *ip4h = tap_push_l2h(c, buf, ETH_P_IP);
+ struct icmphdr *icmp4h = tap_push_ip4h(ip4h, src, dst,
+ l4len, IPPROTO_ICMP);
- memcpy(data, in, len);
- csum_icmp4(icmp4h, icmp4h + 1, len - sizeof(*icmp4h));
+ memcpy(icmp4h, in, l4len);
+ csum_icmp4(icmp4h, icmp4h + 1, l4len - sizeof(*icmp4h));
- if (tap_send(c, buf, len + (data - buf)) < 0)
- debug("tap: failed to send %zu bytes (IPv4)", len);
+ tap_send_single(c, buf, l4len + ((char *)icmp4h - buf));
}
/**
@@ -228,20 +221,18 @@ void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst,
* @c: Execution context
* @src: IPv6 source address
* @dst: IPv6 destination address
- * @len: L4 payload length
+ * @l4len: L4 payload length
* @proto: L4 protocol number
* @flow: IPv6 flow identifier
*
* Return: pointer at which to write the packet's payload
*/
-static void *tap_push_ip6h(char *buf,
+static void *tap_push_ip6h(struct ipv6hdr *ip6h,
const struct in6_addr *src,
const struct in6_addr *dst,
- size_t len, uint8_t proto, uint32_t flow)
+ size_t l4len, uint8_t proto, uint32_t flow)
{
- struct ipv6hdr *ip6h = (struct ipv6hdr *)buf;
-
- ip6h->payload_len = htons(len);
+ ip6h->payload_len = htons(l4len);
ip6h->priority = 0;
ip6h->version = 6;
ip6h->nexthdr = proto;
@@ -263,28 +254,31 @@ static void *tap_push_ip6h(char *buf,
* @dport: UDP destination port
* @flow: Flow label
* @in: UDP payload contents (not including UDP header)
- * @len: UDP payload length (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 len)
+ uint32_t flow, void *in, size_t dlen)
{
- size_t udplen = len + sizeof(struct udphdr);
+ size_t l4len = dlen + sizeof(struct udphdr);
char buf[USHRT_MAX];
- void *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6);
- void *uhp = tap_push_ip6h(ip6h, src, dst, udplen, IPPROTO_UDP, flow);
- struct udphdr *uh = (struct udphdr *)uhp;
+ 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);
+ const struct iovec iov = {
+ .iov_base = in,
+ .iov_len = dlen
+ };
uh->source = htons(sport);
uh->dest = htons(dport);
- uh->len = htons(udplen);
- csum_udp6(uh, src, dst, in, len);
- memcpy(data, in, len);
+ uh->len = htons(l4len);
+ csum_udp6(uh, src, dst, &iov, 1, 0);
+ memcpy(data, in, dlen);
- if (tap_send(c, buf, len + (data - buf)) < 1)
- debug("tap: failed to send %zu bytes (IPv6)", len);
+ tap_send_single(c, buf, dlen + (data - buf));
}
/**
@@ -293,44 +287,50 @@ void tap_udp6_send(const struct ctx *c,
* @src: IPv6 source address
* @dst: IPv6 destination address
* @in: ICMP packet, including ICMP header
- * @len: ICMP packet length, including ICMP header
+ * @l4len: ICMP packet length, including ICMP header
*/
void tap_icmp6_send(const struct ctx *c,
const struct in6_addr *src, const struct in6_addr *dst,
- const void *in, size_t len)
+ const void *in, size_t l4len)
{
char buf[USHRT_MAX];
- void *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6);
- char *data = tap_push_ip6h(ip6h, src, dst, len, IPPROTO_ICMPV6, 0);
- struct icmp6hdr *icmp6h = (struct icmp6hdr *)data;
+ struct ipv6hdr *ip6h = tap_push_l2h(c, buf, ETH_P_IPV6);
+ struct icmp6hdr *icmp6h = tap_push_ip6h(ip6h, src, dst, l4len,
+ IPPROTO_ICMPV6, 0);
- memcpy(data, in, len);
- csum_icmp6(icmp6h, src, dst, icmp6h + 1, len - sizeof(*icmp6h));
+ memcpy(icmp6h, in, l4len);
+ csum_icmp6(icmp6h, src, dst, icmp6h + 1, l4len - sizeof(*icmp6h));
- if (tap_send(c, buf, len + (data - buf)) < 1)
- debug("tap: failed to send %zu bytes (IPv6)", len);
+ tap_send_single(c, buf, l4len + ((char *)icmp6h - buf));
}
/**
* tap_send_frames_pasta() - Send multiple frames to the pasta tap
- * @c: Execution context
- * @iov: Array of buffers, each containing one frame
- * @n: Number of buffers/frames in @iov
+ * @c: Execution context
+ * @iov: Array of buffers
+ * @bufs_per_frame: Number of buffers (iovec entries) per frame
+ * @nframes: Number of frames to send
+ *
+ * @iov must have total length @bufs_per_frame * @nframes, with each set of
+ * @bufs_per_frame contiguous buffers representing a single frame.
*
* Return: number of frames successfully sent
*
* #syscalls:pasta write
*/
static size_t tap_send_frames_pasta(const struct ctx *c,
- const struct iovec *iov, size_t n)
+ const struct iovec *iov,
+ size_t bufs_per_frame, size_t nframes)
{
+ size_t nbufs = bufs_per_frame * nframes;
size_t i;
- for (i = 0; i < n; i++) {
- ssize_t rc = write(c->fd_tap, iov[i].iov_base, iov[i].iov_len);
+ for (i = 0; i < nbufs; i += bufs_per_frame) {
+ ssize_t rc = writev(c->fd_tap, iov + i, bufs_per_frame);
+ size_t framelen = iov_size(iov + i, bufs_per_frame);
if (rc < 0) {
- debug("tap write: %s", strerror(errno));
+ debug_perror("tap write");
switch (errno) {
case EAGAIN:
@@ -340,60 +340,42 @@ static size_t tap_send_frames_pasta(const struct ctx *c,
case EINTR:
case ENOBUFS:
case ENOSPC:
+ case EIO: /* interface down? */
break;
default:
die("Write error on tap device, exiting");
}
- } else if ((size_t)rc < iov[i].iov_len) {
- debug("short write on tuntap: %zd/%zu",
- rc, iov[i].iov_len);
+ } else if ((size_t)rc < framelen) {
+ debug("short write on tuntap: %zd/%zu", rc, framelen);
break;
}
}
- return i;
-}
-
-/**
- * tap_send_iov_pasta() - Send out multiple prepared frames
- * @c: Execution context
- * @iov: Array of frames, each frames is divided in an array of iovecs.
- * The first entry of the iovec is ignored
- * @n: Number of frames in @iov
- *
- * Return: number of frames actually sent
- */
-static size_t tap_send_iov_pasta(const struct ctx *c,
- struct iovec iov[][TCP_IOV_NUM], size_t n)
-{
- unsigned int i;
-
- for (i = 0; i < n; i++) {
- if (!tap_send_frames_pasta(c, &iov[i][TCP_IOV_ETH],
- TCP_IOV_NUM - TCP_IOV_ETH))
- break;
- }
-
- return i;
-
+ return i / bufs_per_frame;
}
/**
* tap_send_frames_passt() - Send multiple frames to the passt tap
- * @c: Execution context
- * @iov: Array of buffers, each containing one frame
- * @n: Number of buffers/frames in @iov
+ * @c: Execution context
+ * @iov: Array of buffers, each containing one frame
+ * @bufs_per_frame: Number of buffers (iovec entries) per frame
+ * @nframes: Number of frames to send
+ *
+ * @iov must have total length @bufs_per_frame * @nframes, with each set of
+ * @bufs_per_frame contiguous buffers representing a single frame.
*
* Return: number of frames successfully sent
*
* #syscalls:passt sendmsg
*/
static size_t tap_send_frames_passt(const struct ctx *c,
- const struct iovec *iov, size_t n)
+ const struct iovec *iov,
+ size_t bufs_per_frame, size_t nframes)
{
+ size_t nbufs = bufs_per_frame * nframes;
struct msghdr mh = {
.msg_iov = (void *)iov,
- .msg_iovlen = n,
+ .msg_iovlen = nbufs,
};
size_t buf_offset;
unsigned int i;
@@ -404,134 +386,61 @@ static size_t tap_send_frames_passt(const struct ctx *c,
return 0;
/* Check for any partial frames due to short send */
- i = iov_skip_bytes(iov, n, sent, &buf_offset);
+ i = iov_skip_bytes(iov, nbufs, sent, &buf_offset);
- if (i < n && buf_offset) {
- /* A partial frame was sent */
- if (write_remainder(c->fd_tap, &iov[i], 1, buf_offset) < 0) {
- err("tap: partial frame send: %s", strerror(errno));
+ if (i < nbufs && (buf_offset || (i % bufs_per_frame))) {
+ /* Number of unsent or partially sent buffers for the frame */
+ size_t rembufs = bufs_per_frame - (i % bufs_per_frame);
+
+ if (write_remainder(c->fd_tap, &iov[i], rembufs, buf_offset) < 0) {
+ err_perror("tap: partial frame send");
return i;
}
- i++;
- }
-
- return i;
-}
-
-/**
- * tap_send_iov_passt() - Send out multiple prepared frames
- * @c: Execution context
- * @iov: Array of frames, each frames is divided in an array of iovecs.
- * The first entry of the iovec is updated to point to an
- * uint32_t storing the frame length.
- * @n: Number of frames in @iov
- *
- * Return: number of frames actually sent
- */
-static size_t tap_send_iov_passt(const struct ctx *c,
- struct iovec iov[][TCP_IOV_NUM],
- size_t n)
-{
- unsigned int i;
-
- for (i = 0; i < n; i++) {
- uint32_t vnet_len;
- int j;
-
- vnet_len = 0;
- for (j = TCP_IOV_ETH; j < TCP_IOV_NUM; j++)
- vnet_len += iov[i][j].iov_len;
-
- vnet_len = htonl(vnet_len);
- iov[i][TCP_IOV_VNET].iov_base = &vnet_len;
- iov[i][TCP_IOV_VNET].iov_len = sizeof(vnet_len);
-
- if (!tap_send_frames_passt(c, iov[i], TCP_IOV_NUM))
- break;
+ i += rembufs;
}
- return i;
-
+ return i / bufs_per_frame;
}
/**
* tap_send_frames() - Send out multiple prepared frames
- * @c: Execution context
- * @iov: Array of buffers, each containing one frame (with L2 headers)
- * @n: Number of buffers/frames in @iov
+ * @c: Execution context
+ * @iov: Array of buffers, each containing one frame (with L2 headers)
+ * @bufs_per_frame: Number of buffers (iovec entries) per frame
+ * @nframes: Number of frames to send
+ *
+ * @iov must have total length @bufs_per_frame * @nframes, with each set of
+ * @bufs_per_frame contiguous buffers representing a single frame.
*
* Return: number of frames actually sent
*/
-size_t tap_send_frames(const struct ctx *c, const struct iovec *iov, size_t n)
+size_t tap_send_frames(const struct ctx *c, const struct iovec *iov,
+ size_t bufs_per_frame, size_t nframes)
{
size_t m;
- if (!n)
+ if (!nframes)
return 0;
switch (c->mode) {
case MODE_PASTA:
- m = tap_send_frames_pasta(c, iov, n);
+ m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes);
break;
case MODE_PASST:
- m = tap_send_frames_passt(c, iov, n);
+ m = tap_send_frames_passt(c, iov, bufs_per_frame, nframes);
break;
case MODE_VU:
- m = tap_send_frames_vu(c, iov, n);
- break;
- default:
- m = 0;
- break;
- }
-
- if (m < n)
- debug("tap: failed to send %zu frames of %zu", n - m, n);
-
- pcap_multiple(iov, 1, n, c->mode == MODE_PASST ? sizeof(uint32_t) : 0);
-
- return m;
-}
-
-/**
- * tap_send_iov() - Send out multiple prepared frames
- * @c: Execution context
- * @iov: Array of frames, each frames is divided in an array of iovecs.
- * iovec array is:
- * TCP_IOV_VNET (0) vnet length
- * TCP_IOV_ETH (1) ethernet header
- * TCP_IOV_IP (2) IP (v4/v6) header
- * TCP_IOV_PAYLOAD (3) IP payload (TCP header + data)
- * TCP_IOV_NUM (4) is the number of entries in the iovec array
- * TCP_IOV_VNET entry is updated with passt, ignored with pasta.
- * @n: Number of frames in @iov
- *
- * Return: number of frames actually sent
- */
-size_t tap_send_iov(const struct ctx *c, struct iovec iov[][TCP_IOV_NUM],
- size_t n)
-{
- size_t m;
- unsigned int i;
-
- if (!n)
- return 0;
-
- switch (c->mode) {
- case MODE_PASST:
- m = tap_send_iov_passt(c, iov, n);
- break;
- case MODE_PASTA:
- m = tap_send_iov_pasta(c, iov, n);
- break;
+ /* fall through */
default:
ASSERT(0);
}
- if (m < n)
- debug("tap: failed to send %zu frames of %zu", n - m, n);
+ if (m < nframes)
+ debug("tap: failed to send %zu frames of %zu",
+ nframes - m, nframes);
- for (i = 0; i < m; i++)
- pcap_iov(&iov[i][TCP_IOV_ETH], TCP_IOV_NUM - TCP_IOV_ETH);
+ pcap_multiple(iov, bufs_per_frame, m,
+ c->mode == MODE_PASST ? sizeof(uint32_t) : 0);
return m;
}
@@ -695,21 +604,21 @@ static int tap4_handler(struct ctx *c, const struct pool *in,
i = 0;
resume:
for (seq_count = 0, seq = NULL; i < in->count; i++) {
- size_t l2_len, l3_len, hlen, l4_len;
+ size_t l2len, l3len, hlen, l4len;
const struct ethhdr *eh;
const struct udphdr *uh;
struct iphdr *iph;
const char *l4h;
- packet_get(in, i, 0, 0, &l2_len);
+ packet_get(in, i, 0, 0, &l2len);
- eh = packet_get(in, i, 0, sizeof(*eh), &l3_len);
+ eh = packet_get(in, i, 0, sizeof(*eh), &l3len);
if (!eh)
continue;
if (ntohs(eh->h_proto) == ETH_P_ARP) {
PACKET_POOL_P(pkt, 1, in->buf, in->buf_size);
- packet_add(pkt, l2_len, (char *)eh);
+ packet_add(pkt, l2len, (char *)eh);
arp(c, pkt);
continue;
}
@@ -719,15 +628,15 @@ resume:
continue;
hlen = iph->ihl * 4UL;
- if (hlen < sizeof(*iph) || htons(iph->tot_len) > l3_len ||
- hlen > l3_len)
+ if (hlen < sizeof(*iph) || htons(iph->tot_len) > l3len ||
+ hlen > l3len)
continue;
/* We don't handle IP fragments, drop them */
if (tap4_is_fragment(iph, now))
continue;
- l4_len = htons(iph->tot_len) - hlen;
+ l4len = htons(iph->tot_len) - hlen;
if (IN4_IS_ADDR_LOOPBACK(&iph->saddr) ||
IN4_IS_ADDR_LOOPBACK(&iph->daddr)) {
@@ -742,7 +651,7 @@ resume:
if (iph->saddr && c->ip4.addr_seen.s_addr != iph->saddr)
c->ip4.addr_seen.s_addr = iph->saddr;
- l4h = packet_get(in, i, sizeof(*eh) + hlen, l4_len, NULL);
+ l4h = packet_get(in, i, sizeof(*eh) + hlen, l4len, NULL);
if (!l4h)
continue;
@@ -754,7 +663,7 @@ resume:
tap_packet_debug(iph, NULL, NULL, 0, NULL, 1);
- packet_add(pkt, l4_len, l4h);
+ packet_add(pkt, l4len, l4h);
icmp_tap_handler(c, PIF_TAP, AF_INET,
&iph->saddr, &iph->daddr,
pkt, now);
@@ -768,7 +677,7 @@ resume:
if (iph->protocol == IPPROTO_UDP) {
PACKET_POOL_P(pkt, 1, in->buf, in->buf_size);
- packet_add(pkt, l2_len, (char *)eh);
+ packet_add(pkt, l2len, (char *)eh);
if (dhcp(c, pkt))
continue;
}
@@ -779,18 +688,18 @@ resume:
continue;
}
-#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)
+#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)
#define L4_SET(iph, uh, seq) \
do { \
- 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)->protocol = (iph)->protocol; \
+ (seq)->source = (uh)->source; \
+ (seq)->dest = (uh)->dest; \
+ (seq)->saddr.s_addr = (iph)->saddr; \
+ (seq)->daddr.s_addr = (iph)->daddr; \
} while (0)
if (seq && L4_MATCH(iph, uh, seq) && seq->p.count < UIO_MAXIOV)
@@ -817,7 +726,7 @@ resume:
#undef L4_SET
append:
- packet_add((struct pool *)&seq->p, l4_len, l4h);
+ packet_add((struct pool *)&seq->p, l4len, l4h);
}
for (j = 0, seq = tap4_l4; j < seq_count; j++, seq++) {
@@ -869,7 +778,7 @@ static int tap6_handler(struct ctx *c, const struct pool *in,
i = 0;
resume:
for (seq_count = 0, seq = NULL; i < in->count; i++) {
- size_t l4_len, plen, check;
+ size_t l4len, plen, check;
struct in6_addr *saddr, *daddr;
const struct ethhdr *eh;
const struct udphdr *uh;
@@ -892,7 +801,7 @@ resume:
if (plen != check)
continue;
- if (!(l4h = ipv6_l4hdr(in, i, sizeof(*eh), &proto, &l4_len)))
+ if (!(l4h = ipv6_l4hdr(in, i, sizeof(*eh), &proto, &l4len)))
continue;
if (IN6_IS_ADDR_LOOPBACK(saddr) || IN6_IS_ADDR_LOOPBACK(daddr)) {
@@ -920,28 +829,29 @@ resume:
if (c->no_icmp)
continue;
- if (l4_len < sizeof(struct icmp6hdr))
+ if (l4len < sizeof(struct icmp6hdr))
continue;
- if (ndp(c, (struct icmp6hdr *)l4h, saddr))
+ packet_add(pkt, l4len, l4h);
+
+ if (ndp(c, (struct icmp6hdr *)l4h, saddr, pkt))
continue;
tap_packet_debug(NULL, ip6h, NULL, proto, NULL, 1);
- packet_add(pkt, l4_len, l4h);
icmp_tap_handler(c, PIF_TAP, AF_INET6,
saddr, daddr, pkt, now);
continue;
}
- if (l4_len < sizeof(*uh))
+ if (l4len < sizeof(*uh))
continue;
uh = (struct udphdr *)l4h;
if (proto == IPPROTO_UDP) {
PACKET_POOL_P(pkt, 1, in->buf, in->buf_size);
- packet_add(pkt, l4_len, l4h);
+ packet_add(pkt, l4len, l4h);
if (dhcpv6(c, pkt, saddr, daddr))
continue;
@@ -953,18 +863,19 @@ resume:
}
#define L4_MATCH(ip6h, proto, uh, seq) \
- (seq->protocol == proto && \
- seq->source == uh->source && seq->dest == uh->dest && \
- IN6_ARE_ADDR_EQUAL(&seq->saddr, saddr) && \
- IN6_ARE_ADDR_EQUAL(&seq->daddr, daddr))
+ ((seq)->protocol == (proto) && \
+ (seq)->source == (uh)->source && \
+ (seq)->dest == (uh)->dest && \
+ IN6_ARE_ADDR_EQUAL(&(seq)->saddr, saddr) && \
+ IN6_ARE_ADDR_EQUAL(&(seq)->daddr, daddr))
#define L4_SET(ip6h, proto, uh, seq) \
do { \
- seq->protocol = proto; \
- seq->source = uh->source; \
- seq->dest = uh->dest; \
- seq->saddr = *saddr; \
- seq->daddr = *daddr; \
+ (seq)->protocol = (proto); \
+ (seq)->source = (uh)->source; \
+ (seq)->dest = (uh)->dest; \
+ (seq)->saddr = *saddr; \
+ (seq)->daddr = *daddr; \
} while (0)
if (seq && L4_MATCH(ip6h, proto, uh, seq) &&
@@ -992,7 +903,7 @@ resume:
#undef L4_SET
append:
- packet_add((struct pool *)&seq->p, l4_len, l4h);
+ packet_add((struct pool *)&seq->p, l4len, l4h);
}
for (j = 0, seq = tap6_l4; j < seq_count; j++, seq++) {
@@ -1025,39 +936,52 @@ append:
return in->count;
}
-void pool_flush_all(void)
+/**
+ * tap_flush_pools() - Flush both IPv4 and IPv6 packet pools
+ */
+void tap_flush_pools(void)
{
pool_flush(pool_tap4);
pool_flush(pool_tap6);
}
-void tap_handler_all(struct ctx *c, const struct timespec *now)
+/**
+ * tap_handler() - IPv4/IPv6 and ARP packet handler for tap file descriptor
+ * @c: Execution context
+ * @now: Current timestamp
+ */
+void tap_handler(struct ctx *c, const struct timespec *now)
{
tap4_handler(c, pool_tap4, now);
tap6_handler(c, pool_tap6, now);
}
-void packet_add_all_do(struct ctx *c, ssize_t len, char *p,
- const char *func, int line)
+/**
+ * tap_add_packet() - Queue/capture packet, update notion of guest MAC address
+ * @c: Execution context
+ * @l2len: Total L2 packet length
+ * @p: Packet buffer
+ */
+void tap_add_packet(struct ctx *c, ssize_t l2len, char *p)
{
const struct ethhdr *eh;
- pcap(p, len);
+ pcap(p, l2len);
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:
- packet_add_do(pool_tap4, len, p, func, line);
+ packet_add(pool_tap4, l2len, p);
break;
case ETH_P_IPV6:
- packet_add_do(pool_tap6, len, p, func, line);
+ packet_add(pool_tap6, l2len, p);
break;
default:
break;
@@ -1070,174 +994,194 @@ void packet_add_all_do(struct ctx *c, ssize_t len, char *p,
*/
void tap_sock_reset(struct ctx *c)
{
- if (c->one_off) {
- info("Client closed connection, exiting");
+ info("Client connection closed%s", c->one_off ? ", exiting" : "");
+
+ if (c->one_off)
exit(EXIT_SUCCESS);
- }
/* Close the connected socket, wait for a new connection */
epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_tap, NULL);
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)
{
- ssize_t n, rem;
+ 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();
-redo:
- p = pkt_buf;
- rem = 0;
+ if (partial_len) {
+ /* We have a partial frame from an earlier pass. Move it to the
+ * start of the buffer, top up with new data, then process all
+ * of it.
+ */
+ memmove(pkt_buf, partial_frame, partial_len);
+ }
- pool_flush_all();
+ do {
+ n = recv(c->fd_tap, pkt_buf + partial_len,
+ TAP_BUF_BYTES - partial_len, MSG_DONTWAIT);
+ } while ((n < 0) && errno == EINTR);
- n = recv(c->fd_tap, p, TAP_BUF_FILL, MSG_DONTWAIT);
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);
+ }
return;
}
- while (n > (ssize_t)sizeof(uint32_t)) {
- ssize_t len = ntohl(*(uint32_t *)p);
+ p = pkt_buf;
+ n += partial_len;
- p += sizeof(uint32_t);
- n -= sizeof(uint32_t);
+ while (n >= (ssize_t)sizeof(uint32_t)) {
+ uint32_t l2len = ntohl_unaligned(p);
- /* At most one packet might not fit in a single read, and this
- * needs to be blocking.
- */
- if (len > n) {
- rem = recv(c->fd_tap, p + n, len - n, 0);
- if ((n += rem) != len)
- return;
+ if (l2len < sizeof(struct ethhdr) || l2len > ETH_MAX_MTU) {
+ err("Bad frame size from guest, resetting connection");
+ tap_sock_reset(c);
+ return;
}
- /* Complete the partial read above before discarding a malformed
- * frame, otherwise the stream will be inconsistent.
- */
- if (len < (ssize_t)sizeof(struct ethhdr) ||
- len > (ssize_t)ETH_MAX_MTU)
- goto next;
+ if (l2len + sizeof(uint32_t) > (size_t)n)
+ /* Leave this incomplete frame for later */
+ break;
- packet_add_all(c, len, p);
+ p += sizeof(uint32_t);
+ n -= sizeof(uint32_t);
-next:
- p += len;
- n -= len;
+ tap_add_packet(c, l2len, p);
+
+ p += l2len;
+ n -= l2len;
}
- tap_handler_all(c, now);
+ partial_len = n;
+ partial_frame = p;
- /* We can't use EPOLLET otherwise. */
- if (rem)
- goto redo;
+ tap_handler(c, now);
}
/**
- * 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))
- die("Disconnect event on /dev/net/tun device, exiting");
+ if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+ tap_sock_reset(c);
+ return;
+ }
-redo:
- n = 0;
+ if (events & EPOLLIN)
+ tap_passt_input(c, now);
+}
- pool_flush_all();
-restart:
- while ((len = read(c->fd_tap, pkt_buf + n, TAP_BUF_BYTES - 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;
- if (len < (ssize_t)sizeof(struct ethhdr) ||
- len > (ssize_t)ETH_MAX_MTU) {
- n += len;
- continue;
- }
+ tap_flush_pools();
+ for (n = 0; n <= (ssize_t)(TAP_BUF_BYTES - ETH_MAX_MTU); n += len) {
+ len = read(c->fd_tap, pkt_buf + n, ETH_MAX_MTU);
- packet_add_all(c, len, pkt_buf + n);
+ if (len == 0) {
+ die("EOF on tap device, exiting");
+ } else if (len < 0) {
+ if (errno == EINTR) {
+ len = 0;
+ continue;
+ }
- if ((n += len) == TAP_BUF_BYTES)
- break;
- }
+ if (errno == EAGAIN && errno == EWOULDBLOCK)
+ break; /* all done for now */
- if (len < 0 && errno == EINTR)
- goto restart;
+ die("Error on tap device, exiting");
+ }
- ret = errno;
+ /* Ignore frames of bad length */
+ if (len < (ssize_t)sizeof(struct ethhdr) ||
+ len > (ssize_t)ETH_MAX_MTU)
+ continue;
- tap_handler_all(c, now);
+ tap_add_packet(c, len, pkt_buf + n);
+ }
- if (len > 0 || ret == EAGAIN)
- return;
+ tap_handler(c, now);
+}
- if (n == TAP_BUF_BYTES)
- goto redo;
+/**
+ * tap_handler_pasta() - Packet handler for /dev/net/tun file descriptor
+ * @c: Execution context
+ * @events: epoll events
+ * @now: Current timestamp
+ */
+void tap_handler_pasta(struct ctx *c, uint32_t events,
+ const struct timespec *now)
+{
+ if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
+ die("Disconnect event on /dev/net/tun device, exiting");
- die("Error on tap device, exiting");
+ if (events & EPOLLIN)
+ tap_pasta_input(c, now);
}
/**
- * tap_sock_unix_init() - Create and bind AF_UNIX socket, listen for connection
- * @c: Execution context
+ * 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
*/
-static void tap_sock_unix_init(struct ctx *c)
+int tap_sock_unix_open(char *sock_path)
{
- int fd = socket(AF_UNIX, SOCK_STREAM, 0);
- union epoll_ref ref = { .type = EPOLL_TYPE_TAP_LISTEN };
- struct epoll_event ev = { 0 };
+ int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
int i;
if (fd < 0)
- die("UNIX socket: %s", strerror(errno));
-
- /* In passt mode, we don't know the guest's MAC until it sends
- * us packets. Use the broadcast address so our first packets
- * will reach it.
- */
- memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
+ 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 (*c->sock_path)
- memcpy(path, c->sock_path, UNIX_PATH_MAX);
- else
- snprintf(path, UNIX_PATH_MAX - 1, UNIX_SOCK_PATH, i);
+ if (*sock_path)
+ memcpy(path, sock_path, UNIX_PATH_MAX);
+ else if (snprintf_check(path, UNIX_PATH_MAX - 1,
+ UNIX_SOCK_PATH, i))
+ die_perror("Can't build UNIX domain socket path");
- ex = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ ex = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
+ 0);
if (ex < 0)
- die("UNIX domain socket check: %s", strerror(errno));
+ 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 (*c->sock_path)
+ if (*sock_path)
die("Socket path %s already in use", path);
close(ex);
@@ -1246,45 +1190,75 @@ static void tap_sock_unix_init(struct ctx *c)
close(ex);
unlink(path);
- if (!bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) ||
- *c->sock_path)
+ ret = bind(fd, (const struct sockaddr *)&addr, sizeof(addr));
+ if (*sock_path && ret)
+ die_perror("Failed to bind UNIX domain socket");
+
+ if (!ret)
break;
}
if (i == UNIX_SOCK_MAX)
- die("UNIX socket bind: %s", strerror(errno));
-
- info("UNIX domain socket bound at %s\n", addr.sun_path);
+ die_perror("Failed to bind UNIX domain socket");
- listen(fd, 0);
+ info("UNIX domain socket bound at %s", addr.sun_path);
+ if (!*sock_path)
+ memcpy(sock_path, addr.sun_path, UNIX_PATH_MAX);
- ref.fd = c->fd_tap_listen = fd;
- ev.events = EPOLLIN | EPOLLET;
- ev.data.u64 = ref.u64;
- epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap_listen, &ev);
+ return fd;
+}
- if (c->mode == 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",
- addr.sun_path);
- } else {
- info("You can now start qemu (>= 7.2, with commit 13c6be96618c):");
+/**
+ * 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",
- addr.sun_path);
+ 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;
}
}
/**
+ * tap_sock_unix_init() - Start listening for connections on AF_UNIX socket
+ * @c: Execution context
+ */
+static void tap_sock_unix_init(const struct ctx *c)
+{
+ union epoll_ref ref = { .type = EPOLL_TYPE_TAP_LISTEN };
+ struct epoll_event ev = { 0 };
+
+ listen(c->fd_tap_listen, 0);
+
+ ref.fd = c->fd_tap_listen;
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.u64 = ref.u64;
+ epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap_listen, &ev);
+}
+
+/**
* tap_listen_handler() - Handle new connection on listening socket
* @c: Execution context
* @events: epoll events
*/
void tap_listen_handler(struct ctx *c, uint32_t events)
{
- union epoll_ref ref;
struct epoll_event ev = { 0 };
+ union epoll_ref ref = { 0 };
int v = INT_MAX / 2;
struct ucred ucred;
socklen_t len;
@@ -1324,13 +1298,11 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
trace("tap: failed to set SO_SNDBUF to %i", v);
ref.fd = c->fd_tap;
- if (c->mode == MODE_VU) {
+ if (c->mode == MODE_VU)
ref.type = EPOLL_TYPE_VHOST_CMD;
- ev.events = EPOLLIN | EPOLLRDHUP;
- } else {
+ else
ref.type = EPOLL_TYPE_TAP_PASST;
- ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
- }
+ ev.events = EPOLLIN | EPOLLRDHUP;
ev.data.u64 = ref.u64;
epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
}
@@ -1356,11 +1328,11 @@ static int tap_ns_tun(void *arg)
fd = open("/dev/net/tun", flags);
if (fd < 0)
- die("Failed to open() /dev/net/tun: %s", strerror(errno));
+ die_perror("Failed to open() /dev/net/tun");
- rc = ioctl(fd, TUNSETIFF, &ifr);
+ rc = ioctl(fd, (int)TUNSETIFF, &ifr);
if (rc < 0)
- die("TUNSETIFF failed: %s", strerror(errno));
+ die_perror("TUNSETIFF ioctl on /dev/net/tun failed");
if (!(c->pasta_ifi = if_nametoindex(c->pasta_ifn)))
die("Tap device opened but no network interface found");
@@ -1391,51 +1363,35 @@ static void tap_sock_tun_init(struct ctx *c)
epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
}
-void tap_sock_update_buf(void *base, size_t size)
+/**
+ * tap_sock_update_pool() - Set the buffer base and size for the pool of packets
+ * @base: Buffer base
+ * @size Buffer size
+ */
+void tap_sock_update_pool(void *base, size_t size)
{
int i;
- pool_tap4_storage.buf = base;
- pool_tap4_storage.buf_size = size;
- pool_tap6_storage.buf = base;
- pool_tap6_storage.buf_size = size;
+ pool_tap4_storage = PACKET_INIT(pool_tap4, TAP_MSGS, base, size);
+ pool_tap6_storage = PACKET_INIT(pool_tap6, TAP_MSGS, base, size);
for (i = 0; i < TAP_SEQS; i++) {
- tap4_l4[i].p.buf = base;
- tap4_l4[i].p.buf_size = size;
- tap6_l4[i].p.buf = base;
- tap6_l4[i].p.buf_size = size;
+ tap4_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, base, size);
+ tap6_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, base, size);
}
}
/**
- * tap_sock_init() - Create and set up AF_UNIX socket or tuntap file descriptor
+ * tap_backend_init() - Create and set up AF_UNIX socket or
+ * tuntap file descriptor
* @c: Execution context
*/
-void tap_sock_init(struct ctx *c)
+void tap_backend_init(struct ctx *c)
{
- 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);
- if (c->mode == MODE_VU) {
- pool_tap4_storage.buf = NULL;
- pool_tap4_storage.buf_size = 0;
- pool_tap6_storage.buf = NULL;
- pool_tap6_storage.buf_size = 0;
- }
-
- 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);
- if (c->mode == MODE_VU) {
- tap4_l4[i].p.buf = NULL;
- tap4_l4[i].p.buf_size = 0;
- tap6_l4[i].p.buf = NULL;
- tap6_l4[i].p.buf_size = 0;
- }
- }
+ if (c->mode == MODE_VU)
+ tap_sock_update_pool(NULL, 0);
+ else
+ tap_sock_update_pool(pkt_buf, sizeof(pkt_buf));
if (c->fd_tap != -1) { /* Passed as --fd */
struct epoll_event ev = { 0 };
@@ -1446,27 +1402,38 @@ void tap_sock_init(struct ctx *c)
switch (c->mode) {
case MODE_PASST:
ref.type = EPOLL_TYPE_TAP_PASST;
- ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
break;
case MODE_PASTA:
ref.type = EPOLL_TYPE_TAP_PASTA;
- ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
break;
case MODE_VU:
ref.type = EPOLL_TYPE_VHOST_CMD;
- ev.events = EPOLLIN | EPOLLRDHUP;
break;
}
+ ev.events = EPOLLIN | EPOLLRDHUP;
ev.data.u64 = ref.u64;
epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev);
return;
}
- if (c->mode == MODE_PASTA) {
+ switch (c->mode) {
+ case MODE_PASTA:
tap_sock_tun_init(c);
- } else {
- if (c->fd_tap_listen == -1)
- tap_sock_unix_init(c);
+ break;
+ case MODE_VU:
+ vu_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->guest_mac, 0xff, sizeof(c->guest_mac));
+ break;
}
+
+ tap_backend_show_hints(c);
}