[Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type

Maxim Samoylov posted 4 patches 7 years ago
[Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type
Posted by Maxim Samoylov 7 years ago
This allows forwarding TCP6 and UDP6 connections down to
netdev=user connected guests.

Signed-off-by: Maxim Samoylov <max7255@yandex-team.ru>
---
 hmp-commands.hx     |  31 ++++++++
 include/net/slirp.h |   2 +
 net/slirp.c         | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi/net.json       |   3 +-
 4 files changed, 249 insertions(+), 1 deletion(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index db0c681..b0e1a08 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1448,6 +1448,37 @@ STEXI
 Remove host-to-guest TCP or UDP redirection.
 ETEXI
 
+#ifdef CONFIG_SLIRP
+    {
+        .name       = "ipv6_hostfwd_add",
+        .args_type  = "arg1:s,arg2:s?,arg3:s?",
+        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport-guestaddr6:guestport",
+        .help       = "redirect TCP6 or UDP6 connections from host to guest (requires -net user)",
+        .cmd        = hmp_ipv6_hostfwd_add,
+    },
+#endif
+STEXI
+@item hostfwd_add
+@findex hostfwd_add
+Redirect TCP6 or UDP6 connections from host to guest (requires -net user).
+ETEXI
+
+#ifdef CONFIG_SLIRP
+    {
+        .name       = "ipv6_hostfwd_remove",
+        .args_type  = "arg1:s,arg2:s?,arg3:s?",
+        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport",
+        .help       = "remove host-to-guest TCP6 or UDP6 redirection",
+        .cmd        = hmp_ipv6_hostfwd_remove,
+    },
+
+#endif
+STEXI
+@item hostfwd_remove
+@findex hostfwd_remove
+Remove host-to-guest TCP6 or UDP6 redirection.
+ETEXI
+
     {
         .name       = "balloon",
         .args_type  = "value:M",
diff --git a/include/net/slirp.h b/include/net/slirp.h
index bad3e1e..4796a5c 100644
--- a/include/net/slirp.h
+++ b/include/net/slirp.h
@@ -29,6 +29,8 @@
 
 void hmp_hostfwd_add(Monitor *mon, const QDict *qdict);
 void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict);
+void hmp_ipv6_hostfwd_add(Monitor *mon, const QDict *qdict);
+void hmp_ipv6_hostfwd_remove(Monitor *mon, const QDict *qdict);
 
 void hmp_info_usernet(Monitor *mon, const QDict *qdict);
 
diff --git a/net/slirp.c b/net/slirp.c
index f6dc039..abe112b 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -67,6 +67,7 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 /* slirp network adapter */
 
 #define SLIRP_CFG_HOSTFWD 1
+#define SLIRP_CFG_IPV6_HOSTFWD 2
 
 struct slirp_config_str {
     struct slirp_config_str *next;
@@ -89,6 +90,8 @@ static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
     QTAILQ_HEAD_INITIALIZER(slirp_stacks);
 
 static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp);
+static int slirp_ipv6_hostfwd(SlirpState *s, const char *redir_str,
+                              Error **errp);
 static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp);
 
 #ifndef _WIN32
@@ -386,6 +389,10 @@ static int net_slirp_init(NetClientState *peer, const char *model,
             if (slirp_hostfwd(s, config->str, errp) < 0) {
                 goto error;
             }
+        } else if (config->flags & SLIRP_CFG_IPV6_HOSTFWD) {
+            if (slirp_ipv6_hostfwd(s, config->str, errp) < 0) {
+                goto error;
+            }
         } else {
             if (slirp_guestfwd(s, config->str, errp) < 0) {
                 goto error;
@@ -504,6 +511,73 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
     monitor_printf(mon, "invalid format\n");
 }
 
+void hmp_ipv6_hostfwd_remove(Monitor *mon, const QDict *qdict)
+{
+    struct in6_addr host_addr = in6addr_any;
+    int host_port;
+    char buf[256];
+    const char *src_str, *p;
+    SlirpState *s;
+    int is_udp = 0;
+    int err;
+    const char *arg1 = qdict_get_str(qdict, "arg1");
+    const char *arg2 = qdict_get_try_str(qdict, "arg2");
+    const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+    if (arg2) {
+        s = slirp_lookup(mon, arg1, arg2);
+        src_str = arg3;
+    } else {
+        s = slirp_lookup(mon, NULL, NULL);
+        src_str = arg1;
+    }
+    if (!s) {
+        return;
+    }
+
+    p = src_str;
+    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+        goto fail_syntax;
+    }
+
+    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+        is_udp = 0;
+    } else if (!strcmp(buf, "udp")) {
+        is_udp = 1;
+    } else {
+        goto fail_syntax;
+    }
+
+    if (*(p++) != '[') {
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ']') < 0) {
+        goto fail_syntax;
+    }
+
+    if (!inet_pton(AF_INET6, buf, &host_addr)) {
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+        goto fail_syntax;
+    }
+
+    if (qemu_strtoi(p, NULL, 10, &host_port) < 0) {
+        goto fail_syntax;
+    }
+
+    err = slirp_remove_ipv6_hostfwd(s->slirp, is_udp, host_addr, host_port);
+
+    monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
+                   err ? "not found" : "removed");
+    return;
+
+ fail_syntax:
+    monitor_printf(mon, "invalid format\n");
+}
+
 static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
 {
     struct in_addr host_addr = { .s_addr = INADDR_ANY };
@@ -577,6 +651,119 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
     return -1;
 }
 
+static int slirp_ipv6_hostfwd(SlirpState *s, const char *redir_str,
+                              Error **errp)
+{
+    struct in6_addr host_addr = in6addr_any;
+    struct in6_addr guest_addr;
+    int host_port, guest_port;
+    const char *p;
+    char buf[256];
+    int is_udp;
+    const char *end;
+    const char *fail_reason = "Unknown reason";
+
+    memset(&guest_addr, 0, sizeof(guest_addr));
+
+    p = redir_str;
+    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+        fail_reason = "No : separators";
+        goto fail_syntax;
+    }
+    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+        is_udp = 0;
+    } else if (!strcmp(buf, "udp")) {
+        is_udp = 1;
+    } else {
+        fail_reason = "Bad protocol name";
+        goto fail_syntax;
+    }
+
+    if (*(p++) != '[') {
+        fail_reason = "IPv6 address must be enclosed in square brackets";
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ']') < 0) {
+        fail_reason = "IPv6 address must be enclosed in square brackets";
+        goto fail_syntax;
+    }
+
+    if (!inet_pton(AF_INET6, buf, &host_addr)) {
+        fail_reason = "Bad host address";
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0 ||
+        buf[0] != '\0') {
+        fail_reason = "Bad ipv6 address and port separator";
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
+        fail_reason = "Bad host port separator";
+        goto fail_syntax;
+    }
+
+    if (qemu_strtoi(buf, &end, 0, &host_port)) {
+        fail_reason = "Bad host port";
+        goto fail_syntax;
+    }
+
+    if (*end != '\0' || host_port < 0 || host_port > 65535) {
+        fail_reason = "Host port out of range";
+        goto fail_syntax;
+    }
+
+    if (*p == '\0') {
+        fail_reason = "Missing guest ipv6 address";
+        goto fail_syntax;
+    }
+
+    if (*(p++) != '[') {
+        fail_reason = "IPv6 address must be enclosed in square brackets";
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p,  ']') < 0) {
+        fail_reason = "IPv6 address must be enclosed in square brackets";
+        goto fail_syntax;
+    }
+
+    if (!inet_pton(AF_INET6, buf, &guest_addr)) {
+        fail_reason = "Bad guest address";
+        goto fail_syntax;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0 || buf[0] != '\0') {
+        fail_reason = "Bad ipv6 address and port separator";
+        goto fail_syntax;
+    }
+
+    if (qemu_strtoi(p, &end, 0, &guest_port) < 0) {
+        fail_reason = "Bad guest port";
+        goto fail_syntax;
+    }
+
+    if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
+        fail_reason = "Guest port number out of range";
+        goto fail_syntax;
+    }
+
+    if (slirp_add_ipv6_hostfwd(s->slirp, is_udp, host_addr, host_port,
+                               guest_addr, guest_port) < 0) {
+        error_report("could not set up host forwarding rule '%s'",
+                     redir_str);
+        return -1;
+    }
+    return 0;
+
+ fail_syntax:
+    error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str,
+               fail_reason);
+    return -1;
+}
+
 void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
 {
     const char *redir_str;
@@ -604,6 +791,32 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
 
 }
 
+void hmp_ipv6_hostfwd_add(Monitor *mon, const QDict *qdict)
+{
+    const char *redir_str;
+    SlirpState *s;
+    const char *arg1 = qdict_get_str(qdict, "arg1");
+    const char *arg2 = qdict_get_try_str(qdict, "arg2");
+    const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+    if (arg2) {
+        s = slirp_lookup(mon, arg1, arg2);
+        redir_str = arg3;
+    } else if (arg2) {
+        s = slirp_lookup(mon, NULL, arg1);
+        redir_str = arg2;
+    } else {
+        s = slirp_lookup(mon, NULL, NULL);
+        redir_str = arg1;
+    }
+    if (s) {
+        Error *err = NULL;
+        if (slirp_ipv6_hostfwd(s, redir_str, &err) < 0) {
+            error_report_err(err);
+        }
+    }
+}
+
 #ifndef _WIN32
 
 /* automatic user mode samba server configuration */
@@ -906,6 +1119,7 @@ int net_init_slirp(const Netdev *netdev, const char *name,
     /* all optional fields are initialized to "all bits zero" */
 
     net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
+    net_init_slirp_configs(user->ipv6_hostfwd, SLIRP_CFG_IPV6_HOSTFWD);
     net_init_slirp_configs(user->guestfwd, 0);
 
     ret = net_slirp_init(peer, "user", name, user->q_restrict,
diff --git a/qapi/net.json b/qapi/net.json
index 8f99fd9..4f67caf 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -201,7 +201,8 @@
     '*smbserver': 'str',
     '*hostfwd':   ['String'],
     '*guestfwd':  ['String'],
-    '*tftp-server-name': 'str' } }
+    '*tftp-server-name': 'str',
+    '*ipv6-hostfwd': ['String']} }
 
 ##
 # @NetdevTapOptions:
-- 
2.7.4


Re: [Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type
Posted by Thomas Huth 7 years ago
On 2018-10-26 01:03, Maxim Samoylov wrote:
> This allows forwarding TCP6 and UDP6 connections down to
> netdev=user connected guests.
> 
> Signed-off-by: Maxim Samoylov <max7255@yandex-team.ru>
> ---
>  hmp-commands.hx     |  31 ++++++++
>  include/net/slirp.h |   2 +
>  net/slirp.c         | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  qapi/net.json       |   3 +-
>  4 files changed, 249 insertions(+), 1 deletion(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index db0c681..b0e1a08 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1448,6 +1448,37 @@ STEXI
>  Remove host-to-guest TCP or UDP redirection.
>  ETEXI
>  
> +#ifdef CONFIG_SLIRP
> +    {
> +        .name       = "ipv6_hostfwd_add",
> +        .args_type  = "arg1:s,arg2:s?,arg3:s?",
> +        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport-guestaddr6:guestport",
> +        .help       = "redirect TCP6 or UDP6 connections from host to guest (requires -net user)",
> +        .cmd        = hmp_ipv6_hostfwd_add,
> +    },
> +#endif
> +STEXI
> +@item hostfwd_add
> +@findex hostfwd_add
> +Redirect TCP6 or UDP6 connections from host to guest (requires -net user).
> +ETEXI
> +
> +#ifdef CONFIG_SLIRP
> +    {
> +        .name       = "ipv6_hostfwd_remove",
> +        .args_type  = "arg1:s,arg2:s?,arg3:s?",
> +        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport",
> +        .help       = "remove host-to-guest TCP6 or UDP6 redirection",
> +        .cmd        = hmp_ipv6_hostfwd_remove,
> +    },

 Hi,

could you please remove the "[hub_id name]" touple here? I recently sent
a patch to deprecate it for the IPv4 version, too:

https://lists.gnu.org/archive/html/qemu-devel/2018-09/msg03198.html

Also I think it would make sense if you mention in the help text that
IPv6 addresses have to be given with square brackets?

 Thanks,
  Thomas


Re: [Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type
Posted by Maxim Samoylov 7 years ago

On 26.10.2018 09:14, Thomas Huth wrote:
> On 2018-10-26 01:03, Maxim Samoylov wrote:
>> This allows forwarding TCP6 and UDP6 connections down to
>> netdev=user connected guests.
>>
>> Signed-off-by: Maxim Samoylov <max7255@yandex-team.ru>
>> ---
>>   hmp-commands.hx     |  31 ++++++++
>>   include/net/slirp.h |   2 +
>>   net/slirp.c         | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   qapi/net.json       |   3 +-
>>   4 files changed, 249 insertions(+), 1 deletion(-)
>>
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index db0c681..b0e1a08 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -1448,6 +1448,37 @@ STEXI
>>   Remove host-to-guest TCP or UDP redirection.
>>   ETEXI
>>   
>> +#ifdef CONFIG_SLIRP
>> +    {
>> +        .name       = "ipv6_hostfwd_add",
>> +        .args_type  = "arg1:s,arg2:s?,arg3:s?",
>> +        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport-guestaddr6:guestport",
>> +        .help       = "redirect TCP6 or UDP6 connections from host to guest (requires -net user)",
>> +        .cmd        = hmp_ipv6_hostfwd_add,
>> +    },
>> +#endif
>> +STEXI
>> +@item hostfwd_add
>> +@findex hostfwd_add
>> +Redirect TCP6 or UDP6 connections from host to guest (requires -net user).
>> +ETEXI
>> +
>> +#ifdef CONFIG_SLIRP
>> +    {
>> +        .name       = "ipv6_hostfwd_remove",
>> +        .args_type  = "arg1:s,arg2:s?,arg3:s?",
>> +        .params     = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr6]:hostport",
>> +        .help       = "remove host-to-guest TCP6 or UDP6 redirection",
>> +        .cmd        = hmp_ipv6_hostfwd_remove,
>> +    },
> 
>   Hi,
> 
> could you please remove the "[hub_id name]" touple here? I recently sent
> a patch to deprecate it for the IPv4 version, too:
> 
> https://lists.gnu.org/archive/html/qemu-devel/2018-09/msg03198.html
> 

Ok

> Also I think it would make sense if you mention in the help text that
> IPv6 addresses have to be given with square brackets?
> 
>   Thanks,
>    Thomas
>

Sure, missed that thing.

---
Maxim Samoylov, max7255@yandex-team.ru

Re: [Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type
Posted by Samuel Thibault 7 years ago
Maxim Samoylov, le ven. 26 oct. 2018 03:03:43 +0300, a ecrit:
> +void hmp_ipv6_hostfwd_remove(Monitor *mon, const QDict *qdict)
> +{

Similarly, a lot can be shared, by introducing 

const char *hmp_hostfwd_lookup(Monitor *mon, const QDict *qdict, int is_v6)

which will contain all the lookup and tcp/udp parsing part, and
just return the src_str (is_v6 to avoid testing only arg1).
hmp_hostfwd_remove and hmp_ipv6_hostfwd_remove can then just start by
calling it and get src_str that it can then use to parse the IPs.

> +    struct in6_addr host_addr = in6addr_any;
> +    if (*(p++) != '[') {
> +        goto fail_syntax;
> +    }

As mentioned, better explicit this in the helper message.

> +static int slirp_ipv6_hostfwd(SlirpState *s, const char *redir_str,
> +                              Error **errp)

For this one I wouldn't bother factorizing, though.

Samuel

Re: [Qemu-devel] [PATCH RFC 4/4] net/slirp: add ipv6-hostfwd option for user netdev type
Posted by Eric Blake 7 years ago
On 10/25/18 7:03 PM, Maxim Samoylov wrote:
> This allows forwarding TCP6 and UDP6 connections down to
> netdev=user connected guests.
> 
> Signed-off-by: Maxim Samoylov <max7255@yandex-team.ru>
> ---
>   hmp-commands.hx     |  31 ++++++++
>   include/net/slirp.h |   2 +
>   net/slirp.c         | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   qapi/net.json       |   3 +-
>   4 files changed, 249 insertions(+), 1 deletion(-)

> +++ b/qapi/net.json
> @@ -201,7 +201,8 @@
>       '*smbserver': 'str',
>       '*hostfwd':   ['String'],
>       '*guestfwd':  ['String'],
> -    '*tftp-server-name': 'str' } }
> +    '*tftp-server-name': 'str',
> +    '*ipv6-hostfwd': ['String']} }

Missing documentation of the new member.  Don't forget a '(since 3.1)' 
comment (if this is still appropriate for the current release; which may 
be doubtful since we are in soft freeze, in which case it will be 3.2 or 
4.0, depending on what the next release is numbered).

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