Re: [Qemu-devel] [PATCH 2/2 v3] slirp: Add classless static routes support to DHCP server

2018-03-19 Thread Eric Blake

On 03/14/2018 02:08 PM, Benjamin Drung wrote:

This patch will allow the user to specify classless static routes for
the replies from the built-in DHCP server, for example:


This mail was sent as a 2/2, but with no In-Reply-To: header pointing to 
a 0/2 cover letter.  Am I missing something?




   qemu --net user,route=10.0.2.0/24,route=192.168.0.0/16 [...]

The QMP schema for the "route" option is ['str'], because the opts
visitor code only supports lists with a single mandatory scalar member.
The option "ipv6-net" takes two pieces and is split in net_client_init()
into "ipv6-prefix" and "ipv6-prefixlen" before calling
visit_type_Netdev()/visit_type_NetLegacy(). But that logic cannot be
used for the "route" option, because there is no intermediate format to
store the split parts for further processing without overhauling the
visitor code. Once the opts visitor supports lists with multiple
members, please update the QMP schema to:

{ 'struct': 'RouteEntry',
   'data': {
 'subnet': 'str',
 'mask_width': 'uint8',
 '*gateway': 'str' } }
[...]
'route': [ 'RouteEntry' ]


Sadly, once the QMP is released, we don't want to be changing it  (other 
than perhaps by use of an 'alternate' that allows both old and new 
styles in parallel), so that we don't break clients expecting the 
original way to work.  It may be better to bite the bullet and fix the 
command line parser to do what we need (Markus has been doing lots of 
work in this area already, so it may just be a matter of waiting for him 
to return to work from his leave).


I'm also afraid that since this patch wasn't picked up in any of the 
pull requests for soft freeze, but at the same time it feels like a new 
feature, that it's better delaying it to 2.13 anyways.  (But that may be 
a good thing, if it lets us fix the problems with the command line 
parsing limitations)




+++ b/qapi/net.json
@@ -163,6 +163,9 @@
  # @domainname: guest-visible domain name of the virtual nameserver
  #  (since 2.12)
  #
+# @route: guest-visible static classless route of the virtual nameserver
+# (since 2.12)


If this still takes a straight string instead of a proper struct, then 
you at least need to document the syntax supported in that string.  And 
again, I'm thinking 2.12 is going to be premature, so your next spin of 
this patch should use 2.13.


--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



[Qemu-devel] [PATCH 2/2 v3] slirp: Add classless static routes support to DHCP server

2018-03-14 Thread Benjamin Drung
This patch will allow the user to specify classless static routes for
the replies from the built-in DHCP server, for example:

  qemu --net user,route=10.0.2.0/24,route=192.168.0.0/16 [...]

The QMP schema for the "route" option is ['str'], because the opts
visitor code only supports lists with a single mandatory scalar member.
The option "ipv6-net" takes two pieces and is split in net_client_init()
into "ipv6-prefix" and "ipv6-prefixlen" before calling
visit_type_Netdev()/visit_type_NetLegacy(). But that logic cannot be
used for the "route" option, because there is no intermediate format to
store the split parts for further processing without overhauling the
visitor code. Once the opts visitor supports lists with multiple
members, please update the QMP schema to:

{ 'struct': 'RouteEntry',
  'data': {
'subnet': 'str',
'mask_width': 'uint8',
'*gateway': 'str' } }
[...]
'route': [ 'RouteEntry' ]

Signed-off-by: Benjamin Drung 
---
v2: Address all remarks from Samuel Thibault:
 * Initialize values directly before usage
 * Make :gateway part optional
 * change formula for significant octet calculation
 * do not calculate significant_octets/option_length twice

v3:
 * change QMP member 'route' from ['String'] to ['str']
 * use '--net' in the documentation

 net/slirp.c  | 68 +---
 qapi/net.json|  4 
 qemu-options.hx  | 14 +++-
 slirp/bootp.c| 20 +
 slirp/bootp.h|  2 ++
 slirp/libslirp.h |  9 +++-
 slirp/slirp.c|  7 +-
 slirp/slirp.h|  2 ++
 8 files changed, 120 insertions(+), 6 deletions(-)

diff --git a/net/slirp.c b/net/slirp.c
index 8c08e5644f..6611eb257f 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -158,7 +158,7 @@ static int net_slirp_init(NetClientState *peer, const char 
*model,
   const char *vnameserver, const char *vnameserver6,
   const char *smb_export, const char *vsmbserver,
   const char **dnssearch, const char *vdomainname,
-  Error **errp)
+  const strList *vroutes, Error **errp)
 {
 /* default settings according to historic slirp */
 struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
@@ -177,8 +177,12 @@ static int net_slirp_init(NetClientState *peer, const char 
*model,
 char buf[20];
 uint32_t addr;
 int shift;
+unsigned int i;
 char *end;
+unsigned int route_count;
 struct slirp_config_str *config;
+struct StaticRoute *routes = NULL;
+const strList *iter;
 
 if (!ipv4 && (vnetwork || vhost || vnameserver)) {
 error_setg(errp, "IPv4 disabled but netmask/host/dns provided");
@@ -365,6 +369,61 @@ static int net_slirp_init(NetClientState *peer, const char 
*model,
 return -1;
 }
 
+iter = vroutes;
+route_count = 0;
+while (iter) {
+route_count++;
+iter = iter->next;
+}
+routes = g_malloc(route_count * sizeof(StaticRoute));
+
+i = 0;
+iter = vroutes;
+while(iter != NULL) {
+char buf2[20];
+const char *gateway = iter->value;
+const char *mask;
+char *end;
+long mask_width;
+
+// Split "subnet/mask[:gateway]" into its components
+if (get_str_sep(buf2, sizeof(buf2), , ':') < 0) {
+// Fallback to default gateway
+routes[i].gateway.s_addr = host.s_addr;
+mask = gateway;
+} else {
+if (!inet_aton(gateway, [i].gateway)) {
+error_setg(errp, "Failed to parse route gateway '%s'", 
gateway);
+return -1;
+}
+mask = buf2;
+}
+
+if (get_str_sep(buf, sizeof(buf), , '/') < 0) {
+error_setg(errp, "Failed to parse route: No slash found in '%s'",
+   mask);
+return -1;
+}
+if (!inet_aton(buf, [i].subnet)) {
+error_setg(errp, "Failed to parse route subnet '%s'", buf);
+return -1;
+}
+
+mask_width = strtol(mask, , 10);
+if (*end != '\0') {
+error_setg(errp,
+   "Failed to parse netmask '%s' (trailing chars)", mask);
+return -1;
+} else if (mask_width < 0 || mask_width > 32) {
+error_setg(errp,
+   "Invalid netmask provided (must be in range 0-32)");
+return -1;
+}
+routes[i].mask_width = (uint8_t)mask_width;
+
+iter = iter->next;
+i++;
+}
 
 nc = qemu_new_net_client(_slirp_info, peer, model, name);
 
@@ -377,7 +436,8 @@ static int net_slirp_init(NetClientState *peer, const char 
*model,
 s->slirp = slirp_init(restricted, ipv4, net, mask, host,
   ipv6, ip6_prefix, vprefix6_len, ip6_host,
   vhostname, tftp_export, bootfile, dhcp,
-