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,
-