[libvirt] [PATCHv5 00/18] LXC IP configuration feature

Hi all, It's time for offering presents, so I'm sending you my updated patch series for IP configuration in LXC containers. The changes with v4 include: * Replacing the <gateway> element by a <route> one. * Addressing the comments you guys made I didn't forbid the use of iproute2 / ifconfig in containers if built without libnl, as I'm not sure about the possible consequences. Could it introduce regressions for some use cases/distros? Cédric Bosdonnat (18): Forgot to cleanup ifname_guest* in domain network def parsing virNetDevSetIPv4Address: libnl implementation Renamed virNetDevSetIPv4Address to virNetDevSetIPAddress virNetDevAddRoute: implementation using netlink virNetDevClearIPv4Address: netlink implementation Renamed virNetDevClearIPv4Address to virNetDevClearIPAddress Domain conf: allow more than one IP address for net devices IP doc 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 <route> element lxc conf2xml: convert lxc.network.ipv[46].gateway LXC: use the new net devices routes definition LXC: honour network devices link state Openvz --ipadd can be provided multiple times Report error if a driver can't handle multiple IP addresses docs/formatdomain.html.in | 46 +++ docs/schemas/domaincommon.rng | 75 ++++- src/conf/domain_conf.c | 320 ++++++++++++++++++-- src/conf/domain_conf.h | 29 +- src/libvirt_private.syms | 6 +- src/lxc/lxc_container.c | 56 +++- src/lxc/lxc_native.c | 202 +++++++++---- src/network/bridge_driver.c | 4 +- src/openvz/openvz_conf.c | 2 +- src/openvz/openvz_driver.c | 11 +- src/qemu/qemu_driver.c | 26 +- src/qemu/qemu_hotplug.c | 5 +- src/uml/uml_conf.c | 2 +- src/util/virnetdev.c | 332 ++++++++++++++++++--- src/util/virnetdev.h | 14 +- src/util/virnetlink.c | 38 +++ src/util/virnetlink.h | 2 + src/util/virsocketaddr.h | 4 + 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 | 4 + tests/openvzutilstest.c | 2 +- tests/sexpr2xmldata/sexpr2xml-bridge-ipaddr.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-net-routed.xml | 2 +- 30 files changed, 1107 insertions(+), 168 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 aafc05e..914faf9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7903,6 +7903,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

On Tue, Dec 30, 2014 at 11:27:10AM +0100, Cédric Bosdonnat wrote:
--- 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 aafc05e..914faf9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7903,6 +7903,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);
ACK 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 :|

Hi Daniel, Thanks a lot for your reviews. I just pushed the patch series with the IP Doc commit squashed. -- Cedric On Mon, 2015-01-05 at 17:26 +0000, Daniel P. Berrange wrote:
On Tue, Dec 30, 2014 at 11:27:10AM +0100, Cédric Bosdonnat wrote:
--- 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 aafc05e..914faf9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7903,6 +7903,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);
ACK
Regards, Daniel

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 aa776b4..65862ec 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1740,6 +1740,7 @@ virNetlinkEventServiceLocalPid; virNetlinkEventServiceStart; virNetlinkEventServiceStop; virNetlinkEventServiceStopAll; +virNetlinkGetErrorCode; virNetlinkShutdown; virNetlinkStartup; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 3514978..9dfaadc 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

On Tue, Dec 30, 2014 at 11:27:11AM +0100, Cédric Bosdonnat wrote:
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(-)
ACK 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 :|

On 12/30/2014 03:27 AM, Cédric Bosdonnat wrote:
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 aa776b4..65862ec 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1740,6 +1740,7 @@ virNetlinkEventServiceLocalPid; virNetlinkEventServiceStart; virNetlinkEventServiceStop; virNetlinkEventServiceStopAll; +virNetlinkGetErrorCode; virNetlinkShutdown; virNetlinkStartup;
This patch fails to build on mingw: CCLD libvirt.la Cannot export virNetlinkGetErrorCode: symbol not defined collect2: error: ld returned 1 exit status -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Renamed virNetDevSetIPv4Address as it also handles IPv6 addresses. --- src/libvirt_private.syms | 2 +- src/network/bridge_driver.c | 4 ++-- src/util/virnetdev.c | 14 +++++++------- src/util/virnetdev.h | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 65862ec..98f773d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1643,7 +1643,7 @@ virNetDevRxFilterFree; virNetDevRxFilterModeTypeFromString; virNetDevRxFilterModeTypeToString; virNetDevRxFilterNew; -virNetDevSetIPv4Address; +virNetDevSetIPAddress; virNetDevSetMAC; virNetDevSetMTU; virNetDevSetMTUFromDevice; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 29222d0..6055c71 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1899,8 +1899,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 9dfaadc..26e5fa4 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

On Tue, Dec 30, 2014 at 11:27:12AM +0100, Cédric Bosdonnat wrote:
Renamed virNetDevSetIPv4Address as it also handles IPv6 addresses. --- src/libvirt_private.syms | 2 +- src/network/bridge_driver.c | 4 ++-- src/util/virnetdev.c | 14 +++++++------- src/util/virnetdev.h | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-)
ACK 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/util/virnetdev.c | 105 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 26e5fa4..cbeba75 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

On Tue, Dec 30, 2014 at 11:27:13AM +0100, Cédric Bosdonnat wrote:
--- src/util/virnetdev.c | 105 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 15 deletions(-)
ACK 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/util/virnetdev.c | 60 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index cbeba75..0948e7b 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

On Tue, Dec 30, 2014 at 11:27:14AM +0100, Cédric Bosdonnat wrote:
--- src/util/virnetdev.c | 60 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-)
ACK 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 :|

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 98f773d..514c3d4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1618,7 +1618,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 0948e7b..64c14e0 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

On Tue, Dec 30, 2014 at 11:27:15AM +0100, Cédric Bosdonnat wrote:
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(-)
ACK 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 :|

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 | 26 ++++- 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, 239 insertions(+), 59 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 8fcbd29..9261f24 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4321,6 +4321,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 cd35485..b3e0940 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2311,14 +2311,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 914faf9..5a106d2 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1414,6 +1414,8 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def) void virDomainNetDefFree(virDomainNetDefPtr def) { + size_t i; + if (!def) return; @@ -1422,7 +1424,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: @@ -1443,7 +1444,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: @@ -1471,6 +1471,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); @@ -4770,6 +4774,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, as some old config may not have a prefix + VIR_DEBUG("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, @@ -7208,6 +7264,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 @@ -7255,6 +7336,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; @@ -7343,11 +7427,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"); @@ -7547,10 +7634,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: @@ -7562,10 +7645,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: @@ -7662,6 +7741,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; @@ -7924,6 +8008,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(linkstate); VIR_FREE(addrtype); VIR_FREE(trustGuestRxFilters); + VIR_FREE(ips); virNWFilterHashTableFree(filterparams); return def; @@ -17042,6 +17127,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, @@ -17274,7 +17383,6 @@ virDomainActualNetDefContentsFormat(virBufferPtr buf, return 0; } - /* virDomainActualNetDefFormat() - format the ActualNetDef * info inside an <actual> element, as required for internal storage * of domain status @@ -17511,9 +17619,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: @@ -17531,10 +17636,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: @@ -17582,6 +17683,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 57297cd..7d177f9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -478,6 +478,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 { @@ -941,7 +948,6 @@ struct _virDomainNetDef { union { struct { char *dev; - char *ipaddr; } ethernet; virDomainChrSourceDefPtr vhostuser; struct { @@ -963,7 +969,6 @@ struct _virDomainNetDef { } network; struct { char *brname; - char *ipaddr; } bridge; struct { char *name; @@ -993,6 +998,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 @@ -2554,6 +2561,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 514c3d4..fb5d003 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -322,6 +322,7 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainNetAppendIpAddress; virDomainNetDefFormat; virDomainNetDefFree; virDomainNetFind; diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index 4b918c0..f955dda 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 d9e27a1..e45ed7d 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -853,7 +853,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); @@ -904,9 +904,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 73a825d..1596d2b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6345,6 +6345,8 @@ static char *qemuConnectDomainXMLToNative(virConnectPtr conn, (brname = virDomainNetGetActualBridgeName(net))) { char *brnamecopy; + size_t j; + if (VIR_STRDUP(brnamecopy, brname) < 0) goto cleanup; @@ -6355,20 +6357,29 @@ 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)); @@ -6376,18 +6387,23 @@ 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 7f93b9b..5d0d8e0 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2043,8 +2043,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 7a5d62b..c4ad889 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -174,7 +174,7 @@ umlBuildCommandLineNet(virConnectPtr conn, virBufferAddLit(&buf, "tuntap,"); 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 e3d89f3..07efe3d 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1306,7 +1306,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 25bdf26..e8ae5fa 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -920,12 +920,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) @@ -1223,16 +1220,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 d8783e9..a1c342b 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 f5a8d12..1065555 100644 --- a/tests/openvzutilstest.c +++ b/tests/openvzutilstest.c @@ -93,7 +93,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 Tue, Dec 30, 2014 at 11:27:16AM +0100, 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. --- 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 | 26 ++++- 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, 239 insertions(+), 59 deletions(-)
ACK 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 :|

--- docs/formatdomain.html.in | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9261f24..a17cd8b 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4328,18 +4328,19 @@ qemu-kvm -net nic,model=? /dev/null <interface type='network'> <source network='default'/> <target dev='vnet0'/> - <b><ip address='192.168.122.5' prefix='24'/></b> + <b><ip family='ipv4' 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 + <span class="since">Since 1.2.12</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> + will simply ignore them or only use the first one. The <code>family</code> + attribute can be set to either <code>ipv4</code> or <code>ipv6</code>, the + <code>address</code> attribute holds the IP address. The <code>prefix</code> is not mandatory since some hypervisors do not handle it. </p> -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:17AM +0100, Cédric Bosdonnat wrote:
--- docs/formatdomain.html.in | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9261f24..a17cd8b 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4328,18 +4328,19 @@ qemu-kvm -net nic,model=? /dev/null <interface type='network'> <source network='default'/> <target dev='vnet0'/> - <b><ip address='192.168.122.5' prefix='24'/></b> + <b><ip family='ipv4' 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 + <span class="since">Since 1.2.12</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> + will simply ignore them or only use the first one. The <code>family</code> + attribute can be set to either <code>ipv4</code> or <code>ipv6</code>, the + <code>address</code> attribute holds the IP address. The <code>prefix</code> is not mandatory since some hypervisors do not handle it. </p>
ACK, but this should be squashed into the previous patch before pushing to GIT 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 | 20 +++++++++++++++++++- src/util/virsocketaddr.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 1b9e2f2..6152df8 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -495,7 +495,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, char **veths) { int rc = 0; - size_t i; + size_t i, j; char *newname = NULL; virDomainNetDefPtr netDef; bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] == @@ -516,6 +516,24 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out; + for (j = 0; j < netDef->nips; j++) { + virDomainNetIpDefPtr ip = netDef->ips[j]; + unsigned int prefix = (ip->prefix > 0) ? ip->prefix : + VIR_SOCKET_ADDR_DEFAULT_PREFIX; + char *ipStr = virSocketAddrFormat(&ip->address); + + VIR_DEBUG("Adding IP address '%s/%u' to '%s'", + ipStr, ip->prefix, newname); + if (virNetDevSetIPAddress(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) diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index 053855b..ba8c912 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -54,6 +54,8 @@ typedef struct { # define VIR_SOCKET_ADDR_FAMILY(s) \ ((s)->data.sa.sa_family) +# define VIR_SOCKET_ADDR_DEFAULT_PREFIX 24 + typedef virSocketAddr *virSocketAddrPtr; typedef struct _virSocketAddrRange virSocketAddrRange; -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:18AM +0100, Cédric Bosdonnat wrote:
Uses the new virDomainNetDef ips to set the IP addresses on the network interfaces in the container. --- src/lxc/lxc_container.c | 20 +++++++++++++++++++- src/util/virsocketaddr.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-)
ACK 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 | 144 +++++++++++++++--------- tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 2 + 3 files changed, 97 insertions(+), 51 deletions(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index 41e069f..3c8f928 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -413,79 +413,83 @@ 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 +498,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 +516,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 +534,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 +578,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 +601,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

On Tue, Dec 30, 2014 at 11:27:19AM +0100, Cédric Bosdonnat wrote:
--- src/lxc/lxc_native.c | 144 +++++++++++++++--------- tests/lxcconf2xmldata/lxcconf2xml-simple.config | 2 + tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 2 + 3 files changed, 97 insertions(+), 51 deletions(-)
ACK 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 :|

--- docs/formatdomain.html.in | 8 ++++++++ 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, 70 insertions(+), 4 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index a17cd8b..3f203a5 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4330,6 +4330,14 @@ qemu-kvm -net nic,model=? /dev/null <target dev='vnet0'/> <b><ip family='ipv4' address='192.168.122.5' prefix='24'/></b> </interface> + ... + <hostdev mode='capabilities' type='net'> + <source> + <interface>eth0</interface> + </source> + <b><ip family='ipv4' address='192.168.122.6' prefix='24'/></b> + </hostdev> + </devices> ... </pre> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index b3e0940..c12aedf 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3808,11 +3808,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 5a106d2..9447ed6 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1818,6 +1818,8 @@ virDomainHostdevSubsysSCSIiSCSIClear(virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc void virDomainHostdevDefClear(virDomainHostdevDefPtr def) { + size_t i; + if (!def) return; @@ -1842,6 +1844,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; @@ -4833,6 +4838,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 @@ -4887,6 +4894,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, @@ -4896,6 +4923,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } ret = 0; error: + VIR_FREE(ipnodes); return ret; } @@ -17302,6 +17330,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 7d177f9..9292613 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -498,6 +498,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

On Tue, Dec 30, 2014 at 11:27:20AM +0100, Cédric Bosdonnat wrote:
--- docs/formatdomain.html.in | 8 ++++++++ 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, 70 insertions(+), 4 deletions(-)
ACK 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 | 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 3c8f928..edc5682 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

On Tue, Dec 30, 2014 at 11:27:21AM +0100, Cédric Bosdonnat wrote:
--- src/lxc/lxc_native.c | 3 +++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.config | 2 ++ tests/lxcconf2xmldata/lxcconf2xml-physnetwork.xml | 2 ++ 3 files changed, 7 insertions(+)
ACK 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 :|

Network interfaces devices and host devices with net capabilities can now have IPv4 and/or an IPv6 routes configured. --- docs/formatdomain.html.in | 19 ++++- docs/schemas/domaincommon.rng | 31 ++++++++ src/conf/domain_conf.c | 135 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 12 ++++ src/util/virnetdev.c | 31 +++++++- src/util/virnetdev.h | 2 +- src/util/virsocketaddr.h | 2 + tests/lxcxml2xmldata/lxc-hostdev.xml | 2 + tests/lxcxml2xmldata/lxc-idmap.xml | 2 + 9 files changed, 230 insertions(+), 6 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3f203a5..499879e 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4328,14 +4328,18 @@ qemu-kvm -net nic,model=? /dev/null <interface type='network'> <source network='default'/> <target dev='vnet0'/> - <b><ip family='ipv4' address='192.168.122.5' prefix='24'/></b> + <b><ip address='192.168.122.5' prefix='24'/></b> + <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b> + <b><route family='ipv4' via='192.168.122.1'/></b> </interface> ... <hostdev mode='capabilities' type='net'> <source> <interface>eth0</interface> </source> - <b><ip family='ipv4' address='192.168.122.6' prefix='24'/></b> + <b><ip address='192.168.122.6' prefix='24'/></b> + <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b> + <b><route family='ipv4' via='192.168.122.1'/></b> </hostdev> </devices> @@ -4352,6 +4356,17 @@ 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.12</span> route elements can also be added + to define the network routes to use for the network device. This element + has a <code>family</code> attribute set either to <code>ipv4</code> or + <code>ipv6</code>, a mandatory <code>via</code> attribute defining the + IP address to route throught and optional <code>address</code> and <code>prefix</code> + attributes defining the target network range. If those aren't given, then + a default route will be set. + 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 c12aedf..879e064 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2329,6 +2329,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="route"> + <ref name="route"/> + </element> + </zeroOrMore> <optional> <element name="script"> <attribute name="path"> @@ -3597,6 +3602,27 @@ </element> </define> + <define name="route"> + <interleave> + <attribute name="family"> + <ref name="addr-family"/> + </attribute> + <attribute name="via"> + <ref name="ipAddr"/> + </attribute> + <optional> + <attribute name="address"> + <ref name="ipAddr"/> + </attribute> + </optional> + <optional> + <attribute name="prefix"> + <ref name="ipPrefix"/> + </attribute> + </optional> + </interleave> + </define> + <define name="hostdev"> <element name="hostdev"> <interleave> @@ -3832,6 +3858,11 @@ <empty/> </element> </zeroOrMore> + <zeroOrMore> + <element name="route"> + <ref name="route"/> + </element> + </zeroOrMore> </interleave> </define> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9447ed6..b9858cd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1475,7 +1475,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def->ips[i]); VIR_FREE(def->ips); - virDomainDeviceInfoClear(&def->info); + for (i = 0; i < def->nroutes; i++) + VIR_FREE(def->routes[i]); + VIR_FREE(def->routes); + + virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); virNWFilterHashTableFree(def->filterparams); @@ -1847,6 +1851,9 @@ 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); + for (i = 0; i < def->source.caps.u.net.nroutes; i++) + VIR_FREE(def->source.caps.u.net.routes[i]); + VIR_FREE(def->source.caps.u.net.routes); break; } break; @@ -4831,6 +4838,64 @@ virDomainNetIpParseXML(xmlNodePtr node) return NULL; } +static virDomainNetRouteDefPtr +virDomainNetRouteParse(xmlNodePtr node) +{ + virDomainNetRouteDefPtr route = NULL; + char *familyStr = NULL; + int family = AF_UNSPEC; + char *via = NULL; + char *to = NULL; + char *prefixStr = NULL; + + to = virXMLPropString(node, "address"); + if (!(via = virXMLPropString(node, "via"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Missing route 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(via); + + if (VIR_ALLOC(route) < 0) + goto error; + + if (virSocketAddrParse(&route->via, via, family) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Failed to parse IP address: '%s'"), + via); + goto error; + } + + if (to && virSocketAddrParse(&route->to, to, family) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Failed to parse IP address: '%s'"), + to); + goto error; + } + + if (!(prefixStr = virXMLPropString(node, "prefix")) || + (virStrToLong_ui(prefixStr, NULL, 10, &route->prefix) < 0)) { + } + + return route; + + error: + VIR_FREE(familyStr); + VIR_FREE(via); + VIR_FREE(to); + VIR_FREE(prefixStr); + VIR_FREE(route); + + return NULL; +} + static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, @@ -4840,6 +4905,8 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr sourcenode; xmlNodePtr *ipnodes = NULL; int nipnodes; + xmlNodePtr *routenodes = NULL; + int nroutenodes; int ret = -1; /* @type is passed in from the caller rather than read from the @@ -4914,6 +4981,26 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, } } } + + /* Look for possible gateways */ + if ((nroutenodes = virXPathNodeSet("./route", ctxt, &routenodes)) < 0) + goto error; + + if (nroutenodes) { + size_t i; + for (i = 0; i < nroutenodes; i++) { + virDomainNetRouteDefPtr route = virDomainNetRouteParse(routenodes[i]); + + if (!route) + goto error; + + if (VIR_APPEND_ELEMENT(def->source.caps.u.net.routes, + def->source.caps.u.net.nroutes, route) < 0) { + VIR_FREE(route); + goto error; + } + } + } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4924,6 +5011,7 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, ret = 0; error: VIR_FREE(ipnodes); + VIR_FREE(routenodes); return ret; } @@ -7367,6 +7455,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, size_t i; size_t nips = 0; virDomainNetIpDefPtr *ips = NULL; + size_t nroutes = 0; + virDomainNetRouteDefPtr *routes = NULL; if (VIR_ALLOC(def) < 0) return NULL; @@ -7463,6 +7553,13 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) goto error; + } else if (xmlStrEqual(cur->name, BAD_CAST "route")) { + virDomainNetRouteDefPtr route = NULL; + if (!(route = virDomainNetRouteParse(cur))) + goto error; + + if (VIR_APPEND_ELEMENT(routes, nroutes, route) < 0) + goto error; } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); @@ -7773,6 +7870,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0) goto error; } + def->nroutes = nroutes; + def->routes = routes; if (script != NULL) { def->script = script; @@ -17179,6 +17278,37 @@ virDomainNetIpsFormat(virBufferPtr buf, virDomainNetIpDefPtr *ips, size_t nips) } } +static void +virDomainNetRoutesFormat(virBufferPtr buf, + virDomainNetRouteDefPtr *routes, + size_t nroutes) +{ + size_t i; + + for (i = 0; i < nroutes; i++) { + virDomainNetRouteDefPtr route = routes[i]; + const char *familyStr = NULL; + char *via = virSocketAddrFormat(&route->via); + char *to = NULL; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&route->via, AF_INET6)) + familyStr = "ipv6"; + else if (VIR_SOCKET_ADDR_IS_FAMILY(&route->via, AF_INET)) + familyStr = "ipv4"; + virBufferAsprintf(buf, "<route family='%s' via='%s'", familyStr, via); + + if (VIR_SOCKET_ADDR_VALID(&route->to)) { + to = virSocketAddrFormat(&route->to); + virBufferAsprintf(buf, " address='%s'", to); + } + + if (route->prefix > 0) + virBufferAsprintf(buf, " prefix='%d'", route->prefix); + + virBufferAddLit(buf, "/>\n"); + } +} + static int virDomainHostdevDefFormatSubsys(virBufferPtr buf, virDomainHostdevDefPtr def, @@ -17334,6 +17464,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); + virDomainNetRoutesFormat(buf, def->source.caps.u.net.routes, + def->source.caps.u.net.nroutes); } return 0; @@ -17718,6 +17850,7 @@ virDomainNetDefFormat(virBufferPtr buf, } virDomainNetIpsFormat(buf, def->ips, def->nips); + virDomainNetRoutesFormat(buf, def->routes, def->nroutes); virBufferEscapeString(buf, "<script path='%s'/>\n", def->script); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9292613..ac1f4f8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -485,6 +485,14 @@ struct _virDomainNetIpDef { unsigned int prefix; /* number of 1 bits in the net mask */ }; +typedef struct _virDomainNetRouteDef virDomainNetRouteDef; +typedef virDomainNetRouteDef *virDomainNetRouteDefPtr; +struct _virDomainNetRouteDef { + virSocketAddr via; + virSocketAddr to; + unsigned int prefix; +}; + typedef struct _virDomainHostdevCaps virDomainHostdevCaps; typedef virDomainHostdevCaps *virDomainHostdevCapsPtr; struct _virDomainHostdevCaps { @@ -500,6 +508,8 @@ struct _virDomainHostdevCaps { char *iface; size_t nips; virDomainNetIpDefPtr *ips; + size_t nroutes; + virDomainNetRouteDefPtr *routes; } net; } u; }; @@ -1002,6 +1012,8 @@ struct _virDomainNetDef { int linkstate; size_t nips; virDomainNetIpDefPtr *ips; + size_t nroutes; + virDomainNetRouteDefPtr *routes; }; /* Used for prefix of ifname of any network name generated dynamically diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 64c14e0..ef96b2b 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -992,8 +992,33 @@ virNetDevAddRoute(const char *ifname, void *addrData = NULL; size_t addrDataLen; int errCode; + virSocketAddr defaultAddr; + virSocketAddrPtr actualAddr; + char *toStr = NULL; + char *viaStr = NULL; + + actualAddr = addr; + + /* If we have no valid network address, then use the default one */ + if (!addr || !VIR_SOCKET_ADDR_VALID(addr)) { + VIR_DEBUG("computing default address"); + int family = VIR_SOCKET_ADDR_FAMILY(gateway); + if (family == AF_INET) { + if (virSocketAddrParseIPv4(&defaultAddr, VIR_SOCKET_ADDR_IPV4_ALL) < 0) + goto cleanup; + } else { + if (virSocketAddrParseIPv6(&defaultAddr, VIR_SOCKET_ADDR_IPV6_ALL) < 0) + goto cleanup; + } + + actualAddr = &defaultAddr; + } + + toStr = virSocketAddrFormat(actualAddr); + viaStr = virSocketAddrFormat(gateway); + VIR_DEBUG("Adding route %s/%d via %s", toStr, prefix, viaStr); - if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0 || + if (virNetDevGetIPAddressBinary(actualAddr, &addrData, &addrDataLen) < 0 || virNetDevGetIPAddressBinary(gateway, &gatewayData, &addrDataLen) < 0) goto cleanup; @@ -1010,7 +1035,7 @@ virNetDevAddRoute(const char *ifname, memset(&rtmsg, 0, sizeof(rtmsg)); - rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(addr); + rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(gateway); rtmsg.rtm_table = RT_TABLE_MAIN; rtmsg.rtm_scope = RT_SCOPE_UNIVERSE; rtmsg.rtm_protocol = RTPROT_BOOT; @@ -1043,6 +1068,8 @@ virNetDevAddRoute(const char *ifname, ret = 0; cleanup: + VIR_FREE(toStr); + VIR_FREE(viaStr); nlmsg_free(nlmsg); return ret; diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 19e17cf..fb7988f 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -96,7 +96,7 @@ int virNetDevAddRoute(const char *ifname, unsigned int prefix, virSocketAddrPtr gateway, unsigned int metric) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; int virNetDevClearIPAddress(const char *ifname, virSocketAddr *addr, diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index ba8c912..99ab46f 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -55,6 +55,8 @@ typedef struct { ((s)->data.sa.sa_family) # define VIR_SOCKET_ADDR_DEFAULT_PREFIX 24 +# define VIR_SOCKET_ADDR_IPV4_ALL "0.0.0.0" +# define VIR_SOCKET_ADDR_IPV6_ALL "::" typedef virSocketAddr *virSocketAddrPtr; diff --git a/tests/lxcxml2xmldata/lxc-hostdev.xml b/tests/lxcxml2xmldata/lxc-hostdev.xml index 0596789..61e8655 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'/> + <route family='ipv4' via='192.168.122.1'/> + <route family='ipv6' via='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..2b04a65 100644 --- a/tests/lxcxml2xmldata/lxc-idmap.xml +++ b/tests/lxcxml2xmldata/lxc-idmap.xml @@ -30,6 +30,8 @@ <source bridge='bri0'/> <ip address='192.168.122.12' family='ipv4' prefix='24'/> <ip address='192.168.122.13' family='ipv4' prefix='24'/> + <route family='ipv4' via='192.168.122.1'/> + <route family='ipv4' via='192.168.124.1' address='192.168.124.0' prefix='24'/> <target dev='veth0'/> <guest dev='eth2'/> </interface> -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:22AM +0100, Cédric Bosdonnat wrote:
Network interfaces devices and host devices with net capabilities can now have IPv4 and/or an IPv6 routes configured. --- docs/formatdomain.html.in | 19 ++++- docs/schemas/domaincommon.rng | 31 ++++++++ src/conf/domain_conf.c | 135 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 12 ++++ src/util/virnetdev.c | 31 +++++++- src/util/virnetdev.h | 2 +- src/util/virsocketaddr.h | 2 + tests/lxcxml2xmldata/lxc-hostdev.xml | 2 + tests/lxcxml2xmldata/lxc-idmap.xml | 2 + 9 files changed, 230 insertions(+), 6 deletions(-)
ACK 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 :|

On 12/30/2014 05:27 AM, Cédric Bosdonnat wrote:
Network interfaces devices and host devices with net capabilities can now have IPv4 and/or an IPv6 routes configured. --- docs/formatdomain.html.in | 19 ++++- docs/schemas/domaincommon.rng | 31 ++++++++ src/conf/domain_conf.c | 135 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 12 ++++ src/util/virnetdev.c | 31 +++++++- src/util/virnetdev.h | 2 +- src/util/virsocketaddr.h | 2 + tests/lxcxml2xmldata/lxc-hostdev.xml | 2 + tests/lxcxml2xmldata/lxc-idmap.xml | 2 + 9 files changed, 230 insertions(+), 6 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3f203a5..499879e 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4328,14 +4328,18 @@ qemu-kvm -net nic,model=? /dev/null <interface type='network'> <source network='default'/> <target dev='vnet0'/> - <b><ip family='ipv4' address='192.168.122.5' prefix='24'/></b> + <b><ip address='192.168.122.5' prefix='24'/></b> + <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b> + <b><route family='ipv4' via='192.168.122.1'/></b>
Sorry I didn't see this before - we already have an XML specification for a <route> element in libvirt networks, and had decided that the gateway should be called, well, "gateway" instead of "via". It also includes a "metric" attribute. I think these two <route> elements should share their syntax, RNG, and parse/format functions. Fortunately we haven't yet done a release with this new functionality, so such a change is still possible. Shall I send a patch for this?

On Tue, Jan 06, 2015 at 01:41:34PM -0500, Laine Stump wrote:
On 12/30/2014 05:27 AM, Cédric Bosdonnat wrote:
Network interfaces devices and host devices with net capabilities can now have IPv4 and/or an IPv6 routes configured. --- docs/formatdomain.html.in | 19 ++++- docs/schemas/domaincommon.rng | 31 ++++++++ src/conf/domain_conf.c | 135 ++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 12 ++++ src/util/virnetdev.c | 31 +++++++- src/util/virnetdev.h | 2 +- src/util/virsocketaddr.h | 2 + tests/lxcxml2xmldata/lxc-hostdev.xml | 2 + tests/lxcxml2xmldata/lxc-idmap.xml | 2 + 9 files changed, 230 insertions(+), 6 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3f203a5..499879e 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -4328,14 +4328,18 @@ qemu-kvm -net nic,model=? /dev/null <interface type='network'> <source network='default'/> <target dev='vnet0'/> - <b><ip family='ipv4' address='192.168.122.5' prefix='24'/></b> + <b><ip address='192.168.122.5' prefix='24'/></b> + <b><route family='ipv4' address='192.168.122.0' prefix='24' via='192.168.122.1'/></b> + <b><route family='ipv4' via='192.168.122.1'/></b>
Sorry I didn't see this before - we already have an XML specification for a <route> element in libvirt networks, and had decided that the gateway should be called, well, "gateway" instead of "via". It also includes a "metric" attribute. I think these two <route> elements should share their syntax, RNG, and parse/format functions. Fortunately we haven't yet done a release with this new functionality, so such a change is still possible.
Shall I send a patch for this?
Sure, sounds like a reasonable idea to keep things consistent. 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 | 57 +++++++++++++++++++++- .../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, 64 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index edc5682..d7cd1d5 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -424,11 +424,37 @@ typedef struct { char *name; virDomainNetIpDefPtr *ips; size_t nips; + char *gateway_ipv4; + char *gateway_ipv6; bool privnet; size_t networks; } lxcNetworkParseData; static int +lxcAddNetworkRouteDefinition(const char *address, + int family, + virDomainNetRouteDefPtr **routes, + size_t *nroutes) +{ + virDomainNetRouteDefPtr route = NULL; + + if (VIR_ALLOC(route) < 0) + goto error; + + if (virSocketAddrParse(&route->via, address, family) < 0) + goto error; + + if (VIR_APPEND_ELEMENT(*routes, *nroutes, route) < 0) + goto error; + + return 0; + + error: + VIR_FREE(route); + return -1; +} + +static int lxcAddNetworkDefinition(lxcNetworkParseData *data) { virDomainNetDefPtr net = NULL; @@ -465,6 +491,18 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) hostdev->source.caps.u.net.ips = data->ips; hostdev->source.caps.u.net.nips = data->nips; + if (data->gateway_ipv4 && + lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET, + &hostdev->source.caps.u.net.routes, + &hostdev->source.caps.u.net.nroutes) < 0) + goto error; + + if (data->gateway_ipv6 && + lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6, + &hostdev->source.caps.u.net.routes, + &hostdev->source.caps.u.net.nroutes) < 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 +515,18 @@ lxcAddNetworkDefinition(lxcNetworkParseData *data) net->ips = data->ips; net->nips = data->nips; + if (data->gateway_ipv4 && + lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET, + &net->routes, + &net->nroutes) < 0) + goto error; + + if (data->gateway_ipv6 && + lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6, + &net->routes, + &net->nroutes) < 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; @@ -568,6 +618,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, @@ -584,7 +638,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..d2cec8f 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'/> + <route family='ipv4' via='192.168.122.1'/> + <route family='ipv6' via='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..b1210e5 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'/> + <route family='ipv4' via='192.168.122.1'/> + <route family='ipv6' via='2003:db8:1:0:214:1234:fe0b:3595'/> <guest dev='eth0'/> <link state='up'/> </interface> -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:23AM +0100, Cédric Bosdonnat wrote:
--- src/lxc/lxc_native.c | 57 +++++++++++++++++++++- .../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, 64 insertions(+), 1 deletion(-)
ACK 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 :|

Actually set routes in lxc containers if there are defined ones. --- src/lxc/lxc_container.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 6152df8..55096fb 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -497,6 +497,8 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, int rc = 0; size_t i, j; char *newname = NULL; + char *toStr = NULL; + char *viaStr = NULL; virDomainNetDefPtr netDef; bool privNet = vmDef->features[VIR_DOMAIN_FEATURE_PRIVNET] == VIR_TRISTATE_SWITCH_ON; @@ -539,6 +541,28 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, if (rc < 0) goto error_out; + /* Set the routes */ + for (j = 0; j < netDef->nroutes; j++) { + virDomainNetRouteDefPtr route = netDef->routes[j]; + if (VIR_SOCKET_ADDR_VALID(&route->to)) + toStr = virSocketAddrFormat(&route->to); + else + if (VIR_STRDUP(toStr, "default") < 0) + goto error_out; + viaStr = virSocketAddrFormat(&route->via); + VIR_DEBUG("Adding route %s/%d via %s", toStr, route->prefix, viaStr); + + if (virNetDevAddRoute(newname, &route->to, route->prefix, + &route->via, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to add route %s/%d via %s"), + toStr, route->prefix, viaStr); + goto error_out; + } + VIR_FREE(toStr); + VIR_FREE(viaStr); + } + VIR_FREE(newname); } @@ -547,6 +571,8 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, rc = virNetDevSetOnline("lo", true); error_out: + VIR_FREE(toStr); + VIR_FREE(viaStr); VIR_FREE(newname); return rc; } -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:24AM +0100, Cédric Bosdonnat wrote:
Actually set routes in lxc containers if there are defined ones. --- src/lxc/lxc_container.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
ACK 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 :|

Don't activate LXC network device if <link state='down'/> has been set in its configuration. --- src/lxc/lxc_container.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 55096fb..380d136 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -536,31 +536,33 @@ 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 routes */ - for (j = 0; j < netDef->nroutes; j++) { - virDomainNetRouteDefPtr route = netDef->routes[j]; - if (VIR_SOCKET_ADDR_VALID(&route->to)) - toStr = virSocketAddrFormat(&route->to); - else - if (VIR_STRDUP(toStr, "default") < 0) + /* Set the routes */ + for (j = 0; j < netDef->nroutes; j++) { + virDomainNetRouteDefPtr route = netDef->routes[j]; + if (VIR_SOCKET_ADDR_VALID(&route->to)) + toStr = virSocketAddrFormat(&route->to); + else + if (VIR_STRDUP(toStr, "default") < 0) + goto error_out; + viaStr = virSocketAddrFormat(&route->via); + VIR_DEBUG("Adding route %s/%d via %s", toStr, route->prefix, viaStr); + + if (virNetDevAddRoute(newname, &route->to, route->prefix, + &route->via, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to add route %s/%d via %s"), + toStr, route->prefix, viaStr); goto error_out; - viaStr = virSocketAddrFormat(&route->via); - VIR_DEBUG("Adding route %s/%d via %s", toStr, route->prefix, viaStr); - - if (virNetDevAddRoute(newname, &route->to, route->prefix, - &route->via, 0) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Failed to add route %s/%d via %s"), - toStr, route->prefix, viaStr); - goto error_out; + } + VIR_FREE(toStr); + VIR_FREE(viaStr); } - VIR_FREE(toStr); - VIR_FREE(viaStr); } VIR_FREE(newname); -- 2.1.2

On Tue, Dec 30, 2014 at 11:27:25AM +0100, Cédric Bosdonnat wrote:
Don't activate LXC network device if <link state='down'/> has been set in its configuration. --- src/lxc/lxc_container.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-)
ACK 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 :|

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 e45ed7d..c144eca 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -905,9 +905,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 Tue, Dec 30, 2014 at 11:27:26AM +0100, 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(-)
ACK 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 :|

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 07efe3d..7887a8b 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1254,7 +1254,7 @@ vboxAttachSound(virDomainDefPtr def, IMachine *machine) VBOX_RELEASE(audioAdapter); } -static void +static int vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) { ISystemProperties *systemProperties = NULL; @@ -1306,10 +1306,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; } } @@ -1393,6 +1397,7 @@ vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) gVBoxAPI.UINetworkAdapter.SetMACAddress(adapter, MACAddress); VBOX_UTF16_FREE(MACAddress); } + return 0; } static void @@ -1938,7 +1943,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 e8ae5fa..e622cb1 100644 --- a/src/xenconfig/xen_common.c +++ b/src/xenconfig/xen_common.c @@ -1220,10 +1220,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; @@ -1231,10 +1235,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 a1c342b..554d28f 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

On Tue, Dec 30, 2014 at 11:27:27AM +0100, Cédric Bosdonnat wrote:
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(-)
ACK 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 :|
participants (5)
-
Cedric Bosdonnat
-
Cédric Bosdonnat
-
Daniel P. Berrange
-
Eric Blake
-
Laine Stump