We allow users to use SLIRP stack. However, there are some knobs
which are not exposed to users, such as host network address, DNS
server, smb, and others.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
docs/formatdomain.html.in | 7 +-
docs/schemas/domaincommon.rng | 23 +++++-
src/conf/domain_conf.c | 88 ++++++++++++++++++++++
src/conf/domain_conf.h | 6 ++
src/qemu/qemu_command.c | 19 +++++
.../qemuxml2argvdata/qemuxml2argv-net-user-ip.args | 7 ++
.../qemuxml2argvdata/qemuxml2argv-net-user-ip.xml | 33 ++++++++
tests/qemuxml2argvtest.c | 1 +
8 files changed, 180 insertions(+), 4 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 7f90455..0a353ca 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -3225,7 +3225,11 @@
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. <span class="since">Since
1.2.3</span> the
+ user network can have the <ip/> element to override the default
+ network of <code>10.0.2.0/24</code>. For example it can be set to
+ <code>192.168.2.0/24</code>. The whole element and its attributes are
+ optional.
</p>
<pre>
@@ -3235,6 +3239,7 @@
...
<interface type='user'>
<mac address="00:11:22:33:44:55"/>
+ <ip address="192.168.2.0" prefix="24"
dns="192.168.2.3" dhcpstart="192.168.2.9"/>
</interface>
</devices>
...</pre>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index bcd8142..5745ce7 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -2152,9 +2152,26 @@
</optional>
<optional>
<element name="ip">
- <attribute name="address">
- <ref name="ipv4Addr"/>
- </attribute>
+ <optional>
+ <attribute name="address">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="prefix">
+ <data type="integer"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dns">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="dhcpstart">
+ <ref name="ipv4Addr"/>
+ </attribute>
+ </optional>
<empty/>
</element>
</optional>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 6fb216e..aec14ed 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1539,6 +1539,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
break;
case VIR_DOMAIN_NET_TYPE_USER:
+ VIR_FREE(def->data.user.ipaddr);
+ VIR_FREE(def->data.user.dns);
+ VIR_FREE(def->data.user.dhcpstart);
+ break;
+
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
@@ -6669,6 +6674,9 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
char *mode = NULL;
char *linkstate = NULL;
char *addrtype = NULL;
+ char *prefix = NULL;
+ char *dns = NULL;
+ char *dhcpstart = NULL;
virNWFilterHashTablePtr filterparams = NULL;
virDomainActualNetDefPtr actual = NULL;
xmlNodePtr oldnode = ctxt->node;
@@ -6750,6 +6758,13 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) &&
xmlStrEqual(cur->name, BAD_CAST "ip")) {
address = virXMLPropString(cur, "address");
+ } else if (!address &&
+ def->type == VIR_DOMAIN_NET_TYPE_USER &&
+ xmlStrEqual(cur->name, BAD_CAST "ip")) {
+ address = virXMLPropString(cur, "address");
+ prefix = virXMLPropString(cur, "prefix");
+ dns = virXMLPropString(cur, "dns");
+ dhcpstart = virXMLPropString(cur, "dhcpstart");
} else if (!ifname &&
xmlStrEqual(cur->name, BAD_CAST "target")) {
ifname = virXMLPropString(cur, "dev");
@@ -6988,6 +7003,59 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
break;
case VIR_DOMAIN_NET_TYPE_USER:
+ if (prefix) {
+ if (!address) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("can't use prefix without an address"));
+ goto error;
+ }
+
+ if (virStrToLong_i(prefix, NULL, 10, &def->data.user.prefix) < 0
||
+ def->data.user.prefix < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid prefix: '%s'"), prefix);
+ goto error;
+ }
+ } else {
+ def->data.user.prefix = -1;
+ }
+
+ if (address) {
+ if (virSocketAddrParse(NULL, address, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid address: '%s'"), address);
+ goto error;
+ }
+
+ def->data.user.ipaddr = address;
+ address = NULL;
+ }
+
+ if (dns) {
+ if (virSocketAddrParse(NULL, dns, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid dns address: '%s'"), dns);
+ goto error;
+ }
+
+ def->data.user.dns = dns;
+ dns = NULL;
+ }
+
+ if (dhcpstart) {
+ if (virSocketAddrParse(NULL, dhcpstart, AF_UNSPEC) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid dhcpstart address: '%s'"),
+ dhcpstart);
+ goto error;
+ }
+
+ def->data.user.dhcpstart = dhcpstart;
+ dhcpstart = NULL;
+ }
+
+ break;
+
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
@@ -7134,6 +7202,9 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
VIR_FREE(mode);
VIR_FREE(linkstate);
VIR_FREE(addrtype);
+ VIR_FREE(prefix);
+ VIR_FREE(dns);
+ VIR_FREE(dhcpstart);
virNWFilterHashTableFree(filterparams);
return def;
@@ -15766,6 +15837,23 @@ virDomainNetDefFormat(virBufferPtr buf,
break;
case VIR_DOMAIN_NET_TYPE_USER:
+ if (def->data.user.ipaddr ||
+ def->data.user.dns ||
+ def->data.user.dhcpstart) {
+ virBufferAddLit(buf, "<ip");
+
+ if (def->data.user.ipaddr) {
+ virBufferAsprintf(buf, " address='%s'",
def->data.user.ipaddr);
+ if (def->data.user.prefix >= 0)
+ virBufferAsprintf(buf, " prefix='%d'",
def->data.user.prefix);
+ }
+
+ virBufferEscapeString(buf, " dns='%s'",
def->data.user.dns);
+ virBufferEscapeString(buf, " dhcpstart='%s'",
def->data.user.dhcpstart);
+ virBufferAddLit(buf, "/>\n");
+ }
+ break;
+
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index f3f24c4..b5d52e9 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1046,6 +1046,12 @@ struct _virDomainNetDef {
struct {
virDomainHostdevDef def;
} hostdev;
+ struct {
+ char *ipaddr;
+ int prefix;
+ char *dns;
+ char *dhcpstart;
+ } user;
} data;
/* virtPortProfile is used by network/bridge/direct/hostdev */
virNetDevVPortProfilePtr virtPortProfile;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 9a314bf..f17061e 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5156,6 +5156,25 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
break;
case VIR_DOMAIN_NET_TYPE_USER:
+ virBufferAddLit(&buf, "user");
+ if (net->data.user.ipaddr) {
+ virBufferAsprintf(&buf, "%cnet=%s", type_sep,
+ net->data.user.ipaddr);
+ type_sep = ',';
+ if (net->data.user.prefix >= 0)
+ virBufferAsprintf(&buf, "/%d", net->data.user.prefix);
+ }
+ if (net->data.user.dns) {
+ virBufferAsprintf(&buf, "%cdns=%s", type_sep,
net->data.user.dns);
+ type_sep = ',';
+ }
+ if (net->data.user.dhcpstart) {
+ virBufferAsprintf(&buf, "%cdhcpstart=%s", type_sep,
+ net->data.user.dhcpstart);
+ type_sep = ',';
+ }
+ break;
+
default:
virBufferAddLit(&buf, "user");
break;
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.args
b/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.args
new file mode 100644
index 0000000..3e355f3
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.args
@@ -0,0 +1,7 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic \
+-monitor unix:/tmp/test-monitor,server,nowait \
+-no-acpi -boot c -usb -hda /dev/HostVG/QEMUGuest1 \
+-net nic,macaddr=00:11:22:33:44:55,vlan=0,model=rtl8139 \
+-net user,net=192.168.2.0/24,dns=192.168.2.3,dhcpstart=192.168.2.9,vlan=0 \
+-serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.xml
b/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.xml
new file mode 100644
index 0000000..5c36c77
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user-ip.xml
@@ -0,0 +1,33 @@
+<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</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'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <ip address='192.168.2.0' prefix='24' dns='192.168.2.3'
dhcpstart='192.168.2.9'/>
+ <mac address='00:11:22:33:44:55'/>
+ <model type='rtl8139'/>
+ </interface>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 56854dc..02ed4ba 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -923,6 +923,7 @@ mymain(void)
DO_TEST("misc-no-reboot", NONE);
DO_TEST("misc-uuid", QEMU_CAPS_NAME, QEMU_CAPS_UUID);
DO_TEST("net-user", NONE);
+ DO_TEST("net-user-ip", NONE);
DO_TEST("net-virtio", NONE);
DO_TEST("net-virtio-device",
QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_TX_ALG);
--
1.9.0