From: Laine Stump <[email protected]>

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 <[email protected]>
---
 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

Reply via email to