[libvirt] [PATCH v4 00/17] LXC IP configuration feature

Hi all, Here is an updated version of the lxc IP configuration patch series. Diffs to v3: * Took care of Daniel's feedback * Fixed openvz driver to add support for multiple --ipadd * Report an error for drivers using IP addresses, but only the first one in case there are more provided. * Squashed Patch 11 to have virDomainNetIpsFormat right in the first place * Fixed regression introduced in qemuConnectDomainXMLToNative: IPs were not kept over memset for bridge type. * Reformatted to fit latest make syntax-check changes Cédric Bosdonnat (17): Forgot to cleanup ifname_guest* in domain network def parsing 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 virNetDevSetIPv4Address: libnl implementation Renamed virNetDevSetIPv4Address to virNetDevSetIPAddress virNetDevAddRoute: implementation using netlink virNetDevClearIPv4Address: netlink implementation Renamed virNetDevClearIPv4Address to virNetDevClearIPAddress Openvz --ipadd can be provided multiple times Report error if a driver can't handle multiple IP addresses docs/formatdomain.html.in | 39 +++ docs/schemas/domaincommon.rng | 65 ++++- src/conf/domain_conf.c | 251 +++++++++++++++-- src/conf/domain_conf.h | 21 +- src/libvirt_private.syms | 6 +- src/lxc/lxc_container.c | 69 ++++- src/lxc/lxc_native.c | 165 +++++++---- src/network/bridge_driver.c | 4 +- src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 11 +- src/qemu/qemu_driver.c | 29 +- src/qemu/qemu_hotplug.c | 5 +- src/uml/uml_conf.c | 2 +- src/util/virnetdev.c | 305 ++++++++++++++++++--- src/util/virnetdev.h | 12 +- src/util/virnetlink.c | 38 +++ src/util/virnetlink.h | 2 + src/util/virsocketaddr.h | 3 + src/vbox/vbox_common.c | 16 +- src/xenconfig/xen_common.c | 29 +- src/xenconfig/xen_sxpr.c | 26 +- .../lxcconf2xmldata/lxcconf2xml-physnetwork.config | 4 + tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 4 + tests/lxcconf2xmldata/lxcconf2xml-simple.config | 4 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 4 + tests/lxcxml2xmldata/lxc-hostdev.xml | 4 + tests/lxcxml2xmldata/lxc-idmap.xml | 3 + tests/openvzutilstest.c | 2 +- tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-net-routed.xml | 2 +- 30 files changed, 963 insertions(+), 166 deletions(-) -- 2.1.2

--- src/conf/domain_conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2c65276..8c00dd1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7772,6 +7772,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(vhostuser_path); VIR_FREE(vhostuser_mode); VIR_FREE(ifname); + VIR_FREE(ifname_guest); + VIR_FREE(ifname_guest_actual); VIR_FREE(dev); virDomainActualNetDefFree(actual); VIR_FREE(script); -- 2.1.2

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 | 16 ++- src/conf/domain_conf.c | 149 ++++++++++++++++++++---- src/conf/domain_conf.h | 15 ++- src/libvirt_private.syms | 1 + src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 7 +- src/qemu/qemu_driver.c | 29 ++++- src/qemu/qemu_hotplug.c | 5 +- src/uml/uml_conf.c | 2 +- src/vbox/vbox_common.c | 6 +- src/xenconfig/xen_common.c | 21 ++-- src/xenconfig/xen_sxpr.c | 18 ++- tests/lxcxml2xmldata/lxc-idmap.xml | 2 + tests/openvzutilstest.c | 2 +- tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-net-routed.xml | 2 +- 17 files changed, 242 insertions(+), 59 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index d4189e6..d414371 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4283,6 +4283,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.10</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 6863ec6..cd82461 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2295,14 +2295,24 @@ <empty/> </element> </optional> - <optional> + <zeroOrMore> <element name="ip"> <attribute name="address"> - <ref name="ipv4Addr"/> + <ref name="ipAddr"/> </attribute> + <optional> + <attribute name="family"> + <ref name="addr-family"/> + </attribute> + </optional> + <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 8c00dd1..44741a9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1367,6 +1367,8 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def) void virDomainNetDefFree(virDomainNetDefPtr def) { + size_t i; + if (!def) return; @@ -1375,7 +1377,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: @@ -1396,7 +1397,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: @@ -1424,6 +1424,10 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ifname_guest); VIR_FREE(def->ifname_guest_actual); + for (i = 0; i < def->nips; i++) + VIR_FREE(def->ips[i]); + VIR_FREE(def->ips); + virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); @@ -4667,6 +4671,58 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, return ret; } +static virDomainNetIpDefPtr +virDomainNetIpParseXML(xmlNodePtr node) +{ + /* Parse the prefix in every case */ + virDomainNetIpDefPtr ip = NULL; + char *prefixStr = NULL; + unsigned int prefixValue = 0; + char *familyStr = NULL; + int family = AF_UNSPEC; + char *address = NULL; + + if (!(prefixStr = virXMLPropString(node, "prefix")) || + (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { + // Don't shout, just warn as some old config may not have a prefix + VIR_WARN("Missing or invalid network prefix"); + } + + if (!(address = virXMLPropString(node, "address"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Missing network address")); + goto error; + } + + familyStr = virXMLPropString(node, "family"); + if (familyStr && STREQ(familyStr, "ipv4")) + family = AF_INET; + else if (familyStr && STREQ(familyStr, "ipv6")) + family = AF_INET6; + else + family = virSocketAddrNumericFamily(address); + + if (VIR_ALLOC(ip) < 0) + goto error; + + if (virSocketAddrParse(&ip->address, address, family) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Failed to parse IP address: '%s'"), + address); + goto error; + } + ip->prefix = prefixValue; + + return ip; + + error: + VIR_FREE(prefixStr); + VIR_FREE(familyStr); + VIR_FREE(address); + VIR_FREE(ip); + return NULL; +} + static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, @@ -7077,6 +7133,31 @@ virDomainActualNetDefParseXML(xmlNodePtr node, #define NET_MODEL_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" + +int +virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + int family, + unsigned int prefix) +{ + virDomainNetIpDefPtr ipDef = NULL; + if (VIR_ALLOC(ipDef) < 0) + return -1; + + if (virSocketAddrParse(&ipDef->address, address, family) < 0) + goto error; + ipDef->prefix = prefix; + + if (VIR_APPEND_ELEMENT(def->ips, def->nips, ipDef) < 0) + goto error; + + return 0; + + error: + VIR_FREE(ipDef); + return -1; +} + /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure @@ -7124,6 +7205,9 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; int ret, val; + size_t i; + size_t nips = 0; + virDomainNetIpDefPtr *ips = NULL; if (VIR_ALLOC(def) < 0) return NULL; @@ -7212,11 +7296,14 @@ 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")) { + virDomainNetIpDefPtr ip = NULL; + + if (!(ip = virDomainNetIpParseXML(cur))) + goto error; + + if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) + goto error; } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7416,10 +7503,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->data.ethernet.dev = dev; dev = NULL; } - if (address != NULL) { - def->data.ethernet.ipaddr = address; - address = NULL; - } break; case VIR_DOMAIN_NET_TYPE_BRIDGE: @@ -7431,10 +7514,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } def->data.bridge.brname = bridge; bridge = NULL; - if (address != NULL) { - def->data.bridge.ipaddr = address; - address = NULL; - } break; case VIR_DOMAIN_NET_TYPE_CLIENT: @@ -7531,6 +7610,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; @@ -7793,6 +7877,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(linkstate); VIR_FREE(addrtype); VIR_FREE(trustGuestRxFilters); + VIR_FREE(ips); virNWFilterHashTableFree(filterparams); return def; @@ -16753,6 +16838,30 @@ virDomainFSDefFormat(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++) { + virSocketAddrPtr address = &ips[i]->address; + char *ipStr = virSocketAddrFormat(address); + const char *familyStr = NULL; + if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET6)) + familyStr = "ipv6"; + else if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET)) + familyStr = "ipv4"; + virBufferAsprintf(buf, "<ip address='%s'", + ipStr); + if (familyStr) + virBufferAsprintf(buf, " family='%s'", familyStr); + if (ips[i]->prefix != 0) + virBufferAsprintf(buf, " prefix='%u'", ips[i]->prefix); + virBufferAddLit(buf, "/>\n"); + } +} + static int virDomainHostdevDefFormatSubsys(virBufferPtr buf, virDomainHostdevDefPtr def, @@ -16980,7 +17089,6 @@ virDomainActualNetDefContentsFormat(virBufferPtr buf, return 0; } - /* virDomainActualNetDefFormat() - format the ActualNetDef * info inside an <actual> element, as required for internal storage * of domain status @@ -17217,9 +17325,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: @@ -17237,10 +17342,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: @@ -17288,6 +17389,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 530a3ca..fbf6067 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -471,6 +471,13 @@ typedef enum { VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST } virDomainHostdevCapsType; +typedef struct _virDomainNetIpDef virDomainNetIpDef; +typedef virDomainNetIpDef *virDomainNetIpDefPtr; +struct _virDomainNetIpDef { + virSocketAddr address; /* ipv4 or ipv6 address */ + unsigned int prefix; /* number of 1 bits in the net mask */ +}; + typedef struct _virDomainHostdevCaps virDomainHostdevCaps; typedef virDomainHostdevCaps *virDomainHostdevCapsPtr; struct _virDomainHostdevCaps { @@ -932,7 +939,6 @@ struct _virDomainNetDef { union { struct { char *dev; - char *ipaddr; } ethernet; virDomainChrSourceDefPtr vhostuser; struct { @@ -954,7 +960,6 @@ struct _virDomainNetDef { } network; struct { char *brname; - char *ipaddr; } bridge; struct { char *name; @@ -984,6 +989,8 @@ struct _virDomainNetDef { virNetDevVlan vlan; int trustGuestRxFilters; /* enum virTristateBool */ int linkstate; + size_t nips; + virDomainNetIpDefPtr *ips; }; /* Used for prefix of ifname of any network name generated dynamically @@ -2522,6 +2529,10 @@ virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); bool virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface); +int virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + int family, + unsigned int prefix); int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0864618..8d02438 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -321,6 +321,7 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainNetAppendIpAddress; virDomainNetDefFormat; virDomainNetDefFree; virDomainNetFind; diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index 856c9f5..b6fdc37 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, AF_UNSPEC, 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 a0346b4..273bac1 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,10 @@ 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) { /* --ipadd ip */ - virCommandAddArgList(cmd, "--ipadd", net->data.ethernet.ipaddr, NULL); + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); } /* TODO: processing NAT and physical device */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index acf2b9a..2863db4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6352,6 +6352,8 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, (brname = virDomainNetGetActualBridgeName(net))) { char *brnamecopy; + size_t j; + if (VIR_STRDUP(brnamecopy, brname) < 0) goto cleanup; @@ -6362,20 +6364,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++) { + VIR_FREE(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++) { + VIR_FREE(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)); @@ -6383,18 +6396,24 @@ 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++) { + VIR_FREE(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; + size_t nips = net->nips; + virDomainNetIpDefPtr *ips = net->ips; 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; + net->nips = nips; + net->ips = ips; } VIR_FREE(net->virtPortProfile); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 13bcd88..60a6fd9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2101,8 +2101,9 @@ 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)) { + olddev->nips == 0 || newdev->nips == 0 || + !virSocketAddrEqual(&olddev->ips[0]->address, + &newdev->ips[0]->address)) { needReconnect = true; } break; diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index a99e8e9..ac6a7da 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_common.c b/src/vbox/vbox_common.c index cd5b478..fa1e12d 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1307,7 +1307,11 @@ 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); + if (def->nets[i]->nips > 0) { + char *ipStr = virSocketAddrFormat(&def->nets[i]->ips[0]->address); + VIR_DEBUG("NIC(%zu): ipaddr: %s", i, ipStr); + VIR_FREE(ipStr); + } } gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter); diff --git a/src/xenconfig/xen_common.c b/src/xenconfig/xen_common.c index 32954f3..bcb3bd3 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -922,12 +922,9 @@ xenParseVif(virConfPtr conf, virDomainDefPtr def) 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) - goto cleanup; - } else { - if (ip[0] && VIR_STRDUP(net->data.ethernet.ipaddr, ip) < 0) - goto cleanup; } + if (ip[0] && virDomainNetAppendIpAddress(net, ip, AF_INET, 0) < 0) + goto cleanup; if (script && script[0] && VIR_STRDUP(net->script, script) < 0) @@ -1225,16 +1222,22 @@ xenFormatNet(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) { + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virBufferAsprintf(&buf, ",ip=%s", ipStr); + VIR_FREE(ipStr); + } 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) { + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virBufferAsprintf(&buf, ",ip=%s", ipStr); + VIR_FREE(ipStr); + } break; case VIR_DOMAIN_NET_TYPE_NETWORK: diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index 6623ea8..caeb464 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -565,14 +565,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 (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 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 (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 0) < 0) goto cleanup; } @@ -1898,8 +1898,11 @@ 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) { + char *ipStr = virSocketAddrFormat(&def->ips[0]->address); + virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); + VIR_FREE(ipStr); + } break; case VIR_DOMAIN_NET_TYPE_NETWORK: @@ -1932,8 +1935,11 @@ 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) { + char *ipStr = virSocketAddrFormat(&def->ips[0]->address); + virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); + VIR_FREE(ipStr); + } break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml index 946d363..d011927 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' family='ipv4' prefix='24'/> + <ip address='192.168.122.13' family='ipv4' prefix='24'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> diff --git a/tests/openvzutilstest.c b/tests/openvzutilstest.c index ed2c098..bcf48db 100644 --- a/tests/openvzutilstest.c +++ b/tests/openvzutilstest.c @@ -95,7 +95,7 @@ testReadNetworkConf(const void *data ATTRIBUTE_UNUSED) " <devices>\n" " <interface type='ethernet'>\n" " <mac address='00:00:00:00:00:00'/>\n" - " <ip address='194.44.18.88'/>\n" + " <ip address='194.44.18.88' family='ipv4'/>\n" " </interface>\n" " <interface type='bridge'>\n" " <mac address='00:18:51:c1:05:ee'/>\n" diff --git a/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml b/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml index 9b5cc3a..25a29fd 100644 --- a/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml +++ b/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml @@ -24,7 +24,7 @@ <interface type='bridge'> <mac address='00:11:22:33:44:55'/> <source bridge='xenbr2'/> - <ip address='192.0.2.1'/> + <ip address='192.0.2.1' family='ipv4'/> <script path='vif-bridge'/> <target dev='vif6.0'/> </interface> diff --git a/tests/sexpr2xmldata/sexpr2xml-net-routed.xml b/tests/sexpr2xmldata/sexpr2xml-net-routed.xml index 0ab3b6d..c03e40b 100644 --- a/tests/sexpr2xmldata/sexpr2xml-net-routed.xml +++ b/tests/sexpr2xmldata/sexpr2xml-net-routed.xml @@ -23,7 +23,7 @@ </disk> <interface type='ethernet'> <mac address='00:11:22:33:44:55'/> - <ip address='172.14.5.6'/> + <ip address='172.14.5.6' family='ipv4'/> <script path='vif-routed'/> <target dev='vif6.0'/> </interface> -- 2.1.2

On Thursday 13 November 2014 10:33:01 Cédric Bosdonnat wrote:
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.
I totally agree with this patch. IP configuration must be separated from device type and connection configuration. ACK
--- docs/formatdomain.html.in | 22 ++++ docs/schemas/domaincommon.rng | 16 ++- src/conf/domain_conf.c | 149 ++++++++++++++++++++---- src/conf/domain_conf.h | 15 ++- src/libvirt_private.syms | 1 + src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 7 +- src/qemu/qemu_driver.c | 29 ++++- src/qemu/qemu_hotplug.c | 5 +- src/uml/uml_conf.c | 2 +- src/vbox/vbox_common.c | 6 +- src/xenconfig/xen_common.c | 21 ++-- src/xenconfig/xen_sxpr.c | 18 ++- tests/lxcxml2xmldata/lxc-idmap.xml | 2 + tests/openvzutilstest.c | 2 +- tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-net-routed.xml | 2 +- 17 files changed, 242 insertions(+), 59 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index d4189e6..d414371 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4283,6 +4283,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.10</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 6863ec6..cd82461 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2295,14 +2295,24 @@ <empty/> </element> </optional> - <optional> + <zeroOrMore> <element name="ip"> <attribute name="address"> - <ref name="ipv4Addr"/> + <ref name="ipAddr"/> </attribute> + <optional> + <attribute name="family"> + <ref name="addr-family"/> + </attribute> + </optional> + <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 8c00dd1..44741a9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1367,6 +1367,8 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def)
void virDomainNetDefFree(virDomainNetDefPtr def) { + size_t i; + if (!def) return;
@@ -1375,7 +1377,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: @@ -1396,7 +1397,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: @@ -1424,6 +1424,10 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ifname_guest); VIR_FREE(def->ifname_guest_actual);
+ for (i = 0; i < def->nips; i++) + VIR_FREE(def->ips[i]); + VIR_FREE(def->ips); + virDomainDeviceInfoClear(&def->info);
VIR_FREE(def->filter); @@ -4667,6 +4671,58 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, return ret; }
+static virDomainNetIpDefPtr +virDomainNetIpParseXML(xmlNodePtr node) +{ + /* Parse the prefix in every case */ + virDomainNetIpDefPtr ip = NULL; + char *prefixStr = NULL; + unsigned int prefixValue = 0; + char *familyStr = NULL; + int family = AF_UNSPEC; + char *address = NULL; + + if (!(prefixStr = virXMLPropString(node, "prefix")) || + (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { + // Don't shout, just warn as some old config may not have a prefix + VIR_WARN("Missing or invalid network prefix"); + } + + if (!(address = virXMLPropString(node, "address"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Missing network address")); + goto error; + } + + familyStr = virXMLPropString(node, "family"); + if (familyStr && STREQ(familyStr, "ipv4")) + family = AF_INET; + else if (familyStr && STREQ(familyStr, "ipv6")) + family = AF_INET6; + else + family = virSocketAddrNumericFamily(address); + + if (VIR_ALLOC(ip) < 0) + goto error; + + if (virSocketAddrParse(&ip->address, address, family) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Failed to parse IP address: '%s'"), + address); + goto error; + } + ip->prefix = prefixValue; + + return ip; + + error: + VIR_FREE(prefixStr); + VIR_FREE(familyStr); + VIR_FREE(address); + VIR_FREE(ip); + return NULL; +} + static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, @@ -7077,6 +7133,31 @@ virDomainActualNetDefParseXML(xmlNodePtr node, #define NET_MODEL_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
+ +int +virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + int family, + unsigned int prefix) +{ + virDomainNetIpDefPtr ipDef = NULL; + if (VIR_ALLOC(ipDef) < 0) + return -1; + + if (virSocketAddrParse(&ipDef->address, address, family) < 0) + goto error; + ipDef->prefix = prefix; + + if (VIR_APPEND_ELEMENT(def->ips, def->nips, ipDef) < 0) + goto error; + + return 0; + + error: + VIR_FREE(ipDef); + return -1; +} + /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure @@ -7124,6 +7205,9 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; int ret, val; + size_t i; + size_t nips = 0; + virDomainNetIpDefPtr *ips = NULL;
if (VIR_ALLOC(def) < 0) return NULL; @@ -7212,11 +7296,14 @@ 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")) { + virDomainNetIpDefPtr ip = NULL; + + if (!(ip = virDomainNetIpParseXML(cur))) + goto error; + + if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) + goto error; } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7416,10 +7503,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->data.ethernet.dev = dev; dev = NULL; } - if (address != NULL) { - def->data.ethernet.ipaddr = address; - address = NULL; - } break;
case VIR_DOMAIN_NET_TYPE_BRIDGE: @@ -7431,10 +7514,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } def->data.bridge.brname = bridge; bridge = NULL; - if (address != NULL) { - def->data.bridge.ipaddr = address; - address = NULL; - } break;
case VIR_DOMAIN_NET_TYPE_CLIENT: @@ -7531,6 +7610,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; @@ -7793,6 +7877,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(linkstate); VIR_FREE(addrtype); VIR_FREE(trustGuestRxFilters); + VIR_FREE(ips); virNWFilterHashTableFree(filterparams);
return def; @@ -16753,6 +16838,30 @@ virDomainFSDefFormat(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++) { + virSocketAddrPtr address = &ips[i]->address; + char *ipStr = virSocketAddrFormat(address); + const char *familyStr = NULL; + if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET6)) + familyStr = "ipv6"; + else if (VIR_SOCKET_ADDR_IS_FAMILY(address, AF_INET)) + familyStr = "ipv4"; + virBufferAsprintf(buf, "<ip address='%s'", + ipStr); + if (familyStr) + virBufferAsprintf(buf, " family='%s'", familyStr); + if (ips[i]->prefix != 0) + virBufferAsprintf(buf, " prefix='%u'", ips[i]->prefix); + virBufferAddLit(buf, "/>\n"); + } +} + static int virDomainHostdevDefFormatSubsys(virBufferPtr buf, virDomainHostdevDefPtr def, @@ -16980,7 +17089,6 @@ virDomainActualNetDefContentsFormat(virBufferPtr buf, return 0; }
- /* virDomainActualNetDefFormat() - format the ActualNetDef * info inside an <actual> element, as required for internal storage * of domain status @@ -17217,9 +17325,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: @@ -17237,10 +17342,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: @@ -17288,6 +17389,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 530a3ca..fbf6067 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -471,6 +471,13 @@ typedef enum { VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST } virDomainHostdevCapsType;
+typedef struct _virDomainNetIpDef virDomainNetIpDef; +typedef virDomainNetIpDef *virDomainNetIpDefPtr; +struct _virDomainNetIpDef { + virSocketAddr address; /* ipv4 or ipv6 address */ + unsigned int prefix; /* number of 1 bits in the net mask */ +}; + typedef struct _virDomainHostdevCaps virDomainHostdevCaps; typedef virDomainHostdevCaps *virDomainHostdevCapsPtr; struct _virDomainHostdevCaps { @@ -932,7 +939,6 @@ struct _virDomainNetDef { union { struct { char *dev; - char *ipaddr; } ethernet; virDomainChrSourceDefPtr vhostuser; struct { @@ -954,7 +960,6 @@ struct _virDomainNetDef { } network; struct { char *brname; - char *ipaddr; } bridge; struct { char *name; @@ -984,6 +989,8 @@ struct _virDomainNetDef { virNetDevVlan vlan; int trustGuestRxFilters; /* enum virTristateBool */ int linkstate; + size_t nips; + virDomainNetIpDefPtr *ips; };
/* Used for prefix of ifname of any network name generated dynamically @@ -2522,6 +2529,10 @@ virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); bool virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface); +int virDomainNetAppendIpAddress(virDomainNetDefPtr def, + const char *address, + int family, + unsigned int prefix);
int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0864618..8d02438 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -321,6 +321,7 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainNetAppendIpAddress; virDomainNetDefFormat; virDomainNetDefFree; virDomainNetFind; diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index 856c9f5..b6fdc37 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, AF_UNSPEC, 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 a0346b4..273bac1 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,10 @@ 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) { /* --ipadd ip */ - virCommandAddArgList(cmd, "--ipadd", net->data.ethernet.ipaddr, NULL); + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); }
/* TODO: processing NAT and physical device */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index acf2b9a..2863db4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6352,6 +6352,8 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, (brname = virDomainNetGetActualBridgeName(net))) {
char *brnamecopy; + size_t j; + if (VIR_STRDUP(brnamecopy, brname) < 0) goto cleanup;
@@ -6362,20 +6364,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++) { + VIR_FREE(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++) { + VIR_FREE(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)); @@ -6383,18 +6396,24 @@ 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++) { + VIR_FREE(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; + size_t nips = net->nips; + virDomainNetIpDefPtr *ips = net->ips;
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; + net->nips = nips; + net->ips = ips; }
VIR_FREE(net->virtPortProfile); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 13bcd88..60a6fd9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2101,8 +2101,9 @@ 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)) { + olddev->nips == 0 || newdev->nips == 0 || + !virSocketAddrEqual(&olddev->ips[0]->address, + &newdev->ips[0]->address)) { needReconnect = true; } break; diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index a99e8e9..ac6a7da 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_common.c b/src/vbox/vbox_common.c index cd5b478..fa1e12d 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1307,7 +1307,11 @@ 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); + if (def->nets[i]->nips > 0) { + char *ipStr = virSocketAddrFormat(&def->nets[i]->ips[0]->address); + VIR_DEBUG("NIC(%zu): ipaddr: %s", i, ipStr); + VIR_FREE(ipStr); + } }
gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter); diff --git a/src/xenconfig/xen_common.c b/src/xenconfig/xen_common.c index 32954f3..bcb3bd3 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -922,12 +922,9 @@ xenParseVif(virConfPtr conf, virDomainDefPtr def) 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) - goto cleanup; - } else { - if (ip[0] && VIR_STRDUP(net->data.ethernet.ipaddr, ip) < 0) - goto cleanup; } + if (ip[0] && virDomainNetAppendIpAddress(net, ip, AF_INET, 0) < 0) + goto cleanup;
if (script && script[0] && VIR_STRDUP(net->script, script) < 0) @@ -1225,16 +1222,22 @@ xenFormatNet(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) { + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virBufferAsprintf(&buf, ",ip=%s", ipStr); + VIR_FREE(ipStr); + } 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) { + char *ipStr = virSocketAddrFormat(&net->ips[0]->address); + virBufferAsprintf(&buf, ",ip=%s", ipStr); + VIR_FREE(ipStr); + } break;
case VIR_DOMAIN_NET_TYPE_NETWORK: diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index 6623ea8..caeb464 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -565,14 +565,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 (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 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 (tmp && virDomainNetAppendIpAddress(net, tmp, AF_UNSPEC, 0) < 0) goto cleanup; }
@@ -1898,8 +1898,11 @@ 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) { + char *ipStr = virSocketAddrFormat(&def->ips[0]->address); + virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); + VIR_FREE(ipStr); + } break;
case VIR_DOMAIN_NET_TYPE_NETWORK: @@ -1932,8 +1935,11 @@ 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) { + char *ipStr = virSocketAddrFormat(&def->ips[0]->address); + virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); + VIR_FREE(ipStr); + } break;
case VIR_DOMAIN_NET_TYPE_VHOSTUSER: diff --git a/tests/lxcxml2xmldata/lxc-idmap.xml b/tests/lxcxml2xmldata/lxc-idmap.xml index 946d363..d011927 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' family='ipv4' prefix='24'/> + <ip address='192.168.122.13' family='ipv4' prefix='24'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> diff --git a/tests/openvzutilstest.c b/tests/openvzutilstest.c index ed2c098..bcf48db 100644 --- a/tests/openvzutilstest.c +++ b/tests/openvzutilstest.c @@ -95,7 +95,7 @@ testReadNetworkConf(const void *data ATTRIBUTE_UNUSED) " <devices>\n" " <interface type='ethernet'>\n" " <mac address='00:00:00:00:00:00'/>\n" - " <ip address='194.44.18.88'/>\n" + " <ip address='194.44.18.88' family='ipv4'/>\n" " </interface>\n" " <interface type='bridge'>\n" " <mac address='00:18:51:c1:05:ee'/>\n" diff --git a/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml b/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml index 9b5cc3a..25a29fd 100644 --- a/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml +++ b/tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml @@ -24,7 +24,7 @@ <interface type='bridge'> <mac address='00:11:22:33:44:55'/> <source bridge='xenbr2'/> - <ip address='192.0.2.1'/> + <ip address='192.0.2.1' family='ipv4'/> <script path='vif-bridge'/> <target dev='vif6.0'/> </interface> diff --git a/tests/sexpr2xmldata/sexpr2xml-net-routed.xml b/tests/sexpr2xmldata/sexpr2xml-net-routed.xml index 0ab3b6d..c03e40b 100644 --- a/tests/sexpr2xmldata/sexpr2xml-net-routed.xml +++ b/tests/sexpr2xmldata/sexpr2xml-net-routed.xml @@ -23,7 +23,7 @@ </disk> <interface type='ethernet'> <mac address='00:11:22:33:44:55'/> - <ip address='172.14.5.6'/> + <ip address='172.14.5.6' family='ipv4'/> <script path='vif-routed'/> <target dev='vif6.0'/> </interface>
-- Dmitry Guryanov

On 11/13/2014 04:33 AM, Cédric Bosdonnat wrote:
@@ -4667,6 +4671,58 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, return ret; }
+static virDomainNetIpDefPtr +virDomainNetIpParseXML(xmlNodePtr node) +{ + /* Parse the prefix in every case */ + virDomainNetIpDefPtr ip = NULL; + char *prefixStr = NULL; + unsigned int prefixValue = 0; + char *familyStr = NULL; + int family = AF_UNSPEC; + char *address = NULL; + + if (!(prefixStr = virXMLPropString(node, "prefix")) || + (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { + // Don't shout, just warn as some old config may not have a prefix + VIR_WARN("Missing or invalid network prefix");
This warning is unnecessary. IPv6 address default to a prefix of 64 if none is specified, and ipv4 addresses to 8, 16, or 24 depending on the range of address you're in (this is specified in an RFC somewhere, basically class A, B, and C networks), and it's quite common for the default prefix to be the correct one. See the function virSocketAddrGetIpPrefix() for details/example.
- <ip address='172.14.5.6'/> + <ip address='172.14.5.6' family='ipv4'/>
Do we really want to automatically add the "ipv4" in there if it's not there? That seems like unnecessary churn and verbosity in the config.

On Thu, Dec 11, 2014 at 01:51:45PM -0500, Laine Stump wrote:
On 11/13/2014 04:33 AM, Cédric Bosdonnat wrote:
@@ -4667,6 +4671,58 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, return ret; }
+static virDomainNetIpDefPtr +virDomainNetIpParseXML(xmlNodePtr node) +{ + /* Parse the prefix in every case */ + virDomainNetIpDefPtr ip = NULL; + char *prefixStr = NULL; + unsigned int prefixValue = 0; + char *familyStr = NULL; + int family = AF_UNSPEC; + char *address = NULL; + + if (!(prefixStr = virXMLPropString(node, "prefix")) || + (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { + // Don't shout, just warn as some old config may not have a prefix + VIR_WARN("Missing or invalid network prefix");
This warning is unnecessary. IPv6 address default to a prefix of 64 if none is specified, and ipv4 addresses to 8, 16, or 24 depending on the range of address you're in (this is specified in an RFC somewhere, basically class A, B, and C networks), and it's quite common for the default prefix to be the correct one. See the function virSocketAddrGetIpPrefix() for details/example.
- <ip address='172.14.5.6'/> + <ip address='172.14.5.6' family='ipv4'/>
Do we really want to automatically add the "ipv4" in there if it's not there? That seems like unnecessary churn and verbosity in the config.
Given that we will now allow use of IPv6 I think we need to be explicit about the address family. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container. --- src/lxc/lxc_container.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 8aba3ba..4aeb19c 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -496,7 +496,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] == @@ -517,6 +517,23 @@ 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; + char *ipStr = virSocketAddrFormat(&ip->address); + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ipStr, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &ip->address, prefix) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ipStr, newname); + VIR_FREE(ipStr); + goto error_out; + } + VIR_FREE(ipStr); + } + VIR_DEBUG("Enabling %s", newname); rc = virNetDevSetOnline(newname, true); if (rc < 0) -- 2.1.2

On Thursday 13 November 2014 10:33:02 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 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 8aba3ba..4aeb19c 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -496,7 +496,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] == @@ -517,6 +517,23 @@ 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;
Why is default prefix hardcoded here? I think we should add a constant for it in some common header file.
+ char *ipStr = virSocketAddrFormat(&ip->address); + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ipStr, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &ip->address, prefix) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ipStr, newname); + VIR_FREE(ipStr); + goto error_out; + } + VIR_FREE(ipStr); + } + VIR_DEBUG("Enabling %s", newname); rc = virNetDevSetOnline(newname, true); if (rc < 0)
-- Dmitry Guryanov

On Tuesday 02 December 2014 16:57:12 Dmitry Guryanov wrote:
On Thursday 13 November 2014 10:33:02 Cédric Bosdonnat wrote:
Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container.
Also I think it should be forbidden to set ip addresses if libnl is not found, because because behavior will be defined by guest filesystem content, if ip of ifconfig utility is found there or not.
---
src/lxc/lxc_container.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 8aba3ba..4aeb19c 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -496,7 +496,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] ==
@@ -517,6 +517,23 @@ 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;
Why is default prefix hardcoded here? I think we should add a constant for it in some common header file.
+ char *ipStr = virSocketAddrFormat(&ip->address); + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ipStr, ip->prefix, newname); + if (virNetDevSetIPv4Address(newname, &ip->address, prefix) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ipStr, newname); + VIR_FREE(ipStr); + goto error_out; + } + VIR_FREE(ipStr); + } +
VIR_DEBUG("Enabling %s", newname); rc = virNetDevSetOnline(newname, true); if (rc < 0)
-- Dmitry Guryanov

--- src/lxc/lxc_native.c | 145 +++++++++++++++--------- tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 2 + 3 files changed, 98 insertions(+), 51 deletions(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index 41e069f..6ab093d 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -413,79 +413,84 @@ 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; + virDomainNetIpDefPtr *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 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) { + 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 = data->ips; + net->nips = data->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 < data->nips; i++) { + VIR_FREE(data->ips[i]); + } + VIR_FREE(data->ips); 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) { @@ -494,12 +499,7 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) 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 +517,9 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) parseData->vlanid = NULL; parseData->name = NULL; + parseData->ips = NULL; + parseData->nips = 0; + /* Keep the new value */ parseData->type = value->str; } @@ -532,10 +535,42 @@ 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")) { + int family = AF_INET; + char **ipparts = NULL; + virDomainNetIpDefPtr ip = NULL; + + if (VIR_ALLOC(ip) < 0) + return -1; + + if (STREQ(name, "lxc.network.ipv6")) + family = AF_INET6; + + ipparts = virStringSplit(value->str, "/", 2); + if (virStringListLength(ipparts) != 2 || + virSocketAddrParse(&ip->address, ipparts[0], family) < 0 || + virStrToLong_ui(ipparts[1], NULL, 10, &ip->prefix) < 0) { + + virReportError(VIR_ERR_INVALID_ARG, + _("Invalid CIDR address: '%s'"), value->str); + + virStringFreeList(ipparts); + VIR_FREE(ip); + return -1; + } + + virStringFreeList(ipparts); + + if (VIR_APPEND_ELEMENT(parseData->ips, parseData->nips, ip) < 0) { + VIR_FREE(ip); + return -1; + } + } else if (STRPREFIX(name, "lxc.network")) { VIR_WARN("Unhandled network property: %s = %s", name, value->str); + } return 0; } @@ -544,19 +579,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 error; - 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 error; else if (status > 0) data.networks++; else if (data.type != NULL && STREQ(data.type, "none")) @@ -566,8 +602,15 @@ 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; + return result; + + error: + for (i = 0; i < data.nips; i++) + VIR_FREE(data.ips[i]); + VIR_FREE(data.ips); + return -1; } 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..0dbbc71 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' family='ipv4' prefix='24'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='64'/> <guest dev='eth0'/> <link state='up'/> </interface> -- 2.1.2

--- docs/formatdomain.html.in | 12 ++++++++++-- docs/schemas/domaincommon.rng | 28 ++++++++++++++++++++++++---- src/conf/domain_conf.c | 34 ++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 2 ++ tests/lxcxml2xmldata/lxc-hostdev.xml | 2 ++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index d414371..0984145 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4292,13 +4292,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.10</span> the network devices can be provided - zero or more IP addresses to set + <span class="since">Since 1.2.10</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 cd82461..227491f 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3779,11 +3779,31 @@ <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="family"> + <ref name="addr-family"/> + </attribute> + </optional> + <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 44741a9..2477ebe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1771,6 +1771,8 @@ virDomainHostdevSubsysSCSIiSCSIClear(virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc void virDomainHostdevDefClear(virDomainHostdevDefPtr def) { + size_t i; + if (!def) return; @@ -1795,6 +1797,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++) + VIR_FREE(def->source.caps.u.net.ips[i]); + VIR_FREE(def->source.caps.u.net.ips); break; } break; @@ -4730,6 +4735,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 @@ -4784,6 +4791,26 @@ 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++) { + virDomainNetIpDefPtr ip = virDomainNetIpParseXML(ipnodes[i]); + + if (!ip) + goto error; + + if (VIR_APPEND_ELEMENT(def->source.caps.u.net.ips, + def->source.caps.u.net.nips, ip) < 0) { + VIR_FREE(ip); + goto error; + } + } + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4793,6 +4820,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } ret = 0; error: + VIR_FREE(ipnodes); return ret; } @@ -17013,6 +17041,12 @@ 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 fbf6067..bbbc8da 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -491,6 +491,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..0596789 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' family='ipv4'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='24'/> </hostdev> </devices> </domain> -- 2.1.2

--- 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 6ab093d..f0bc1c5 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -462,6 +462,9 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) goto error; } + hostdev->source.caps.u.net.ips = data->ips; + hostdev->source.caps.u.net.nips = data->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..e000816 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' family='ipv4' prefix='24'/> + <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='64'/> </hostdev> </devices> </domain> -- 2.1.2

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 | 21 ++++++++++++ src/conf/domain_conf.c | 66 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 +++ tests/lxcxml2xmldata/lxc-hostdev.xml | 2 ++ tests/lxcxml2xmldata/lxc-idmap.xml | 1 + 6 files changed, 103 insertions(+) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0984145..8500f81 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4291,6 +4291,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'> @@ -4298,6 +4299,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> @@ -4313,6 +4315,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.10</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 227491f..2c6bc94 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2313,6 +2313,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="gateway"> + <ref name="gateway"/> + </element> + </zeroOrMore> <optional> <element name="script"> <attribute name="path"> @@ -3568,6 +3573,17 @@ </element> </define> + <define name="gateway"> + <interleave> + <attribute name="family"> + <ref name="addr-family"/> + </attribute> + <attribute name="address"> + <ref name="ipAddr"/> + </attribute> + </interleave> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -3803,6 +3819,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="gateway"> + <ref name="gateway"/> + </element> + </zeroOrMore> </interleave> </define> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2477ebe..388a193 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1428,6 +1428,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ips[i]); VIR_FREE(def->ips); + VIR_FREE(def->gateway_ipv4); + VIR_FREE(def->gateway_ipv6); + virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); @@ -1800,6 +1803,8 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) for (i = 0; i < def->source.caps.u.net.nips; i++) VIR_FREE(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; @@ -4728,6 +4733,22 @@ virDomainNetIpParseXML(xmlNodePtr node) return NULL; } +static void +virDomainNetGatewayParse(xmlNodePtr node, char **ipv4, char **ipv6) { + char *family = virXMLPropString(node, "family"); + if (!*ipv4 && + STREQ_NULLABLE(family, "ipv4")) { + char *address = virXMLPropString(node, "address"); + *ipv4 = address; + } + if (!*ipv6 && + STREQ_NULLABLE(family, "ipv6")) { + char *address = virXMLPropString(node, "address"); + *ipv6 = address; + } + VIR_FREE(family); +} + static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, @@ -4737,6 +4758,8 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr sourcenode; xmlNodePtr *ipnodes = NULL; int nipnodes; + xmlNodePtr *gwnodes = NULL; + int ngwnodes; int ret = -1; /* @type is passed in from the caller rather than read from the @@ -4811,6 +4834,20 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } } } + + /* Look for possible gateways */ + if ((ngwnodes = virXPathNodeSet("./gateway", ctxt, &gwnodes)) < 0) + goto error; + + if (ngwnodes) { + size_t i; + for (i = 0; i < ngwnodes; i++) { + + virDomainNetGatewayParse(gwnodes[i], + &def->source.caps.u.net.gateway_ipv4, + &def->source.caps.u.net.gateway_ipv6); + } + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4821,6 +4858,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, ret = 0; error: VIR_FREE(ipnodes); + VIR_FREE(gwnodes); return ret; } @@ -7236,6 +7274,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; @@ -7332,6 +7372,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) goto error; + } else if (xmlStrEqual(cur->name, BAD_CAST "gateway")) { + virDomainNetGatewayParse(cur, &gateway_ipv4, &gateway_ipv6); } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7642,6 +7684,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; @@ -7907,6 +7953,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(trustGuestRxFilters); VIR_FREE(ips); virNWFilterHashTableFree(filterparams); + VIR_FREE(gateway_ipv4); + VIR_FREE(gateway_ipv6); return def; @@ -16890,6 +16938,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) { + virBufferAsprintf(buf, "<gateway family='ipv4' address='%s'/>\n", + gateway_ipv4); + } + if (gateway_ipv6) { + virBufferAsprintf(buf, "<gateway family='ipv6' address='%s'/>\n", + gateway_ipv6); + } +} + static int virDomainHostdevDefFormatSubsys(virBufferPtr buf, virDomainHostdevDefPtr def, @@ -17045,6 +17108,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; @@ -17424,6 +17489,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 bbbc8da..5678960 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -493,6 +493,8 @@ struct _virDomainHostdevCaps { char *iface; size_t nips; virDomainNetIpDefPtr *ips; + char *gateway_ipv4; + char *gateway_ipv6; } net; } u; }; @@ -993,6 +995,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 0596789..6e4c8cf 100644 --- a/tests/lxcxml2xmldata/lxc-hostdev.xml +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -37,6 +37,8 @@ </source> <ip address='192.168.122.2' family='ipv4'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='24'/> + <gateway family='ipv4' address='192.168.122.1'/> + <gateway family='ipv6' address='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 d011927..150a7d6 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' family='ipv4' prefix='24'/> <ip address='192.168.122.13' family='ipv4' prefix='24'/> + <gateway family='ipv4' address='192.168.122.1'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> -- 2.1.2

On Thursday 13 November 2014 10:33:06 Cédric Bosdonnat wrote:
Network interfaces devices and host devices with net capabilities can now have an IPv4 and/or an IPv6 address configured.
I would rename "gateway" to "route", so it will mean an entry in a routing table. Because there will certainly be people, who would want to configure routing table more precisely than just setting default gateway. Something like <route to="10.10.0.0/16" via="10.3.0.1"> Routing table is separated from devices configuration in the kernel, and some route types don't have output device, for example blackhole. So maybe it's better to move route from interface definition to outer scope and add "dev" parameter.
--- docs/formatdomain.html.in | 9 +++++ docs/schemas/domaincommon.rng | 21 ++++++++++++ src/conf/domain_conf.c | 66 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 +++ tests/lxcxml2xmldata/lxc-hostdev.xml | 2 ++ tests/lxcxml2xmldata/lxc-idmap.xml | 1 + 6 files changed, 103 insertions(+)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0984145..8500f81 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4291,6 +4291,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'> @@ -4298,6 +4299,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> @@ -4313,6 +4315,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.10</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 227491f..2c6bc94 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2313,6 +2313,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="gateway"> + <ref name="gateway"/> + </element> + </zeroOrMore> <optional> <element name="script"> <attribute name="path"> @@ -3568,6 +3573,17 @@ </element> </define>
+ <define name="gateway"> + <interleave> + <attribute name="family"> + <ref name="addr-family"/> + </attribute> + <attribute name="address"> + <ref name="ipAddr"/> + </attribute> + </interleave> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -3803,6 +3819,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="gateway"> + <ref name="gateway"/> + </element> + </zeroOrMore> </interleave> </define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2477ebe..388a193 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1428,6 +1428,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ips[i]); VIR_FREE(def->ips);
+ VIR_FREE(def->gateway_ipv4); + VIR_FREE(def->gateway_ipv6); + virDomainDeviceInfoClear(&def->info);
VIR_FREE(def->filter); @@ -1800,6 +1803,8 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) for (i = 0; i < def->source.caps.u.net.nips; i++) VIR_FREE(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; @@ -4728,6 +4733,22 @@ virDomainNetIpParseXML(xmlNodePtr node) return NULL; }
+static void +virDomainNetGatewayParse(xmlNodePtr node, char **ipv4, char **ipv6) { + char *family = virXMLPropString(node, "family"); + if (!*ipv4 && + STREQ_NULLABLE(family, "ipv4")) { + char *address = virXMLPropString(node, "address"); + *ipv4 = address; + } + if (!*ipv6 && + STREQ_NULLABLE(family, "ipv6")) { + char *address = virXMLPropString(node, "address"); + *ipv6 = address; + } + VIR_FREE(family); +} + static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, @@ -4737,6 +4758,8 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr sourcenode; xmlNodePtr *ipnodes = NULL; int nipnodes; + xmlNodePtr *gwnodes = NULL; + int ngwnodes; int ret = -1;
/* @type is passed in from the caller rather than read from the @@ -4811,6 +4834,20 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } } } + + /* Look for possible gateways */ + if ((ngwnodes = virXPathNodeSet("./gateway", ctxt, &gwnodes)) < 0) + goto error; + + if (ngwnodes) { + size_t i; + for (i = 0; i < ngwnodes; i++) { + + virDomainNetGatewayParse(gwnodes[i], + &def->source.caps.u.net.gateway_ipv4, + &def->source.caps.u.net.gateway_ipv6); + } + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4821,6 +4858,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, ret = 0; error: VIR_FREE(ipnodes); + VIR_FREE(gwnodes); return ret; }
@@ -7236,6 +7274,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; @@ -7332,6 +7372,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) goto error; + } else if (xmlStrEqual(cur->name, BAD_CAST "gateway")) { + virDomainNetGatewayParse(cur, &gateway_ipv4, &gateway_ipv6); } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7642,6 +7684,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; @@ -7907,6 +7953,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(trustGuestRxFilters); VIR_FREE(ips); virNWFilterHashTableFree(filterparams); + VIR_FREE(gateway_ipv4); + VIR_FREE(gateway_ipv6);
return def;
@@ -16890,6 +16938,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) { + virBufferAsprintf(buf, "<gateway family='ipv4' address='%s'/>\n", + gateway_ipv4); + } + if (gateway_ipv6) { + virBufferAsprintf(buf, "<gateway family='ipv6' address='%s'/>\n", + gateway_ipv6); + } +} + static int virDomainHostdevDefFormatSubsys(virBufferPtr buf, virDomainHostdevDefPtr def, @@ -17045,6 +17108,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; @@ -17424,6 +17489,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 bbbc8da..5678960 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -493,6 +493,8 @@ struct _virDomainHostdevCaps { char *iface; size_t nips; virDomainNetIpDefPtr *ips; + char *gateway_ipv4; + char *gateway_ipv6; } net; } u; }; @@ -993,6 +995,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 0596789..6e4c8cf 100644 --- a/tests/lxcxml2xmldata/lxc-hostdev.xml +++ b/tests/lxcxml2xmldata/lxc-hostdev.xml @@ -37,6 +37,8 @@ </source> <ip address='192.168.122.2' family='ipv4'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='24'/> + <gateway family='ipv4' address='192.168.122.1'/> + <gateway family='ipv6' address='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 d011927..150a7d6 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' family='ipv4' prefix='24'/> <ip address='192.168.122.13' family='ipv4' prefix='24'/> + <gateway family='ipv4' address='192.168.122.1'/> <target dev='veth0'/> <guest dev='eth2'/> </interface>
-- Dmitry Guryanov

On Tue, Dec 02, 2014 at 04:39:02PM +0300, Dmitry Guryanov wrote:
On Thursday 13 November 2014 10:33:06 Cédric Bosdonnat wrote:
Network interfaces devices and host devices with net capabilities can now have an IPv4 and/or an IPv6 address configured.
I would rename "gateway" to "route", so it will mean an entry in a routing table. Because there will certainly be people, who would want to configure routing table more precisely than just setting default gateway.
Something like <route to="10.10.0.0/16" via="10.3.0.1">
Routing table is separated from devices configuration in the kernel, and some route types don't have output device, for example blackhole. So maybe it's better to move route from interface definition to outer scope and add "dev" parameter.
Hmm, if you have multiple IP addresses per device, you I guess you can also have multiple default routes - one for each subnet range really eg <ip address='192.168.122.5' prefix='24'/> <ip address='10.0.2.4' prefix='24'/> <gateway ipv4='192.168.122.1'/> <gateway ipv4='192.0.2.1'/> So I at leaast agree that we need more than just a single <gateway> per address type. I do like the routs associated with the <interface> though. I'd be inclined to have <route family="ipv4|ipv6" address="10.0.0.0" prefix="16" via="10.0.0.1"/> under the <interface>. The address + prefix attrs could be optional if there is only a single <ip> for a given family. eg in the simple case we could get this for a "default route" <route family="ipv4|ipv6" via="10.0.0.1"/> If we ever find a real world need to have routes not associated with a device, we can address that then. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- src/lxc/lxc_native.c | 19 ++++++++++++++++++- tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 2 ++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index f0bc1c5..c4c2386 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -424,6 +424,8 @@ typedef struct { char *name; virDomainNetIpDefPtr *ips; size_t nips; + char *gateway_ipv4; + char *gateway_ipv6; bool privnet; size_t networks; } lxcNetworkParseData; @@ -465,6 +467,12 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) hostdev->source.caps.u.net.ips = data->ips; hostdev->source.caps.u.net.nips = data->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; @@ -477,6 +485,10 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) net->ips = data->ips; net->nips = data->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; @@ -569,6 +581,10 @@ lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data) VIR_FREE(ip); 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, @@ -585,7 +601,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, 0, + NULL, NULL, true, 0}; if (virConfWalk(properties, lxcNetworkWalkCallback, &data) < 0) goto error; 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 e000816..cfb3c08 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml @@ -27,6 +27,8 @@ </source> <ip address='192.168.122.2' family='ipv4' prefix='24'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='64'/> + <gateway family='ipv4' address='192.168.122.1'/> + <gateway family='ipv6' address='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 0dbbc71..661e6ac 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml @@ -39,6 +39,8 @@ <source bridge='virbr0'/> <ip address='192.168.122.2' family='ipv4' prefix='24'/> <ip address='2003:db8:1:0:214:1234:fe0b:3596' family='ipv6' prefix='64'/> + <gateway family='ipv4' address='192.168.122.1'/> + <gateway family='ipv6' address='2003:db8:1:0:214:1234:fe0b:3595'/> <guest dev='eth0'/> <link state='up'/> </interface> -- 2.1.2

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 | 40 ++++++++++++++++++++++++++++++++++++++++ src/util/virsocketaddr.h | 3 +++ 2 files changed, 43 insertions(+) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 4aeb19c..727565d 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -480,6 +480,35 @@ lxcContainerGetNetDef(virDomainDefPtr vmDef, const char *devName) return NULL; } +static int +lxcContainerAddDefaultRoute(const char *ifname, + const char *gateway, + int family) +{ + virSocketAddr address; + virSocketAddr network; + + VIR_DEBUG("Adding default route via %s on dev %s", gateway, ifname); + if (virSocketAddrParse(&address, gateway, family) < 0) + return -1; + + if (family == AF_INET) { + if (virSocketAddrParseIPv4(&network, VIR_SOCKET_ADDR_IPV4_ALL) < 0) + return -1; + } else { + if (virSocketAddrParseIPv6(&network, VIR_SOCKET_ADDR_IPV6_ALL) < 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 +568,17 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out; + /* Set the gateways */ + if (netDef->gateway_ipv4 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv4, + AF_INET) < 0) + goto error_out; + + if (netDef->gateway_ipv6 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6, + AF_INET6) < 0) + goto error_out; + VIR_FREE(newname); } diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index 053855b..198b109 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -54,6 +54,9 @@ typedef struct { # define VIR_SOCKET_ADDR_FAMILY(s) \ ((s)->data.sa.sa_family) +# define VIR_SOCKET_ADDR_IPV4_ALL "0.0.0.0" +# define VIR_SOCKET_ADDR_IPV6_ALL "::" + typedef virSocketAddr *virSocketAddrPtr; typedef struct _virSocketAddrRange virSocketAddrRange; -- 2.1.2

Don't activate LXC network device if <link state='down'/> has been set in its configuration. --- src/lxc/lxc_container.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 727565d..3befd1c 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -563,21 +563,23 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, VIR_FREE(ipStr); } - 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, - AF_INET) < 0) - goto error_out; + /* Set the gateways */ + if (netDef->gateway_ipv4 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv4, + AF_INET) < 0) + goto error_out; - if (netDef->gateway_ipv6 && - lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6, - AF_INET6) < 0) - goto error_out; + if (netDef->gateway_ipv6 && + lxcContainerAddDefaultRoute(newname, netDef->gateway_ipv6, + AF_INET6) < 0) + goto error_out; + } VIR_FREE(newname); } -- 2.1.2

Add a default implementation of virNetDevSetIPv4Address using netlink and libnl. This avoids requiring /usr/sbin/ip or /usr/sbin/ifconfig external binaries. --- src/libvirt_private.syms | 1 + src/util/virnetdev.c | 136 +++++++++++++++++++++++++++++++++++++++++++++-- src/util/virnetlink.c | 38 +++++++++++++ src/util/virnetlink.h | 2 + 4 files changed, 174 insertions(+), 3 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8d02438..b326639 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1720,6 +1720,7 @@ virNetlinkEventServiceLocalPid; virNetlinkEventServiceStart; virNetlinkEventServiceStop; virNetlinkEventServiceStopAll; +virNetlinkGetErrorCode; virNetlinkShutdown; virNetlinkStartup; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 3831009..57904af 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -823,6 +823,88 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED, #endif /* ! SIOCGIFVLAN */ +#if defined(__linux__) && defined(HAVE_LIBNL) + +static int +virNetDevGetIPAddressBinary(virSocketAddr *addr, void **data, size_t *len) +{ + if (!addr) + return -1; + + switch (VIR_SOCKET_ADDR_FAMILY(addr)) { + case AF_INET: + *data = &addr->data.inet4.sin_addr; + *len = sizeof(struct in_addr); + break; + case AF_INET6: + *data = &addr->data.inet6.sin6_addr; + *len = sizeof(struct in6_addr); + break; + default: + return -1; + } + return 0; +} + +static struct nl_msg * +virNetDevCreateNetlinkAddressMessage(int messageType, + const char *ifname, + virSocketAddr *addr, + unsigned int prefix, + virSocketAddr *broadcast) +{ + struct nl_msg *nlmsg = NULL; + struct ifaddrmsg ifa; + unsigned int ifindex; + void *addrData = NULL; + void *broadcastData = NULL; + size_t addrDataLen; + + if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0) + return NULL; + + if (broadcast && virNetDevGetIPAddressBinary(broadcast, &broadcastData, + &addrDataLen) < 0) + return NULL; + + /* Get the interface index */ + if ((ifindex = if_nametoindex(ifname)) == 0) + return NULL; + + if (!(nlmsg = nlmsg_alloc_simple(messageType, + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL))) { + virReportOOMError(); + return NULL; + } + + memset(&ifa, 0, sizeof(ifa)); + + ifa.ifa_prefixlen = prefix; + ifa.ifa_family = VIR_SOCKET_ADDR_FAMILY(addr); + ifa.ifa_index = ifindex; + ifa.ifa_scope = 0; + + if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) + goto buffer_too_small; + + if (nla_put(nlmsg, IFA_LOCAL, addrDataLen, addrData) < 0) + goto buffer_too_small; + + if (nla_put(nlmsg, IFA_ADDRESS, addrDataLen, addrData) < 0) + goto buffer_too_small; + + if (broadcastData && + nla_put(nlmsg, IFA_BROADCAST, addrDataLen, broadcastData) < 0) + goto buffer_too_small; + + return nlmsg; + + buffer_too_small: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("allocated netlink buffer is too small")); + nlmsg_free(nlmsg); + return NULL; +} /** * virNetDevSetIPv4Address: @@ -836,6 +918,52 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED, * * Returns 0 in case of success or -1 in case of error. */ +int virNetDevSetIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + virSocketAddr *broadcast = NULL; + int ret = -1; + struct nl_msg *nlmsg = NULL; + struct nlmsghdr *resp = NULL; + unsigned int recvbuflen; + + + /* The caller needs to provide a correct address */ + if (VIR_SOCKET_ADDR_FAMILY(addr) == AF_INET) { + /* compute a broadcast address if this is IPv4 */ + if (VIR_ALLOC(broadcast) < 0) + return -1; + + if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) + goto cleanup; + } + + if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_NEWADDR, ifname, + addr, prefix, + broadcast))) + goto cleanup; + + if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, + NETLINK_ROUTE, 0) < 0) + goto cleanup; + + + if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Error adding IP address to %s"), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + nlmsg_free(nlmsg); + VIR_FREE(resp); + VIR_FREE(broadcast); + return ret; +} + +#else /* defined(__linux__) && defined(HAVE_LIBNL) */ int virNetDevSetIPv4Address(const char *ifname, virSocketAddr *addr, @@ -854,7 +982,7 @@ int virNetDevSetIPv4Address(const char *ifname, !(bcaststr = virSocketAddrFormat(&broadcast)))) { goto cleanup; } -#ifdef IFCONFIG_PATH +# ifdef IFCONFIG_PATH cmd = virCommandNew(IFCONFIG_PATH); virCommandAddArg(cmd, ifname); if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) @@ -865,14 +993,14 @@ int virNetDevSetIPv4Address(const char *ifname, if (bcaststr) virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); virCommandAddArg(cmd, "alias"); -#else +# else cmd = virCommandNew(IP_PATH); virCommandAddArgList(cmd, "addr", "add", NULL); virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); if (bcaststr) virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); virCommandAddArgList(cmd, "dev", ifname, NULL); -#endif +# endif if (virCommandRun(cmd, NULL) < 0) goto cleanup; @@ -885,6 +1013,8 @@ int virNetDevSetIPv4Address(const char *ifname, return ret; } +#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ + /** * virNetDevAddRoute: * @ifname: the interface name diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c index eab888f..1a2b7a1 100644 --- a/src/util/virnetlink.c +++ b/src/util/virnetlink.c @@ -276,6 +276,44 @@ int virNetlinkCommand(struct nl_msg *nl_msg, return ret; } +int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen) +{ + struct nlmsgerr *err; + int result = 0; + + if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) + goto malformed_resp; + + switch (resp->nlmsg_type) { + case NLMSG_ERROR: + err = (struct nlmsgerr *)NLMSG_DATA(resp); + if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) + goto malformed_resp; + + switch (err->error) { + case 0: /* ACK */ + break; + + default: + result = err->error; + } + break; + + case NLMSG_DONE: + break; + + default: + goto malformed_resp; + } + + return result; + + malformed_resp: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -EINVAL; +} + static void virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) { diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h index c478691..1a3e06d 100644 --- a/src/util/virnetlink.h +++ b/src/util/virnetlink.h @@ -52,6 +52,8 @@ int virNetlinkCommand(struct nl_msg *nl_msg, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups); +int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen); + typedef void (*virNetlinkEventHandleCallback)(struct nlmsghdr *, unsigned int length, struct sockaddr_nl *peer, -- 2.1.2

Renamed virNetDevSetIPv4Address as it also handles IPv6 addresses. --- src/libvirt_private.syms | 2 +- src/lxc/lxc_container.c | 2 +- src/network/bridge_driver.c | 4 ++-- src/util/virnetdev.c | 14 +++++++------- src/util/virnetdev.h | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b326639..f336ac6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1631,7 +1631,7 @@ virNetDevRxFilterFree; virNetDevRxFilterModeTypeFromString; virNetDevRxFilterModeTypeToString; virNetDevRxFilterNew; -virNetDevSetIPv4Address; +virNetDevSetIPAddress; virNetDevSetMAC; virNetDevSetMTU; virNetDevSetMTUFromDevice; diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 3befd1c..77e5c51 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -553,7 +553,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, VIR_DEBUG("Adding IP address '%s/%u' to '%s'", ipStr, ip->prefix, newname); - if (virNetDevSetIPv4Address(newname, &ip->address, prefix) < 0) { + if (virNetDevSetIPAddress(newname, &ip->address, prefix) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Failed to set IP address '%s' on %s"), ipStr, newname); diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 12c3b50..206d273 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1887,8 +1887,8 @@ networkAddAddrToBridge(virNetworkObjPtr network, return -1; } - if (virNetDevSetIPv4Address(network->def->bridge, - &ipdef->address, prefix) < 0) + if (virNetDevSetIPAddress(network->def->bridge, + &ipdef->address, prefix) < 0) return -1; return 0; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 57904af..3eaf0e4 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -907,7 +907,7 @@ virNetDevCreateNetlinkAddressMessage(int messageType, } /** - * virNetDevSetIPv4Address: + * virNetDevSetIPAddress: * @ifname: the interface name * @addr: the IP address (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask @@ -918,9 +918,9 @@ virNetDevCreateNetlinkAddressMessage(int messageType, * * Returns 0 in case of success or -1 in case of error. */ -int virNetDevSetIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevSetIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) { virSocketAddr *broadcast = NULL; int ret = -1; @@ -965,9 +965,9 @@ int virNetDevSetIPv4Address(const char *ifname, #else /* defined(__linux__) && defined(HAVE_LIBNL) */ -int virNetDevSetIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevSetIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) { virCommandPtr cmd = NULL; char *addrstr = NULL, *bcaststr = NULL; diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 2791b0d..14d6a79 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -87,9 +87,9 @@ int virNetDevIsOnline(const char *ifname, bool *online) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevSetIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevSetIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; int virNetDevAddRoute(const char *ifname, virSocketAddrPtr addr, -- 2.1.2

--- src/util/virnetdev.c | 105 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 3eaf0e4..a4b4ab3 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -963,6 +963,95 @@ int virNetDevSetIPAddress(const char *ifname, return ret; } +/** + * virNetDevAddRoute: + * @ifname: the interface name + * @addr: the IP network address (IPv4 or IPv6) + * @prefix: number of 1 bits in the netmask + * @gateway: via address for route (same as @addr) + * + * Add a route for a network IP address to an interface. This function + * *does not* remove any previously added IP static routes. + * + * Returns 0 in case of success or -1 in case of error. + */ +int +virNetDevAddRoute(const char *ifname, + virSocketAddrPtr addr, + unsigned int prefix, + virSocketAddrPtr gateway, + unsigned int metric) +{ + int ret = -1; + struct nl_msg *nlmsg = NULL; + struct nlmsghdr *resp = NULL; + unsigned int recvbuflen; + unsigned int ifindex; + struct rtmsg rtmsg; + void *gatewayData = NULL; + void *addrData = NULL; + size_t addrDataLen; + int errCode; + + if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0 || + virNetDevGetIPAddressBinary(gateway, &gatewayData, &addrDataLen) < 0) + goto cleanup; + + /* Get the interface index */ + if ((ifindex = if_nametoindex(ifname)) == 0) + goto cleanup; + + if (!(nlmsg = nlmsg_alloc_simple(RTM_NEWROUTE, + NLM_F_REQUEST | NLM_F_CREATE | + NLM_F_EXCL))) { + virReportOOMError(); + goto cleanup; + } + + memset(&rtmsg, 0, sizeof(rtmsg)); + + rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(addr); + rtmsg.rtm_table = RT_TABLE_MAIN; + rtmsg.rtm_scope = RT_SCOPE_UNIVERSE; + rtmsg.rtm_protocol = RTPROT_BOOT; + rtmsg.rtm_type = RTN_UNICAST; + rtmsg.rtm_dst_len = prefix; + + if (nlmsg_append(nlmsg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) + goto buffer_too_small; + + if (prefix > 0 && nla_put(nlmsg, RTA_DST, addrDataLen, addrData) < 0) + goto buffer_too_small; + + if (nla_put(nlmsg, RTA_GATEWAY, addrDataLen, gatewayData) < 0) + goto buffer_too_small; + + if (nla_put_u32(nlmsg, RTA_OIF, ifindex) < 0) + goto buffer_too_small; + + if (metric > 0 && nla_put_u32(nlmsg, RTA_PRIORITY, metric) < 0) + goto buffer_too_small; + + if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, + NETLINK_ROUTE, 0) < 0) + goto cleanup; + + if ((errCode = virNetlinkGetErrorCode(resp, recvbuflen)) < 0) { + virReportSystemError(errCode, _("Error adding route to %s"), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + nlmsg_free(nlmsg); + return ret; + + buffer_too_small: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("allocated netlink buffer is too small")); + goto cleanup; +} + #else /* defined(__linux__) && defined(HAVE_LIBNL) */ int virNetDevSetIPAddress(const char *ifname, @@ -1013,21 +1102,6 @@ int virNetDevSetIPAddress(const char *ifname, return ret; } -#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ - -/** - * virNetDevAddRoute: - * @ifname: the interface name - * @addr: the IP network address (IPv4 or IPv6) - * @prefix: number of 1 bits in the netmask - * @gateway: via address for route (same as @addr) - * - * Add a route for a network IP address to an interface. This function - * *does not* remove any previously added IP static routes. - * - * Returns 0 in case of success or -1 in case of error. - */ - int virNetDevAddRoute(const char *ifname, virSocketAddrPtr addr, @@ -1060,6 +1134,7 @@ virNetDevAddRoute(const char *ifname, virCommandFree(cmd); return ret; } +#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ /** * virNetDevClearIPv4Address: -- 2.1.2

--- src/util/virnetdev.c | 60 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index a4b4ab3..c61431b 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1052,6 +1052,47 @@ virNetDevAddRoute(const char *ifname, goto cleanup; } +/** + * virNetDevClearIPv4Address: + * @ifname: the interface name + * @addr: the IP address (IPv4 or IPv6) + * @prefix: number of 1 bits in the netmask + * + * Delete an IP address from an interface. + * + * Returns 0 in case of success or -1 in case of error. + */ +int virNetDevClearIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + int ret = -1; + struct nl_msg *nlmsg = NULL; + struct nlmsghdr *resp = NULL; + unsigned int recvbuflen; + + if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_DELADDR, ifname, + addr, prefix, + NULL))) + goto cleanup; + + if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, + NETLINK_ROUTE, 0) < 0) + goto cleanup; + + if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Error removing IP address from %s"), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + nlmsg_free(nlmsg); + VIR_FREE(resp); + return ret; +} + #else /* defined(__linux__) && defined(HAVE_LIBNL) */ int virNetDevSetIPAddress(const char *ifname, @@ -1134,18 +1175,6 @@ virNetDevAddRoute(const char *ifname, virCommandFree(cmd); return ret; } -#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ - -/** - * virNetDevClearIPv4Address: - * @ifname: the interface name - * @addr: the IP address (IPv4 or IPv6) - * @prefix: number of 1 bits in the netmask - * - * Delete an IP address from an interface. - * - * Returns 0 in case of success or -1 in case of error. - */ int virNetDevClearIPv4Address(const char *ifname, virSocketAddr *addr, @@ -1157,7 +1186,7 @@ int virNetDevClearIPv4Address(const char *ifname, if (!(addrstr = virSocketAddrFormat(addr))) goto cleanup; -#ifdef IFCONFIG_PATH +# ifdef IFCONFIG_PATH cmd = virCommandNew(IFCONFIG_PATH); virCommandAddArg(cmd, ifname); if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) @@ -1166,12 +1195,12 @@ int virNetDevClearIPv4Address(const char *ifname, virCommandAddArg(cmd, "inet"); virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); virCommandAddArg(cmd, "-alias"); -#else +# else cmd = virCommandNew(IP_PATH); virCommandAddArgList(cmd, "addr", "del", NULL); virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); virCommandAddArgList(cmd, "dev", ifname, NULL); -#endif +# endif if (virCommandRun(cmd, NULL) < 0) goto cleanup; @@ -1183,6 +1212,7 @@ int virNetDevClearIPv4Address(const char *ifname, return ret; } +#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ /** * virNetDevGetIPv4Address: -- 2.1.2

Make clear that virNetDevClearIPv4Address can also handle IPv6 addresses by changing the name --- src/libvirt_private.syms | 2 +- src/util/virnetdev.c | 14 +++++++------- src/util/virnetdev.h | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f336ac6..2119e2e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1606,7 +1606,7 @@ virMacAddrSetRaw; # util/virnetdev.h virNetDevAddMulti; virNetDevAddRoute; -virNetDevClearIPv4Address; +virNetDevClearIPAddress; virNetDevDelMulti; virNetDevExists; virNetDevGetIndex; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index c61431b..ac88922 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1053,7 +1053,7 @@ virNetDevAddRoute(const char *ifname, } /** - * virNetDevClearIPv4Address: + * virNetDevClearIPAddress: * @ifname: the interface name * @addr: the IP address (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask @@ -1062,9 +1062,9 @@ virNetDevAddRoute(const char *ifname, * * Returns 0 in case of success or -1 in case of error. */ -int virNetDevClearIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevClearIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) { int ret = -1; struct nl_msg *nlmsg = NULL; @@ -1176,9 +1176,9 @@ virNetDevAddRoute(const char *ifname, return ret; } -int virNetDevClearIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevClearIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) { virCommandPtr cmd = NULL; char *addrstr; diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 14d6a79..19e17cf 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -98,9 +98,9 @@ int virNetDevAddRoute(const char *ifname, unsigned int metric) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; -int virNetDevClearIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) +int virNetDevClearIPAddress(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; int virNetDevGetIPv4Address(const char *ifname, virSocketAddrPtr addr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -- 2.1.2

Vzctl man page says that --ipadd can be provided multiple times to add several IP addresses. Looping over the configured ip addresses to add one --ipadd for each. This would even handle the multiple IPs handled by openvz_conf.c --- src/openvz/openvz_driver.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 273bac1..68cc841 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -907,9 +907,13 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, virCommandAddArgBuffer(cmd, &buf); } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && net->nips > 0) { + size_t i; + /* --ipadd ip */ - char *ipStr = virSocketAddrFormat(&net->ips[0]->address); - virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); + for (i = 0; i < net->nips; i++) { + char *ipStr = virSocketAddrFormat(&net->ips[i]->address); + virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); + } } /* TODO: processing NAT and physical device */ -- 2.1.2

On Thursday 13 November 2014 10:33:15 Cédric Bosdonnat wrote:
Vzctl man page says that --ipadd can be provided multiple times to add several IP addresses. Looping over the configured ip addresses to add one --ipadd for each. This would even handle the multiple IPs handled by openvz_conf.c --- src/openvz/openvz_driver.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 273bac1..68cc841 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -907,9 +907,13 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid, virCommandAddArgBuffer(cmd, &buf); } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET && net->nips > 0) { + size_t i; + /* --ipadd ip */ - char *ipStr = virSocketAddrFormat(&net->ips[0]->address); - virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); + for (i = 0; i < net->nips; i++) { + char *ipStr = virSocketAddrFormat(&net->ips[i]->address); + virCommandAddArgList(cmd, "--ipadd", ipStr, NULL); + } }
/* TODO: processing NAT and physical device */
ACK -- Dmitry Guryanov

Drivers supporting one and only one IP address raise an error if more IP addresses are configured. --- src/vbox/vbox_common.c | 12 +++++++++--- src/xenconfig/xen_common.c | 12 ++++++++++-- src/xenconfig/xen_sxpr.c | 12 ++++++++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index fa1e12d..96d09f6 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1255,7 +1255,7 @@ vboxAttachSound(virDomainDefPtr def, IMachine *machine) VBOX_RELEASE(audioAdapter); } -static void +static int vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) { ISystemProperties *systemProperties = NULL; @@ -1307,10 +1307,14 @@ 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); - if (def->nets[i]->nips > 0) { + if (def->nets[i]->nips == 1) { char *ipStr = virSocketAddrFormat(&def->nets[i]->ips[0]->address); VIR_DEBUG("NIC(%zu): ipaddr: %s", i, ipStr); VIR_FREE(ipStr); + } else if (def->nets[i]->nips > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Driver does not support setting multiple IP addresses")); + return -1; } } @@ -1394,6 +1398,7 @@ vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) gVBoxAPI.UINetworkAdapter.SetMACAddress(adapter, MACAddress); VBOX_UTF16_FREE(MACAddress); } + return 0; } static void @@ -1939,7 +1944,8 @@ static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml) vboxSetBootDeviceOrder(def, data, machine); vboxAttachDrives(def, data, machine); vboxAttachSound(def, machine); - vboxAttachNetwork(def, data, machine); + if (vboxAttachNetwork(def, data, machine) < 0) + goto cleanup; vboxAttachSerial(def, data, machine); vboxAttachParallel(def, data, machine); vboxAttachVideo(def, machine); diff --git a/src/xenconfig/xen_common.c b/src/xenconfig/xen_common.c index bcb3bd3..77915eb 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -1222,10 +1222,14 @@ xenFormatNet(virConnectPtr conn, switch (net->type) { case VIR_DOMAIN_NET_TYPE_BRIDGE: virBufferAsprintf(&buf, ",bridge=%s", net->data.bridge.brname); - if (net->nips > 0) { + if (net->nips == 1) { char *ipStr = virSocketAddrFormat(&net->ips[0]->address); virBufferAsprintf(&buf, ",ip=%s", ipStr); VIR_FREE(ipStr); + } else if (net->nips > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Driver does not support setting multiple IP addresses")); + goto cleanup; } virBufferAsprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); break; @@ -1233,10 +1237,14 @@ xenFormatNet(virConnectPtr conn, case VIR_DOMAIN_NET_TYPE_ETHERNET: if (net->script) virBufferAsprintf(&buf, ",script=%s", net->script); - if (net->nips > 0) { + if (net->nips == 1) { char *ipStr = virSocketAddrFormat(&net->ips[0]->address); virBufferAsprintf(&buf, ",ip=%s", ipStr); VIR_FREE(ipStr); + } else if (net->nips > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Driver does not support setting multiple IP addresses")); + goto cleanup; } break; diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index caeb464..d3c1d9b 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -1898,10 +1898,14 @@ xenFormatSxprNet(virConnectPtr conn, script = def->script; virBufferEscapeSexpr(buf, "(script '%s')", script); - if (def->nips > 0) { + if (def->nips == 1) { char *ipStr = virSocketAddrFormat(&def->ips[0]->address); virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); VIR_FREE(ipStr); + } else if (def->nips > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Driver does not support setting multiple IP addresses")); + return -1; } break; @@ -1935,10 +1939,14 @@ xenFormatSxprNet(virConnectPtr conn, if (def->script) virBufferEscapeSexpr(buf, "(script '%s')", def->script); - if (def->nips > 0) { + if (def->nips == 1) { char *ipStr = virSocketAddrFormat(&def->ips[0]->address); virBufferEscapeSexpr(buf, "(ip '%s')", ipStr); VIR_FREE(ipStr); + } else if (def->nips > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Driver does not support setting multiple IP addresses")); + return -1; } break; -- 2.1.2
participants (4)
-
Cédric Bosdonnat
-
Daniel P. Berrange
-
Dmitry Guryanov
-
Laine Stump