static void vnic_poll(VLANClientState *ncp, bool enable) { VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); vnic_read_poll(vsp, 1); vnic_write_poll(vsp, 1); }
/* * Because this is a single packet API, just read(2). If QEMU's net backend were * better we could send more packets at once. */ static int vnic_read_packet(VNICState *vsp, uint8_t *buf, int len) { int ret; do { ret = read(vsp->vns_fd, buf, len); } while (ret == -1 && errno == EINTR); if (ret == -1 && errno == EAGAIN) { vnic_read_poll(vsp, 1); return (0); } return (ret); }
/* outside world -> VM */ static void vnic_send(void *opaque) { VNICState *vsp = opaque; int ret; do { ret = vnic_read_packet(vsp, vsp->vns_buf, sizeof (vsp->vns_buf)); if (ret <= 0) break; ret = qemu_send_packet_async(&vsp->vns_nc, vsp->vns_buf, ret, vnic_send_completed); if (ret == 0) vnic_read_poll(vsp, 0); } while (ret > 0 && qemu_can_send_packet(&vsp->vns_nc)); }
/* VM -> outside world */ static ssize_t vnic_receive(VLANClientState *ncp, const uint8_t *buf, size_t size) { VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); if (vsp->vns_ds.vnds_enabled && is_dhcp_request(buf, size)) { int ret; ret = create_dhcp_response(buf, size, &vsp->vns_ds); if (!ret) return size; ret = qemu_send_packet_async(&vsp->vns_nc, vsp->vns_ds.vnds_buf, ret, vnic_send_completed); if (ret == 0) vnic_read_poll(vsp, 0); return size; } return (vnic_write_packet(vsp, buf, size)); }
int net_init_vnic(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) { int fd, len, vnderr, syserr; const char *ifname, *mac; uchar_t *macaddr; VLANClientState *ncp; VNICState *vsp; vnd_prop_buf_t vib; if ((ifname = qemu_opt_get(opts, "ifname")) == NULL) { error_report("missing ifname required for vnic\n"); return (-1); } mac = qemu_opt_get(opts, "macaddr"); if (mac != NULL) { macaddr = _link_aton(mac, &len); if (macaddr == NULL || len != ETHERADDRL) { error_report("invalid macaddr for vnic: %s\n", mac); return (-1); } } ncp = qemu_new_net_client(&net_vnic_info, vlan, NULL, "vnic", name); vsp = DO_UPCAST(VNICState, vns_nc, ncp); vsp->vns_hdl = vnd_open(NULL, ifname, &vnderr, &syserr); if (vsp->vns_hdl == NULL) { const char *err = vnderr != VND_E_SYS ? vnd_strerror(vnderr) : vnd_strsyserror(syserr); error_report("vnic: failed to open interface %s - %s\n", ifname, err); return (-1); } vib.vpb_size = 1024 * 1024 * 4; /* 4 MB */ if (vnd_prop_set(vsp->vns_hdl, VND_PROP_RXBUF, &vib, sizeof (vib)) != 0) { const char *err = vnderr != VND_E_SYS ? vnd_strerror(vnderr) : vnd_strsyserror(syserr); error_report("failed to change rx buf size: %s\n", err); return (-1); } vib.vpb_size = 1024 * 1024 * 4; /* 4 MB */ if (vnd_prop_set(vsp->vns_hdl, VND_PROP_TXBUF, &vib, sizeof (vib)) != 0) { const char *err = vnderr != VND_E_SYS ? vnd_strerror(vnderr) : vnd_strsyserror(syserr); error_report("failed to change tx buf size: %s\n", err); return (-1); } fd = vnd_pollfd(vsp->vns_hdl); if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { error_report("vnic: failed to set fd on interface %s to " "non-blocking: %s\n", ifname, strerror(errno)); return (-1); } vsp->vns_fd = fd; snprintf(vsp->vns_nc.info_str, sizeof (vsp->vns_nc.info_str), "ifname=%s", qemu_opt_get(opts, "ifname")); if (vnic_dhcp_init(&vsp->vns_ds, opts) == 0) return (-1); if (vnic_frameio_init(vsp) != 0) { error_report("vnic: failed initialize frameio: %s\n", strerror(errno)); return (-1); } /* We have to manually intialize the polling for read */ vnic_read_poll(vsp, 1); return (0); }
static ssize_t vnic_receive_iov(VLANClientState *ncp, const struct iovec *iov, int iovcnt) { int ret, fvec, i; size_t total; VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); assert(iovcnt <= FRAMEIO_NVECS_MAX); /* * Copy the iovcs to our write frameio. Also, check if any of these is * valid dhcp and handle it immediately. */ for (i = 0, fvec = 0; i < iovcnt; i++, iov++) { if (vsp->vns_ds.vnds_enabled && is_dhcp_request(iov->iov_base, iov->iov_len)) { /* * Basically drop the packet because we can't send a * reply at this time. It's unfortunate, but we don't * really have the proper infrastructure to do something * else with this at this time. */ if (!vnic_can_send(vsp)) continue; ret = create_dhcp_response(iov->iov_base, iov->iov_len, &vsp->vns_ds); /* This failed, drop it and continue */ if (ret == 0) continue; ret = qemu_send_packet_async(&vsp->vns_nc, vsp->vns_ds.vnds_buf, ret, vnic_send_completed); /* * qemu has told us that it can't receive any more data * at this time for the guest (host->guest traffic) so * turn off our read poll until we get that the send has * completed. */ if (ret == 0) vnic_read_poll(vsp, 0); continue; } vsp->vns_wfio->fio_vecs[fvec].fv_buf = iov->iov_base; vsp->vns_wfio->fio_vecs[fvec].fv_buflen = iov->iov_len; fvec++; } vsp->vns_wfio->fio_nvecs = fvec; do { ret = vnd_frameio_write(vsp->vns_hdl, vsp->vns_wfio); } while (ret == -1 && errno == EINTR); if (ret == -1 && errno == EAGAIN) { vnic_write_poll(vsp, 1); return (0); } total = 0; for (i = 0; i < vsp->vns_wfio->fio_nvecs; i++) { if (vsp->vns_wfio->fio_vecs[i].fv_actlen == 0 && vsp->vns_wfio->fio_vecs[i].fv_buflen == 0) break; total += vsp->vns_wfio->fio_vecs[i].fv_actlen; } return (total); }
static void vnic_send_completed(VLANClientState *nc, ssize_t len) { VNICState *vsp = DO_UPCAST(VNICState, vns_nc, nc); vnic_read_poll(vsp, 1); }
static ssize_t vnic_receive_iov(VLANClientState *ncp, const struct iovec *iov, int iovcnt) { int ret, i; size_t total, altsize; VNICState *vsp = DO_UPCAST(VNICState, vns_nc, ncp); for (total = 0, i = 0; i < iovcnt; i++) { total += (iov + i)->iov_len; } if (vsp->vns_ds.vnds_enabled && is_dhcp_requestv(iov, iovcnt)) { /* * Basically drop the packet because we can't send a * reply at this time. It's unfortunate, but we don't * really have the proper infrastructure to do something * else with this at this time. */ if (!vnic_can_send(vsp)) return (total); ret = create_dhcp_responsev(iov, iovcnt, &vsp->vns_ds); /* This failed, drop it and continue */ if (ret == 0) return (total); ret = qemu_send_packet_async(&vsp->vns_nc, vsp->vns_ds.vnds_buf, ret, vnic_send_completed); /* * qemu has told us that it can't receive any more data * at this time for the guest (host->guest traffic) so * turn off our read poll until we get that the send has * completed. */ if (ret == 0) vnic_read_poll(vsp, 0); return (total); } /* * Copy the iovcs to our write frameio. Be on the lookout for someone * giving us more vectors than we support in frameio. In that case, * let's go ahead and just simply concat the rest. */ for (i = 0; i < MIN(iovcnt, FRAMEIO_NVECS_MAX - 1); i++, iov++) { vsp->vns_wfio->fio_vecs[i].fv_buf = iov->iov_base; vsp->vns_wfio->fio_vecs[i].fv_buflen = iov->iov_len; } altsize = 0; for (i = MIN(iovcnt, FRAMEIO_NVECS_MAX - 1); i != iovcnt; i++, iov++) { /* * The packet is too large. We're goin to silently drop it... */ if (altsize + iov->iov_len > VNIC_BUFSIZE) return (total); bcopy(iov->iov_base, vsp->vns_txbuf + altsize, iov->iov_len); altsize += iov->iov_len; } if (altsize != 0) { vsp->vns_wfio->fio_vecs[FRAMEIO_NVECS_MAX-1].fv_buf = vsp->vns_txbuf; vsp->vns_wfio->fio_vecs[FRAMEIO_NVECS_MAX-1].fv_buflen = altsize; } vsp->vns_wfio->fio_nvecs = MIN(iovcnt, FRAMEIO_NVECS_MAX); vsp->vns_wfio->fio_nvpf = MIN(iovcnt, FRAMEIO_NVECS_MAX); do { ret = vnd_frameio_write(vsp->vns_hdl, vsp->vns_wfio); } while (ret == -1 && errno == EINTR); if (ret == -1 && errno == EAGAIN) { vnic_write_poll(vsp, 1); return (0); } else if (ret == -1) { abort(); } total = 0; for (i = 0; i < vsp->vns_wfio->fio_nvecs; i++) { if (vsp->vns_wfio->fio_vecs[i].fv_actlen == 0 && vsp->vns_wfio->fio_vecs[i].fv_buflen == 0) break; total += vsp->vns_wfio->fio_vecs[i].fv_actlen; } return (total); }