[libvirt] netdev ethernet allow to specify ip address and routes

Allow to use ip address and routes elements inside netowrk type='ethernet'. Also add ability to create point to point device addresses. Signed-off-by: Vasiliy Tolstov <v.tolstov@selfip.ru> --- docs/schemas/interface.rng | 6 +++++ include/libvirt/libvirt-domain.h | 1 + src/conf/domain_conf.c | 14 ++++++++++- src/conf/domain_conf.h | 1 + src/conf/network_conf.c | 26 ++++++++++++++++++- src/conf/network_conf.h | 1 + src/lxc/lxc_container.c | 2 +- src/network/bridge_driver.c | 2 +- src/qemu/qemu_interface.c | 39 +++++++++++++++++++++++++++++ src/util/virnetdev.c | 54 ++++++++++++++++++++++++++++------------ src/util/virnetdev.h | 1 + 11 files changed, 127 insertions(+), 20 deletions(-) diff --git a/docs/schemas/interface.rng b/docs/schemas/interface.rng index 052703ce84aa..b81c38b9d07c 100644 --- a/docs/schemas/interface.rng +++ b/docs/schemas/interface.rng @@ -332,6 +332,9 @@ <optional> <attribute name="prefix"><ref name="ipv4Prefix"/></attribute> </optional> + <optional> + <attribute name="peer"><ref name="ipv4Addr"/></attribute> + </optional> </element> </zeroOrMore> <optional> @@ -361,6 +364,9 @@ <optional> <attribute name="prefix"><ref name="ipv6Prefix"/></attribute> </optional> + <optional> + <attribute name="peer"><ref name="ipv6Addr"/></attribute> + </optional> </element> </zeroOrMore> <optional> diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 7e06796c3c73..437e87fac01c 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3937,6 +3937,7 @@ typedef virDomainIPAddress *virDomainIPAddressPtr; struct _virDomainInterfaceIPAddress { int type; /* virIPAddrType */ char *addr; /* IP address */ + char *peer; /* IP peer */ unsigned int prefix; /* IP address prefix */ }; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d5d9ff702f42..34855233ad15 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5725,7 +5725,7 @@ virDomainNetIpParseXML(xmlNodePtr node) unsigned int prefixValue = 0; char *familyStr = NULL; int family = AF_UNSPEC; - char *address = NULL; + char *address = NULL, *peer = NULL; if (!(prefixStr = virXMLPropString(node, "prefix")) || (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { @@ -5739,6 +5739,9 @@ virDomainNetIpParseXML(xmlNodePtr node) goto cleanup; } + if ((peer = virXMLPropString(node, "peer")) == NULL) + VIR_DEBUG("Peer is empty"); + familyStr = virXMLPropString(node, "family"); if (familyStr && STREQ(familyStr, "ipv4")) family = AF_INET; @@ -5756,6 +5759,14 @@ virDomainNetIpParseXML(xmlNodePtr node) address); goto cleanup; } + + if ((peer != NULL) && (virSocketAddrParse(&ip->peer, peer, family) < 0)) { + virReportError(VIR_ERR_INVALID_ARG, + _("Failed to parse IP address: '%s'"), + peer); + goto cleanup; + } + ip->prefix = prefixValue; ret = ip; @@ -5765,6 +5776,7 @@ virDomainNetIpParseXML(xmlNodePtr node) VIR_FREE(prefixStr); VIR_FREE(familyStr); VIR_FREE(address); + VIR_FREE(peer); VIR_FREE(ip); return ret; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 83bdd67dec45..040882ec8460 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -513,6 +513,7 @@ typedef struct _virDomainNetIpDef virDomainNetIpDef; typedef virDomainNetIpDef *virDomainNetIpDefPtr; struct _virDomainNetIpDef { virSocketAddr address; /* ipv4 or ipv6 address */ + virSocketAddr peer; /* ipv4 or ipv6 address of peer */ unsigned int prefix; /* number of 1 bits in the net mask */ }; diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 4fb2e2a75b9d..1a0cf7fdcdd2 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1456,7 +1456,7 @@ virNetworkIPDefParseXML(const char *networkName, */ xmlNodePtr cur, save; - char *address = NULL, *netmask = NULL; + char *address = NULL, *netmask = NULL, *peer = NULL; unsigned long prefix = 0; int prefixRc; int result = -1; @@ -1502,6 +1502,14 @@ virNetworkIPDefParseXML(const char *networkName, else def->prefix = prefix; + peer = virXPathString("string(./@peer)", ctxt); + if (peer && (virSocketAddrParse(&def->peer, peer, AF_UNSPEC) < 0)) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid peer '%s' in network '%s'"), + peer, networkName); + goto cleanup; + } + /* validate address, etc. for each family */ if ((def->family == NULL) || (STREQ(def->family, "ipv4"))) { if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) || @@ -1531,6 +1539,14 @@ virNetworkIPDefParseXML(const char *networkName, prefix, networkName); goto cleanup; } + if (peer) { + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->peer, AF_INET)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Family 'ipv4' specified for non-IPv4 address '%s' in network '%s'"), + peer, networkName); + goto cleanup; + } + } } else if (STREQ(def->family, "ipv6")) { if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -1550,6 +1566,14 @@ virNetworkIPDefParseXML(const char *networkName, prefix, networkName); goto cleanup; } + if (peer) { + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->peer, AF_INET6)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Family 'ipv6' specified for non-IPv6 address '%s' in network '%s'"), + peer, networkName); + goto cleanup; + } + } } else { virReportError(VIR_ERR_XML_ERROR, _("Unrecognized family '%s' in network '%s'"), diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index b72257b970a8..9b8e807fda3c 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -152,6 +152,7 @@ struct _virNetworkIpDef { */ unsigned int prefix; /* ipv6 - only prefix allowed */ virSocketAddr netmask; /* ipv4 - either netmask or prefix specified */ + virSocketAddr peer; /* ipv4 or ipv6 peer address */ size_t nranges; /* Zero or more dhcp ranges */ virSocketAddrRangePtr ranges; diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 348bbfbc01fc..a1deb0c00d4c 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -520,7 +520,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, VIR_DEBUG("Adding IP address '%s/%u' to '%s'", ipStr, ip->prefix, newname); - if (virNetDevSetIPAddress(newname, &ip->address, prefix) < 0) { + if (virNetDevSetIPAddress(newname, &ip->address, &ip->peer, prefix) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Failed to set IP address '%s' on %s"), ipStr, newname); diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index a09a7e474fc5..f3ff88ff55a6 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1970,7 +1970,7 @@ networkAddAddrToBridge(virNetworkObjPtr network, } if (virNetDevSetIPAddress(network->def->bridge, - &ipdef->address, prefix) < 0) + &ipdef->address, &ipdef->peer, prefix) < 0) return -1; return 0; diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index 13a513152876..5729325fadb9 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -474,6 +474,45 @@ qemuInterfaceEthernetConnect(virDomainDefPtr def, if (virNetDevSetMAC(net->ifname, &tapmac) < 0) goto cleanup; + for (j = 0; j < net->nips; j++) { + virDomainNetIpDefPtr ip = net->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, net->ifname); + + if (virNetDevSetIPAddress(net->ifname, &ip->address, &ip->peer, prefix) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Failed to set IP address '%s' on %s"), + ipStr, net->ifname); + VIR_FREE(ipStr); + goto cleanup; + } + VIR_FREE(ipStr); + } + + if (net->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP || + net->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DEFAULT) { + if (virNetDevSetOnline(net->ifname, true) < 0) + goto cleanup; + + /* Set the routes */ + for (j = 0; j < net->nroutes; j++) { + virNetworkRouteDefPtr route = net->routes[j]; + + if (virNetDevAddRoute(net->ifname, + virNetworkRouteDefGetAddress(route), + virNetworkRouteDefGetPrefix(route), + virNetworkRouteDefGetGateway(route), + virNetworkRouteDefGetMetric(route)) < 0) { + goto cleanup; + } + } + } + + if (net->script && qemuExecuteEthernetScript(net->ifname, net->script) < 0) goto cleanup; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index aed50f546263..6e32ebbf6cee 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1039,21 +1039,28 @@ virNetDevCreateNetlinkAddressMessage(int messageType, const char *ifname, virSocketAddr *addr, unsigned int prefix, - virSocketAddr *broadcast) + virSocketAddr *broadcast, + virSocketAddr *peer) { struct nl_msg *nlmsg = NULL; struct ifaddrmsg ifa; unsigned int ifindex; void *addrData = NULL; + void *peerData = NULL; void *broadcastData = NULL; size_t addrDataLen; if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0) return NULL; - if (broadcast && virNetDevGetIPAddressBinary(broadcast, &broadcastData, - &addrDataLen) < 0) - return NULL; + if (peer && VIR_SOCKET_ADDR_VALID(peer)) { + if (virNetDevGetIPAddressBinary(peer, &peerData, &addrDataLen) < 0) + return NULL; + } else if (broadcast) { + if (virNetDevGetIPAddressBinary(broadcast, &broadcastData, + &addrDataLen) < 0) + return NULL; + } /* Get the interface index */ if ((ifindex = if_nametoindex(ifname)) == 0) @@ -1078,12 +1085,15 @@ virNetDevCreateNetlinkAddressMessage(int messageType, 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 (peerData) { + if (nla_put(nlmsg, IFA_ADDRESS, addrDataLen, peerData) < 0) + goto buffer_too_small; + } - if (broadcastData && - nla_put(nlmsg, IFA_BROADCAST, addrDataLen, broadcastData) < 0) - goto buffer_too_small; + if (broadcastData) { + if (nla_put(nlmsg, IFA_BROADCAST, addrDataLen, broadcastData) < 0) + goto buffer_too_small; + } return nlmsg; @@ -1098,6 +1108,7 @@ virNetDevCreateNetlinkAddressMessage(int messageType, * virNetDevSetIPAddress: * @ifname: the interface name * @addr: the IP address (IPv4 or IPv6) + * @peer: The IP address of peer (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask * * Add an IP address to an interface. This function *does not* remove @@ -1108,6 +1119,7 @@ virNetDevCreateNetlinkAddressMessage(int messageType, */ int virNetDevSetIPAddress(const char *ifname, virSocketAddr *addr, + virSocketAddr *peer, unsigned int prefix) { virSocketAddr *broadcast = NULL; @@ -1116,9 +1128,8 @@ int virNetDevSetIPAddress(const char *ifname, struct nlmsghdr *resp = NULL; unsigned int recvbuflen; - /* The caller needs to provide a correct address */ - if (VIR_SOCKET_ADDR_FAMILY(addr) == AF_INET) { + if (VIR_SOCKET_ADDR_FAMILY(addr) == AF_INET && !VIR_SOCKET_ADDR_VALID(peer)) { /* compute a broadcast address if this is IPv4 */ if (VIR_ALLOC(broadcast) < 0) return -1; @@ -1129,7 +1140,7 @@ int virNetDevSetIPAddress(const char *ifname, if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_NEWADDR, ifname, addr, prefix, - broadcast))) + broadcast, peer))) goto cleanup; if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, @@ -1288,7 +1299,7 @@ int virNetDevClearIPAddress(const char *ifname, if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_DELADDR, ifname, addr, prefix, - NULL))) + NULL, NULL))) goto cleanup; if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, @@ -1423,21 +1434,27 @@ virNetDevWaitDadFinish(virSocketAddrPtr *addrs, size_t count) int virNetDevSetIPAddress(const char *ifname, virSocketAddr *addr, + virSocketAddr *peer, unsigned int prefix) { virCommandPtr cmd = NULL; - char *addrstr = NULL, *bcaststr = NULL; + char *addrstr = NULL, *bcaststr = NULL, *peerstr = NULL; virSocketAddr broadcast; int ret = -1; if (!(addrstr = virSocketAddrFormat(addr))) goto cleanup; + + if (VIR_SOCKET_ADDR_VALID(peer) && !(peerstr = virSocketAddrFormat(&peer))) + goto cleanup; + /* format up a broadcast address if this is IPv4 */ - if ((VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) && + if (!peerstr && ((VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) && ((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) || - !(bcaststr = virSocketAddrFormat(&broadcast)))) { + !(bcaststr = virSocketAddrFormat(&broadcast))))) { goto cleanup; } + # ifdef IFCONFIG_PATH cmd = virCommandNew(IFCONFIG_PATH); virCommandAddArg(cmd, ifname); @@ -1446,6 +1463,8 @@ int virNetDevSetIPAddress(const char *ifname, else virCommandAddArg(cmd, "inet"); virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + if (peerstr) + virCommandAddArgList(cmd, "pointopoint", peerstr, NULL); if (bcaststr) virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); virCommandAddArg(cmd, "alias"); @@ -1453,6 +1472,8 @@ int virNetDevSetIPAddress(const char *ifname, cmd = virCommandNew(IP_PATH); virCommandAddArgList(cmd, "addr", "add", NULL); virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + if (peerstr) + virCommandAddArgList(cmd, "peer", peerstr, NULL); if (bcaststr) virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); virCommandAddArgList(cmd, "dev", ifname, NULL); @@ -1465,6 +1486,7 @@ int virNetDevSetIPAddress(const char *ifname, cleanup: VIR_FREE(addrstr); VIR_FREE(bcaststr); + VIR_FREE(peerstr); virCommandFree(cmd); return ret; } diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index e7719d58a410..240fff774d30 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -90,6 +90,7 @@ int virNetDevGetOnline(const char *ifname, int virNetDevSetIPAddress(const char *ifname, virSocketAddr *addr, + virSocketAddr *peer, unsigned int prefix) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; int virNetDevAddRoute(const char *ifname, -- 2.7.3

On 03/23/2016 11:35 AM, Vasiliy Tolstov wrote:
Allow to use ip address and routes elements inside netowrk type='ethernet'. Also add ability to create point to point device addresses.
Signed-off-by: Vasiliy Tolstov <v.tolstov@selfip.ru> --- docs/schemas/interface.rng | 6 +++++
I just mentioned elsewhere that this is the wrong file - interface.rng is the grammer for the XML that can be sent to netcf to configure host interfaces, and has nothing to do with the XML in either domain or libvirt network configuration.
include/libvirt/libvirt-domain.h | 1 + src/conf/domain_conf.c | 14 ++++++++++- src/conf/domain_conf.h | 1 + src/conf/network_conf.c | 26 ++++++++++++++++++- src/conf/network_conf.h | 1 +
Since there is no documentation included with the patch, and the wrong RNG file has been modified, I'm not clear on exactly why a libvirt virtual network would use a peer address. Normally libvirt networks are made by creating a bridge device, adding in some iptables rules, and running an instance of dnsmasq to service dhcp and dns requests made by guests who have tap devices connected to that network. But if I understand correctly, your patches are intended to allow setting the local and peer address for guest-connected tap devices that aren't attached to a bridge on the host side, but instead rely on the host's IP stack to route appropriate traffic through the tap device. If so, then why is a libvirt network involved at all? Why/how could a bridge device be used for a point-to-point link? If this isn't just a misunderstanding of which parts of libvirt code affect what, then some examples (and patches to formatdomain.html.in/formatnetwork.html.in) would be very useful to help me understand.

2016-03-23 20:46 GMT+03:00 Laine Stump <laine@laine.org>:
Since there is no documentation included with the patch, and the wrong RNG file has been modified, I'm not clear on exactly why a libvirt virtual network would use a peer address.
Normally libvirt networks are made by creating a bridge device, adding in some iptables rules, and running an instance of dnsmasq to service dhcp and dns requests made by guests who have tap devices connected to that network. But if I understand correctly, your patches are intended to allow setting the local and peer address for guest-connected tap devices that aren't attached to a bridge on the host side, but instead rely on the host's IP stack to route appropriate traffic through the tap device. If so, then why is a libvirt network involved at all? Why/how could a bridge device be used for a point-to-point link? If this isn't just a misunderstanding of which parts of libvirt code affect what, then some examples (and patches to formatdomain.html.in/formatnetwork.html.in) would be very useful to help me understand.
I'm send new patch with some docs in formatdomain. Sometimes bridges not allowed or not needed. We use plain tap devices on host side and bird routing daemon to route traffic to/from tap devices. -- Vasiliy Tolstov, e-mail: v.tolstov@selfip.ru

On 03/31/2016 05:23 AM, Vasiliy Tolstov wrote:
2016-03-23 20:46 GMT+03:00 Laine Stump <laine@laine.org>:
Since there is no documentation included with the patch, and the wrong RNG file has been modified, I'm not clear on exactly why a libvirt virtual network would use a peer address.
Normally libvirt networks are made by creating a bridge device, adding in some iptables rules, and running an instance of dnsmasq to service dhcp and dns requests made by guests who have tap devices connected to that network. But if I understand correctly, your patches are intended to allow setting the local and peer address for guest-connected tap devices that aren't attached to a bridge on the host side, but instead rely on the host's IP stack to route appropriate traffic through the tap device. If so, then why is a libvirt network involved at all? Why/how could a bridge device be used for a point-to-point link? If this isn't just a misunderstanding of which parts of libvirt code affect what, then some examples (and patches to formatdomain.html.in/formatnetwork.html.in) would be very useful to help me understand.
I'm send new patch with some docs in formatdomain. Sometimes bridges not allowed or not needed. We use plain tap devices on host side and bird routing daemon to route traffic to/from tap devices.
But the changes to network_conf.c, network_conf.h, and bridge_driver.c only serve to assign a POINTOPOINT IP address pair to *the bridge* of a libvirt-managed network. This is unrelated to any IP address pair assigned to a tap device used by an lxc or qemu domain. I'll comment further in your new patch.
participants (2)
-
Laine Stump
-
Vasiliy Tolstov