Message ID | aa4c6913ca2bc37e049f04f498c6414a4ec544bd.1337167727.git.amit.shah@redhat.com |
---|---|
State | New |
Headers | show |
Il 16/05/2012 13:30, Amit Shah ha scritto: > hw-obj-$(CONFIG_VIRTIO) += virtio-console.o > +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o This needs to be conditional on CONFIG_LINUX too. Paolo
On (Wed) 16 May 2012 [13:38:06], Paolo Bonzini wrote: > Il 16/05/2012 13:30, Amit Shah ha scritto: > > hw-obj-$(CONFIG_VIRTIO) += virtio-console.o > > +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o > > This needs to be conditional on CONFIG_LINUX too. Right. Thanks, Amit
On 05/16/2012 06:30 AM, Amit Shah wrote: > The Linux kernel already has a virtio-rng driver, this is the device > implementation. > > When Linux needs more entropy, it puts a buffer in the vq. We then put > entropy into that buffer, and push it back to the guest. > > Feeding randomness from host's /dev/urandom into the guest is > sufficient, so this is a simple implementation that opens /dev/urandom > and reads from it whenever required. > > Invocation is simple: > > qemu ... -device virtio-rng-pci > > In the guest, we see > > $ cat /sys/devices/virtual/misc/hw_random/rng_available > virtio > > $ cat /sys/devices/virtual/misc/hw_random/rng_current > virtio > > There are ways to extend the device to be more generic and collect > entropy from other sources, but this is simple enough and works for now. > > Signed-off-by: Amit Shah<amit.shah@redhat.com> It's not this simple unfortunately. If you did this with libvirt, one guest could exhaust the available entropy for the remaining guests. This could be used as a mechanism for one guest to attack another (reducing the available entropy for key generation). You need to rate limit the amount of entropy that a guest can obtain to allow management tools to mitigate this attack. Regards, Anthony Liguori > --- > Makefile.objs | 1 + > hw/pci.h | 1 + > hw/virtio-pci.c | 50 +++++++++++++++++++++ > hw/virtio-rng.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/virtio-rng.h | 18 ++++++++ > hw/virtio.h | 2 + > 6 files changed, 202 insertions(+), 0 deletions(-) > create mode 100644 hw/virtio-rng.c > create mode 100644 hw/virtio-rng.h > > diff --git a/Makefile.objs b/Makefile.objs > index 70c5c79..5850762 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -210,6 +210,7 @@ user-obj-y += $(qom-obj-twice-y) > hw-obj-y = > hw-obj-y += vl.o loader.o > hw-obj-$(CONFIG_VIRTIO) += virtio-console.o > +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o > hw-obj-y += usb/libhw.o > hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o > hw-obj-y += fw_cfg.o > diff --git a/hw/pci.h b/hw/pci.h > index 8d0aa49..0a22f91 100644 > --- a/hw/pci.h > +++ b/hw/pci.h > @@ -76,6 +76,7 @@ > #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 > #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 > #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 > +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 > > #define FMT_PCIBUS PRIx64 > > diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c > index 4a4413d..7f2d630 100644 > --- a/hw/virtio-pci.c > +++ b/hw/virtio-pci.c > @@ -812,6 +812,28 @@ static int virtio_balloon_exit_pci(PCIDevice *pci_dev) > return virtio_exit_pci(pci_dev); > } > > +static int virtio_rng_init_pci(PCIDevice *pci_dev) > +{ > + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); > + VirtIODevice *vdev; > + > + vdev = virtio_rng_init(&pci_dev->qdev); > + if (!vdev) { > + return -1; > + } > + virtio_init_pci(proxy, vdev); > + return 0; > +} > + > +static int virtio_rng_exit_pci(PCIDevice *pci_dev) > +{ > + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); > + > + virtio_pci_stop_ioeventfd(proxy); > + virtio_rng_exit(proxy->vdev); > + return virtio_exit_pci(pci_dev); > +} > + > static Property virtio_blk_properties[] = { > DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), > DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), > @@ -937,6 +959,33 @@ static TypeInfo virtio_balloon_info = { > .class_init = virtio_balloon_class_init, > }; > > +static Property virtio_rng_properties[] = { > + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void virtio_rng_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); > + > + k->init = virtio_rng_init_pci; > + k->exit = virtio_rng_exit_pci; > + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; > + k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; > + k->revision = VIRTIO_PCI_ABI_VERSION; > + k->class_id = PCI_CLASS_OTHERS; > + dc->reset = virtio_pci_reset; > + dc->props = virtio_rng_properties; > +} > + > +static TypeInfo virtio_rng_info = { > + .name = "virtio-rng-pci", > + .parent = TYPE_PCI_DEVICE, > + .instance_size = sizeof(VirtIOPCIProxy), > + .class_init = virtio_rng_class_init, > +}; > + > static int virtio_scsi_init_pci(PCIDevice *pci_dev) > { > VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); > @@ -998,6 +1047,7 @@ static void virtio_pci_register_types(void) > type_register_static(&virtio_serial_info); > type_register_static(&virtio_balloon_info); > type_register_static(&virtio_scsi_info); > + type_register_static(&virtio_rng_info); > } > > type_init(virtio_pci_register_types) > diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c > new file mode 100644 > index 0000000..e1f3d1c > --- /dev/null > +++ b/hw/virtio-rng.c > @@ -0,0 +1,130 @@ > +/* A virtio device for feeding entropy into a guest. > + * > + * Copyright 2012 Red Hat, Inc. > + * Copyright 2012 Amit Shah<amit.shah@redhat.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > + * (at your option) any later version. See the COPYING file in the > + * top-level directory. > + */ > + > +#include "iov.h" > +#include "qdev.h" > +#include "virtio.h" > +#include "virtio-rng.h" > + > +typedef struct VirtIORNG { > + VirtIODevice vdev; > + > + DeviceState *qdev; > + > + /* Only one vq - guest puts a buffer on it when it needs entropy */ > + VirtQueue *vq; > + > + int input_fd; > +} VirtIORNG; > + > +static void handle_input(VirtIODevice *vdev, VirtQueue *vq) > +{ > + VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); > + VirtQueueElement elem; > + char *buf; > + ssize_t size, offset, ret; > + > + if (!virtqueue_pop(vq,&elem)) { > + return; > + } > + size = iov_size(elem.in_sg, elem.in_num); > + > + buf = g_malloc(size); > + do { > + ret = read(vrng->input_fd, buf, size); > + } while (ret == -1&& errno == EINTR); > + if (ret< 0) { > + /* We can't get randomness -- give up for now. */ > + virtqueue_push(vq,&elem, 0); > + goto skip; > + } > + > + offset = 0; > + size = ret; > + while (offset< size) { > + size_t len; > + > + /* We've already popped the first elem */ > + if (offset&& !virtqueue_pop(vq,&elem)) { > + break; > + } > + > + len = iov_from_buf(elem.in_sg, elem.in_num, > + buf + offset, 0, size - offset); > + offset += len; > + > + virtqueue_push(vq,&elem, len); > + } > +skip: > + g_free(buf); > + virtio_notify(vdev, vq); > +} > + > +static uint32_t get_features(VirtIODevice *vdev, uint32_t f) > +{ > + return f; > +} > + > +static void virtio_rng_save(QEMUFile *f, void *opaque) > +{ > + VirtIORNG *vrng = opaque; > + > + virtio_save(&vrng->vdev, f); > +} > + > +static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) > +{ > + VirtIORNG *vrng = opaque; > + > + if (version_id != 1) { > + return -EINVAL; > + } > + virtio_load(&vrng->vdev, f); > + > + return 0; > +} > + > +VirtIODevice *virtio_rng_init(DeviceState *dev) > +{ > + VirtIORNG *vrng; > + VirtIODevice *vdev; > + int input_fd; > + > + input_fd = open("/dev/urandom", O_RDONLY); > + if (input_fd< 0) { > + error_report("error %d opening /dev/urandom", errno); > + return NULL; > + } > + > + vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0, > + sizeof(VirtIORNG)); > + > + vrng = DO_UPCAST(VirtIORNG, vdev, vdev); > + > + vrng->input_fd = input_fd; > + > + vrng->vq = virtio_add_queue(vdev, 8, handle_input); > + vrng->vdev.get_features = get_features; > + > + vrng->qdev = dev; > + register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save, > + virtio_rng_load, vrng); > + > + return vdev; > +} > + > +void virtio_rng_exit(VirtIODevice *vdev) > +{ > + VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); > + > + close(vrng->input_fd); > + unregister_savevm(vrng->qdev, "virtio-rng", vrng); > + virtio_cleanup(vdev); > +} > diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h > new file mode 100644 > index 0000000..2e1eba3 > --- /dev/null > +++ b/hw/virtio-rng.h > @@ -0,0 +1,18 @@ > +/* > + * Virtio RNG Support > + * > + * Copyright Red Hat, Inc. 2012 > + * Copyright Amit Shah<amit.shah@redhat.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > + * (at your option) any later version. See the COPYING file in the > + * top-level directory. > + */ > + > +#ifndef _QEMU_VIRTIO_RNG_H > +#define _QEMU_VIRTIO_RNG_H > + > +/* The Virtio ID for the virtio rng device */ > +#define VIRTIO_ID_RNG 4 > + > +#endif > diff --git a/hw/virtio.h b/hw/virtio.h > index 0aef7d1..0315e0c 100644 > --- a/hw/virtio.h > +++ b/hw/virtio.h > @@ -201,6 +201,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); > VirtIODevice *virtio_balloon_init(DeviceState *dev); > typedef struct VirtIOSCSIConf VirtIOSCSIConf; > VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); > +VirtIODevice *virtio_rng_init(DeviceState *dev); > #ifdef CONFIG_LINUX > VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); > #endif > @@ -211,6 +212,7 @@ void virtio_blk_exit(VirtIODevice *vdev); > void virtio_serial_exit(VirtIODevice *vdev); > void virtio_balloon_exit(VirtIODevice *vdev); > void virtio_scsi_exit(VirtIODevice *vdev); > +void virtio_rng_exit(VirtIODevice *vdev); > > #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ > DEFINE_PROP_BIT("indirect_desc", _state, _field, \
On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > On 05/16/2012 06:30 AM, Amit Shah wrote: > >The Linux kernel already has a virtio-rng driver, this is the device > >implementation. > > > >When Linux needs more entropy, it puts a buffer in the vq. We then put > >entropy into that buffer, and push it back to the guest. > > > >Feeding randomness from host's /dev/urandom into the guest is > >sufficient, so this is a simple implementation that opens /dev/urandom > >and reads from it whenever required. > > > >Invocation is simple: > > > > qemu ... -device virtio-rng-pci > > > >In the guest, we see > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_available > > virtio > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_current > > virtio > > > >There are ways to extend the device to be more generic and collect > >entropy from other sources, but this is simple enough and works for now. > > > >Signed-off-by: Amit Shah<amit.shah@redhat.com> > > It's not this simple unfortunately. > > If you did this with libvirt, one guest could exhaust the available > entropy for the remaining guests. This could be used as a mechanism > for one guest to attack another (reducing the available entropy for > key generation). > > You need to rate limit the amount of entropy that a guest can obtain > to allow management tools to mitigate this attack. Ultimately I think you need to have a push mechanism, where an external process feeds entropy to QEMU, rather than a pull mechanism where QEMU grabs entropy itself. I tend to think that virtio-rng should have a chardev backend associated with it. The driver should just read from this chardev to get its entropy. Either libvirtd, or better yet a separate virt-entropyd daemonm would connect to each guest & feed the entropy into each guest according to some desired metrics. Regards, Daniel
On 05/16/2012 08:45 AM, Daniel P. Berrange wrote: > On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: >> On 05/16/2012 06:30 AM, Amit Shah wrote: >>> The Linux kernel already has a virtio-rng driver, this is the device >>> implementation. >>> >>> When Linux needs more entropy, it puts a buffer in the vq. We then put >>> entropy into that buffer, and push it back to the guest. >>> >>> Feeding randomness from host's /dev/urandom into the guest is >>> sufficient, so this is a simple implementation that opens /dev/urandom >>> and reads from it whenever required. >>> >>> Invocation is simple: >>> >>> qemu ... -device virtio-rng-pci >>> >>> In the guest, we see >>> >>> $ cat /sys/devices/virtual/misc/hw_random/rng_available >>> virtio >>> >>> $ cat /sys/devices/virtual/misc/hw_random/rng_current >>> virtio >>> >>> There are ways to extend the device to be more generic and collect >>> entropy from other sources, but this is simple enough and works for now. >>> >>> Signed-off-by: Amit Shah<amit.shah@redhat.com> >> >> It's not this simple unfortunately. >> >> If you did this with libvirt, one guest could exhaust the available >> entropy for the remaining guests. This could be used as a mechanism >> for one guest to attack another (reducing the available entropy for >> key generation). >> >> You need to rate limit the amount of entropy that a guest can obtain >> to allow management tools to mitigate this attack. > > Ultimately I think you need to have a push mechanism, where an external > process feeds entropy to QEMU, rather than a pull mechanism where QEMU > grabs entropy itself. A previous patch didn't open urandom directly but instead talked to an entropy daemon. This approach would allow libvirt to hand out entropy as it saw fit without requiring a new driver. Regards, Anthony Liguori > > I tend to think that virtio-rng should have a chardev backend associated > with it. The driver should just read from this chardev to get its entropy. > Either libvirtd, or better yet a separate virt-entropyd daemonm would > connect to each guest& feed the entropy into each guest according to > some desired metrics. > > Regards, > Daniel
On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote: > On 05/16/2012 08:45 AM, Daniel P. Berrange wrote: > >On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > >>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>The Linux kernel already has a virtio-rng driver, this is the device > >>>implementation. > >>> > >>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>entropy into that buffer, and push it back to the guest. > >>> > >>>Feeding randomness from host's /dev/urandom into the guest is > >>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>and reads from it whenever required. > >>> > >>>Invocation is simple: > >>> > >>> qemu ... -device virtio-rng-pci > >>> > >>>In the guest, we see > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>> virtio > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>> virtio > >>> > >>>There are ways to extend the device to be more generic and collect > >>>entropy from other sources, but this is simple enough and works for now. > >>> > >>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >> > >>It's not this simple unfortunately. > >> > >>If you did this with libvirt, one guest could exhaust the available > >>entropy for the remaining guests. This could be used as a mechanism > >>for one guest to attack another (reducing the available entropy for > >>key generation). > >> > >>You need to rate limit the amount of entropy that a guest can obtain > >>to allow management tools to mitigate this attack. > > > >Ultimately I think you need to have a push mechanism, where an external > >process feeds entropy to QEMU, rather than a pull mechanism where QEMU > >grabs entropy itself. > > A previous patch didn't open urandom directly but instead talked to > an entropy daemon. This approach would allow libvirt to hand out > entropy as it saw fit without requiring a new driver. The nice thing about just using a plain chardev backend for virtiorng is that it would let you have the flexibility to integrate with any kind of entropy daemon, or even just run without a daemon & rely on some other process to periodically open the chardev & feed in data. > >I tend to think that virtio-rng should have a chardev backend associated > >with it. The driver should just read from this chardev to get its entropy. > >Either libvirtd, or better yet a separate virt-entropyd daemonm would > >connect to each guest& feed the entropy into each guest according to > >some desired metrics. Regards, Daniel
On 05/16/2012 08:53 AM, Daniel P. Berrange wrote: > On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote: >> On 05/16/2012 08:45 AM, Daniel P. Berrange wrote: >>> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: >>>> On 05/16/2012 06:30 AM, Amit Shah wrote: >>>>> The Linux kernel already has a virtio-rng driver, this is the device >>>>> implementation. >>>>> >>>>> When Linux needs more entropy, it puts a buffer in the vq. We then put >>>>> entropy into that buffer, and push it back to the guest. >>>>> >>>>> Feeding randomness from host's /dev/urandom into the guest is >>>>> sufficient, so this is a simple implementation that opens /dev/urandom >>>>> and reads from it whenever required. >>>>> >>>>> Invocation is simple: >>>>> >>>>> qemu ... -device virtio-rng-pci >>>>> >>>>> In the guest, we see >>>>> >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available >>>>> virtio >>>>> >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current >>>>> virtio >>>>> >>>>> There are ways to extend the device to be more generic and collect >>>>> entropy from other sources, but this is simple enough and works for now. >>>>> >>>>> Signed-off-by: Amit Shah<amit.shah@redhat.com> >>>> >>>> It's not this simple unfortunately. >>>> >>>> If you did this with libvirt, one guest could exhaust the available >>>> entropy for the remaining guests. This could be used as a mechanism >>>> for one guest to attack another (reducing the available entropy for >>>> key generation). >>>> >>>> You need to rate limit the amount of entropy that a guest can obtain >>>> to allow management tools to mitigate this attack. >>> >>> Ultimately I think you need to have a push mechanism, where an external >>> process feeds entropy to QEMU, rather than a pull mechanism where QEMU >>> grabs entropy itself. >> >> A previous patch didn't open urandom directly but instead talked to >> an entropy daemon. This approach would allow libvirt to hand out >> entropy as it saw fit without requiring a new driver. > > The nice thing about just using a plain chardev backend for virtiorng > is that it would let you have the flexibility to integrate with any kind > of entropy daemon, or even just run without a daemon& rely on some other > process to periodically open the chardev& feed in data. Ack. But there is an entropy daemon that does use a protocol. The protocol may be "just read raw random data" but we should at least check to make sure that is the protocol. Regards, Anthony Liguori > >>> I tend to think that virtio-rng should have a chardev backend associated >>> with it. The driver should just read from this chardev to get its entropy. >>> Either libvirtd, or better yet a separate virt-entropyd daemonm would >>> connect to each guest& feed the entropy into each guest according to >>> some desired metrics. > > Regards, > Daniel
On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote: > On 05/16/2012 06:30 AM, Amit Shah wrote: > >The Linux kernel already has a virtio-rng driver, this is the device > >implementation. > > > >When Linux needs more entropy, it puts a buffer in the vq. We then put > >entropy into that buffer, and push it back to the guest. > > > >Feeding randomness from host's /dev/urandom into the guest is > >sufficient, so this is a simple implementation that opens /dev/urandom > >and reads from it whenever required. > > > >Invocation is simple: > > > > qemu ... -device virtio-rng-pci > > > >In the guest, we see > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_available > > virtio > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_current > > virtio > > > >There are ways to extend the device to be more generic and collect > >entropy from other sources, but this is simple enough and works for now. > > > >Signed-off-by: Amit Shah<amit.shah@redhat.com> > > It's not this simple unfortunately. > > If you did this with libvirt, one guest could exhaust the available > entropy for the remaining guests. This could be used as a mechanism > for one guest to attack another (reducing the available entropy for > key generation). > > You need to rate limit the amount of entropy that a guest can obtain > to allow management tools to mitigate this attack. Hm, rate-limiting is a good point. However, we're using /dev/urandom here, which is nonblocking, and will keep on providing data as long as we keep reading. Amit
On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote: > On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > > On 05/16/2012 06:30 AM, Amit Shah wrote: > > >The Linux kernel already has a virtio-rng driver, this is the device > > >implementation. > > > > > >When Linux needs more entropy, it puts a buffer in the vq. We then put > > >entropy into that buffer, and push it back to the guest. > > > > > >Feeding randomness from host's /dev/urandom into the guest is > > >sufficient, so this is a simple implementation that opens /dev/urandom > > >and reads from it whenever required. > > > > > >Invocation is simple: > > > > > > qemu ... -device virtio-rng-pci > > > > > >In the guest, we see > > > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_available > > > virtio > > > > > > $ cat /sys/devices/virtual/misc/hw_random/rng_current > > > virtio > > > > > >There are ways to extend the device to be more generic and collect > > >entropy from other sources, but this is simple enough and works for now. > > > > > >Signed-off-by: Amit Shah<amit.shah@redhat.com> > > > > It's not this simple unfortunately. > > > > If you did this with libvirt, one guest could exhaust the available > > entropy for the remaining guests. This could be used as a mechanism > > for one guest to attack another (reducing the available entropy for > > key generation). > > > > You need to rate limit the amount of entropy that a guest can obtain > > to allow management tools to mitigate this attack. > > Ultimately I think you need to have a push mechanism, where an external > process feeds entropy to QEMU, rather than a pull mechanism where QEMU > grabs entropy itself. Yes, that's the goal. That was my first instinct as well. However, we already have guests which have the current virtio-rng driver that works only in pull mode. Also, Linux's hw-rng interface doesn't have a pull mechanism at all -- it asks the h/w for more entropy when the OS is low on it. > I tend to think that virtio-rng should have a chardev backend associated > with it. The driver should just read from this chardev to get its entropy. I even started with this approach. Adapting the chardev layer to actually read from a source of data, only when needed (for the pull-based mechanism) quickly turned ugly. When we do get a push-based mechanism working, we'll then also have to think of buffering the data from the daemon somewhere. It's not going to be ideal. > Either libvirtd, or better yet a separate virt-entropyd daemonm would > connect to each guest & feed the entropy into each guest according to > some desired metrics. I'd prefer a separate daemon. There already is egd, which we can use. However, there are restrictions with certification (as always). Adding new daemons to the mix increases complexity and the time it takes for certification, so doing it in libvirt may end up to be the preferred approach. Amit
On (Wed) 16 May 2012 [09:02:03], Anthony Liguori wrote: > On 05/16/2012 08:53 AM, Daniel P. Berrange wrote: > >On Wed, May 16, 2012 at 08:48:20AM -0500, Anthony Liguori wrote: > >>On 05/16/2012 08:45 AM, Daniel P. Berrange wrote: > >>>On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > >>>>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>>>The Linux kernel already has a virtio-rng driver, this is the device > >>>>>implementation. > >>>>> > >>>>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>>>entropy into that buffer, and push it back to the guest. > >>>>> > >>>>>Feeding randomness from host's /dev/urandom into the guest is > >>>>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>>>and reads from it whenever required. > >>>>> > >>>>>Invocation is simple: > >>>>> > >>>>> qemu ... -device virtio-rng-pci > >>>>> > >>>>>In the guest, we see > >>>>> > >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>>>> virtio > >>>>> > >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>>>> virtio > >>>>> > >>>>>There are ways to extend the device to be more generic and collect > >>>>>entropy from other sources, but this is simple enough and works for now. > >>>>> > >>>>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >>>> > >>>>It's not this simple unfortunately. > >>>> > >>>>If you did this with libvirt, one guest could exhaust the available > >>>>entropy for the remaining guests. This could be used as a mechanism > >>>>for one guest to attack another (reducing the available entropy for > >>>>key generation). > >>>> > >>>>You need to rate limit the amount of entropy that a guest can obtain > >>>>to allow management tools to mitigate this attack. > >>> > >>>Ultimately I think you need to have a push mechanism, where an external > >>>process feeds entropy to QEMU, rather than a pull mechanism where QEMU > >>>grabs entropy itself. > >> > >>A previous patch didn't open urandom directly but instead talked to > >>an entropy daemon. This approach would allow libvirt to hand out > >>entropy as it saw fit without requiring a new driver. > > > >The nice thing about just using a plain chardev backend for virtiorng > >is that it would let you have the flexibility to integrate with any kind > >of entropy daemon, or even just run without a daemon& rely on some other > >process to periodically open the chardev& feed in data. > > Ack. > > But there is an entropy daemon that does use a protocol. The > protocol may be "just read raw random data" but we should at least > check to make sure that is the protocol. Yes, that's egd. Ultimately, we'll use that, but there are more problems to be solved, both in qemu and in Linux, before we get there (as described in the previous mails). Amit
On 05/16/2012 12:21 PM, Amit Shah wrote: > On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote: >> On 05/16/2012 06:30 AM, Amit Shah wrote: >>> The Linux kernel already has a virtio-rng driver, this is the device >>> implementation. >>> >>> When Linux needs more entropy, it puts a buffer in the vq. We then put >>> entropy into that buffer, and push it back to the guest. >>> >>> Feeding randomness from host's /dev/urandom into the guest is >>> sufficient, so this is a simple implementation that opens /dev/urandom >>> and reads from it whenever required. >>> >>> Invocation is simple: >>> >>> qemu ... -device virtio-rng-pci >>> >>> In the guest, we see >>> >>> $ cat /sys/devices/virtual/misc/hw_random/rng_available >>> virtio >>> >>> $ cat /sys/devices/virtual/misc/hw_random/rng_current >>> virtio >>> >>> There are ways to extend the device to be more generic and collect >>> entropy from other sources, but this is simple enough and works for now. >>> >>> Signed-off-by: Amit Shah<amit.shah@redhat.com> >> >> It's not this simple unfortunately. >> >> If you did this with libvirt, one guest could exhaust the available >> entropy for the remaining guests. This could be used as a mechanism >> for one guest to attack another (reducing the available entropy for >> key generation). >> >> You need to rate limit the amount of entropy that a guest can obtain >> to allow management tools to mitigate this attack. > > Hm, rate-limiting is a good point. However, we're using /dev/urandom > here, which is nonblocking, and will keep on providing data as long as > we keep reading. But you're still exhausting the entropy pool (which is a global resource). That's the problem. Regards, Anthony Liguori > > Amit
On 05/16/2012 12:26 PM, Amit Shah wrote: > On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote: >> On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: >>> On 05/16/2012 06:30 AM, Amit Shah wrote: >>>> The Linux kernel already has a virtio-rng driver, this is the device >>>> implementation. >>>> >>>> When Linux needs more entropy, it puts a buffer in the vq. We then put >>>> entropy into that buffer, and push it back to the guest. >>>> >>>> Feeding randomness from host's /dev/urandom into the guest is >>>> sufficient, so this is a simple implementation that opens /dev/urandom >>>> and reads from it whenever required. >>>> >>>> Invocation is simple: >>>> >>>> qemu ... -device virtio-rng-pci >>>> >>>> In the guest, we see >>>> >>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available >>>> virtio >>>> >>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current >>>> virtio >>>> >>>> There are ways to extend the device to be more generic and collect >>>> entropy from other sources, but this is simple enough and works for now. >>>> >>>> Signed-off-by: Amit Shah<amit.shah@redhat.com> >>> >>> It's not this simple unfortunately. >>> >>> If you did this with libvirt, one guest could exhaust the available >>> entropy for the remaining guests. This could be used as a mechanism >>> for one guest to attack another (reducing the available entropy for >>> key generation). >>> >>> You need to rate limit the amount of entropy that a guest can obtain >>> to allow management tools to mitigate this attack. >> >> Ultimately I think you need to have a push mechanism, where an external >> process feeds entropy to QEMU, rather than a pull mechanism where QEMU >> grabs entropy itself. > > Yes, that's the goal. > > That was my first instinct as well. However, we already have guests > which have the current virtio-rng driver that works only in pull > mode. Also, Linux's hw-rng interface doesn't have a pull mechanism at > all -- it asks the h/w for more entropy when the OS is low on it. > >> I tend to think that virtio-rng should have a chardev backend associated >> with it. The driver should just read from this chardev to get its entropy. > > I even started with this approach. Adapting the chardev layer to > actually read from a source of data, only when needed (for the > pull-based mechanism) quickly turned ugly. > > When we do get a push-based mechanism working, we'll then also have to > think of buffering the data from the daemon somewhere. It's not going > to be ideal. push == pull with flow control. There's no need to implement a different guest interface. > >> Either libvirtd, or better yet a separate virt-entropyd daemonm would >> connect to each guest& feed the entropy into each guest according to >> some desired metrics. > > I'd prefer a separate daemon. There already is egd, which we can use. > However, there are restrictions with certification (as always). > Adding new daemons to the mix increases complexity and the time it > takes for certification, so doing it in libvirt may end up to be the > preferred approach. I don't think time to certify is a reasonable technical consideration. Regards, Anthony Liguori > > Amit
On (Wed) 16 May 2012 [08:48:20], Anthony Liguori wrote: > On 05/16/2012 08:45 AM, Daniel P. Berrange wrote: > >On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > >>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>The Linux kernel already has a virtio-rng driver, this is the device > >>>implementation. > >>> > >>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>entropy into that buffer, and push it back to the guest. > >>> > >>>Feeding randomness from host's /dev/urandom into the guest is > >>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>and reads from it whenever required. > >>> > >>>Invocation is simple: > >>> > >>> qemu ... -device virtio-rng-pci > >>> > >>>In the guest, we see > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>> virtio > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>> virtio > >>> > >>>There are ways to extend the device to be more generic and collect > >>>entropy from other sources, but this is simple enough and works for now. > >>> > >>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >> > >>It's not this simple unfortunately. > >> > >>If you did this with libvirt, one guest could exhaust the available > >>entropy for the remaining guests. This could be used as a mechanism > >>for one guest to attack another (reducing the available entropy for > >>key generation). > >> > >>You need to rate limit the amount of entropy that a guest can obtain > >>to allow management tools to mitigate this attack. > > > >Ultimately I think you need to have a push mechanism, where an external > >process feeds entropy to QEMU, rather than a pull mechanism where QEMU > >grabs entropy itself. > > A previous patch didn't open urandom directly but instead talked to > an entropy daemon. This approach would allow libvirt to hand out > entropy as it saw fit without requiring a new driver. Using egd isn't necessary -- libvirt could still read urandom and pass on to qemu at configured intervals. Amit
On (Wed) 16 May 2012 [13:24:10], Anthony Liguori wrote: > On 05/16/2012 12:26 PM, Amit Shah wrote: > >On (Wed) 16 May 2012 [14:45:34], Daniel P. Berrange wrote: > >>On Wed, May 16, 2012 at 08:24:22AM -0500, Anthony Liguori wrote: > >>>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>>The Linux kernel already has a virtio-rng driver, this is the device > >>>>implementation. > >>>> > >>>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>>entropy into that buffer, and push it back to the guest. > >>>> > >>>>Feeding randomness from host's /dev/urandom into the guest is > >>>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>>and reads from it whenever required. > >>>> > >>>>Invocation is simple: > >>>> > >>>> qemu ... -device virtio-rng-pci > >>>> > >>>>In the guest, we see > >>>> > >>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>>> virtio > >>>> > >>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>>> virtio > >>>> > >>>>There are ways to extend the device to be more generic and collect > >>>>entropy from other sources, but this is simple enough and works for now. > >>>> > >>>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >>> > >>>It's not this simple unfortunately. > >>> > >>>If you did this with libvirt, one guest could exhaust the available > >>>entropy for the remaining guests. This could be used as a mechanism > >>>for one guest to attack another (reducing the available entropy for > >>>key generation). > >>> > >>>You need to rate limit the amount of entropy that a guest can obtain > >>>to allow management tools to mitigate this attack. > >> > >>Ultimately I think you need to have a push mechanism, where an external > >>process feeds entropy to QEMU, rather than a pull mechanism where QEMU > >>grabs entropy itself. > > > >Yes, that's the goal. > > > >That was my first instinct as well. However, we already have guests > >which have the current virtio-rng driver that works only in pull > >mode. Also, Linux's hw-rng interface doesn't have a pull mechanism at > >all -- it asks the h/w for more entropy when the OS is low on it. > > > >>I tend to think that virtio-rng should have a chardev backend associated > >>with it. The driver should just read from this chardev to get its entropy. > > > >I even started with this approach. Adapting the chardev layer to > >actually read from a source of data, only when needed (for the > >pull-based mechanism) quickly turned ugly. > > > >When we do get a push-based mechanism working, we'll then also have to > >think of buffering the data from the daemon somewhere. It's not going > >to be ideal. > > push == pull with flow control. There's no need to implement a > different guest interface. Though, the current implementation can be easily extended -- if we do go without a chardev for v1 for virtio-rng, with qemu directly opening urandom and feeding entropy, libvirt just has to add a 'virtio-rng-pci' device. When the ',chardev' property becomes available, it can connect the chardev to itself or a daemon and start feeding data. > >>Either libvirtd, or better yet a separate virt-entropyd daemonm would > >>connect to each guest& feed the entropy into each guest according to > >>some desired metrics. > > > >I'd prefer a separate daemon. There already is egd, which we can use. > >However, there are restrictions with certification (as always). > >Adding new daemons to the mix increases complexity and the time it > >takes for certification, so doing it in libvirt may end up to be the > >preferred approach. > > I don't think time to certify is a reasonable technical consideration. Absolutely -- but if it's as easy to implement it in libvirt vs another standalone program, the libvirt approach should be preferred considering the above. Amit
On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote: > On 05/16/2012 12:21 PM, Amit Shah wrote: > >On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote: > >>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>The Linux kernel already has a virtio-rng driver, this is the device > >>>implementation. > >>> > >>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>entropy into that buffer, and push it back to the guest. > >>> > >>>Feeding randomness from host's /dev/urandom into the guest is > >>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>and reads from it whenever required. > >>> > >>>Invocation is simple: > >>> > >>> qemu ... -device virtio-rng-pci > >>> > >>>In the guest, we see > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>> virtio > >>> > >>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>> virtio > >>> > >>>There are ways to extend the device to be more generic and collect > >>>entropy from other sources, but this is simple enough and works for now. > >>> > >>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >> > >>It's not this simple unfortunately. > >> > >>If you did this with libvirt, one guest could exhaust the available > >>entropy for the remaining guests. This could be used as a mechanism > >>for one guest to attack another (reducing the available entropy for > >>key generation). > >> > >>You need to rate limit the amount of entropy that a guest can obtain > >>to allow management tools to mitigate this attack. > > > >Hm, rate-limiting is a good point. However, we're using /dev/urandom > >here, which is nonblocking, and will keep on providing data as long as > >we keep reading. > > But you're still exhausting the entropy pool (which is a global > resource). That's the problem. I understand. It's been shown, however, that /dev/urandom isn't easily exhausted, and can be used as a reliable random source for quite a few years without new seeding. And even if a guest (or more) is malicious, the guest doing such activities would itself continue to generate some seed for the host's pool, strengthening /dev/urandom. I don't know where to cite the data from, but I'll pass on that info when I have a reference. In the meantime, I'll add a rate-limiting option to the device, it does seem like a good idea to implement nevertheless. Amit
On 05/21/2012 02:39 PM, Amit Shah wrote: > On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote: >> On 05/16/2012 12:21 PM, Amit Shah wrote: >>> On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote: >>>> On 05/16/2012 06:30 AM, Amit Shah wrote: >>>>> The Linux kernel already has a virtio-rng driver, this is the device >>>>> implementation. >>>>> >>>>> When Linux needs more entropy, it puts a buffer in the vq. We then put >>>>> entropy into that buffer, and push it back to the guest. >>>>> >>>>> Feeding randomness from host's /dev/urandom into the guest is >>>>> sufficient, so this is a simple implementation that opens /dev/urandom >>>>> and reads from it whenever required. >>>>> >>>>> Invocation is simple: >>>>> >>>>> qemu ... -device virtio-rng-pci >>>>> >>>>> In the guest, we see >>>>> >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available >>>>> virtio >>>>> >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current >>>>> virtio >>>>> >>>>> There are ways to extend the device to be more generic and collect >>>>> entropy from other sources, but this is simple enough and works for now. >>>>> >>>>> Signed-off-by: Amit Shah<amit.shah@redhat.com> >>>> >>>> It's not this simple unfortunately. >>>> >>>> If you did this with libvirt, one guest could exhaust the available >>>> entropy for the remaining guests. This could be used as a mechanism >>>> for one guest to attack another (reducing the available entropy for >>>> key generation). >>>> >>>> You need to rate limit the amount of entropy that a guest can obtain >>>> to allow management tools to mitigate this attack. >>> >>> Hm, rate-limiting is a good point. However, we're using /dev/urandom >>> here, which is nonblocking, and will keep on providing data as long as >>> we keep reading. >> >> But you're still exhausting the entropy pool (which is a global >> resource). That's the problem. > > I understand. It's been shown, however, that /dev/urandom isn't > easily exhausted, and can be used as a reliable random source for > quite a few years without new seeding. And even if a guest (or more) > is malicious, the guest doing such activities would itself continue to > generate some seed for the host's pool, strengthening /dev/urandom. > > I don't know where to cite the data from, but I'll pass on that info > when I have a reference. Okay, I need to some type of reference here. Not implementing virtio-rng for all of these years wasn't a simple oversight. It was specifically out of fear of exhausting the entropy pool. I'm pretty opposed to merging this without addressing entropy exhaustion because it's a subtle enough issue that I don't think most users would be capable of understanding the ramifications. Regards, Anthony Liguori > > In the meantime, I'll add a rate-limiting option to the device, it > does seem like a good idea to implement nevertheless. > > Amit
On (Mon) 21 May 2012 [15:34:59], Anthony Liguori wrote: > On 05/21/2012 02:39 PM, Amit Shah wrote: > >On (Wed) 16 May 2012 [13:23:11], Anthony Liguori wrote: > >>On 05/16/2012 12:21 PM, Amit Shah wrote: > >>>On (Wed) 16 May 2012 [08:24:22], Anthony Liguori wrote: > >>>>On 05/16/2012 06:30 AM, Amit Shah wrote: > >>>>>The Linux kernel already has a virtio-rng driver, this is the device > >>>>>implementation. > >>>>> > >>>>>When Linux needs more entropy, it puts a buffer in the vq. We then put > >>>>>entropy into that buffer, and push it back to the guest. > >>>>> > >>>>>Feeding randomness from host's /dev/urandom into the guest is > >>>>>sufficient, so this is a simple implementation that opens /dev/urandom > >>>>>and reads from it whenever required. > >>>>> > >>>>>Invocation is simple: > >>>>> > >>>>> qemu ... -device virtio-rng-pci > >>>>> > >>>>>In the guest, we see > >>>>> > >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_available > >>>>> virtio > >>>>> > >>>>> $ cat /sys/devices/virtual/misc/hw_random/rng_current > >>>>> virtio > >>>>> > >>>>>There are ways to extend the device to be more generic and collect > >>>>>entropy from other sources, but this is simple enough and works for now. > >>>>> > >>>>>Signed-off-by: Amit Shah<amit.shah@redhat.com> > >>>> > >>>>It's not this simple unfortunately. > >>>> > >>>>If you did this with libvirt, one guest could exhaust the available > >>>>entropy for the remaining guests. This could be used as a mechanism > >>>>for one guest to attack another (reducing the available entropy for > >>>>key generation). > >>>> > >>>>You need to rate limit the amount of entropy that a guest can obtain > >>>>to allow management tools to mitigate this attack. > >>> > >>>Hm, rate-limiting is a good point. However, we're using /dev/urandom > >>>here, which is nonblocking, and will keep on providing data as long as > >>>we keep reading. > >> > >>But you're still exhausting the entropy pool (which is a global > >>resource). That's the problem. > > > >I understand. It's been shown, however, that /dev/urandom isn't > >easily exhausted, and can be used as a reliable random source for > >quite a few years without new seeding. And even if a guest (or more) > >is malicious, the guest doing such activities would itself continue to > >generate some seed for the host's pool, strengthening /dev/urandom. > > > >I don't know where to cite the data from, but I'll pass on that info > >when I have a reference. > > Okay, I need to some type of reference here. > > Not implementing virtio-rng for all of these years wasn't a simple > oversight. It was specifically out of fear of exhausting the entropy > pool. > > I'm pretty opposed to merging this without addressing entropy > exhaustion because it's a subtle enough issue that I don't think > most users would be capable of understanding the ramifications. Yes, I understand. We just have to wait for the references. There is /proc/sys/kernel/random/entropy_avail and qemu can be configured to never let that number go below a certain watermark. However, libvirt is in a better position to understand how the system works, and how much demand for entropy comes from multiple guests. Having thought more about it, qemu can't really make the call rate-limiting. So I think we should leave this to libvirt: qemu can emit an event, like ENTROPY_NEEDED along with the number of bytes that got requested by the guest. libvirt can then fill in the bytes via a chardev. If libvirt wants to rate-limit this particular guest, it can just not provide any data (for a certain amount of time). This also then doesn't depend on the chardev flow control work to land before this gets implemented. Also, the patch carried in this thread can be enabled for developer debugging (i.e. -device virtio-rng-pci w/o any chardev). Does this sound OK? Amit
diff --git a/Makefile.objs b/Makefile.objs index 70c5c79..5850762 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -210,6 +210,7 @@ user-obj-y += $(qom-obj-twice-y) hw-obj-y = hw-obj-y += vl.o loader.o hw-obj-$(CONFIG_VIRTIO) += virtio-console.o +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o hw-obj-y += usb/libhw.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-y += fw_cfg.o diff --git a/hw/pci.h b/hw/pci.h index 8d0aa49..0a22f91 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -76,6 +76,7 @@ #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 #define FMT_PCIBUS PRIx64 diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 4a4413d..7f2d630 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -812,6 +812,28 @@ static int virtio_balloon_exit_pci(PCIDevice *pci_dev) return virtio_exit_pci(pci_dev); } +static int virtio_rng_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_rng_init(&pci_dev->qdev); + if (!vdev) { + return -1; + } + virtio_init_pci(proxy, vdev); + return 0; +} + +static int virtio_rng_exit_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + + virtio_pci_stop_ioeventfd(proxy); + virtio_rng_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); +} + static Property virtio_blk_properties[] = { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), @@ -937,6 +959,33 @@ static TypeInfo virtio_balloon_info = { .class_init = virtio_balloon_class_init, }; +static Property virtio_rng_properties[] = { + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = virtio_rng_init_pci; + k->exit = virtio_rng_exit_pci; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; + k->revision = VIRTIO_PCI_ABI_VERSION; + k->class_id = PCI_CLASS_OTHERS; + dc->reset = virtio_pci_reset; + dc->props = virtio_rng_properties; +} + +static TypeInfo virtio_rng_info = { + .name = "virtio-rng-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VirtIOPCIProxy), + .class_init = virtio_rng_class_init, +}; + static int virtio_scsi_init_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); @@ -998,6 +1047,7 @@ static void virtio_pci_register_types(void) type_register_static(&virtio_serial_info); type_register_static(&virtio_balloon_info); type_register_static(&virtio_scsi_info); + type_register_static(&virtio_rng_info); } type_init(virtio_pci_register_types) diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c new file mode 100644 index 0000000..e1f3d1c --- /dev/null +++ b/hw/virtio-rng.c @@ -0,0 +1,130 @@ +/* A virtio device for feeding entropy into a guest. + * + * Copyright 2012 Red Hat, Inc. + * Copyright 2012 Amit Shah <amit.shah@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "iov.h" +#include "qdev.h" +#include "virtio.h" +#include "virtio-rng.h" + +typedef struct VirtIORNG { + VirtIODevice vdev; + + DeviceState *qdev; + + /* Only one vq - guest puts a buffer on it when it needs entropy */ + VirtQueue *vq; + + int input_fd; +} VirtIORNG; + +static void handle_input(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); + VirtQueueElement elem; + char *buf; + ssize_t size, offset, ret; + + if (!virtqueue_pop(vq, &elem)) { + return; + } + size = iov_size(elem.in_sg, elem.in_num); + + buf = g_malloc(size); + do { + ret = read(vrng->input_fd, buf, size); + } while (ret == -1 && errno == EINTR); + if (ret < 0) { + /* We can't get randomness -- give up for now. */ + virtqueue_push(vq, &elem, 0); + goto skip; + } + + offset = 0; + size = ret; + while (offset < size) { + size_t len; + + /* We've already popped the first elem */ + if (offset && !virtqueue_pop(vq, &elem)) { + break; + } + + len = iov_from_buf(elem.in_sg, elem.in_num, + buf + offset, 0, size - offset); + offset += len; + + virtqueue_push(vq, &elem, len); + } +skip: + g_free(buf); + virtio_notify(vdev, vq); +} + +static uint32_t get_features(VirtIODevice *vdev, uint32_t f) +{ + return f; +} + +static void virtio_rng_save(QEMUFile *f, void *opaque) +{ + VirtIORNG *vrng = opaque; + + virtio_save(&vrng->vdev, f); +} + +static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIORNG *vrng = opaque; + + if (version_id != 1) { + return -EINVAL; + } + virtio_load(&vrng->vdev, f); + + return 0; +} + +VirtIODevice *virtio_rng_init(DeviceState *dev) +{ + VirtIORNG *vrng; + VirtIODevice *vdev; + int input_fd; + + input_fd = open("/dev/urandom", O_RDONLY); + if (input_fd < 0) { + error_report("error %d opening /dev/urandom", errno); + return NULL; + } + + vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0, + sizeof(VirtIORNG)); + + vrng = DO_UPCAST(VirtIORNG, vdev, vdev); + + vrng->input_fd = input_fd; + + vrng->vq = virtio_add_queue(vdev, 8, handle_input); + vrng->vdev.get_features = get_features; + + vrng->qdev = dev; + register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save, + virtio_rng_load, vrng); + + return vdev; +} + +void virtio_rng_exit(VirtIODevice *vdev) +{ + VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); + + close(vrng->input_fd); + unregister_savevm(vrng->qdev, "virtio-rng", vrng); + virtio_cleanup(vdev); +} diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h new file mode 100644 index 0000000..2e1eba3 --- /dev/null +++ b/hw/virtio-rng.h @@ -0,0 +1,18 @@ +/* + * Virtio RNG Support + * + * Copyright Red Hat, Inc. 2012 + * Copyright Amit Shah <amit.shah@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef _QEMU_VIRTIO_RNG_H +#define _QEMU_VIRTIO_RNG_H + +/* The Virtio ID for the virtio rng device */ +#define VIRTIO_ID_RNG 4 + +#endif diff --git a/hw/virtio.h b/hw/virtio.h index 0aef7d1..0315e0c 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -201,6 +201,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); VirtIODevice *virtio_balloon_init(DeviceState *dev); typedef struct VirtIOSCSIConf VirtIOSCSIConf; VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); +VirtIODevice *virtio_rng_init(DeviceState *dev); #ifdef CONFIG_LINUX VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); #endif @@ -211,6 +212,7 @@ void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); void virtio_balloon_exit(VirtIODevice *vdev); void virtio_scsi_exit(VirtIODevice *vdev); +void virtio_rng_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \
The Linux kernel already has a virtio-rng driver, this is the device implementation. When Linux needs more entropy, it puts a buffer in the vq. We then put entropy into that buffer, and push it back to the guest. Feeding randomness from host's /dev/urandom into the guest is sufficient, so this is a simple implementation that opens /dev/urandom and reads from it whenever required. Invocation is simple: qemu ... -device virtio-rng-pci In the guest, we see $ cat /sys/devices/virtual/misc/hw_random/rng_available virtio $ cat /sys/devices/virtual/misc/hw_random/rng_current virtio There are ways to extend the device to be more generic and collect entropy from other sources, but this is simple enough and works for now. Signed-off-by: Amit Shah <amit.shah@redhat.com> --- Makefile.objs | 1 + hw/pci.h | 1 + hw/virtio-pci.c | 50 +++++++++++++++++++++ hw/virtio-rng.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/virtio-rng.h | 18 ++++++++ hw/virtio.h | 2 + 6 files changed, 202 insertions(+), 0 deletions(-) create mode 100644 hw/virtio-rng.c create mode 100644 hw/virtio-rng.h