]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/net_backends.c
bhyve: abstraction for network backends
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / net_backends.c
1 /*-
2  * Copyright (c) 2019 Vincenzo Maffione <vmaffione@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
18  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
19  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
20  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27
28 /*
29  * This file implements multiple network backends (tap, netmap, ...),
30  * to be used by network frontends such as virtio-net and e1000.
31  * The API to access the backend (e.g. send/receive packets, negotiate
32  * features) is exported by net_backends.h.
33  */
34
35 #include <sys/types.h>          /* u_short etc */
36 #ifndef WITHOUT_CAPSICUM
37 #include <sys/capsicum.h>
38 #endif
39 #include <sys/cdefs.h>
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
42 #include <sys/uio.h>
43
44 #include <net/if.h>
45 #include <net/netmap.h>
46 #include <net/netmap_virt.h>
47 #define NETMAP_WITH_LIBS
48 #include <net/netmap_user.h>
49
50 #ifndef WITHOUT_CAPSICUM
51 #include <capsicum_helpers.h>
52 #endif
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <stdint.h>
59 #include <string.h>
60 #include <unistd.h>
61 #include <sysexits.h>
62 #include <assert.h>
63 #include <pthread.h>
64 #include <pthread_np.h>
65 #include <poll.h>
66 #include <assert.h>
67
68
69 #include "iov.h"
70 #include "mevent.h"
71 #include "net_backends.h"
72
73 #include <sys/linker_set.h>
74
75 /*
76  * Each network backend registers a set of function pointers that are
77  * used to implement the net backends API.
78  * This might need to be exposed if we implement backends in separate files.
79  */
80 struct net_backend {
81         const char *prefix;     /* prefix matching this backend */
82
83         /*
84          * Routines used to initialize and cleanup the resources needed
85          * by a backend. The cleanup function is used internally,
86          * and should not be called by the frontend.
87          */
88         int (*init)(struct net_backend *be, const char *devname,
89             net_be_rxeof_t cb, void *param);
90         void (*cleanup)(struct net_backend *be);
91
92         /*
93          * Called to serve a guest transmit request. The scatter-gather
94          * vector provided by the caller has 'iovcnt' elements and contains
95          * the packet to send.
96          */
97         ssize_t (*send)(struct net_backend *be, struct iovec *iov, int iovcnt);
98
99         /*
100          * Called to receive a packet from the backend. When the function
101          * returns a positive value 'len', the scatter-gather vector
102          * provided by the caller contains a packet with such length.
103          * The function returns 0 if the backend doesn't have a new packet to
104          * receive.
105          */
106         ssize_t (*recv)(struct net_backend *be, struct iovec *iov, int iovcnt);
107
108         /*
109          * Ask the backend for the virtio-net features it is able to
110          * support. Possible features are TSO, UFO and checksum offloading
111          * in both rx and tx direction and for both IPv4 and IPv6.
112          */
113         uint64_t (*get_cap)(struct net_backend *be);
114
115         /*
116          * Tell the backend to enable/disable the specified virtio-net
117          * features (capabilities).
118          */
119         int (*set_cap)(struct net_backend *be, uint64_t features,
120             unsigned int vnet_hdr_len);
121
122         struct pci_vtnet_softc *sc;
123         int fd;
124
125         /*
126          * Length of the virtio-net header used by the backend and the
127          * frontend, respectively. A zero value means that the header
128          * is not used.
129          */
130         unsigned int be_vnet_hdr_len;
131         unsigned int fe_vnet_hdr_len;
132
133         /* Size of backend-specific private data. */
134         size_t priv_size;
135
136         /* Room for backend-specific data. */
137         char opaque[0];
138 };
139
140 SET_DECLARE(net_backend_set, struct net_backend);
141
142 #define VNET_HDR_LEN    sizeof(struct virtio_net_rxhdr)
143
144 #define WPRINTF(params) printf params
145
146 /*
147  * The tap backend
148  */
149
150 struct tap_priv {
151         struct mevent *mevp;
152 };
153
154 static void
155 tap_cleanup(struct net_backend *be)
156 {
157         struct tap_priv *priv = (struct tap_priv *)be->opaque;
158
159         if (priv->mevp) {
160                 mevent_delete(priv->mevp);
161         }
162         if (be->fd != -1) {
163                 close(be->fd);
164                 be->fd = -1;
165         }
166 }
167
168 static int
169 tap_init(struct net_backend *be, const char *devname,
170          net_be_rxeof_t cb, void *param)
171 {
172         struct tap_priv *priv = (struct tap_priv *)be->opaque;
173         char tbuf[80];
174         int fd;
175         int opt = 1;
176 #ifndef WITHOUT_CAPSICUM
177         cap_rights_t rights;
178 #endif
179
180         if (cb == NULL) {
181                 WPRINTF(("TAP backend requires non-NULL callback\n"));
182                 return (-1);
183         }
184
185         strcpy(tbuf, "/dev/");
186         strlcat(tbuf, devname, sizeof(tbuf));
187
188         fd = open(tbuf, O_RDWR);
189         if (fd == -1) {
190                 WPRINTF(("open of tap device %s failed\n", tbuf));
191                 goto error;
192         }
193
194         /*
195          * Set non-blocking and register for read
196          * notifications with the event loop
197          */
198         if (ioctl(fd, FIONBIO, &opt) < 0) {
199                 WPRINTF(("tap device O_NONBLOCK failed\n"));
200                 goto error;
201         }
202
203 #ifndef WITHOUT_CAPSICUM
204         cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
205         if (caph_rights_limit(fd, &rights) == -1)
206                 errx(EX_OSERR, "Unable to apply rights for sandbox");
207 #endif
208
209         priv->mevp = mevent_add(fd, EVF_READ, cb, param);
210         if (priv->mevp == NULL) {
211                 WPRINTF(("Could not register event\n"));
212                 goto error;
213         }
214
215         be->fd = fd;
216
217         return (0);
218
219 error:
220         tap_cleanup(be);
221         return (-1);
222 }
223
224 /*
225  * Called to send a buffer chain out to the tap device
226  */
227 static ssize_t
228 tap_send(struct net_backend *be, struct iovec *iov, int iovcnt)
229 {
230         return (writev(be->fd, iov, iovcnt));
231 }
232
233 static ssize_t
234 tap_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
235 {
236         ssize_t ret;
237
238         /* Should never be called without a valid tap fd */
239         assert(be->fd != -1);
240
241         ret = readv(be->fd, iov, iovcnt);
242
243         if (ret < 0 && errno == EWOULDBLOCK) {
244                 return (0);
245         }
246
247         return (ret);
248 }
249
250 static uint64_t
251 tap_get_cap(struct net_backend *be)
252 {
253
254         return (0); /* no capabilities for now */
255 }
256
257 static int
258 tap_set_cap(struct net_backend *be, uint64_t features,
259                 unsigned vnet_hdr_len)
260 {
261
262         return ((features || vnet_hdr_len) ? -1 : 0);
263 }
264
265 static struct net_backend tap_backend = {
266         .prefix = "tap",
267         .priv_size = sizeof(struct tap_priv),
268         .init = tap_init,
269         .cleanup = tap_cleanup,
270         .send = tap_send,
271         .recv = tap_recv,
272         .get_cap = tap_get_cap,
273         .set_cap = tap_set_cap,
274 };
275
276 /* A clone of the tap backend, with a different prefix. */
277 static struct net_backend vmnet_backend = {
278         .prefix = "vmnet",
279         .priv_size = sizeof(struct tap_priv),
280         .init = tap_init,
281         .cleanup = tap_cleanup,
282         .send = tap_send,
283         .recv = tap_recv,
284         .get_cap = tap_get_cap,
285         .set_cap = tap_set_cap,
286 };
287
288 DATA_SET(net_backend_set, tap_backend);
289 DATA_SET(net_backend_set, vmnet_backend);
290
291 /*
292  * The netmap backend
293  */
294
295 /* The virtio-net features supported by netmap. */
296 #define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \
297                 VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \
298                 VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \
299                 VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO)
300
301 struct netmap_priv {
302         char ifname[IFNAMSIZ];
303         struct nm_desc *nmd;
304         uint16_t memid;
305         struct netmap_ring *rx;
306         struct netmap_ring *tx;
307         struct mevent *mevp;
308         net_be_rxeof_t cb;
309         void *cb_param;
310 };
311
312 static void
313 nmreq_init(struct nmreq *req, char *ifname)
314 {
315
316         memset(req, 0, sizeof(*req));
317         strlcpy(req->nr_name, ifname, sizeof(req->nr_name));
318         req->nr_version = NETMAP_API;
319 }
320
321 static int
322 netmap_set_vnet_hdr_len(struct net_backend *be, int vnet_hdr_len)
323 {
324         int err;
325         struct nmreq req;
326         struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
327
328         nmreq_init(&req, priv->ifname);
329         req.nr_cmd = NETMAP_BDG_VNET_HDR;
330         req.nr_arg1 = vnet_hdr_len;
331         err = ioctl(be->fd, NIOCREGIF, &req);
332         if (err) {
333                 WPRINTF(("Unable to set vnet header length %d\n",
334                                 vnet_hdr_len));
335                 return (err);
336         }
337
338         be->be_vnet_hdr_len = vnet_hdr_len;
339
340         return (0);
341 }
342
343 static int
344 netmap_has_vnet_hdr_len(struct net_backend *be, unsigned vnet_hdr_len)
345 {
346         int prev_hdr_len = be->be_vnet_hdr_len;
347         int ret;
348
349         if (vnet_hdr_len == prev_hdr_len) {
350                 return (1);
351         }
352
353         ret = netmap_set_vnet_hdr_len(be, vnet_hdr_len);
354         if (ret) {
355                 return (0);
356         }
357
358         netmap_set_vnet_hdr_len(be, prev_hdr_len);
359
360         return (1);
361 }
362
363 static uint64_t
364 netmap_get_cap(struct net_backend *be)
365 {
366
367         return (netmap_has_vnet_hdr_len(be, VNET_HDR_LEN) ?
368             NETMAP_FEATURES : 0);
369 }
370
371 static int
372 netmap_set_cap(struct net_backend *be, uint64_t features,
373                unsigned vnet_hdr_len)
374 {
375
376         return (netmap_set_vnet_hdr_len(be, vnet_hdr_len));
377 }
378
379 static int
380 netmap_init(struct net_backend *be, const char *devname,
381             net_be_rxeof_t cb, void *param)
382 {
383         struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
384
385         strlcpy(priv->ifname, devname, sizeof(priv->ifname));
386         priv->ifname[sizeof(priv->ifname) - 1] = '\0';
387
388         priv->nmd = nm_open(priv->ifname, NULL, NETMAP_NO_TX_POLL, NULL);
389         if (priv->nmd == NULL) {
390                 WPRINTF(("Unable to nm_open(): interface '%s', errno (%s)\n",
391                         devname, strerror(errno)));
392                 free(priv);
393                 return (-1);
394         }
395
396         priv->memid = priv->nmd->req.nr_arg2;
397         priv->tx = NETMAP_TXRING(priv->nmd->nifp, 0);
398         priv->rx = NETMAP_RXRING(priv->nmd->nifp, 0);
399         priv->cb = cb;
400         priv->cb_param = param;
401         be->fd = priv->nmd->fd;
402
403         priv->mevp = mevent_add(be->fd, EVF_READ, cb, param);
404         if (priv->mevp == NULL) {
405                 WPRINTF(("Could not register event\n"));
406                 return (-1);
407         }
408
409         return (0);
410 }
411
412 static void
413 netmap_cleanup(struct net_backend *be)
414 {
415         struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
416
417         if (priv->mevp) {
418                 mevent_delete(priv->mevp);
419         }
420         if (priv->nmd) {
421                 nm_close(priv->nmd);
422         }
423         be->fd = -1;
424 }
425
426 static ssize_t
427 netmap_send(struct net_backend *be, struct iovec *iov,
428             int iovcnt)
429 {
430         struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
431         struct netmap_ring *ring;
432         ssize_t totlen = 0;
433         int nm_buf_size;
434         int nm_buf_len;
435         uint32_t head;
436         void *nm_buf;
437         int j;
438
439         ring = priv->tx;
440         head = ring->head;
441         if (head == ring->tail) {
442                 WPRINTF(("No space, drop %zu bytes\n", count_iov(iov, iovcnt)));
443                 goto txsync;
444         }
445         nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
446         nm_buf_size = ring->nr_buf_size;
447         nm_buf_len = 0;
448
449         for (j = 0; j < iovcnt; j++) {
450                 int iov_frag_size = iov[j].iov_len;
451                 void *iov_frag_buf = iov[j].iov_base;
452
453                 totlen += iov_frag_size;
454
455                 /*
456                  * Split each iovec fragment over more netmap slots, if
457                  * necessary.
458                  */
459                 for (;;) {
460                         int copylen;
461
462                         copylen = iov_frag_size < nm_buf_size ? iov_frag_size : nm_buf_size;
463                         memcpy(nm_buf, iov_frag_buf, copylen);
464
465                         iov_frag_buf += copylen;
466                         iov_frag_size -= copylen;
467                         nm_buf += copylen;
468                         nm_buf_size -= copylen;
469                         nm_buf_len += copylen;
470
471                         if (iov_frag_size == 0) {
472                                 break;
473                         }
474
475                         ring->slot[head].len = nm_buf_len;
476                         ring->slot[head].flags = NS_MOREFRAG;
477                         head = nm_ring_next(ring, head);
478                         if (head == ring->tail) {
479                                 /*
480                                  * We ran out of netmap slots while
481                                  * splitting the iovec fragments.
482                                  */
483                                 WPRINTF(("No space, drop %zu bytes\n",
484                                    count_iov(iov, iovcnt)));
485                                 goto txsync;
486                         }
487                         nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
488                         nm_buf_size = ring->nr_buf_size;
489                         nm_buf_len = 0;
490                 }
491         }
492
493         /* Complete the last slot, which must not have NS_MOREFRAG set. */
494         ring->slot[head].len = nm_buf_len;
495         ring->slot[head].flags = 0;
496         head = nm_ring_next(ring, head);
497
498         /* Now update ring->head and ring->cur. */
499         ring->head = ring->cur = head;
500 txsync:
501         ioctl(be->fd, NIOCTXSYNC, NULL);
502
503         return (totlen);
504 }
505
506 static ssize_t
507 netmap_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
508 {
509         struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
510         struct netmap_slot *slot = NULL;
511         struct netmap_ring *ring;
512         void *iov_frag_buf;
513         int iov_frag_size;
514         ssize_t totlen = 0;
515         uint32_t head;
516
517         assert(iovcnt);
518
519         ring = priv->rx;
520         head = ring->head;
521         iov_frag_buf = iov->iov_base;
522         iov_frag_size = iov->iov_len;
523
524         do {
525                 int nm_buf_len;
526                 void *nm_buf;
527
528                 if (head == ring->tail) {
529                         return (0);
530                 }
531
532                 slot = ring->slot + head;
533                 nm_buf = NETMAP_BUF(ring, slot->buf_idx);
534                 nm_buf_len = slot->len;
535
536                 for (;;) {
537                         int copylen = nm_buf_len < iov_frag_size ?
538                             nm_buf_len : iov_frag_size;
539
540                         memcpy(iov_frag_buf, nm_buf, copylen);
541                         nm_buf += copylen;
542                         nm_buf_len -= copylen;
543                         iov_frag_buf += copylen;
544                         iov_frag_size -= copylen;
545                         totlen += copylen;
546
547                         if (nm_buf_len == 0) {
548                                 break;
549                         }
550
551                         iov++;
552                         iovcnt--;
553                         if (iovcnt == 0) {
554                                 /* No space to receive. */
555                                 WPRINTF(("Short iov, drop %zd bytes\n",
556                                     totlen));
557                                 return (-ENOSPC);
558                         }
559                         iov_frag_buf = iov->iov_base;
560                         iov_frag_size = iov->iov_len;
561                 }
562
563                 head = nm_ring_next(ring, head);
564
565         } while (slot->flags & NS_MOREFRAG);
566
567         /* Release slots to netmap. */
568         ring->head = ring->cur = head;
569
570         return (totlen);
571 }
572
573 static struct net_backend netmap_backend = {
574         .prefix = "netmap",
575         .priv_size = sizeof(struct netmap_priv),
576         .init = netmap_init,
577         .cleanup = netmap_cleanup,
578         .send = netmap_send,
579         .recv = netmap_recv,
580         .get_cap = netmap_get_cap,
581         .set_cap = netmap_set_cap,
582 };
583
584 /* A clone of the netmap backend, with a different prefix. */
585 static struct net_backend vale_backend = {
586         .prefix = "vale",
587         .priv_size = sizeof(struct netmap_priv),
588         .init = netmap_init,
589         .cleanup = netmap_cleanup,
590         .send = netmap_send,
591         .recv = netmap_recv,
592         .get_cap = netmap_get_cap,
593         .set_cap = netmap_set_cap,
594 };
595
596 DATA_SET(net_backend_set, netmap_backend);
597 DATA_SET(net_backend_set, vale_backend);
598
599 /*
600  * Initialize a backend and attach to the frontend.
601  * This is called during frontend initialization.
602  *  @pbe is a pointer to the backend to be initialized
603  *  @devname is the backend-name as supplied on the command line,
604  *      e.g. -s 2:0,frontend-name,backend-name[,other-args]
605  *  @cb is the receive callback supplied by the frontend,
606  *      and it is invoked in the event loop when a receive
607  *      event is generated in the hypervisor,
608  *  @param is a pointer to the frontend, and normally used as
609  *      the argument for the callback.
610  */
611 int
612 netbe_init(struct net_backend **ret, const char *devname, net_be_rxeof_t cb,
613     void *param)
614 {
615         struct net_backend **pbe, *nbe, *tbe = NULL;
616         int err;
617
618         /*
619          * Find the network backend that matches the user-provided
620          * device name. net_backend_set is built using a linker set.
621          */
622         SET_FOREACH(pbe, net_backend_set) {
623                 if (strncmp(devname, (*pbe)->prefix,
624                     strlen((*pbe)->prefix)) == 0) {
625                         tbe = *pbe;
626                         assert(tbe->init != NULL);
627                         assert(tbe->cleanup != NULL);
628                         assert(tbe->send != NULL);
629                         assert(tbe->recv != NULL);
630                         assert(tbe->get_cap != NULL);
631                         assert(tbe->set_cap != NULL);
632                         break;
633                 }
634         }
635
636         *ret = NULL;
637         if (tbe == NULL)
638                 return (EINVAL);
639         nbe = calloc(1, sizeof(*nbe) + tbe->priv_size);
640         *nbe = *tbe;    /* copy the template */
641         nbe->fd = -1;
642         nbe->sc = param;
643         nbe->be_vnet_hdr_len = 0;
644         nbe->fe_vnet_hdr_len = 0;
645
646         /* Initialize the backend. */
647         err = nbe->init(nbe, devname, cb, param);
648         if (err) {
649                 free(nbe);
650                 return (err);
651         }
652
653         *ret = nbe;
654
655         return (0);
656 }
657
658 void
659 netbe_cleanup(struct net_backend *be)
660 {
661
662         if (be != NULL) {
663                 be->cleanup(be);
664                 free(be);
665         }
666 }
667
668 uint64_t
669 netbe_get_cap(struct net_backend *be)
670 {
671
672         assert(be != NULL);
673         return (be->get_cap(be));
674 }
675
676 int
677 netbe_set_cap(struct net_backend *be, uint64_t features,
678               unsigned vnet_hdr_len)
679 {
680         int ret;
681
682         assert(be != NULL);
683
684         /* There are only three valid lengths, i.e., 0, 10 and 12. */
685         if (vnet_hdr_len && vnet_hdr_len != VNET_HDR_LEN
686                 && vnet_hdr_len != (VNET_HDR_LEN - sizeof(uint16_t)))
687                 return (-1);
688
689         be->fe_vnet_hdr_len = vnet_hdr_len;
690
691         ret = be->set_cap(be, features, vnet_hdr_len);
692         assert(be->be_vnet_hdr_len == 0 ||
693                be->be_vnet_hdr_len == be->fe_vnet_hdr_len);
694
695         return (ret);
696 }
697
698 static __inline struct iovec *
699 iov_trim(struct iovec *iov, int *iovcnt, unsigned int tlen)
700 {
701         struct iovec *riov;
702
703         /* XXX short-cut: assume first segment is >= tlen */
704         assert(iov[0].iov_len >= tlen);
705
706         iov[0].iov_len -= tlen;
707         if (iov[0].iov_len == 0) {
708                 assert(*iovcnt > 1);
709                 *iovcnt -= 1;
710                 riov = &iov[1];
711         } else {
712                 iov[0].iov_base = (void *)((uintptr_t)iov[0].iov_base + tlen);
713                 riov = &iov[0];
714         }
715
716         return (riov);
717 }
718
719 ssize_t
720 netbe_send(struct net_backend *be, struct iovec *iov, int iovcnt)
721 {
722
723         assert(be != NULL);
724         if (be->be_vnet_hdr_len != be->fe_vnet_hdr_len) {
725                 /*
726                  * The frontend uses a virtio-net header, but the backend
727                  * does not. We ignore it (as it must be all zeroes) and
728                  * strip it.
729                  */
730                 assert(be->be_vnet_hdr_len == 0);
731                 iov = iov_trim(iov, &iovcnt, be->fe_vnet_hdr_len);
732         }
733
734         return (be->send(be, iov, iovcnt));
735 }
736
737 /*
738  * Try to read a packet from the backend, without blocking.
739  * If no packets are available, return 0. In case of success, return
740  * the length of the packet just read. Return -1 in case of errors.
741  */
742 ssize_t
743 netbe_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
744 {
745         /* Length of prepended virtio-net header. */
746         unsigned int hlen = be->fe_vnet_hdr_len;
747         int ret;
748
749         assert(be != NULL);
750
751         if (hlen && hlen != be->be_vnet_hdr_len) {
752                 /*
753                  * The frontend uses a virtio-net header, but the backend
754                  * does not. We need to prepend a zeroed header.
755                  */
756                 struct virtio_net_rxhdr *vh;
757
758                 assert(be->be_vnet_hdr_len == 0);
759
760                 /*
761                  * Get a pointer to the rx header, and use the
762                  * data immediately following it for the packet buffer.
763                  */
764                 vh = iov[0].iov_base;
765                 iov = iov_trim(iov, &iovcnt, hlen);
766
767                 /*
768                  * The only valid field in the rx packet header is the
769                  * number of buffers if merged rx bufs were negotiated.
770                  */
771                 memset(vh, 0, hlen);
772                 if (hlen == VNET_HDR_LEN) {
773                         vh->vrh_bufs = 1;
774                 }
775         }
776
777         ret = be->recv(be, iov, iovcnt);
778         if (ret > 0) {
779                 ret += hlen;
780         }
781
782         return (ret);
783 }
784
785 /*
786  * Read a packet from the backend and discard it.
787  * Returns the size of the discarded packet or zero if no packet was available.
788  * A negative error code is returned in case of read error.
789  */
790 ssize_t
791 netbe_rx_discard(struct net_backend *be)
792 {
793         /*
794          * MP note: the dummybuf is only used to discard frames,
795          * so there is no need for it to be per-vtnet or locked.
796          * We only make it large enough for TSO-sized segment.
797          */
798         static uint8_t dummybuf[65536 + 64];
799         struct iovec iov;
800
801         iov.iov_base = dummybuf;
802         iov.iov_len = sizeof(dummybuf);
803
804         return netbe_recv(be, &iov, 1);
805 }
806