From: Laine Stump <laine@redhat.com> libvirt's <interface> element has for a long time supported adding <route> sub-elements to specify arbitrary routes to be added to the guest OS networking, but historically this has only worked for LXC guests. If you tried to add <route> to the interface of a QEMU guest, it would be rejected. passt networking doesn't support setting *any arbitrary* route but it does support setting a default route (using the passt commandline "--gateway" parameter). A default route is really just a "route with unspecified destination/prefix", so a default route can be specified in libvirt XML with: <route gateway='192.168.0.1'/> Attempts to give a specified destination, prefix, or metric will result in a validation error. Resolves: https://issues.redhat.com/browse/RHEL-46602 Signed-off-by: Laine Stump <laine@redhat.com> --- src/qemu/qemu_passt.c | 16 +++++++++++++ src/qemu/qemu_validate.c | 50 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c index 56d048d585..125227747d 100644 --- a/src/qemu/qemu_passt.c +++ b/src/qemu/qemu_passt.c @@ -263,6 +263,22 @@ qemuPasstBuildCommand(char **socketName, } } + /* Add default route(s) */ + for (i = 0; i < net->guestIP.nroutes; i++) { + const virNetDevIPRoute *route = net->guestIP.routes[i]; + g_autofree char *gateway = NULL; + + if (!(gateway = virSocketAddrFormat(&route->gateway))) + return NULL; + + /* validation has already guaranteed that there is at most 1 + * IPv4 and 1 IPv6 route, and that they are only default + * routes (i.e. destination 0.0.0.0/0) + */ + + virCommandAddArgList(cmd, "--gateway", gateway, NULL); + } + /* Add port forwarding info */ for (i = 0; i < net->nPortForwards; i++) { diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 1c6662751b..c194a8a7cd 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1884,6 +1884,8 @@ qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net, { bool hasV4Addr = false; bool hasV6Addr = false; + bool hasV4Route = false; + bool hasV6Route = false; size_t i; if (net->type == VIR_DOMAIN_NET_TYPE_USER || @@ -1958,10 +1960,50 @@ qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net, } } - if (net->guestIP.nroutes) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Invalid attempt to set network interface guest-side IP route, not supported by QEMU")); - return -1; + + for (i = 0; i < net->guestIP.nroutes; i++) { + const virNetDevIPRoute *route = net->guestIP.routes[i]; + + if (net->backend.type != VIR_DOMAIN_NET_BACKEND_PASST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Invalid attempt to set network interface guest-side IP route, not supported for this interface type/backend")); + return -1; + } + + switch (VIR_SOCKET_ADDR_FAMILY(&route->gateway)) { + case AF_INET: + if (hasV4Route) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only one IPv4 default route can be specified for an interface using the passt backend")); + return -1; + } + hasV4Route = true; + break; + case AF_INET6: + if (hasV6Route) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only one IPv6 default route can be specified for an interface using the passt backend")); + return -1; + } + hasV6Route = true; + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("All <route> elements of an interface using the passt backend must be default routes, with an IPv4 or IPv6 gateway specified")); + return -1; + } + + /* the only type of route that can be specified for passt is + * the default route, so none of the parameters except gateway + * are acceptable + */ + if (VIR_SOCKET_ADDR_VALID(&route->address) || + virNetDevIPRouteGetPrefix(route) != 0 || + route->has_metric) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("<route> elements of an interface using the passt backend must be default routes, with only a gateway specified")); + return -1; + } } if (net->type == VIR_DOMAIN_NET_TYPE_VDPA) { -- 2.52.0