[libvirt] [PATCH 0/5] Misc

*** BLURB HERE *** Michal Privoznik (5): rng: Drop useless <choice/> rng: Fix formatting qemuBuildHostNetStr: Don't leak buffer conf: Allow usernet to have an address qemu: Implement usernet address docs/formatdomain.html.in | 13 +- docs/schemas/capability.rng | 28 ++-- docs/schemas/domaincommon.rng | 141 ++++++++------------- docs/schemas/network.rng | 2 +- docs/schemas/nodedev.rng | 8 +- docs/schemas/nwfilter.rng | 54 ++++---- docs/schemas/storagecommon.rng | 4 +- docs/schemas/storagepool.rng | 4 +- src/conf/domain_conf.c | 5 +- src/qemu/qemu_command.c | 35 ++++- src/qemu/qemu_domain.c | 49 +++++++ .../qemuxml2argv-net-user-addr.args | 25 ++++ .../qemuxml2argv-net-user-addr.xml | 42 ++++++ tests/qemuxml2argvtest.c | 1 + .../qemuxml2xmlout-net-user-addr.xml | 1 + tests/qemuxml2xmltest.c | 1 + 16 files changed, 267 insertions(+), 146 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml -- 2.13.5

If we have <choice/> with just one value to chose from, it's no choice. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/schemas/domaincommon.rng | 72 +++++++++++------------------------------- docs/schemas/nodedev.rng | 4 +-- docs/schemas/storagecommon.rng | 4 +-- 3 files changed, 20 insertions(+), 60 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 06c5a91b3..a8e32014b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1554,9 +1554,7 @@ <element name="source"> <interleave> <attribute name="protocol"> - <choice> - <value>rbd</value> - </choice> + <value>rbd</value> </attribute> <optional> <attribute name="name"/> @@ -1617,9 +1615,7 @@ <define name="diskSourceNetworkProtocolNBD"> <element name="source"> <attribute name="protocol"> - <choice> - <value>nbd</value> - </choice> + <value>nbd</value> </attribute> <optional> <attribute name="name"/> @@ -1631,9 +1627,7 @@ <define name="diskSourceNetworkProtocolGluster"> <element name="source"> <attribute name="protocol"> - <choice> - <value>gluster</value> - </choice> + <value>gluster</value> </attribute> <attribute name="name"/> <oneOrMore> @@ -2370,9 +2364,7 @@ <interleave> <element name="source"> <attribute name="type"> - <choice> - <value>unix</value> - </choice> + <value>unix</value> </attribute> <attribute name="path"> <ref name="absFilePath"/> @@ -2475,9 +2467,7 @@ </group> <group> <attribute name="type"> - <choice> - <value>udp</value> - </choice> + <value>udp</value> </attribute> <interleave> <element name="source"> @@ -2967,9 +2957,7 @@ </optional> <optional> <attribute name="connected"> - <choice> - <value>keep</value> - </choice> + <value>keep</value> </attribute> </optional> <ref name="listenElements"/> @@ -3693,9 +3681,7 @@ </optional> <empty/> </group> - <choice> - <ref name="qemucdev"/> - </choice> + <ref name="qemucdev"/> </choice> </element> </define> @@ -3730,9 +3716,7 @@ <ref name="address"/> </optional> <zeroOrMore> - <choice> - <ref name="codec"/> - </choice> + <ref name="codec"/> </zeroOrMore> </interleave> </element> @@ -3985,9 +3969,7 @@ <element name="tpm"> <optional> <attribute name="model"> - <choice> - <value>tpm-tis</value> - </choice> + <value>tpm-tis</value> </attribute> </optional> <ref name="tpm-backend"/> @@ -4106,9 +4088,7 @@ <define name="hub"> <element name="hub"> <attribute name="type"> - <choice> - <value>usb</value> - </choice> + <value>usb</value> </attribute> <optional> <ref name="alias"/> @@ -4121,9 +4101,7 @@ <define name="redirdev"> <element name="redirdev"> <attribute name="bus"> - <choice> - <value>usb</value> - </choice> + <value>usb</value> </attribute> <attribute name="type"> <ref name="qemucdevSrcTypeChoice"/> @@ -4286,9 +4264,7 @@ <group> <!-- scsi_host adapter --> <optional> <attribute name="protocol"> - <choice> - <value>adapter</value> <!-- scsi_host, default, optional --> - </choice> + <value>adapter</value> <!-- scsi_host, default, optional --> </attribute> </optional> <interleave> @@ -4300,9 +4276,7 @@ </group> <group> <!-- iscsi adapter --> <attribute name="protocol"> - <choice> - <value>iscsi</value> <!-- iscsi, required --> - </choice> + <value>iscsi</value> <!-- iscsi, required --> </attribute> <attribute name="name"> <text/> @@ -4338,9 +4312,7 @@ <choice> <group> <attribute name="protocol"> - <choice> - <value>vhost</value> <!-- vhost, required --> - </choice> + <value>vhost</value> <!-- vhost, required --> </attribute> <attribute name="wwpn"> <data type="string"> @@ -4357,9 +4329,7 @@ <value>mdev</value> </attribute> <attribute name="model"> - <choice> - <value>vfio-pci</value> - </choice> + <value>vfio-pci</value> </attribute> <element name="source"> <ref name="mdevaddress"/> @@ -4821,9 +4791,7 @@ <zeroOrMore> <element name="table"> <attribute name="type"> - <choice> - <value>slic</value> - </choice> + <value>slic</value> </attribute> <ref name="absFilePath"/> </element> @@ -5035,9 +5003,7 @@ <define name="rng"> <element name="rng"> <attribute name="model"> - <choice> - <value>virtio</value> - </choice> + <value>virtio</value> </attribute> <interleave> <ref name="rng-backend"/> @@ -5162,9 +5128,7 @@ </optional> <optional> <attribute name='job'> - <choice> - <value>copy</value> - </choice> + <value>copy</value> </attribute> </optional> <optional> diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 5bcf31787..2704eeb25 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -220,9 +220,7 @@ <element name='name'><text/></element> </optional> <element name='deviceAPI'> - <choice> - <value>vfio-pci</value> - </choice> + <value>vfio-pci</value> </element> <element name='availableInstances'> <ref name='unsignedInt'/> diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index 316fbaee3..717f3c603 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -34,9 +34,7 @@ <define name='secret'> <element name='secret'> <attribute name='type'> - <choice> - <value>passphrase</value> - </choice> + <value>passphrase</value> </attribute> <choice> <attribute name='uuid'> -- 2.13.5

Some elements are offset just one space compared to their parent, some are misaligned completely, and so on. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/schemas/capability.rng | 28 +++++++++--------- docs/schemas/domaincommon.rng | 66 +++++++++++++++++++++---------------------- docs/schemas/network.rng | 2 +- docs/schemas/nodedev.rng | 4 +-- docs/schemas/nwfilter.rng | 54 +++++++++++++++++------------------ docs/schemas/storagepool.rng | 4 +-- 6 files changed, 79 insertions(+), 79 deletions(-) diff --git a/docs/schemas/capability.rng b/docs/schemas/capability.rng index 071090c2d..e1ab5c224 100644 --- a/docs/schemas/capability.rng +++ b/docs/schemas/capability.rng @@ -57,21 +57,21 @@ <define name='secmodel'> <element name='secmodel'> <interleave> - <element name='model'> - <text/> - </element> - <element name='doi'> - <text/> - </element> - <zeroOrMore> - <element name='baselabel'> - <attribute name='type'> - <text/> - </attribute> + <element name='model'> + <text/> + </element> + <element name='doi'> + <text/> + </element> + <zeroOrMore> + <element name='baselabel'> + <attribute name='type'> <text/> - </element> - </zeroOrMore> - </interleave> + </attribute> + <text/> + </element> + </zeroOrMore> + </interleave> </element> </define> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a8e32014b..c9a4f7a9a 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -532,7 +532,7 @@ <attribute name="count"> <ref name="unsignedInt"/> </attribute> - <empty/> + <empty/> </element> </zeroOrMore> </interleave> @@ -1866,7 +1866,7 @@ <attribute name='copy_on_read'> <ref name="virOnOff"/> </attribute> - </define> + </define> <define name="discard"> <attribute name='discard'> <choice> @@ -2362,21 +2362,21 @@ <value>vhostuser</value> </attribute> <interleave> - <element name="source"> - <attribute name="type"> - <value>unix</value> - </attribute> - <attribute name="path"> - <ref name="absFilePath"/> - </attribute> - <attribute name="mode"> - <choice> - <value>server</value> - <value>client</value> - </choice> - </attribute> - <empty/> - </element> + <element name="source"> + <attribute name="type"> + <value>unix</value> + </attribute> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + <attribute name="mode"> + <choice> + <value>server</value> + <value>client</value> + </choice> + </attribute> + <empty/> + </element> <ref name="interface-options"/> </interleave> </group> @@ -2660,14 +2660,14 @@ <optional> <element name="backend"> <optional> - <attribute name='tap'> - <ref name='absFilePath'/> - </attribute> + <attribute name='tap'> + <ref name='absFilePath'/> + </attribute> </optional> <optional> - <attribute name='vhost'> - <ref name='absFilePath'/> - </attribute> + <attribute name='vhost'> + <ref name='absFilePath'/> + </attribute> </optional> </element> </optional> @@ -3981,14 +3981,14 @@ <define name="tpm-backend"> <element name="backend"> - <choice> - <group> - <attribute name="type"> - <value>passthrough</value> - </attribute> - <ref name="tpm-passthrough-device"/> - </group> - </choice> + <choice> + <group> + <attribute name="type"> + <value>passthrough</value> + </attribute> + <ref name="tpm-passthrough-device"/> + </group> + </choice> </element> </define> @@ -4001,7 +4001,7 @@ </attribute> </optional> </element> - </optional> + </optional> </define> <define name="iommu"> @@ -4254,7 +4254,7 @@ <value>scsi</value> </attribute> <optional> - <ref name="sgIO"/> + <ref name="sgIO"/> </optional> <optional> <ref name="rawIO"/> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 1048dabf3..f37c422bf 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -324,7 +324,7 @@ <ref name="bandwidth"/> </optional> <optional> - <ref name="vlan"/> + <ref name="vlan"/> </optional> <!-- <ip> element --> diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 2704eeb25..6b063cc22 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -228,7 +228,7 @@ </element> </oneOrMore> </element> - </optional> + </optional> <optional> <element name='iommuGroup'> @@ -531,7 +531,7 @@ <optional> <element name='model'> <text/> - </element> + </element> </optional> <optional> <element name='vendor'> diff --git a/docs/schemas/nwfilter.rng b/docs/schemas/nwfilter.rng index dbe6af76c..7cfc05fa2 100644 --- a/docs/schemas/nwfilter.rng +++ b/docs/schemas/nwfilter.rng @@ -18,7 +18,7 @@ <ref name="filterref-node-attributes"/> </element> <element name="rule"> - <ref name="rule-node-attributes"/> + <ref name="rule-node-attributes"/> <optional> <zeroOrMore> <element name="mac"> @@ -360,7 +360,7 @@ <ref name='action-type'/> </attribute> <attribute name="direction"> - <ref name='direction-type'/> + <ref name='direction-type'/> </attribute> <optional> <attribute name="priority"> @@ -377,9 +377,9 @@ <define name="match-attribute"> <interleave> <optional> - <attribute name="match"> - <ref name="virYesNo"/> - </attribute> + <attribute name="match"> + <ref name="virYesNo"/> + </attribute> </optional> </interleave> </define> @@ -387,9 +387,9 @@ <define name="srcmac-attribute"> <interleave> <optional> - <attribute name="srcmacaddr"> - <ref name="addrMAC"/> - </attribute> + <attribute name="srcmacaddr"> + <ref name="addrMAC"/> + </attribute> </optional> </interleave> </define> @@ -398,9 +398,9 @@ <interleave> <ref name="srcmac-attribute"/> <optional> - <attribute name="srcmacmask"> - <ref name="addrMAC"/> - </attribute> + <attribute name="srcmacmask"> + <ref name="addrMAC"/> + </attribute> </optional> </interleave> </define> @@ -409,14 +409,14 @@ <interleave> <ref name="srcmacandmask-attributes"/> <optional> - <attribute name="dstmacaddr"> - <ref name="addrMAC"/> - </attribute> + <attribute name="dstmacaddr"> + <ref name="addrMAC"/> + </attribute> </optional> <optional> - <attribute name="dstmacmask"> - <ref name="addrMAC"/> - </attribute> + <attribute name="dstmacmask"> + <ref name="addrMAC"/> + </attribute> </optional> </interleave> </define> @@ -794,13 +794,13 @@ <ref name="boolean"/> </attribute> </optional> - </interleave> + </interleave> </define> <define name="ip-attributes"> <optional> <attribute name="protocol"> - <ref name="ipProtocolType"/> + <ref name="ipProtocolType"/> </attribute> </optional> </define> @@ -854,7 +854,7 @@ <data type="string"> <param name="pattern">([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9]</param> </data> - </choice> + </choice> </define> <define name="addrIPv6"> @@ -864,7 +864,7 @@ <data type="string"> <param name="pattern">([a-fA-F0-9]{0,4}:){2,7}([a-fA-F0-9]*)(([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9])?</param> </data> - </choice> + </choice> </define> <define name="addrMask"> @@ -879,7 +879,7 @@ <data type="string"> <param name="pattern">([0-2]?[0-9]?[0-9]\.){3}[0-2]?[0-9]?[0-9]</param> </data> - </choice> + </choice> </define> <define name="addrMaskv6"> @@ -894,7 +894,7 @@ <data type="string"> <param name="pattern">([a-fA-F0-9]{0,4}:){2,7}([a-fA-F0-9]*)</param> </data> - </choice> + </choice> </define> <define name="sixbitrange"> @@ -1062,10 +1062,10 @@ </define> <define name='priority-type'> - <data type="int"> - <param name="minInclusive">-1000</param> - <param name="maxInclusive">1000</param> - </data> + <data type="int"> + <param name="minInclusive">-1000</param> + <param name="maxInclusive">1000</param> + </data> </define> <define name='statematch-type'> <data type="string"> diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng index 8386f29aa..f0117bd69 100644 --- a/docs/schemas/storagepool.rng +++ b/docs/schemas/storagepool.rng @@ -521,7 +521,7 @@ <ref name='sourceinfodir'/> <ref name='sourcefmtnetfs'/> <optional> - <ref name='sourceinfovendor'/> + <ref name='sourceinfovendor'/> </optional> </interleave> </group> @@ -538,7 +538,7 @@ </attribute> </element> <optional> - <ref name='sourceinfovendor'/> + <ref name='sourceinfovendor'/> </optional> </interleave> </group> -- 2.13.5

If there was an error when constructing the buffer, NULL is returned. The buffer is never freed though. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index e57a3278e..d553df57f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3805,13 +3805,13 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virDomainNetType netType = virDomainNetGetActualType(net); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); size_t i; + char *ret = NULL; if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("scripts are not supported on interfaces of type %s"), virDomainNetTypeToString(netType)); - virObjectUnref(cfg); - return NULL; + goto cleanup; } switch (netType) { @@ -3919,13 +3919,16 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virBufferAsprintf(&buf, "sndbuf=%lu,", net->tune.sndbuf); } - virObjectUnref(cfg); virBufferTrim(&buf, ",", -1); if (virBufferCheckError(&buf) < 0) - return NULL; + goto cleanup; - return virBufferContentAndReset(&buf); + ret = virBufferContentAndReset(&buf); + cleanup: + virBufferFreeAndReset(&buf); + virObjectUnref(cfg); + return ret; } -- 2.13.5

https://bugzilla.redhat.com/show_bug.cgi?id=1075520 Currently, all that users can specify for an interface type of 'user' is the common attributes: PCI address, NIC model (and that's basically it). However, some need to configure other address range than the default one. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- Notes: Frankly, I'm not that convinced about this one. I mean, this puts IP addresses into net->hostIP while we are configuring guest side of the SLIRP. However, it just feels better to have the IP addresses under <source/> than under <interface/>. Which actually is the other option. So it's either: <interface type='user'> <source> <ip address='1.2.2.4'/> </source> </interface> for net->hostIP, or it's: <interface type='user'> <ip address='1.2.2.4'/> </interface> for net->guestIP. I went for the former one, but I don't have a strong opinion on that. docs/formatdomain.html.in | 13 ++++++- docs/schemas/domaincommon.rng | 5 +++ src/conf/domain_conf.c | 5 +-- .../qemuxml2argv-net-user-addr.xml | 42 ++++++++++++++++++++++ .../qemuxml2xmlout-net-user-addr.xml | 1 + tests/qemuxml2xmltest.c | 1 + 6 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8ca7637a4..65a8886ee 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4701,7 +4701,14 @@ starting from <code>10.0.2.15</code>. The default router will be <code>10.0.2.2</code> and the DNS server will be <code>10.0.2.3</code>. This networking is the only option for unprivileged users who need their - VMs to have outgoing access. + VMs to have outgoing access. However, <span class="since">since + 3.8.0</span>, it is possible to override the default network by + including <code>source</code> element. The element can have <code>ip</code> + element for each IPv4 and IPv6. The element has <code>family</code> + attribute which accepts <code>ipv4</code> and <code>ipv6</code> values. + Then there's <code>address</code> attribute for specifying IP address + corresponding to the family. Optionally, the default prefix length can be + overridden via <code>prefix</code> attribute. </p> <pre> @@ -4711,6 +4718,10 @@ ... <interface type='user'> <mac address="00:11:22:33:44:55"/> + <source> + <ip family='ipv4' address='172.17.2.0' prefix='24'/> + <ip family='ipv6' address='2001:db8:ac10:fd01::' prefix='64'/> + </source> </interface> </devices> ...</pre> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index c9a4f7a9a..7b5292bd3 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2428,6 +2428,11 @@ <value>user</value> </attribute> <interleave> + <optional> + <element name="source"> + <ref name="interface-ip-info"/> + </element> + </optional> <ref name="interface-options"/> </interleave> </group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 676fc0f34..ef236757a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5214,12 +5214,13 @@ static int virDomainNetDefValidate(const virDomainNetDef *net) { if ((net->hostIP.nroutes || net->hostIP.nips) && - net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + net->type != VIR_DOMAIN_NET_TYPE_ETHERNET && + net->type != VIR_DOMAIN_NET_TYPE_USER) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid attempt to set network interface " "host-side IP route and/or address info on " "interface of type '%s'. This is only supported " - "on interfaces of type 'ethernet'"), + "on interfaces of type 'ethernet' and 'user'"), virDomainNetTypeToString(net->type)); return -1; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml new file mode 100644 index 000000000..65362d8aa --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i686</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <source> + <ip address='172.17.2.0' family='ipv4' prefix='24'/> + </source> + <model type='rtl8139'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml new file mode 120000 index 000000000..fd909ec24 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/qemuxml2argv-net-user-addr.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0a87cedf2..99b15ad0c 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -531,6 +531,7 @@ mymain(void) DO_TEST("misc-uuid", NONE); DO_TEST("net-vhostuser", NONE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", NONE); DO_TEST("net-virtio-disable-offloads", NONE); -- 2.13.5

On Tue, Sep 12, 2017 at 11:32:52AM +0200, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Currently, all that users can specify for an interface type of 'user' is the common attributes: PCI address, NIC model (and that's basically it). However, some need to configure other address range than the default one.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
Notes: Frankly, I'm not that convinced about this one. I mean, this puts IP addresses into net->hostIP while we are configuring guest side of the SLIRP. However, it just feels better to have the IP addresses under <source/> than under <interface/>. Which actually is the other option. So it's either:
<interface type='user'> <source> <ip address='1.2.2.4'/> </source> </interface>
for net->hostIP, or it's:
<interface type='user'> <ip address='1.2.2.4'/> </interface>
I'm not convinced either. If you don't like the fact that it's in hostIP (even though we're setting up the backend of that device, not the device itself, it's just the IP that the internal DHCP server should send to the guest) and want to have it in guestIP (which would make sense as well) then it should be: <interface type='user'> <target> <ip address='1.2.2.4'/> </target> </interface> Which would cleanly correlate to that (and I also don't like the look of it). So I'll leave this to a further discussion.
for net->guestIP. I went for the former one, but I don't have a strong opinion on that.
docs/formatdomain.html.in | 13 ++++++- docs/schemas/domaincommon.rng | 5 +++ src/conf/domain_conf.c | 5 +-- .../qemuxml2argv-net-user-addr.xml | 42 ++++++++++++++++++++++ .../qemuxml2xmlout-net-user-addr.xml | 1 + tests/qemuxml2xmltest.c | 1 + 6 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8ca7637a4..65a8886ee 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4701,7 +4701,14 @@ starting from <code>10.0.2.15</code>. The default router will be <code>10.0.2.2</code> and the DNS server will be <code>10.0.2.3</code>. This networking is the only option for unprivileged users who need their - VMs to have outgoing access. + VMs to have outgoing access. However, <span class="since">since + 3.8.0</span>, it is possible to override the default network by + including <code>source</code> element. The element can have <code>ip</code> + element for each IPv4 and IPv6. The element has <code>family</code> + attribute which accepts <code>ipv4</code> and <code>ipv6</code> values. + Then there's <code>address</code> attribute for specifying IP address + corresponding to the family. Optionally, the default prefix length can be + overridden via <code>prefix</code> attribute. </p>
<pre> @@ -4711,6 +4718,10 @@ ... <interface type='user'> <mac address="00:11:22:33:44:55"/> + <source> + <ip family='ipv4' address='172.17.2.0' prefix='24'/> + <ip family='ipv6' address='2001:db8:ac10:fd01::' prefix='64'/> + </source> </interface> </devices> ...</pre> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index c9a4f7a9a..7b5292bd3 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2428,6 +2428,11 @@ <value>user</value> </attribute> <interleave> + <optional> + <element name="source"> + <ref name="interface-ip-info"/>
This also allows <route/> here, I would split the definition and disallow that. Just for the sake of pointless bugs flying by.
+ </element> + </optional> <ref name="interface-options"/> </interleave> </group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 676fc0f34..ef236757a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5214,12 +5214,13 @@ static int virDomainNetDefValidate(const virDomainNetDef *net) { if ((net->hostIP.nroutes || net->hostIP.nips) && - net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + net->type != VIR_DOMAIN_NET_TYPE_ETHERNET && + net->type != VIR_DOMAIN_NET_TYPE_USER) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid attempt to set network interface " "host-side IP route and/or address info on " "interface of type '%s'. This is only supported " - "on interfaces of type 'ethernet'"), + "on interfaces of type 'ethernet' and 'user'"),
Same here, you even give the hint that routes are supported for the type='user'
virDomainNetTypeToString(net->type)); return -1; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml new file mode 100644 index 000000000..65362d8aa --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i686</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <source> + <ip address='172.17.2.0' family='ipv4' prefix='24'/>
Also add ipv6 into the test.
+ </source> + <model type='rtl8139'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml new file mode 120000 index 000000000..fd909ec24 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/qemuxml2argv-net-user-addr.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0a87cedf2..99b15ad0c 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -531,6 +531,7 @@ mymain(void) DO_TEST("misc-uuid", NONE); DO_TEST("net-vhostuser", NONE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", NONE); DO_TEST("net-virtio-disable-offloads", NONE); -- 2.13.5
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 09/12/2017 05:53 AM, Martin Kletzander wrote:
On Tue, Sep 12, 2017 at 11:32:52AM +0200, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Currently, all that users can specify for an interface type of 'user' is the common attributes: PCI address, NIC model (and that's basically it). However, some need to configure other address range than the default one.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
Notes: Frankly, I'm not that convinced about this one. I mean, this puts IP addresses into net->hostIP while we are configuring guest side of the SLIRP. However, it just feels better to have the IP addresses under <source/> than under <interface/>. Which actually is the other option. So it's either:
<interface type='user'> <source> <ip address='1.2.2.4'/> </source> </interface>
for net->hostIP, or it's:
<interface type='user'> <ip address='1.2.2.4'/> </interface>
In order to be consistent between all different hypervisors and interface connection types, any ip addresses and routes that are set to be visible on the host side of the interface "apparatus" must be under <source>, and those that are set to be visible on the guest side must be directly under the top level of <interface>. Since the IP you're configuring in this patch will be seen in the guest, it needs to be at the toplevel (i.e. it will be parsed into net->guestIP).
I'm not convinced either. If you don't like the fact that it's in hostIP (even though we're setting up the backend of that device, not the device itself, it's just the IP that the internal DHCP server should send to the guest) and want to have it in guestIP (which would make sense as well) then it should be:
<interface type='user'> <target> <ip address='1.2.2.4'/> </target> </interface>
When I dropped into this code to add support for specifying the IP address and routes on the *host* side of a tap device/veth device (see commit 98fa8f3ef) for QEMU and LXC (along with the "peer IP" for the other end of a tap/veth, which is *not* the same thing!), I *wished* that we had added the new <ip> and <route> elements for specifying guest-side <ip> and <route> under <target> as you suggest, since <target> is intended to contain items configuring how the device appears on the guest side[*]. Unfortunately, the decision on the location of guest-side <ip> was made *long* ago (at least prior to July of 2008, I didn't feel like spelunking any further), when <ip> was added directly under <interface> to support setting the guest-side IP address on Xen. Many years later (2014), when cbossdonnat added support for setting the guest-side IP of an LXC veth pair, he also used the same element in order to provide consistency. So, we are unable to put it under <target>, and that decision was made sometime befor 2008, [*] Of course, the <target> element is used inconsistently within <interface> when compared to other uses - <target dev='blah'/> is used to specify the name of the tap/macvtap device *as it is known on the host side*. <target> has been described to me as a place to configure what things look like to the guest (e.g. the intended name for a disk device on the guest)(which is anyway ignored everywhere except Xen, but that's a different topic!), so using it to configure a name that's visible only to the host seems wrong, but it's been that way since sometime long before even I was involved with libvirt, so there is no changing it now :-P
Which would cleanly correlate to that (and I also don't like the look of it). So I'll leave this to a further discussion.
I think it *would* have been good under <target>, if anyone had foreseen the need for a <target> grouping (and consistently used it) from the beginning. But that train sailed into space long ago, so in order to be backward compatible and consistent, the guest-side IP/routes need to be directly under <interface> (just like the guest-side PCI address for PCI devices is), and the host-side IP/routes need to go under <source> (just as host-side PCI addresses of <hostdev> devices are).
for net->guestIP. I went for the former one, but I don't have a strong opinion on that.
That's good, because I *do*! :-P
docs/formatdomain.html.in | 13 ++++++- docs/schemas/domaincommon.rng | 5 +++ src/conf/domain_conf.c | 5 +-- .../qemuxml2argv-net-user-addr.xml | 42 ++++++++++++++++++++++ .../qemuxml2xmlout-net-user-addr.xml | 1 + tests/qemuxml2xmltest.c | 1 + 6 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8ca7637a4..65a8886ee 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4701,7 +4701,14 @@ starting from <code>10.0.2.15</code>. The default router will be <code>10.0.2.2</code> and the DNS server will be <code>10.0.2.3</code>. This networking is the only option for unprivileged users who need their - VMs to have outgoing access. + VMs to have outgoing access. However, <span class="since">since + 3.8.0</span>, it is possible to override the default network by + including <code>source</code> element. The element can have <code>ip</code> + element for each IPv4 and IPv6. The element has <code>family</code> + attribute which accepts <code>ipv4</code> and <code>ipv6</code> values. + Then there's <code>address</code> attribute for specifying IP address + corresponding to the family. Optionally, the default prefix length can be + overridden via <code>prefix</code> attribute.
This will of course need to be rewritten (and grammar cleaned up). You realize now that you've added support for setting the IP address, inevitably someone will expect you to write patches to support setting of the DHCP server, DNS server, default route, DHCP range, hostname, NMBD server, domain name, tftp directory and bootfile,.... :-O)
</p>
<pre> @@ -4711,6 +4718,10 @@ ... <interface type='user'> <mac address="00:11:22:33:44:55"/> + <source> + <ip family='ipv4' address='172.17.2.0' prefix='24'/> + <ip family='ipv6' address='2001:db8:ac10:fd01::' prefix='64'/> + </source> </interface> </devices> ...</pre> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index c9a4f7a9a..7b5292bd3 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2428,6 +2428,11 @@ <value>user</value> </attribute> <interleave> + <optional> + <element name="source"> + <ref name="interface-ip-info"/>
This also allows <route/> here, I would split the definition and disallow that. Just for the sake of pointless bugs flying by.
Or maybe just check for presence of <route> during parsing and issue an ENOTSUPP log if it's found (that will be needed anyway in case XML validation is turned off). There's a certain point of detail when we stop caring about the exactness of the RNG. Eventually it becomes more difficult to read the RNG than it's worth, and the error messages provided when validation fails.
+ </element> + </optional> <ref name="interface-options"/> </interleave> </group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 676fc0f34..ef236757a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5214,12 +5214,13 @@ static int virDomainNetDefValidate(const virDomainNetDef *net) { if ((net->hostIP.nroutes || net->hostIP.nips) && - net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + net->type != VIR_DOMAIN_NET_TYPE_ETHERNET && + net->type != VIR_DOMAIN_NET_TYPE_USER) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid attempt to set network interface " "host-side IP route and/or address info on " "interface of type '%s'. This is only supported " - "on interfaces of type 'ethernet'"), + "on interfaces of type 'ethernet' and 'user'"),
Same here, you even give the hint that routes are supported for the type='user'
Yeah, this should be separated. It was previously combined for brevity, only because the two were both only supported in exactly the same cases. Also, it looks like qemu only supports a single IPv4 and single IPv6 address on each interface, so you need to validate there is at most one of each type.
virDomainNetTypeToString(net->type)); return -1; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml new file mode 100644 index 000000000..65362d8aa --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml @@ -0,0 +1,42 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i686</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controlleer> + <controller type='pci' index='0' model='pci-root'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <source> + <ip address='172.17.2.0' family='ipv4' prefix='24'/>
Also add ipv6 into the test.
+ </source> + <model type='rtl8139'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml new file mode 120000 index 000000000..fd909ec24 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/qemuxml2argv-net-user-addr.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0a87cedf2..99b15ad0c 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -531,6 +531,7 @@ mymain(void) DO_TEST("misc-uuid", NONE); DO_TEST("net-vhostuser", NONE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", NONE); DO_TEST("net-virtio-disable-offloads", NONE); -- 2.13.5
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Tue, Sep 12, 2017 at 01:35:08PM -0400, Laine Stump wrote:
On 09/12/2017 05:53 AM, Martin Kletzander wrote:
On Tue, Sep 12, 2017 at 11:32:52AM +0200, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Currently, all that users can specify for an interface type of 'user' is the common attributes: PCI address, NIC model (and that's basically it). However, some need to configure other address range than the default one.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
Notes: Frankly, I'm not that convinced about this one. I mean, this puts IP addresses into net->hostIP while we are configuring guest side of the SLIRP. However, it just feels better to have the IP addresses under <source/> than under <interface/>. Which actually is the other option. So it's either:
<interface type='user'> <source> <ip address='1.2.2.4'/> </source> </interface>
for net->hostIP, or it's:
<interface type='user'> <ip address='1.2.2.4'/> </interface>
In order to be consistent between all different hypervisors and interface connection types, any ip addresses and routes that are set to be visible on the host side of the interface "apparatus" must be under <source>, and those that are set to be visible on the guest side must be directly under the top level of <interface>. Since the IP you're configuring in this patch will be seen in the guest, it needs to be at the toplevel (i.e. it will be parsed into net->guestIP).
I'm not convinced either. If you don't like the fact that it's in hostIP (even though we're setting up the backend of that device, not the device itself, it's just the IP that the internal DHCP server should send to the guest) and want to have it in guestIP (which would make sense as well) then it should be:
<interface type='user'> <target> <ip address='1.2.2.4'/> </target> </interface>
When I dropped into this code to add support for specifying the IP address and routes on the *host* side of a tap device/veth device (see commit 98fa8f3ef) for QEMU and LXC (along with the "peer IP" for the other end of a tap/veth, which is *not* the same thing!), I *wished* that we had added the new <ip> and <route> elements for specifying guest-side <ip> and <route> under <target> as you suggest, since <target> is intended to contain items configuring how the device appears on the guest side[*]. Unfortunately, the decision on the location of guest-side <ip> was made *long* ago (at least prior to July of 2008, I didn't feel like spelunking any further), when <ip> was added directly under <interface> to support setting the guest-side IP address on Xen.
I totally agree with <interface><ip/>..., after I sent that mail Michal explained to me that there already is such concept for LXC for example. Had I known that, I wouldn't have talked about any <target/>. But thanks for thorough explanation anyway ;)

On 09/12/2017 07:35 PM, Laine Stump wrote:
On 09/12/2017 05:53 AM, Martin Kletzander wrote:
On Tue, Sep 12, 2017 at 11:32:52AM +0200, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Currently, all that users can specify for an interface type of 'user' is the common attributes: PCI address, NIC model (and that's basically it). However, some need to configure other address range than the default one.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
Notes: Frankly, I'm not that convinced about this one. I mean, this puts IP addresses into net->hostIP while we are configuring guest side of the SLIRP. However, it just feels better to have the IP addresses under <source/> than under <interface/>. Which actually is the other option. So it's either:
<interface type='user'> <source> <ip address='1.2.2.4'/> </source> </interface>
for net->hostIP, or it's:
<interface type='user'> <ip address='1.2.2.4'/> </interface>
In order to be consistent between all different hypervisors and interface connection types, any ip addresses and routes that are set to be visible on the host side of the interface "apparatus" must be under <source>, and those that are set to be visible on the guest side must be directly under the top level of <interface>. Since the IP you're configuring in this patch will be seen in the guest, it needs to be at the toplevel (i.e. it will be parsed into net->guestIP).
I'm not convinced either. If you don't like the fact that it's in hostIP (even though we're setting up the backend of that device, not the device itself, it's just the IP that the internal DHCP server should send to the guest) and want to have it in guestIP (which would make sense as well) then it should be:
<interface type='user'> <target> <ip address='1.2.2.4'/> </target> </interface>
When I dropped into this code to add support for specifying the IP address and routes on the *host* side of a tap device/veth device (see commit 98fa8f3ef) for QEMU and LXC (along with the "peer IP" for the other end of a tap/veth, which is *not* the same thing!), I *wished* that we had added the new <ip> and <route> elements for specifying guest-side <ip> and <route> under <target> as you suggest, since <target> is intended to contain items configuring how the device appears on the guest side[*]. Unfortunately, the decision on the location of guest-side <ip> was made *long* ago (at least prior to July of 2008, I didn't feel like spelunking any further), when <ip> was added directly under <interface> to support setting the guest-side IP address on Xen.
Many years later (2014), when cbossdonnat added support for setting the guest-side IP of an LXC veth pair, he also used the same element in order to provide consistency. So, we are unable to put it under <target>, and that decision was made sometime befor 2008,
[*] Of course, the <target> element is used inconsistently within <interface> when compared to other uses - <target dev='blah'/> is used to specify the name of the tap/macvtap device *as it is known on the host side*. <target> has been described to me as a place to configure what things look like to the guest (e.g. the intended name for a disk device on the guest)(which is anyway ignored everywhere except Xen, but that's a different topic!), so using it to configure a name that's visible only to the host seems wrong, but it's been that way since sometime long before even I was involved with libvirt, so there is no changing it now :-P
Okay, I'll move it up so that it's under <interface/> directly.
Which would cleanly correlate to that (and I also don't like the look of it). So I'll leave this to a further discussion.
I think it *would* have been good under <target>, if anyone had foreseen the need for a <target> grouping (and consistently used it) from the beginning. But that train sailed into space long ago, so in order to be backward compatible and consistent, the guest-side IP/routes need to be directly under <interface> (just like the guest-side PCI address for PCI devices is), and the host-side IP/routes need to go under <source> (just as host-side PCI addresses of <hostdev> devices are).
for net->guestIP. I went for the former one, but I don't have a strong opinion on that.
That's good, because I *do*! :-P
docs/formatdomain.html.in | 13 ++++++- docs/schemas/domaincommon.rng | 5 +++ src/conf/domain_conf.c | 5 +-- .../qemuxml2argv-net-user-addr.xml | 42 ++++++++++++++++++++++ .../qemuxml2xmlout-net-user-addr.xml | 1 + tests/qemuxml2xmltest.c | 1 + 6 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-net-user-addr.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8ca7637a4..65a8886ee 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4701,7 +4701,14 @@ starting from <code>10.0.2.15</code>. The default router will be <code>10.0.2.2</code> and the DNS server will be <code>10.0.2.3</code>. This networking is the only option for unprivileged users who need their - VMs to have outgoing access. + VMs to have outgoing access. However, <span class="since">since + 3.8.0</span>, it is possible to override the default network by + including <code>source</code> element. The element can have <code>ip</code> + element for each IPv4 and IPv6. The element has <code>family</code> + attribute which accepts <code>ipv4</code> and <code>ipv6</code> values. + Then there's <code>address</code> attribute for specifying IP address + corresponding to the family. Optionally, the default prefix length can be + overridden via <code>prefix</code> attribute.
This will of course need to be rewritten (and grammar cleaned up).
You realize now that you've added support for setting the IP address, inevitably someone will expect you to write patches to support setting of the DHCP server, DNS server, default route, DHCP range, hostname, NMBD server, domain name, tftp directory and bootfile,.... :-O)
For that I have already an answer prepared: Patches are welcome :-P
</p>
<pre> @@ -4711,6 +4718,10 @@ ... <interface type='user'> <mac address="00:11:22:33:44:55"/> + <source> + <ip family='ipv4' address='172.17.2.0' prefix='24'/> + <ip family='ipv6' address='2001:db8:ac10:fd01::' prefix='64'/> + </source> </interface> </devices> ...</pre> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index c9a4f7a9a..7b5292bd3 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2428,6 +2428,11 @@ <value>user</value> </attribute> <interleave> + <optional> + <element name="source"> + <ref name="interface-ip-info"/>
This also allows <route/> here, I would split the definition and disallow that. Just for the sake of pointless bugs flying by.
Or maybe just check for presence of <route> during parsing and issue an ENOTSUPP log if it's found (that will be needed anyway in case XML validation is turned off). There's a certain point of detail when we stop caring about the exactness of the RNG. Eventually it becomes more difficult to read the RNG than it's worth, and the error messages provided when validation fails.
Agreed. There's a lot of places where our schema is looser than our parser. And I don't think that's a problem. The only way we could have 1:1 correspondence is if we generated the parser from RNG schema. And even then there are per driver nuances (what's allowed in driver X is forbidden/not yet implemented in driver Y).
+ </element> + </optional> <ref name="interface-options"/> </interleave> </group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 676fc0f34..ef236757a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5214,12 +5214,13 @@ static int virDomainNetDefValidate(const virDomainNetDef *net) { if ((net->hostIP.nroutes || net->hostIP.nips) && - net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + net->type != VIR_DOMAIN_NET_TYPE_ETHERNET && + net->type != VIR_DOMAIN_NET_TYPE_USER) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid attempt to set network interface " "host-side IP route and/or address info on " "interface of type '%s'. This is only supported " - "on interfaces of type 'ethernet'"), + "on interfaces of type 'ethernet' and 'user'"),
Same here, you even give the hint that routes are supported for the type='user'
Yeah, this should be separated. It was previously combined for brevity, only because the two were both only supported in exactly the same cases.
Also, it looks like qemu only supports a single IPv4 and single IPv6 address on each interface, so you need to validate there is at most one of each type.
Yep. I'm doing that in the next patch. Since this patch aims on config only, I wanted to keep it generic enough and check only obviously wrong values, e.g. IP addresses from movies like 218.108.149.373 (Mr Robot ftw! But apart from that I was surprised how quite accurate it was). Then, if a driver poses stricter limits those should be checked there now that we have per driver per device validation callbacks. One exception to this rule is, if all drivers forbid certain value/combination, it can go to the generic src/conf/ callback instead of copying the same lines all over the place. Michal

https://bugzilla.redhat.com/show_bug.cgi?id=1075520 Apart from generic checks, we need to constrain netmask/prefix lenght a bit. Thing is, with current implementation QEMU needs to be able to 'assign' some IP addresses to the virtual network. For instance, the default gateway is at x.x.x.2, dns is at x.x.x.3, the default DHCP range is x.x.x.15-x.x.x.30. Since we don't expose these settings yet, it's safer to require shorter prefix to have room for the defaults. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 22 ++++++++++ src/qemu/qemu_domain.c | 49 ++++++++++++++++++++++ .../qemuxml2argv-net-user-addr.args | 25 +++++++++++ tests/qemuxml2argvtest.c | 1 + 4 files changed, 97 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d553df57f..3e1bc5fa8 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3805,6 +3805,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virDomainNetType netType = virDomainNetGetActualType(net); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); size_t i; + char *addr = NULL; char *ret = NULL; if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) { @@ -3873,6 +3874,26 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break; case VIR_DOMAIN_NET_TYPE_USER: + virBufferAsprintf(&buf, "user%c", type_sep); + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + const char *prefix = ""; + + if (!(addr = virSocketAddrFormat(&ip->address))) + goto cleanup; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) + prefix = "net="; + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + prefix = "ipv6-net"; + + virBufferAsprintf(&buf, "%s%s", prefix, addr); + if (ip->prefix) + virBufferAsprintf(&buf, "/%u", ip->prefix); + virBufferAddChar(&buf, ','); + } + break; + case VIR_DOMAIN_NET_TYPE_INTERNAL: virBufferAsprintf(&buf, "user%c", type_sep); break; @@ -3928,6 +3949,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, cleanup: virBufferFreeAndReset(&buf); virObjectUnref(cfg); + VIR_FREE(addr); return ret; } diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 72031893f..bf0c7300c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3338,9 +3338,11 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, void *opaque ATTRIBUTE_UNUSED) { int ret = -1; + size_t i; if (dev->type == VIR_DOMAIN_DEVICE_NET) { const virDomainNetDef *net = dev->data.net; + bool hasIPv4 = false, hasIPv6 = false; if (net->guestIP.nroutes || net->guestIP.nips) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3350,6 +3352,53 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, goto cleanup; } + if (net->type == VIR_DOMAIN_NET_TYPE_USER) { + if (net->hostIP.nroutes) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid attempt to set network interface " + "guest-side IP address info, " + "not supported by QEMU")); + goto cleanup; + } + + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + + if (VIR_SOCKET_ADDR_VALID(&ip->peer)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("peers are not supported by QEMU")); + goto cleanup; + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) { + if (hasIPv4) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv4 address allowed")); + goto cleanup; + } + hasIPv4 = true; + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) { + if (hasIPv6) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv6 address allowed")); + goto cleanup; + } + hasIPv6 = true; + } + + /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long")); + goto cleanup; + } + } + } + if (STREQ_NULLABLE(net->model, "virtio")) { if (net->driver.virtio.rx_queue_size & (net->driver.virtio.rx_queue_size - 1)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args new file mode 100644 index 000000000..51aacedb3 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args @@ -0,0 +1,25 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-device rtl8139,vlan=0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x3 \ +-net user,net=172.17.2.0/24,vlan=0,name=hostnet0 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2c040e4c0..584df017a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1159,6 +1159,7 @@ mymain(void) QEMU_CAPS_NETDEV, QEMU_CAPS_VHOSTUSER_MULTIQUEUE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_TX_ALG); -- 2.13.5

On Tue, Sep 12, 2017 at 11:32:53AM +0200, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Apart from generic checks, we need to constrain netmask/prefix lenght a bit. Thing is, with current implementation QEMU needs to be able to 'assign' some IP addresses to the virtual network. For instance, the default gateway is at x.x.x.2, dns is at x.x.x.3, the default DHCP range is x.x.x.15-x.x.x.30. Since we don't expose these settings yet, it's safer to require shorter prefix to have room for the defaults.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 22 ++++++++++ src/qemu/qemu_domain.c | 49 ++++++++++++++++++++++ .../qemuxml2argv-net-user-addr.args | 25 +++++++++++ tests/qemuxml2argvtest.c | 1 + 4 files changed, 97 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d553df57f..3e1bc5fa8 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3805,6 +3805,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virDomainNetType netType = virDomainNetGetActualType(net); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); size_t i; + char *addr = NULL; char *ret = NULL;
if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) { @@ -3873,6 +3874,26 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break;
case VIR_DOMAIN_NET_TYPE_USER: + virBufferAsprintf(&buf, "user%c", type_sep); + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + const char *prefix = ""; + + if (!(addr = virSocketAddrFormat(&ip->address))) + goto cleanup; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) + prefix = "net="; + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + prefix = "ipv6-net"; + + virBufferAsprintf(&buf, "%s%s", prefix, addr); + if (ip->prefix) + virBufferAsprintf(&buf, "/%u", ip->prefix); + virBufferAddChar(&buf, ','); + } + break; + case VIR_DOMAIN_NET_TYPE_INTERNAL: virBufferAsprintf(&buf, "user%c", type_sep); break; @@ -3928,6 +3949,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, cleanup: virBufferFreeAndReset(&buf); virObjectUnref(cfg); + VIR_FREE(addr); return ret; }
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 72031893f..bf0c7300c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3338,9 +3338,11 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, void *opaque ATTRIBUTE_UNUSED) { int ret = -1; + size_t i;
if (dev->type == VIR_DOMAIN_DEVICE_NET) { const virDomainNetDef *net = dev->data.net; + bool hasIPv4 = false, hasIPv6 = false;
if (net->guestIP.nroutes || net->guestIP.nips) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3350,6 +3352,53 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, goto cleanup; }
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER) { + if (net->hostIP.nroutes) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid attempt to set network interface " + "guest-side IP address info, " + "not supported by QEMU")); + goto cleanup;
Oh, here it is, I'd just say that routes are not supported. But probably omit the previous message for other devices. Join them nicely together, please.
+ } + + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + + if (VIR_SOCKET_ADDR_VALID(&ip->peer)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("peers are not supported by QEMU"));
I'm afraid this error message is pointless to a random observer. No matter how funny I find it, this could be better...
+ goto cleanup; + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) { + if (hasIPv4) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv4 address allowed")); + goto cleanup; + } + hasIPv4 = true; + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) { + if (hasIPv6) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv6 address allowed")); + goto cleanup; + } + hasIPv6 = true; + } + + /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) {
This could fail if you have: <ip family='ipv6' prefix='119'/> <ip family='ipv4' prefix='24'/> I hope you see why ;)
+ virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long")); + goto cleanup; + } + } + } + if (STREQ_NULLABLE(net->model, "virtio")) { if (net->driver.virtio.rx_queue_size & (net->driver.virtio.rx_queue_size - 1)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args new file mode 100644 index 000000000..51aacedb3 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args @@ -0,0 +1,25 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-device rtl8139,vlan=0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x3 \ +-net user,net=172.17.2.0/24,vlan=0,name=hostnet0 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2c040e4c0..584df017a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1159,6 +1159,7 @@ mymain(void) QEMU_CAPS_NETDEV, QEMU_CAPS_VHOSTUSER_MULTIQUEUE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_TX_ALG); -- 2.13.5
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 09/12/2017 05:32 AM, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Apart from generic checks, we need to constrain netmask/prefix lenght a bit. Thing is, with current implementation QEMU needs to be able to 'assign' some IP addresses to the virtual network. For instance, the default gateway is at x.x.x.2, dns is at x.x.x.3, the default DHCP range is x.x.x.15-x.x.x.30. Since we don't expose these settings yet, it's safer to require shorter prefix to have room for the defaults.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 22 ++++++++++ src/qemu/qemu_domain.c | 49 ++++++++++++++++++++++ .../qemuxml2argv-net-user-addr.args | 25 +++++++++++ tests/qemuxml2argvtest.c | 1 + 4 files changed, 97 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d553df57f..3e1bc5fa8 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3805,6 +3805,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virDomainNetType netType = virDomainNetGetActualType(net); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); size_t i; + char *addr = NULL; char *ret = NULL;
if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) { @@ -3873,6 +3874,26 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break;
case VIR_DOMAIN_NET_TYPE_USER: + virBufferAsprintf(&buf, "user%c", type_sep); + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + const char *prefix = ""; + + if (!(addr = virSocketAddrFormat(&ip->address))) + goto cleanup; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) + prefix = "net="; + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + prefix = "ipv6-net";
You're missing an "=" after ipv6-net.
+ + virBufferAsprintf(&buf, "%s%s", prefix, addr); + if (ip->prefix) + virBufferAsprintf(&buf, "/%u", ip->prefix); + virBufferAddChar(&buf, ','); + } + break; + case VIR_DOMAIN_NET_TYPE_INTERNAL: virBufferAsprintf(&buf, "user%c", type_sep); break; @@ -3928,6 +3949,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, cleanup: virBufferFreeAndReset(&buf); virObjectUnref(cfg); + VIR_FREE(addr);
You could have made addr local to the NET_TYPE_USER case, but then we would have to move it out if we added any additional error checking in the future (i.e. I agree with you putting it in the scope of the entire function)
return ret; }
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 72031893f..bf0c7300c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3338,9 +3338,11 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, void *opaque ATTRIBUTE_UNUSED) { int ret = -1; + size_t i;
if (dev->type == VIR_DOMAIN_DEVICE_NET) { const virDomainNetDef *net = dev->data.net; + bool hasIPv4 = false, hasIPv6 = false;
if (net->guestIP.nroutes || net->guestIP.nips) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3350,6 +3352,53 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, goto cleanup; }
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER) { + if (net->hostIP.nroutes) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid attempt to set network interface " + "guest-side IP address info, " + "not supported by QEMU"));
I agree with Martin that you should specifically say that you can't set <route>, not "IP address info". Of course this will need to change, since it's guestIP.nips that we want to allow.
+ goto cleanup; + } + + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + + if (VIR_SOCKET_ADDR_VALID(&ip->peer)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("peers are not supported by QEMU")); + goto cleanup;
Aside from the humor that Martin finds in this message, it's also not correct. It *is* allowed to set a peer IP on the host side of a *tap-based* network device with qemu. (and not allowed to set the peer IP on the guest side of *any* type of network device).
+ } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) { + if (hasIPv4) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv4 address allowed")); + goto cleanup; + } + hasIPv4 = true;
Aha! You *are* checking for this! (maybe say "... per interface ...")
+ } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) { + if (hasIPv6) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv6 address allowed")); + goto cleanup; + } + hasIPv6 = true; + } + + /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long"));
You should also probably check for conflict with the addresses that are automatically used by slirp for DNS, default route, dhcp server, etc. (Although I *hate* needing to hard code stuff like that :-/)
+ goto cleanup; + } + } + } + if (STREQ_NULLABLE(net->model, "virtio")) { if (net->driver.virtio.rx_queue_size & (net->driver.virtio.rx_queue_size - 1)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args new file mode 100644 index 000000000..51aacedb3 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args @@ -0,0 +1,25 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-device rtl8139,vlan=0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x3 \ +-net user,net=172.17.2.0/24,vlan=0,name=hostnet0
You really should add QEMU_CAPS_NETDEV to the test case (in the previous patch, too, for consistency) so that this test represents what is produced on a more modern qemu. (actually, I would propose that all other tests for network devices be changed to do the same thing - right now the network test cases are verifying that we generate correct commandlines for a version of qemu that probably hasn't existed in the wild for a very long time).
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2c040e4c0..584df017a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1159,6 +1159,7 @@ mymain(void) QEMU_CAPS_NETDEV, QEMU_CAPS_VHOSTUSER_MULTIQUEUE); DO_TEST("net-user", NONE); + DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_TX_ALG);

On 09/12/2017 09:16 PM, Laine Stump wrote:
On 09/12/2017 05:32 AM, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
Apart from generic checks, we need to constrain netmask/prefix lenght a bit. Thing is, with current implementation QEMU needs to be able to 'assign' some IP addresses to the virtual network. For instance, the default gateway is at x.x.x.2, dns is at x.x.x.3, the default DHCP range is x.x.x.15-x.x.x.30. Since we don't expose these settings yet, it's safer to require shorter prefix to have room for the defaults.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 22 ++++++++++ src/qemu/qemu_domain.c | 49 ++++++++++++++++++++++ .../qemuxml2argv-net-user-addr.args | 25 +++++++++++ tests/qemuxml2argvtest.c | 1 + 4 files changed, 97 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-addr.args
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d553df57f..3e1bc5fa8 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3805,6 +3805,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virDomainNetType netType = virDomainNetGetActualType(net); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); size_t i; + char *addr = NULL; char *ret = NULL;
if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) { @@ -3873,6 +3874,26 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break;
case VIR_DOMAIN_NET_TYPE_USER: + virBufferAsprintf(&buf, "user%c", type_sep); + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + const char *prefix = ""; + + if (!(addr = virSocketAddrFormat(&ip->address))) + goto cleanup; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) + prefix = "net="; + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + prefix = "ipv6-net";
You're missing an "=" after ipv6-net.
+ + virBufferAsprintf(&buf, "%s%s", prefix, addr); + if (ip->prefix) + virBufferAsprintf(&buf, "/%u", ip->prefix); + virBufferAddChar(&buf, ','); + } + break; + case VIR_DOMAIN_NET_TYPE_INTERNAL: virBufferAsprintf(&buf, "user%c", type_sep); break; @@ -3928,6 +3949,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, cleanup: virBufferFreeAndReset(&buf); virObjectUnref(cfg); + VIR_FREE(addr);
You could have made addr local to the NET_TYPE_USER case, but then we would have to move it out if we added any additional error checking in the future (i.e. I agree with you putting it in the scope of the entire function)
return ret; }
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 72031893f..bf0c7300c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3338,9 +3338,11 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, void *opaque ATTRIBUTE_UNUSED) { int ret = -1; + size_t i;
if (dev->type == VIR_DOMAIN_DEVICE_NET) { const virDomainNetDef *net = dev->data.net; + bool hasIPv4 = false, hasIPv6 = false;
if (net->guestIP.nroutes || net->guestIP.nips) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3350,6 +3352,53 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev, goto cleanup; }
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER) { + if (net->hostIP.nroutes) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid attempt to set network interface " + "guest-side IP address info, " + "not supported by QEMU"));
I agree with Martin that you should specifically say that you can't set <route>, not "IP address info". Of course this will need to change, since it's guestIP.nips that we want to allow.
+ goto cleanup; + } + + for (i = 0; i < net->hostIP.nips; i++) { + const virNetDevIPAddr *ip = net->hostIP.ips[i]; + + if (VIR_SOCKET_ADDR_VALID(&ip->peer)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("peers are not supported by QEMU")); + goto cleanup;
Aside from the humor that Martin finds in this message, it's also not correct. It *is* allowed to set a peer IP on the host side of a *tap-based* network device with qemu. (and not allowed to set the peer IP on the guest side of *any* type of network device).
+ } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) { + if (hasIPv4) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv4 address allowed")); + goto cleanup; + } + hasIPv4 = true;
Aha! You *are* checking for this! (maybe say "... per interface ...")
+ } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) { + if (hasIPv6) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only one IPv6 address allowed")); + goto cleanup; + } + hasIPv6 = true; + } + + /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long"));
You should also probably check for conflict with the addresses that are automatically used by slirp for DNS, default route, dhcp server, etc. (Although I *hate* needing to hard code stuff like that :-/)
Do I? If the prefix allows sufficiently enough room for them then the defaults shouldn't clash. Or you mean checking against host's IPs? The defaults according to the qemu docs are as follows: host = x.x.x.2 dhcpstart = x.x.x.15 - x.x.x.30 dns = x.x.x.3 smb = x.x.x.4 Michal

On 09/13/2017 06:46 AM, Michal Privoznik wrote:
On 09/12/2017 09:16 PM, Laine Stump wrote:
On 09/12/2017 05:32 AM, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
+ /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long")); You should also probably check for conflict with the addresses that are automatically used by slirp for DNS, default route, dhcp server, etc. (Although I *hate* needing to hard code stuff like that :-/) Do I? If the prefix allows sufficiently enough room for them then the defaults shouldn't clash. Or you mean checking against host's IPs? The defaults according to the qemu docs are as follows:
host = x.x.x.2 dhcpstart = x.x.x.15 - x.x.x.30 dns = x.x.x.3 smb = x.x.x.4
Right. So let's say the user requests x.x.x.3 for the IP - that would conflict with the DNS server address. I would really hate to have those specific checks hardcoded though (in case qemu changed the addresses for some reason. Maybe if we're lucky, qemu itself will error out if there is a conflict. If that's the case, then we can just leave it to them to report the error (as long as we're able to adequately scrape the error message so we can report something sensible).

On 09/13/2017 06:02 PM, Laine Stump wrote:
On 09/13/2017 06:46 AM, Michal Privoznik wrote:
On 09/12/2017 09:16 PM, Laine Stump wrote:
On 09/12/2017 05:32 AM, Michal Privoznik wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1075520
+ /* QEMU needs some space to have + * some other 'hosts' on the network. */ + if ((hasIPv4 && ip->prefix > 27) || + (hasIPv6 && ip->prefix > 120)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("prefix too long")); You should also probably check for conflict with the addresses that are automatically used by slirp for DNS, default route, dhcp server, etc. (Although I *hate* needing to hard code stuff like that :-/) Do I? If the prefix allows sufficiently enough room for them then the defaults shouldn't clash. Or you mean checking against host's IPs? The defaults according to the qemu docs are as follows:
host = x.x.x.2 dhcpstart = x.x.x.15 - x.x.x.30 dns = x.x.x.3 smb = x.x.x.4
Right. So let's say the user requests x.x.x.3 for the IP - that would conflict with the DNS server address.
I would really hate to have those specific checks hardcoded though (in case qemu changed the addresses for some reason. Maybe if we're lucky, qemu itself will error out if there is a conflict. If that's the case, then we can just leave it to them to report the error (as long as we're able to adequately scrape the error message so we can report something sensible).
Ah, qemu does not report any error. They merely take the default netmask for given IP address and zero out the subnet part. This is what I tried: <interface type='user'> <mac address='00:11:22:33:44:55'/> <ip address='172.17.2.2' family='ipv4'/> <ip address='2001:db8:ac10:fd01::3' family='ipv6'/> <model type='rtl8139'/> </interface> and this is how guest sees the interface: 3: ens8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff inet 172.16.2.15/12 brd 172.31.255.255 scope global dynamic ens8 valid_lft 86373sec preferred_lft 86373sec inet6 2001:db8:ac10:fd01:a37e:8d3e:c189:d4f1/64 scope global noprefixroute dynamic valid_lft 86374sec preferred_lft 14374sec So, what if while parsing the IP address we would do the same? I mean, zero out the subnet part of the address. If prefix was provided use that otherwise use the default. Users are required to reload the domain XML after any edit anyway. This also makes more sense IMO because users are providing network address, not a host one. Having said that, thanks for review on v2, but I'll postpone the push until we have a clear agreement here. Michal

On Tue, Sep 12, 2017 at 11:32:48AM +0200, Michal Privoznik wrote:
*** BLURB HERE ***
Michal Privoznik (5): rng: Drop useless <choice/> rng: Fix formatting qemuBuildHostNetStr: Don't leak buffer
ACK to those ^^
conf: Allow usernet to have an address
Let's discuss ^^
qemu: Implement usernet address
Some nits to fix ^^ Martin
participants (3)
-
Laine Stump
-
Martin Kletzander
-
Michal Privoznik