[libvirt] [PATCH 0/9] LXC network configuration support

Hi all, This patch series aims at filling one of the remaining gaps between lxc and our lxc driver. It opens up the possibilities to setup IP addresses on network devices, adds the possibility to add them default gateways, and uses them all in the libvirt driver. The LXC conf2xml conversion has also been updated to help migrating the remaining untouched lxc.network.ipv[46]* properties. The first patch in the series has almost nothing to do with the rest: it just adds some forgotten VIR_FREE from one of my previous patch. -- Cedric Cédric Bosdonnat (9): Domain conf: allow more than one IP address for net devices LXC: set IP addresses to veth devices in the container lxc conf2xml: convert IP addresses Allow network capabilities hostdev to configure IP addresses lxc conf2xml: convert ip addresses for hostdev NICs Domain network devices can now have a <gateway> element lxc conf2xml: convert lxc.network.ipv[46].gateway LXC: use the new net devices gateway definition LXC: honour network devices link state docs/formatdomain.html.in | 39 ++++ docs/schemas/domaincommon.rng | 55 +++++- src/conf/domain_conf.c | 211 +++++++++++++++++++-- src/conf/domain_conf.h | 22 ++- src/libvirt_private.syms | 2 + src/lxc/lxc_container.c | 66 ++++++- src/lxc/lxc_native.c | 173 ++++++++++++----- src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 6 +- src/qemu/qemu_driver.c | 25 ++- src/qemu/qemu_hotplug.c | 6 +- src/uml/uml_conf.c | 2 +- src/vbox/vbox_tmpl.c | 3 +- src/xenxs/xen_sxpr.c | 12 +- src/xenxs/xen_xm.c | 12 +- .../lxcconf2xmldata/lxcconf2xml-physnetwork.config | 4 + tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 3 + tests/lxcconf2xmldata/lxcconf2xml-simple.config | 4 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 3 + tests/lxcxml2xmldata/lxc-hostdev.xml | 3 + tests/lxcxml2xmldata/lxc-idmap.xml | 3 + 21 files changed, 548 insertions(+), 108 deletions(-) -- 1.8.4.5

Add the possibility to have more than one IP address configured for a domain network interface. IP addresses can also have a prefix to define the corresponding netmask. --- docs/formatdomain.html.in | 22 +++++++ docs/schemas/domaincommon.rng | 11 +++- src/conf/domain_conf.c | 123 +++++++++++++++++++++++++++++++------ src/conf/domain_conf.h | 16 ++++- src/libvirt_private.syms | 2 + src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 6 +- src/qemu/qemu_driver.c | 25 ++++++-- src/qemu/qemu_hotplug.c | 6 +- src/uml/uml_conf.c | 2 +- src/vbox/vbox_tmpl.c | 3 +- src/xenxs/xen_sxpr.c | 12 ++-- src/xenxs/xen_xm.c | 12 ++-- tests/lxcxml2xmldata/lxc-idmap.xml | 2 + 14 files changed, 195 insertions(+), 49 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8950959..0baf961 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3982,6 +3982,28 @@ qemu-kvm -net nic,model=? /dev/null <span class="since">Since 0.9.5</span> </p> + <h5><a name="ipconfig">IP configuration</a></h5> +<pre> + ... + <devices> + <interface type='network'> + <source network='default'/> + <target dev='vnet0'/> + <b><ip address='192.168.122.5' prefix='24'/></b> + </interface> + </devices> + ... +</pre> + + <p> + <span class="since">Since 1.2.8</span> the network devices can be provided + zero or more IP addresses to set + on the target device. Note that some hypervisors or network device types + will simply ignore them or only use the first one. The <code>address</code> + attribute can hold either an IPv4 or IPv6 address. The <code>prefix</code> + is not mandatory since some hypervisors do not handle it. + </p> + <h5><a name="elementVhostuser">vhost-user interface</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index f6f697c..cbfe1ef 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2243,14 +2243,19 @@ <empty/> </element> </optional> - <optional> + <zeroOrMore> <element name="ip"> <attribute name="address"> - <ref name="ipv4Addr"/> + <ref name="ipAddr"/> </attribute> + <optional> + <attribute name="prefix"> + <ref name="ipPrefix"/> + </attribute> + </optional> <empty/> </element> - </optional> + </zeroOrMore> <optional> <element name="script"> <attribute name="path"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1608939..4cdb435 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1322,6 +1322,8 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def) void virDomainNetDefFree(virDomainNetDefPtr def) { + size_t i; + if (!def) return; @@ -1330,7 +1332,6 @@ void virDomainNetDefFree(virDomainNetDefPtr def) switch (def->type) { case VIR_DOMAIN_NET_TYPE_ETHERNET: VIR_FREE(def->data.ethernet.dev); - VIR_FREE(def->data.ethernet.ipaddr); break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: @@ -1351,7 +1352,6 @@ void virDomainNetDefFree(virDomainNetDefPtr def) case VIR_DOMAIN_NET_TYPE_BRIDGE: VIR_FREE(def->data.bridge.brname); - VIR_FREE(def->data.bridge.ipaddr); break; case VIR_DOMAIN_NET_TYPE_INTERNAL: @@ -1377,6 +1377,10 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ifname_guest); VIR_FREE(def->ifname_guest_actual); + for (i = 0; i < def->nips; i++) + virDomainNetIpDefFree(def->ips[i]); + VIR_FREE(def->ips); + virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); @@ -1388,6 +1392,12 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def); } +void virDomainNetIpDefFree(virDomainNetIpDefPtr def) +{ + VIR_FREE(def->address); + VIR_FREE(def); +} + void ATTRIBUTE_NONNULL(1) virDomainChrSourceDefClear(virDomainChrSourceDefPtr def) { @@ -6575,6 +6585,29 @@ virDomainActualNetDefParseXML(xmlNodePtr node, #define NET_MODEL_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" + +int +virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + unsigned int prefix) +{ + virDomainNetIpDefPtr ipDef = NULL; + if (VIR_ALLOC(ipDef) < 0) + return -1; + + if (VIR_STRDUP(ipDef->address, address) < 0) + return -1; + + ipDef->prefix = prefix; + + if (VIR_APPEND_ELEMENT(def->ips, def->nips, ipDef) < 0) { + virDomainNetIpDefFree(ipDef); + return -1; + } + + return 0; +} + /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure @@ -6620,6 +6653,10 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; int ret; + unsigned int prefix = 0; + size_t i; + size_t nips = 0; + virDomainNetIpDefPtr *ips = NULL; if (VIR_ALLOC(def) < 0) return NULL; @@ -6698,11 +6735,44 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, xmlStrEqual(cur->name, BAD_CAST "source")) { address = virXMLPropString(cur, "address"); port = virXMLPropString(cur, "port"); - } else if (!address && - (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET || - def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) && - xmlStrEqual(cur->name, BAD_CAST "ip")) { - address = virXMLPropString(cur, "address"); + } else if (xmlStrEqual(cur->name, BAD_CAST "ip")) { + /* Parse the prefix in every case */ + char *prefixStr = NULL; + unsigned int prefixValue = 0; + + if ((prefixStr = virXMLPropString(cur, "prefix")) && + (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { + + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid network prefix: '%s'"), + prefixStr); + VIR_FREE(prefixStr); + goto error; + } + VIR_FREE(prefixStr); + + /* Previous behavior: make sure this address it the first one + in the resulting list */ + if (!address && + (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET || + def->type == VIR_DOMAIN_NET_TYPE_BRIDGE)) { + + address = virXMLPropString(cur, "address"); + prefix = prefixValue; + } else { + /* All other <ip/> elements will be added after */ + virDomainNetIpDefPtr ip = NULL; + + if (VIR_ALLOC(ip) < 0) + goto error; + + ip->address = virXMLPropString(cur, "address"); + ip->prefix = prefixValue; + + if (ip->address != NULL && + VIR_APPEND_ELEMENT(ips, nips, ip) < 0) + goto error; + } } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -6893,8 +6963,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, dev = NULL; } if (address != NULL) { - def->data.ethernet.ipaddr = address; - address = NULL; + virDomainNetAppendIpAddress(def, address, prefix); + VIR_FREE(address); } break; @@ -6908,8 +6978,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->data.bridge.brname = bridge; bridge = NULL; if (address != NULL) { - def->data.bridge.ipaddr = address; - address = NULL; + virDomainNetAppendIpAddress(def, address, prefix); + VIR_FREE(address); } break; @@ -7008,6 +7078,11 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, break; } + for (i = 0; i < nips; i++) { + if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0) + goto error; + } + if (script != NULL) { def->script = script; script = NULL; @@ -7163,6 +7238,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(mode); VIR_FREE(linkstate); VIR_FREE(addrtype); + VIR_FREE(ips); virNWFilterHashTableFree(filterparams); return def; @@ -15595,6 +15671,21 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf, return 0; } +static void +virDomainNetIpsFormat(virBufferPtr buf, virDomainNetIpDefPtr *ips, size_t nips) +{ + size_t i; + + /* Output IP addresses */ + for (i = 0; i < nips; i++) { + virBufferAsprintf(buf, "<ip address='%s'", + ips[i]->address); + if (ips[i]->prefix != 0) + virBufferAsprintf(buf, " prefix='%u'", ips[i]->prefix); + virBufferAddLit(buf, "/>\n"); + } +} + static int virDomainHostdevDefFormatCaps(virBufferPtr buf, virDomainHostdevDefPtr def) @@ -15701,7 +15792,6 @@ virDomainActualNetDefContentsFormat(virBufferPtr buf, return 0; } - /* virDomainActualNetDefFormat() - format the ActualNetDef * info inside an <actual> element, as required for internal storage * of domain status @@ -15822,9 +15912,6 @@ virDomainNetDefFormat(virBufferPtr buf, case VIR_DOMAIN_NET_TYPE_ETHERNET: virBufferEscapeString(buf, "<source dev='%s'/>\n", def->data.ethernet.dev); - if (def->data.ethernet.ipaddr) - virBufferAsprintf(buf, "<ip address='%s'/>\n", - def->data.ethernet.ipaddr); break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: @@ -15841,10 +15928,6 @@ virDomainNetDefFormat(virBufferPtr buf, case VIR_DOMAIN_NET_TYPE_BRIDGE: virBufferEscapeString(buf, "<source bridge='%s'/>\n", def->data.bridge.brname); - if (def->data.bridge.ipaddr) { - virBufferAsprintf(buf, "<ip address='%s'/>\n", - def->data.bridge.ipaddr); - } break; case VIR_DOMAIN_NET_TYPE_SERVER: @@ -15892,6 +15975,8 @@ virDomainNetDefFormat(virBufferPtr buf, return -1; } + virDomainNetIpsFormat(buf, def->ips, def->nips); + virBufferEscapeString(buf, "<script path='%s'/>\n", def->script); if (def->ifname && diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 45c8552..d322295 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -425,6 +425,15 @@ typedef enum { VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST } virDomainHostdevCapsType; +typedef struct _virDomainNetIpDef virDomainNetIpDef; +typedef virDomainNetIpDef *virDomainNetIpDefPtr; +struct _virDomainNetIpDef { + char *address; /* ipv4 or ipv6 address */ + unsigned int prefix; /* number of 1 bits in the net mask */ +}; + +void virDomainNetIpDefFree(virDomainNetIpDefPtr def); + typedef struct _virDomainHostdevCaps virDomainHostdevCaps; typedef virDomainHostdevCaps *virDomainHostdevCapsPtr; struct _virDomainHostdevCaps { @@ -848,7 +857,6 @@ struct _virDomainNetDef { union { struct { char *dev; - char *ipaddr; } ethernet; virDomainChrSourceDefPtr vhostuser; struct { @@ -870,7 +878,6 @@ struct _virDomainNetDef { } network; struct { char *brname; - char *ipaddr; } bridge; struct { char *name; @@ -899,6 +906,8 @@ struct _virDomainNetDef { virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; int linkstate; + size_t nips; + virDomainNetIpDefPtr *ips; }; /* Used for prefix of ifname of any network name generated dynamically @@ -2340,6 +2349,9 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface); virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); +int virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + unsigned int prefix); int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 51504d1..af31b4f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -316,6 +316,7 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainNetAppendIpAddress; virDomainNetDefFormat; virDomainNetDefFree; virDomainNetFind; @@ -330,6 +331,7 @@ virDomainNetGetActualType; virDomainNetGetActualVirtPortProfile; virDomainNetGetActualVlan; virDomainNetInsert; +virDomainNetIpDefFree; virDomainNetRemove; virDomainNetRemoveHostdev; virDomainNetTypeToString; diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index 856c9f5..ea45e96 100644 --- a/src/openvz/openvz_conf.c +++ b/src/openvz/openvz_conf.c @@ -230,7 +230,7 @@ openvzReadNetworkConf(virDomainDefPtr def, goto error; net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - if (VIR_STRDUP(net->data.ethernet.ipaddr, token) < 0) + if (virDomainNetAppendIpAddress(net, token, 0) < 0) goto error; if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0) diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 851ed30..96c3ce9 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -855,7 +855,7 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - net->data.ethernet.ipaddr == NULL)) { + net->nips == 0)) { virBuffer buf = VIR_BUFFER_INITIALIZER; int veid = openvzGetVEID(vpsid); @@ -906,9 +906,9 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, virCommandAddArg(cmd, "--netif_add"); virCommandAddArgBuffer(cmd, &buf); } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - net->data.ethernet.ipaddr != NULL) { + net->nips > 0 && net->ips[0]->address != NULL) { /* --ipadd ip */ - virCommandAddArgList(cmd, "--ipadd", net->data.ethernet.ipaddr, NULL); + virCommandAddArgList(cmd, "--ipadd", net->ips[0]->address, NULL); } /* TODO: processing NAT and physical device */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b85d909..08e2194 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5918,6 +5918,8 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, (brname = virDomainNetGetActualBridgeName(net))) { char *brnamecopy; + size_t j; + if (VIR_STRDUP(brnamecopy, brname) < 0) goto cleanup; @@ -5928,20 +5930,31 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; net->script = NULL; net->data.ethernet.dev = brnamecopy; - net->data.ethernet.ipaddr = NULL; + for (j = 0; j < net->nips; j++) { + virDomainNetIpDefFree(net->ips[j]); + } + VIR_FREE(net->ips); + net->nips = 0; + } else { /* actualType is either NETWORK or DIRECT. In either * case, the best we can do is NULL everything out. */ + size_t j; virDomainActualNetDefFree(net->data.network.actual); memset(net, 0, sizeof(*net)); net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; net->script = NULL; net->data.ethernet.dev = NULL; - net->data.ethernet.ipaddr = NULL; + for (j = 0; j < net->nips; j++) { + virDomainNetIpDefFree(net->ips[j]); + } + VIR_FREE(net->ips); + net->nips = 0; } } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + size_t j; VIR_FREE(net->data.direct.linkdev); memset(net, 0, sizeof(*net)); @@ -5949,18 +5962,20 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; net->script = NULL; net->data.ethernet.dev = NULL; - net->data.ethernet.ipaddr = NULL; + for (j = 0; j < net->nips; j++) { + virDomainNetIpDefFree(net->ips[j]); + } + VIR_FREE(net->ips); + net->nips = 0; } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { char *script = net->script; char *brname = net->data.bridge.brname; - char *ipaddr = net->data.bridge.ipaddr; memset(net, 0, sizeof(*net)); net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; net->script = script; net->data.ethernet.dev = brname; - net->data.ethernet.ipaddr = ipaddr; } VIR_FREE(net->virtPortProfile); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1fc28b8..6d10672 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2048,8 +2048,10 @@ qemuDomainChangeNet(virQEMUDriverPtr driver, case VIR_DOMAIN_NET_TYPE_ETHERNET: if (STRNEQ_NULLABLE(olddev->data.ethernet.dev, newdev->data.ethernet.dev) || - STRNEQ_NULLABLE(olddev->data.ethernet.ipaddr, - newdev->data.ethernet.ipaddr)) { + STRNEQ_NULLABLE(olddev->nips > 0 ? olddev->ips[0]->address + : "", + newdev->nips > 0 ? newdev->ips[0]->address + : "")) { needReconnect = true; } break; diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 41ce03c..3945ddf 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -175,7 +175,7 @@ umlBuildCommandLineNet(virConnectPtr conn, if (def->ifname) { virBufferAdd(&buf, def->ifname, -1); } - if (def->data.ethernet.ipaddr) { + if (def->nips > 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("IP address not supported for ethernet interface")); goto error; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index ccba736..3d54706 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -4477,7 +4477,8 @@ vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) } else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { VIR_DEBUG("NIC(%zu): brname: %s", i, def->nets[i]->data.bridge.brname); VIR_DEBUG("NIC(%zu): script: %s", i, def->nets[i]->script); - VIR_DEBUG("NIC(%zu): ipaddr: %s", i, def->nets[i]->data.bridge.ipaddr); + VIR_DEBUG("NIC(%zu): ipaddr: %s", i, + def->nets[i]->nips > 0 ? def->nets[i]->ips[0]->address : NULL); } machine->vtbl->GetNetworkAdapter(machine, i, &adapter); diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 610b7e4..000ec93 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -563,14 +563,14 @@ xenParseSxprNets(virDomainDefPtr def, VIR_STRDUP(net->script, tmp2) < 0) goto cleanup; tmp = sexpr_node(node, "device/vif/ip"); - if (VIR_STRDUP(net->data.bridge.ipaddr, tmp) < 0) + if (virDomainNetAppendIpAddress(net, tmp, 0) < 0) goto cleanup; } else { net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; if (VIR_STRDUP(net->script, tmp2) < 0) goto cleanup; tmp = sexpr_node(node, "device/vif/ip"); - if (VIR_STRDUP(net->data.ethernet.ipaddr, tmp) < 0) + if (virDomainNetAppendIpAddress(net, tmp, 0) < 0) goto cleanup; } @@ -1893,8 +1893,8 @@ xenFormatSxprNet(virConnectPtr conn, script = def->script; virBufferEscapeSexpr(buf, "(script '%s')", script); - if (def->data.bridge.ipaddr != NULL) - virBufferEscapeSexpr(buf, "(ip '%s')", def->data.bridge.ipaddr); + if (def->nips > 0) + virBufferEscapeSexpr(buf, "(ip '%s')", def->ips[0]->address); break; case VIR_DOMAIN_NET_TYPE_NETWORK: @@ -1927,8 +1927,8 @@ xenFormatSxprNet(virConnectPtr conn, if (def->script) virBufferEscapeSexpr(buf, "(script '%s')", def->script); - if (def->data.ethernet.ipaddr != NULL) - virBufferEscapeSexpr(buf, "(ip '%s')", def->data.ethernet.ipaddr); + if (def->nips > 0) + virBufferEscapeSexpr(buf, "(ip '%s')", def->ips[0]->address); break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 4461654..ce0f8fe 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -770,10 +770,10 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { if (bridge[0] && VIR_STRDUP(net->data.bridge.brname, bridge) < 0) goto cleanup; - if (ip[0] && VIR_STRDUP(net->data.bridge.ipaddr, ip) < 0) + if (ip[0] && virDomainNetAppendIpAddress(net, ip, 0) < 0) goto cleanup; } else { - if (ip[0] && VIR_STRDUP(net->data.ethernet.ipaddr, ip) < 0) + if (ip[0] && virDomainNetAppendIpAddress(net, ip, 0) < 0) goto cleanup; } @@ -1338,16 +1338,16 @@ static int xenFormatXMNet(virConnectPtr conn, switch (net->type) { case VIR_DOMAIN_NET_TYPE_BRIDGE: virBufferAsprintf(&buf, ",bridge=%s", net->data.bridge.brname); - if (net->data.bridge.ipaddr) - virBufferAsprintf(&buf, ",ip=%s", net->data.bridge.ipaddr); + if (net->nips > 0) + virBufferAsprintf(&buf, ",ip=%s", net->ips[0]->address); virBufferAsprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); break; case VIR_DOMAIN_NET_TYPE_ETHERNET: if (net->script) virBufferAsprintf(&buf, ",script=%s", net->script); - if (net->data.ethernet.ipaddr) - virBufferAsprintf(&buf, ",ip=%s", net->data.ethernet.ipaddr); + if (net->nips > 0) + virBufferAsprintf(&buf, ",ip=%s", net->ips[0]->address); break; case VIR_DOMAIN_NET_TYPE_NETWORK: diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml index 946d363..a52da0b 100644 --- a/tests/lxcxml2xmldata/lxc-idmap.xml +++ b/tests/lxcxml2xmldata/lxc-idmap.xml @@ -28,6 +28,8 @@ <interface type='bridge'> <mac address='00:16:3e:0f:ef:8a'/> <source bridge='bri0'/> + <ip address='192.168.122.12' prefix='24'/> + <ip address='192.168.122.13' prefix='24'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> -- 1.8.4.5

Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container. --- src/lxc/lxc_container.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 1cf2c8f..62e9d76 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -495,7 +495,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, char **veths) { int rc = 0; - size_t i; + size_t i, j; char *newname = NULL; virDomainNetDefPtr netDef; bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] == @@ -516,6 +516,24 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out; + for (j = 0; j < netDef->nips; j++) { + virDomainNetIpDefPtr ip = netDef->ips[j]; + unsigned int prefix = (ip->prefix > 0) ? ip->prefix : 24; + virSocketAddr address; + + if (virSocketAddrParse(&address, ip->address, AF_UNSPEC) < 0) + goto error_out; + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ip->address, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &address, prefix) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ip->address, newname); + goto error_out; + } + } + VIR_DEBUG("Enabling %s", newname); rc = virNetDevSetOnline(newname, true); if (rc < 0) -- 1.8.4.5

Hi all, On Fri, 2014-07-25 at 17:03 +0200, Cédric Bosdonnat wrote:
Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container. --- src/lxc/lxc_container.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 1cf2c8f..62e9d76 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -495,7 +495,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, char **veths) { int rc = 0; - size_t i; + size_t i, j; char *newname = NULL; virDomainNetDefPtr netDef; bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] == @@ -516,6 +516,24 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out;
+ for (j = 0; j < netDef->nips; j++) { + virDomainNetIpDefPtr ip = netDef->ips[j]; + unsigned int prefix = (ip->prefix > 0) ? ip->prefix : 24; + virSocketAddr address; + + if (virSocketAddrParse(&address, ip->address, AF_UNSPEC) < 0) + goto error_out; + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ip->address, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &address, prefix) < 0) {
I'm just thinking that this requires to have either ip-route or ifconfig installed in the container... which is pretty unlikely. Should I go for an implementation using the kernel functions directly? -- Cedric
+ virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ip->address, newname); + goto error_out; + } + } + VIR_DEBUG("Enabling %s", newname); rc = virNetDevSetOnline(newname, true); if (rc < 0)

On Wed, Jul 30, 2014 at 8:14 PM, Cedric Bosdonnat <cbosdonnat@suse.com> wrote:
Hi all,
On Fri, 2014-07-25 at 17:03 +0200, Cédric Bosdonnat wrote:
Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container. --- src/lxc/lxc_container.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 1cf2c8f..62e9d76 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -495,7 +495,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, char **veths) { int rc = 0; - size_t i; + size_t i, j; char *newname = NULL; virDomainNetDefPtr netDef; bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] == @@ -516,6 +516,24 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out;
+ for (jvirNetDevSetIPv4Address() is not optimal as it needs tools installed in the container because it runs everything within it. = 0; j < netDef->nips; j++) { + virDomainNetIpDefPtr ip = netDef->ips[j]; + unsigned int prefix = (ip->prefix > 0) ? ip->prefix : 24; + virSocketAddr address; + + if (virSocketAddrParse(&address, ip->address, AF_UNSPEC) < 0) + goto error_out; + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ip->address, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &address, prefix) < 0) {
I'm just thinking that this requires to have either ip-route or ifconfig installed in the container... which is pretty unlikely. Should I go for an implementation using the kernel functions directly?
I'd not say unlikely but it is a use case to consider. Implementing ip/ifconfig directly in libvirtd and using the raw kernel interface seems cumbersome to me. The problem with virNetDevSetIPv4Address() is that you call it after entering all namespaces and hence you need ip/ifconfig installed in the container. Enter only the network namespace and then call it. This way you can configure the network stuff easily for the container using the host tools. Like ip netns exec ... does. -- Thanks, //richard

--- src/lxc/lxc_native.c | 153 ++++++++++++++++-------- tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 2 + 3 files changed, 106 insertions(+), 51 deletions(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index d3fde07..bc13d89 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -413,93 +413,124 @@ lxcCreateHostdevDef(int mode, int type, const char *data) return hostdev; } +typedef struct { + virDomainDefPtr def; + char *type; + char *link; + char *mac; + char *flag; + char *macvlanmode; + char *vlanid; + char *name; + char **ips; + size_t nips; + bool privnet; + size_t networks; +} lxcNetworkParseData; + static int -lxcAddNetworkDefinition(virDomainDefPtr def, - const char *type, - const char *linkdev, - const char *mac, - const char *flag, - const char *macvlanmode, - const char *vlanid, - const char *name) +lxcAddNetworkDefinition(lxcNetworkParseData *data) { virDomainNetDefPtr net = NULL; virDomainHostdevDefPtr hostdev = NULL; bool isPhys, isVlan = false; + size_t nips = 0; + virDomainNetIpDefPtr *ips = NULL; + virDomainNetIpDefPtr ip = NULL; + char **ipparts; + size_t i; - if ((type == NULL) || STREQ(type, "empty") || STREQ(type, "") || - STREQ(type, "none")) + if ((data->type == NULL) || STREQ(data->type, "empty") || + STREQ(data->type, "") || STREQ(data->type, "none")) return 0; - isPhys = STREQ(type, "phys"); - isVlan = STREQ(type, "vlan"); - if (type != NULL && (isPhys || isVlan)) { - if (!linkdev) { + /* Add the IP addresses */ + for (i = 0; i < data->nips; i++) { + if (VIR_ALLOC(ip) < 0) + goto error; + + ipparts = virStringSplit(data->ips[i], "/", 2); + if (virStringListLength(ipparts) != 2 || + strlen(ipparts[0]) == 0 || + virStrToLong_ui(ipparts[1], NULL, 10, &ip->prefix) < 0) { + + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid CIDR address: '%s'"), data->ips[i]); + goto error; + } + + if (VIR_STRDUP(ip->address, ipparts[0]) < 0) + goto error; + + if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) + goto error; + + virStringFreeList(ipparts); + } + + isPhys = STREQ(data->type, "phys"); + isVlan = STREQ(data->type, "vlan"); + if (data->type != NULL && (isPhys || isVlan)) { + if (!data->link) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Missing 'link' attribute for NIC")); goto error; } if (!(hostdev = lxcCreateHostdevDef(VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET, - linkdev))) + data->link))) goto error; /* This still requires the user to manually setup the vlan interface * on the host */ - if (isVlan && vlanid) { + if (isVlan && data->vlanid) { VIR_FREE(hostdev->source.caps.u.net.iface); if (virAsprintf(&hostdev->source.caps.u.net.iface, - "%s.%s", linkdev, vlanid) < 0) + "%s.%s", data->link, data->vlanid) < 0) goto error; } - if (VIR_EXPAND_N(def->hostdevs, def->nhostdevs, 1) < 0) + if (VIR_EXPAND_N(data->def->hostdevs, data->def->nhostdevs, 1) < 0) goto error; - def->hostdevs[def->nhostdevs - 1] = hostdev; + data->def->hostdevs[data->def->nhostdevs - 1] = hostdev; } else { - if (!(net = lxcCreateNetDef(type, linkdev, mac, flag, macvlanmode, name))) + if (!(net = lxcCreateNetDef(data->type, data->link, data->mac, + data->flag, data->macvlanmode, + data->name))) goto error; - if (VIR_EXPAND_N(def->nets, def->nnets, 1) < 0) + net->ips = ips; + net->nips = nips; + + if (VIR_EXPAND_N(data->def->nets, data->def->nnets, 1) < 0) goto error; - def->nets[def->nnets - 1] = net; + data->def->nets[data->def->nnets - 1] = net; } return 1; error: + for (i = 0; i < nips; i++) { + virDomainNetIpDefFree(ips[i]); + } + VIR_FREE(ips); + virStringFreeList(ipparts); + virDomainNetIpDefFree(ip); virDomainNetDefFree(net); virDomainHostdevDefFree(hostdev); return -1; } -typedef struct { - virDomainDefPtr def; - char *type; - char *link; - char *mac; - char *flag; - char *macvlanmode; - char *vlanid; - char *name; - bool privnet; - size_t networks; -} lxcNetworkParseData; - static int lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) { lxcNetworkParseData *parseData = data; int status; + size_t i; if (STREQ(name, "lxc.network.type")) { /* Store the previous NIC */ - status = lxcAddNetworkDefinition(parseData->def, parseData->type, - parseData->link, parseData->mac, - parseData->flag, - parseData->macvlanmode, - parseData->vlanid, - parseData->name); + status = lxcAddNetworkDefinition(parseData); if (status < 0) return -1; @@ -517,6 +548,12 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) parseData->vlanid = NULL; parseData->name = NULL; + /* IPs array needs to be free'd as all IPs are dup'ed there */ + for (i = 0; i < parseData->nips; i++) + VIR_FREE(parseData->ips[i]); + VIR_FREE(parseData->ips); + parseData->nips = 0; + /* Keep the new value */ parseData->type = value->str; } @@ -532,7 +569,14 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) parseData->vlanid = value->str; else if (STREQ(name, "lxc.network.name")) parseData->name = value->str; - else if (STRPREFIX(name, "lxc.network")) + else if (STREQ(name, "lxc.network.ipv4") || + STREQ(name, "lxc.network.ipv6")) { + + if (VIR_EXPAND_N(parseData->ips, parseData->nips, 1) < 0) + return -1; + if (VIR_STRDUP(parseData->ips[parseData->nips - 1], value->str) < 0) + return -1; + } else if (STRPREFIX(name, "lxc.network")) VIR_WARN("Unhandled network property: %s = %s", name, value->str); @@ -544,19 +588,20 @@ static int lxcConvertNetworkSettings(virDomainDefPtr def, virConfPtr properties) { int status; + int result = -1; + size_t i; lxcNetworkParseData data = {def, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, true, 0}; + NULL, NULL, NULL, NULL, 0, true, 0}; + + if (virConfWalk(properties, lxcNetworkWalkCallback, &data) < 0) + goto cleanup; - virConfWalk(properties, lxcNetworkWalkCallback, &data); /* Add the last network definition found */ - status = lxcAddNetworkDefinition(def, data.type, data.link, - data.mac, data.flag, - data.macvlanmode, - data.vlanid, - data.name); + status = lxcAddNetworkDefinition(&data); + if (status < 0) - return -1; + goto cleanup; else if (status > 0) data.networks++; else if (data.type != NULL && STREQ(data.type, "none")) @@ -566,8 +611,14 @@ lxcConvertNetworkSettings(virDomainDefPtr def, virConfPtr properties) /* When no network type is provided LXC only adds loopback */ def->features[VIR_DOMAIN_FEATURE_PRIVNET] = VIR_TRISTATE_SWITCH_ON; } + result = 0; - return 0; + cleanup: + for (i = 0; i < data.nips; i++) + VIR_FREE(data.ips[i]); + VIR_FREE(data.ips); + + return result; } static int diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.config b/tests/lxcconf2xmldata/lxcconf2xml-simple.config index b90abc1..d417ba0 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.config +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.config @@ -6,6 +6,8 @@ lxc.network.flags = up lxc.network.link = virbr0 lxc.network.hwaddr = 02:00:15:8f:05:c1 lxc.network.name = eth0 +lxc.network.ipv4 = 192.168.122.2/24 +lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596/64 #remove next line if host DNS configuration should not be available to container lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml index 10428ec..a73d05c 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml @@ -37,6 +37,8 @@ <interface type='bridge'> <mac address='02:00:15:8f:05:c1'/> <source bridge='virbr0'/> + <ip address='192.168.122.2' prefix='24'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='64'/> <guest dev='eth0'/> <link state='up'/> </interface> -- 1.8.4.5

--- docs/formatdomain.html.in | 12 ++++++++-- docs/schemas/domaincommon.rng | 23 ++++++++++++++---- src/conf/domain_conf.c | 46 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 2 ++ tests/lxcxml2xmldata/lxc-hostdev.xml | 2 ++ 5 files changed, 79 insertions(+), 6 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0baf961..7fe5976 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3991,13 +3991,21 @@ qemu-kvm -net nic,model=? /dev/null <target dev='vnet0'/> <b><ip address='192.168.122.5' prefix='24'/></b> </interface> + ... + <hostdev mode='capabilities' type='net'> + <source> + <interface>eth0</interface> + </source> + <b><ip address='192.168.122.6' prefix='24'/></b> + </hostdev> + </devices> ... </pre> <p> - <span class="since">Since 1.2.8</span> the network devices can be provided - zero or more IP addresses to set + <span class="since">Since 1.2.8</span> the network devices and host devices + with network capabilities can be provided zero or more IP addresses to set on the target device. Note that some hypervisors or network device types will simply ignore them or only use the first one. The <code>address</code> attribute can hold either an IPv4 or IPv6 address. The <code>prefix</code> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cbfe1ef..74fba3d 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3604,11 +3604,26 @@ <attribute name="type"> <value>net</value> </attribute> - <element name="source"> - <element name="interface"> - <ref name="deviceName"/> + <interleave> + <element name="source"> + <element name="interface"> + <ref name="deviceName"/> + </element> </element> - </element> + <zeroOrMore> + <element name="ip"> + <attribute name="address"> + <ref name="ipAddr"/> + </attribute> + <optional> + <attribute name="prefix"> + <ref name="ipPrefix"/> + </attribute> + </optional> + <empty/> + </element> + </zeroOrMore> + </interleave> </define> <define name="usbproduct"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4cdb435..8307428 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1707,6 +1707,8 @@ virDomainHostdevDefPtr virDomainHostdevDefAlloc(void) void virDomainHostdevDefClear(virDomainHostdevDefPtr def) { + size_t i; + if (!def) return; @@ -1731,6 +1733,9 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) break; case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET: VIR_FREE(def->source.caps.u.net.iface); + for (i = 0; i < def->source.caps.u.net.nips; i++) + virDomainNetIpDefFree(def->source.caps.u.net.ips[i]); + VIR_FREE(def->source.caps.u.net.ips); break; } break; @@ -4373,6 +4378,8 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, virDomainHostdevDefPtr def) { xmlNodePtr sourcenode; + xmlNodePtr *ipnodes = NULL; + int nipnodes; int ret = -1; /* @type is passed in from the caller rather than read from the @@ -4427,6 +4434,40 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, _("Missing <interface> element in hostdev net device")); goto error; } + + /* Parse possible IP addresses */ + if ((nipnodes = virXPathNodeSet("./ip", ctxt, &ipnodes)) < 0) + goto error; + + if (nipnodes) { + size_t i; + for (i = 0; i < nipnodes; i++) { + char *prefixStr = NULL; + virDomainNetIpDefPtr ip = NULL; + + if (VIR_ALLOC(ip) < 0) + goto error; + + ip->address = virXMLPropString(ipnodes[i], "address"); + + if ((prefixStr = virXMLPropString(ipnodes[i], "prefix")) && + (virStrToLong_ui(prefixStr, NULL, 10, &ip->prefix) < 0)) { + + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid network prefix: '%s'"), + prefixStr); + VIR_FREE(prefixStr); + goto error; + } + VIR_FREE(prefixStr); + + if (ip->address != NULL && + VIR_APPEND_ELEMENT(def->source.caps.u.net.ips, + def->source.caps.u.net.nips, ip) < 0) + goto error; + } + VIR_FREE(ipnodes); + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -15716,6 +15757,11 @@ virDomainHostdevDefFormatCaps(virBufferPtr buf, virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</source>\n"); + + if (def->source.caps.type == VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET) { + virDomainNetIpsFormat(buf, def->source.caps.u.net.ips, + def->source.caps.u.net.nips); + } return 0; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d322295..8ef69b4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -447,6 +447,8 @@ struct _virDomainHostdevCaps { } misc; struct { char *iface; + size_t nips; + virDomainNetIpDefPtr *ips; } net; } u; }; diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml index befe0db..23bf04d 100644 --- a/tests/lxcxml2xmldata/lxc-hostdev.xml +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -35,6 +35,8 @@ <source> <interface>eth0</interface> </source> + <ip address='192.168.122.2'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='24'/> </hostdev> </devices> </domain> -- 1.8.4.5

--- src/lxc/lxc_native.c | 3 +++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index bc13d89..8ff5c9b 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -490,6 +490,9 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) goto error; } + hostdev->source.caps.u.net.ips = ips; + hostdev->source.caps.u.net.nips = nips; + if (VIR_EXPAND_N(data->def->hostdevs, data->def->nhostdevs, 1) < 0) goto error; data->def->hostdevs[data->def->nhostdevs - 1] = hostdev; diff --git a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config index ed196e1..94f7c61 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config +++ b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config @@ -1,6 +1,8 @@ lxc.network.type = phys lxc.network.link = eth0 lxc.network.name = eth1 +lxc.network.ipv4 = 192.168.122.2/24 +lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596/64 lxc.rootfs = /var/lib/lxc/migrate_test/rootfs lxc.utsname = migrate_test diff --git a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml index cfaceb5..7cd9e6f 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml @@ -25,6 +25,8 @@ <source> <interface>eth0</interface> </source> + <ip address='192.168.122.2' prefix='24'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='64'/> </hostdev> </devices> </domain> -- 1.8.4.5

Network interfaces devices and host devices with net capabilities can now have an IPv4 and/or an IPv6 address configured. --- docs/formatdomain.html.in | 9 ++++++++ docs/schemas/domaincommon.rng | 23 ++++++++++++++++++++ src/conf/domain_conf.c | 42 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ tests/lxcxml2xmldata/lxc-hostdev.xml | 1 + tests/lxcxml2xmldata/lxc-idmap.xml | 1 + 6 files changed, 80 insertions(+) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 7fe5976..760631d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3990,6 +3990,7 @@ qemu-kvm -net nic,model=? /dev/null <source network='default'/> <target dev='vnet0'/> <b><ip address='192.168.122.5' prefix='24'/></b> + <b><gateway ipv4='192.168.122.1'/></b> </interface> ... <hostdev mode='capabilities' type='net'> @@ -3997,6 +3998,7 @@ qemu-kvm -net nic,model=? /dev/null <interface>eth0</interface> </source> <b><ip address='192.168.122.6' prefix='24'/></b> + <b><gateway ipv4='192.168.122.1'/></b> </hostdev> </devices> @@ -4012,6 +4014,13 @@ qemu-kvm -net nic,model=? /dev/null is not mandatory since some hypervisors do not handle it. </p> + <p> + <span class="since">Since 1.2.8</span> a gateway element can also be added + to provide the default gateway to use for the network device. This element + can have either or both <code>ipv4</code> or <code>ipv6</code> attributes. + This is only used by the LXC driver. + </p> + <h5><a name="elementVhostuser">vhost-user interface</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 74fba3d..eddac2e 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2257,6 +2257,11 @@ </element> </zeroOrMore> <optional> + <element name="gateway"> + <ref name="gateway"/> + </element> + </optional> + <optional> <element name="script"> <attribute name="path"> <ref name="filePath"/> @@ -3432,6 +3437,19 @@ </element> </define> + <define name="gateway"> + <optional> + <attribute name="ipv4"> + <ref name="ipv4Addr"/> + </attribute> + </optional> + <optional> + <attribute name="ipv6"> + <ref name="ipv6Addr"/> + </attribute> + </optional> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -3623,6 +3641,11 @@ <empty/> </element> </zeroOrMore> + <optional> + <element name="gateway"> + <ref name="gateway"/> + </element> + </optional> </interleave> </define> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8307428..607defe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1381,6 +1381,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def) virDomainNetIpDefFree(def->ips[i]); VIR_FREE(def->ips); + VIR_FREE(def->gateway_ipv4); + VIR_FREE(def->gateway_ipv6); + virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); @@ -1736,6 +1739,8 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) for (i = 0; i < def->source.caps.u.net.nips; i++) virDomainNetIpDefFree(def->source.caps.u.net.ips[i]); VIR_FREE(def->source.caps.u.net.ips); + VIR_FREE(def->source.caps.u.net.gateway_ipv4); + VIR_FREE(def->source.caps.u.net.gateway_ipv6); break; } break; @@ -4380,6 +4385,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr sourcenode; xmlNodePtr *ipnodes = NULL; int nipnodes; + xmlNodePtr gwnode; int ret = -1; /* @type is passed in from the caller rather than read from the @@ -4468,6 +4474,12 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } VIR_FREE(ipnodes); } + + /* Look for possible gateways */ + if ((gwnode = virXPathNode("./gateway", ctxt))) { + def->source.caps.u.net.gateway_ipv4 = virXMLPropString(gwnode, "ipv4"); + def->source.caps.u.net.gateway_ipv6 = virXMLPropString(gwnode, "ipv6"); + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -6698,6 +6710,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, size_t i; size_t nips = 0; virDomainNetIpDefPtr *ips = NULL; + char *gateway_ipv4 = NULL; + char *gateway_ipv6 = NULL; if (VIR_ALLOC(def) < 0) return NULL; @@ -6814,6 +6828,10 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_APPEND_ELEMENT(ips, nips, ip) < 0) goto error; } + } else if (!gateway_ipv4 && !gateway_ipv6 && + xmlStrEqual(cur->name, BAD_CAST "gateway")) { + gateway_ipv4 = virXMLPropString(cur, "ipv4"); + gateway_ipv6 = virXMLPropString(cur, "ipv6"); } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7123,6 +7141,10 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0) goto error; } + def->gateway_ipv4 = gateway_ipv4; + gateway_ipv4 = NULL; + def->gateway_ipv6 = gateway_ipv6; + gateway_ipv6 = NULL; if (script != NULL) { def->script = script; @@ -7281,6 +7303,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(addrtype); VIR_FREE(ips); virNWFilterHashTableFree(filterparams); + VIR_FREE(gateway_ipv4); + VIR_FREE(gateway_ipv6); return def; @@ -15727,6 +15751,21 @@ virDomainNetIpsFormat(virBufferPtr buf, virDomainNetIpDefPtr *ips, size_t nips) } } +static void +virDomainNetGatewayFormat(virBufferPtr buf, + const char* gateway_ipv4, + const char* gateway_ipv6) +{ + if (gateway_ipv4 || gateway_ipv6) { + virBufferAddLit(buf, "<gateway"); + if (gateway_ipv4) + virBufferAsprintf(buf, " ipv4='%s'", gateway_ipv4); + if (gateway_ipv6) + virBufferAsprintf(buf, " ipv6='%s'", gateway_ipv6); + virBufferAddLit(buf, "/>\n"); + } +} + static int virDomainHostdevDefFormatCaps(virBufferPtr buf, virDomainHostdevDefPtr def) @@ -15761,6 +15800,8 @@ virDomainHostdevDefFormatCaps(virBufferPtr buf, if (def->source.caps.type == VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET) { virDomainNetIpsFormat(buf, def->source.caps.u.net.ips, def->source.caps.u.net.nips); + virDomainNetGatewayFormat(buf, def->source.caps.u.net.gateway_ipv4, + def->source.caps.u.net.gateway_ipv6); } return 0; } @@ -16022,6 +16063,7 @@ virDomainNetDefFormat(virBufferPtr buf, } virDomainNetIpsFormat(buf, def->ips, def->nips); + virDomainNetGatewayFormat(buf, def->gateway_ipv4, def->gateway_ipv6); virBufferEscapeString(buf, "<script path='%s'/>\n", def->script); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8ef69b4..91cf065 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -449,6 +449,8 @@ struct _virDomainHostdevCaps { char *iface; size_t nips; virDomainNetIpDefPtr *ips; + char *gateway_ipv4; + char *gateway_ipv6; } net; } u; }; @@ -910,6 +912,8 @@ struct _virDomainNetDef { int linkstate; size_t nips; virDomainNetIpDefPtr *ips; + char *gateway_ipv4; + char *gateway_ipv6; }; /* Used for prefix of ifname of any network name generated dynamically diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml index 23bf04d..694bc87 100644 --- a/tests/lxcxml2xmldata/lxc-hostdev.xml +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -37,6 +37,7 @@ </source> <ip address='192.168.122.2'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='24'/> + <gateway ipv4='192.168.122.1' ipv6='2003:db8:1:0:214:1234:fe0b:3595'/> </hostdev> </devices> </domain> diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml index a52da0b..80ee432 100644 --- a/tests/lxcxml2xmldata/lxc-idmap.xml +++ b/tests/lxcxml2xmldata/lxc-idmap.xml @@ -30,6 +30,7 @@ <source bridge='bri0'/> <ip address='192.168.122.12' prefix='24'/> <ip address='192.168.122.13' prefix='24'/> + <gateway ipv4='192.168.122.1'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> -- 1.8.4.5

--- src/lxc/lxc_native.c | 19 ++++++++++++++++++- tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 1 + tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 1 + 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index 8ff5c9b..bfcf5b5 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -423,6 +423,8 @@ typedef struct { char *vlanid; char *name; char **ips; + char *gateway_ipv4; + char *gateway_ipv6; size_t nips; bool privnet; size_t networks; @@ -493,6 +495,12 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) hostdev->source.caps.u.net.ips = ips; hostdev->source.caps.u.net.nips = nips; + if (VIR_STRDUP(hostdev->source.caps.u.net.gateway_ipv4, + data->gateway_ipv4) < 0 || + VIR_STRDUP(hostdev->source.caps.u.net.gateway_ipv6, + data->gateway_ipv6) < 0) + goto error; + if (VIR_EXPAND_N(data->def->hostdevs, data->def->nhostdevs, 1) < 0) goto error; data->def->hostdevs[data->def->nhostdevs - 1] = hostdev; @@ -505,6 +513,10 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) net->ips = ips; net->nips = nips; + if (VIR_STRDUP(net->gateway_ipv4, data->gateway_ipv4) < 0 || + VIR_STRDUP(net->gateway_ipv6, data->gateway_ipv6) < 0) + goto error; + if (VIR_EXPAND_N(data->def->nets, data->def->nnets, 1) < 0) goto error; data->def->nets[data->def->nnets - 1] = net; @@ -579,6 +591,10 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) return -1; if (VIR_STRDUP(parseData->ips[parseData->nips - 1], value->str) < 0) return -1; + } else if (STREQ(name, "lxc.network.ipv4.gateway")) { + parseData->gateway_ipv4 = value->str; + } else if (STREQ(name, "lxc.network.ipv6.gateway")) { + parseData->gateway_ipv6 = value->str; } else if (STRPREFIX(name, "lxc.network")) VIR_WARN("Unhandled network property: %s = %s", name, @@ -594,7 +610,8 @@ lxcConvertNetworkSettings(virDomainDefPtr def, virConfPtr properties) int result = -1; size_t i; lxcNetworkParseData data = {def, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, 0, true, 0}; + NULL, NULL, NULL, NULL, NULL, + NULL, 0, true, 0}; if (virConfWalk(properties, lxcNetworkWalkCallback, &data) < 0) goto cleanup; diff --git a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config index 94f7c61..779dac2 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config +++ b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config @@ -2,7 +2,9 @@ lxc.network.type = phys lxc.network.link = eth0 lxc.network.name = eth1 lxc.network.ipv4 = 192.168.122.2/24 +lxc.network.ipv4.gateway = 192.168.122.1 lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596/64 +lxc.network.ipv6.gateway = 2003:db8:1:0:214:1234:fe0b:3595 lxc.rootfs = /var/lib/lxc/migrate_test/rootfs lxc.utsname = migrate_test diff --git a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml index 7cd9e6f..b80147f 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml @@ -27,6 +27,7 @@ </source> <ip address='192.168.122.2' prefix='24'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='64'/> + <gateway ipv4='192.168.122.1' ipv6='2003:db8:1:0:214:1234:fe0b:3595'/> </hostdev> </devices> </domain> diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.config b/tests/lxcconf2xmldata/lxcconf2xml-simple.config index d417ba0..50a44bb 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.config +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.config @@ -7,7 +7,9 @@ lxc.network.link = virbr0 lxc.network.hwaddr = 02:00:15:8f:05:c1 lxc.network.name = eth0 lxc.network.ipv4 = 192.168.122.2/24 +lxc.network.ipv4.gateway = 192.168.122.1 lxc.network.ipv6 = 2003:db8:1:0:214:1234:fe0b:3596/64 +lxc.network.ipv6.gateway = 2003:db8:1:0:214:1234:fe0b:3595 #remove next line if host DNS configuration should not be available to container lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml index a73d05c..32494e4 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml @@ -39,6 +39,7 @@ <source bridge='virbr0'/> <ip address='192.168.122.2' prefix='24'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' prefix='64'/> + <gateway ipv4='192.168.122.1' ipv6='2003:db8:1:0:214:1234:fe0b:3595'/> <guest dev='eth0'/> <link state='up'/> </interface> -- 1.8.4.5

When a gateway is set on a network device, a new default route via this gateway through the devoce will be added in the container. --- src/lxc/lxc_container.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 62e9d76..4a373cb 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -479,6 +479,33 @@ lxcContainerGetNetDef(virDomainDefPtr vmDef, const char *devName) return NULL; } +static int +lxcContainerAddDefaultRoute(const char *ifname, const char *gateway) +{ + virSocketAddr address; + virSocketAddr network; + + VIR_DEBUG("Adding default route via %s on dev %s", gateway, ifname); + if (virSocketAddrParse(&address, gateway, AF_UNSPEC) < 0) + return -1; + + if (!strchr(gateway, ':')) { + if (virSocketAddrParseIPv4(&network, "0.0.0.0") < 0) + return -1; + } else { + if (virSocketAddrParseIPv6(&network, "::") < 0) + return -1; + } + + if (virNetDevAddRoute(ifname, &network, 0, &address, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed adding default route via %s on dev %s"), + gateway, ifname); + return -1; + } + return 0; +} + /** * lxcContainerRenameAndEnableInterfaces: * @nveths: number of interfaces @@ -539,6 +566,15 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out; + /* Set the gateways */ + if (netDef->gateway_ipv4 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv4) < 0) + goto error_out; + + if (netDef->gateway_ipv6 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6) < 0) + goto error_out; + VIR_FREE(newname); } -- 1.8.4.5

Don't activate LXC network device if <link state='down'/> has been set in its configuration. --- src/lxc/lxc_container.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 4a373cb..480b045 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -561,19 +561,21 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, } } - VIR_DEBUG("Enabling %s", newname); - rc = virNetDevSetOnline(newname, true); - if (rc < 0) - goto error_out; + if (netDef->linkstate != VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) { + VIR_DEBUG("Enabling %s", newname); + rc = virNetDevSetOnline(newname, true); + if (rc < 0) + goto error_out; - /* Set the gateways */ - if (netDef->gateway_ipv4 && - lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv4) < 0) - goto error_out; + /* Set the gateways */ + if (netDef->gateway_ipv4 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv4) < 0) + goto error_out; - if (netDef->gateway_ipv6 && - lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6) < 0) - goto error_out; + if (netDef->gateway_ipv6 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6) < 0) + goto error_out; + } VIR_FREE(newname); } -- 1.8.4.5
participants (3)
-
Cedric Bosdonnat
-
Cédric Bosdonnat
-
Richard Weinberger