All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add ipv6 hostfwd support
@ 2021-01-21  8:23 dje--- via
  2021-01-21  8:23 ` [PATCH 1/2] slirp: " dje--- via
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: dje--- via @ 2021-01-21  8:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Samuel Thibault, Doug Evans

Hi. This patchset takes the original patch from Maxim,
https://www.mail-archive.com/qemu-devel@nongnu.org/msg569573.html
and updates it.

The first patch is the slirp additions, the second patch adds the u/i.

Doug Evans (2):
  slirp: Add ipv6 hostfwd support
  net: Add ipv6_hostfwd option

Slirp:
 src/libslirp.h |   6 +++
 src/slirp.c    |  51 +++++++++++++++++++++--
 src/socket.c   | 109 +++++++++++++++++++++++++++++++++++--------------
 src/socket.h   |   9 ++++
 src/udp.c      |  73 ++++++++++++++++++++++++++-------
 src/udp.h      |   2 +
 6 files changed, 202 insertions(+), 48 deletions(-)

QEMU:
 hmp-commands.hx     |  29 ++++-
 include/net/slirp.h |   2 +
 net/slirp.c         | 311 +++++++++++++++++++++++++++++++++++---------
 qapi/net.json       |   1 +
 slirp               |   2 +-
 5 files changed, 279 insertions(+), 66 deletions(-)

-- 
2.30.0.296.g2bfb1c46d8-goog



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] slirp: Add ipv6 hostfwd support
  2021-01-21  8:23 [PATCH 0/2] Add ipv6 hostfwd support dje--- via
@ 2021-01-21  8:23 ` dje--- via
  2021-01-21  8:23 ` [PATCH 2/2] net: Add ipv6_hostfwd option dje--- via
  2021-01-21  9:41 ` [PATCH 0/2] Add ipv6 hostfwd support Marc-André Lureau
  2 siblings, 0 replies; 7+ messages in thread
From: dje--- via @ 2021-01-21  8:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Samuel Thibault, Doug Evans

Signed-off-by: Doug Evans <dje@google.com>
---
 src/libslirp.h |   6 +++
 src/slirp.c    |  51 +++++++++++++++++++++--
 src/socket.c   | 109 +++++++++++++++++++++++++++++++++++--------------
 src/socket.h   |   9 ++++
 src/udp.c      |  73 ++++++++++++++++++++++++++-------
 src/udp.h      |   2 +
 6 files changed, 202 insertions(+), 48 deletions(-)

diff --git a/src/libslirp.h b/src/libslirp.h
index 27e1f61..280d3d0 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -142,6 +142,12 @@ int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
                       int host_port, struct in_addr guest_addr, int guest_port);
 int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
                          int host_port);
+int slirp_add_ipv6_hostfwd(Slirp *slirp, int is_udp,
+                           struct in6_addr host_addr, int host_port,
+                           struct in6_addr guest_addr, int guest_port);
+int slirp_remove_ipv6_hostfwd(Slirp *slirp, int is_udp,
+                              struct in6_addr host_addr, int host_port);
+
 int slirp_add_exec(Slirp *slirp, const char *cmdline,
                    struct in_addr *guest_addr, int guest_port);
 int slirp_add_unix(Slirp *slirp, const char *unixsock,
diff --git a/src/slirp.c b/src/slirp.c
index abb6f9a..a07ef83 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -1091,7 +1091,6 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
 }
 
 /* Drop host forwarding rule, return 0 if found. */
-/* TODO: IPv6 */
 int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
                          int host_port)
 {
@@ -1105,7 +1104,10 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
         addr_len = sizeof(addr);
         if ((so->so_state & SS_HOSTFWD) &&
             getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
-            addr.sin_addr.s_addr == host_addr.s_addr && addr.sin_port == port) {
+            addr_len == sizeof(addr) &&
+            addr.sin_family == AF_INET &&
+            addr.sin_addr.s_addr == host_addr.s_addr &&
+            addr.sin_port == port) {
             so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
             closesocket(so->s);
             sofree(so);
@@ -1116,7 +1118,6 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
     return -1;
 }
 
-/* TODO: IPv6 */
 int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
                       int host_port, struct in_addr guest_addr, int guest_port)
 {
@@ -1135,6 +1136,50 @@ int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
     return 0;
 }
 
+int slirp_remove_ipv6_hostfwd(Slirp *slirp, int is_udp,
+                              struct in6_addr host_addr, int host_port)
+{
+    struct socket *so;
+    struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb);
+    struct sockaddr_in6 addr;
+    int port = htons(host_port);
+    socklen_t addr_len;
+
+    for (so = head->so_next; so != head; so = so->so_next) {
+        addr_len = sizeof(addr);
+        if ((so->so_state & SS_HOSTFWD) &&
+            getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
+            addr_len == sizeof(addr) &&
+            addr.sin6_family == AF_INET6 &&
+            !memcmp(&addr.sin6_addr, &host_addr, sizeof(host_addr)) &&
+            addr.sin6_port == port) {
+            so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
+            closesocket(so->s);
+            sofree(so);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+int slirp_add_ipv6_hostfwd(Slirp *slirp, int is_udp,
+                          struct in6_addr host_addr, int host_port,
+                          struct in6_addr guest_addr, int guest_port)
+{
+    if (is_udp) {
+        if (!udp6_listen(slirp, host_addr, htons(host_port),
+                         guest_addr, htons(guest_port), SS_HOSTFWD))
+            return -1;
+    } else {
+        if (!tcp6_listen(slirp, host_addr, htons(host_port),
+                         guest_addr, htons(guest_port), SS_HOSTFWD))
+            return -1;
+    }
+
+    return 0;
+}
+
 /* TODO: IPv6 */
 static bool check_guestfwd(Slirp *slirp, struct in_addr *guest_addr,
                            int guest_port)
diff --git a/src/socket.c b/src/socket.c
index c0b02ad..46be587 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -736,22 +736,18 @@ int sosendto(struct socket *so, struct mbuf *m)
 /*
  * Listen for incoming TCP connections
  */
-struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
-                          uint32_t laddr, unsigned lport, int flags)
+static struct socket *tcpx_listen(Slirp *slirp, int family,
+                                  in4or6_addr haddr, unsigned hport,
+                                  in4or6_addr laddr, unsigned lport,
+                                  int flags)
 {
-    /* TODO: IPv6 */
-    struct sockaddr_in addr;
+    union {
+        struct sockaddr_in addr4;
+        struct sockaddr_in6 addr6;
+    } addr;
     struct socket *so;
     int s, opt = 1;
     socklen_t addrlen = sizeof(addr);
-    memset(&addr, 0, addrlen);
-
-    DEBUG_CALL("tcp_listen");
-    DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){ .s_addr = haddr }));
-    DEBUG_ARG("hport = %d", ntohs(hport));
-    DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){ .s_addr = laddr }));
-    DEBUG_ARG("lport = %d", ntohs(lport));
-    DEBUG_ARG("flags = %x", flags);
 
     so = socreate(slirp);
 
@@ -770,20 +766,35 @@ struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
 
     so->so_state &= SS_PERSISTENT_MASK;
     so->so_state |= (SS_FACCEPTCONN | flags);
-    so->so_lfamily = AF_INET;
-    so->so_lport = lport; /* Kept in network format */
-    so->so_laddr.s_addr = laddr; /* Ditto */
+    so->so_lfamily = family;
+    /* Address,port are kept in network format */
+    if (family == AF_INET) {
+        so->so_laddr.s_addr = laddr.addr4;
+        so->so_lport = lport;
+    } else {
+        so->so_laddr6 = laddr.addr6;
+        so->so_lport6 = lport;
+    }
 
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = haddr;
-    addr.sin_port = hport;
+    memset(&addr, 0, addrlen);
+    if (family == AF_INET) {
+        addr.addr4.sin_family = family;
+        addr.addr4.sin_addr.s_addr = haddr.addr4;
+        addr.addr4.sin_port = hport;
+        addrlen = sizeof(addr.addr4);
+    } else {
+        addr.addr6.sin6_family = family;
+        addr.addr6.sin6_addr = haddr.addr6;
+        addr.addr6.sin6_port = hport;
+        addrlen = sizeof(addr.addr6);
+    }
 
-    if (((s = slirp_socket(AF_INET, SOCK_STREAM, 0)) < 0) ||
+    s = slirp_socket(family, SOCK_STREAM, 0);
+    if ((s < 0) ||
         (slirp_socket_set_fast_reuse(s) < 0) ||
-        (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) ||
+        (bind(s, (struct sockaddr *)&addr, addrlen) < 0) ||
         (listen(s, 1) < 0)) {
         int tmperrno = errno; /* Don't clobber the real reason we failed */
-
         if (s >= 0) {
             closesocket(s);
         }
@@ -797,22 +808,60 @@ struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
         return NULL;
     }
     setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
-    opt = 1;
-    setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int));
+    slirp_socket_set_nodelay(s);
 
     getsockname(s, (struct sockaddr *)&addr, &addrlen);
-    so->so_ffamily = AF_INET;
-    so->so_fport = addr.sin_port;
-    if (addr.sin_addr.s_addr == 0 ||
-        addr.sin_addr.s_addr == loopback_addr.s_addr)
-        so->so_faddr = slirp->vhost_addr;
-    else
-        so->so_faddr = addr.sin_addr;
+    if (family == AF_INET) {
+        so->fhost.sin = addr.addr4;
+    } else {
+        so->fhost.sin6 = addr.addr6;
+    }
+    sotranslate_accept(so);
 
     so->s = s;
     return so;
 }
 
+struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
+                          uint32_t laddr, unsigned lport, int flags)
+{
+    DEBUG_CALL("tcp_listen");
+    DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){ .s_addr = haddr }));
+    DEBUG_ARG("hport = %d", ntohs(hport));
+    DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){ .s_addr = laddr }));
+    DEBUG_ARG("lport = %d", ntohs(lport));
+    DEBUG_ARG("flags = %x", flags);
+
+    in4or6_addr haddr4, laddr4;
+
+    haddr4.addr4 = haddr;
+    laddr4.addr4 = laddr;
+    return tcpx_listen(slirp, AF_INET, haddr4, hport, laddr4, lport, flags);
+}
+
+struct socket *
+tcp6_listen(Slirp *slirp, struct in6_addr haddr, u_int hport,
+            struct in6_addr laddr, u_int lport, int flags)
+{
+    DEBUG_CALL("tcp6_listen");
+    char addrstr[INET6_ADDRSTRLEN];
+    const char *str = inet_ntop(AF_INET6, &haddr, addrstr, INET6_ADDRSTRLEN);
+    g_assert(str != NULL);
+    DEBUG_ARG("haddr = %s", str);
+    DEBUG_ARG("hport = %d", ntohs(hport));
+    str = inet_ntop(AF_INET6, &laddr, addrstr, INET6_ADDRSTRLEN);
+    g_assert(str != NULL);
+    DEBUG_ARG("laddr = %s", str);
+    DEBUG_ARG("lport = %d", ntohs(lport));
+    DEBUG_ARG("flags = %x", flags);
+
+    in4or6_addr haddr6, laddr6;
+
+    haddr6.addr6 = haddr;
+    laddr6.addr6 = laddr;
+    return tcpx_listen(slirp, AF_INET6, haddr6, hport, laddr6, lport, flags);
+}
+
 /*
  * Various session state calls
  * XXX Should be #define's
diff --git a/src/socket.h b/src/socket.h
index a6a1e5e..425c607 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -11,6 +11,13 @@
 #define SO_EXPIRE 240000
 #define SO_EXPIREFAST 10000
 
+/* Helps unify some in/in6 routines. */
+union in4or6_addr {
+    uint32_t addr4;
+    struct in6_addr addr6;
+};
+typedef union in4or6_addr in4or6_addr;
+
 /*
  * Our socket structure
  */
@@ -148,6 +155,8 @@ int sowrite(struct socket *);
 void sorecvfrom(struct socket *);
 int sosendto(struct socket *, struct mbuf *);
 struct socket *tcp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int);
+struct socket *tcp6_listen(Slirp *, struct in6_addr, u_int,
+                           struct in6_addr, u_int, int);
 void soisfconnecting(register struct socket *);
 void soisfconnected(register struct socket *);
 void sofwdrain(struct socket *);
diff --git a/src/udp.c b/src/udp.c
index 050cee4..52502b9 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -353,17 +353,20 @@ static uint8_t udp_tos(struct socket *so)
     return 0;
 }
 
-struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
-                          uint32_t laddr, unsigned lport, int flags)
+static struct socket *udpx_listen(Slirp *slirp, int family,
+                                  in4or6_addr haddr, unsigned hport,
+                                  in4or6_addr laddr, unsigned lport,
+                                  int flags)
 {
-    /* TODO: IPv6 */
-    struct sockaddr_in addr;
+    union {
+        struct sockaddr_in addr4;
+        struct sockaddr_in6 addr6;
+    } addr;
     struct socket *so;
-    socklen_t addrlen = sizeof(struct sockaddr_in);
+    socklen_t addrlen;
 
-    memset(&addr, 0, sizeof(addr));
     so = socreate(slirp);
-    so->s = slirp_socket(AF_INET, SOCK_DGRAM, 0);
+    so->s = slirp_socket(family, SOCK_DGRAM, 0);
     if (so->s < 0) {
         sofree(so);
         return NULL;
@@ -371,9 +374,18 @@ struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
     so->so_expire = curtime + SO_EXPIRE;
     insque(so, &slirp->udb);
 
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = haddr;
-    addr.sin_port = hport;
+    memset(&addr, 0, sizeof(addr));
+    if (family == AF_INET) {
+        addr.addr4.sin_family = family;
+        addr.addr4.sin_addr.s_addr = haddr.addr4;
+        addr.addr4.sin_port = hport;
+        addrlen = sizeof(addr.addr4);
+    } else {
+        addr.addr6.sin6_family = family;
+        addr.addr6.sin6_addr = haddr.addr6;
+        addr.addr6.sin6_port = hport;
+        addrlen = sizeof(addr.addr6);
+    }
 
     if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) {
         udp_detach(so);
@@ -382,16 +394,47 @@ struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
     slirp_socket_set_fast_reuse(so->s);
 
     getsockname(so->s, (struct sockaddr *)&addr, &addrlen);
-    so->fhost.sin = addr;
+    if (family == AF_INET) {
+        so->fhost.sin = addr.addr4;
+    } else {
+        so->fhost.sin6 = addr.addr6;
+    }
     sotranslate_accept(so);
-    so->so_lfamily = AF_INET;
-    so->so_lport = lport;
-    so->so_laddr.s_addr = laddr;
+
+    so->so_lfamily = family;
+    if (family == AF_INET) {
+        so->so_laddr.s_addr = laddr.addr4;
+        so->so_lport = lport;
+    } else {
+        so->so_laddr6 = laddr.addr6;
+        so->so_lport6 = lport;
+    }
+
     if (flags != SS_FACCEPTONCE)
         so->so_expire = 0;
-
     so->so_state &= SS_PERSISTENT_MASK;
     so->so_state |= SS_ISFCONNECTED | flags;
 
     return so;
 }
+
+struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
+                          uint32_t laddr, unsigned lport, int flags)
+{
+    in4or6_addr haddr4, laddr4;
+
+    haddr4.addr4 = haddr;
+    laddr4.addr4 = laddr;
+    return udpx_listen(slirp, AF_INET, haddr4, hport, laddr4, lport, flags);
+}
+
+struct socket *
+udp6_listen(Slirp *slirp, struct in6_addr haddr, u_int hport,
+            struct in6_addr laddr, u_int lport, int flags)
+{
+    in4or6_addr haddr6, laddr6;
+
+    haddr6.addr6 = haddr;
+    laddr6.addr6 = laddr;
+    return udpx_listen(slirp, AF_INET6, haddr6, hport, laddr6, lport, flags);
+}
diff --git a/src/udp.h b/src/udp.h
index c3b83fd..b3fbeac 100644
--- a/src/udp.h
+++ b/src/udp.h
@@ -80,6 +80,8 @@ void udp_input(register struct mbuf *, int);
 int udp_attach(struct socket *, unsigned short af);
 void udp_detach(struct socket *);
 struct socket *udp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int);
+struct socket *udp6_listen(Slirp *slirp, struct in6_addr, u_int,
+                            struct in6_addr, u_int, int);
 int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr,
                struct sockaddr_in *daddr, int iptos);
 
-- 
2.30.0.296.g2bfb1c46d8-goog



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] net: Add ipv6_hostfwd option
  2021-01-21  8:23 [PATCH 0/2] Add ipv6 hostfwd support dje--- via
  2021-01-21  8:23 ` [PATCH 1/2] slirp: " dje--- via
@ 2021-01-21  8:23 ` dje--- via
  2021-01-21 22:30   ` Eric Blake
  2021-01-21  9:41 ` [PATCH 0/2] Add ipv6 hostfwd support Marc-André Lureau
  2 siblings, 1 reply; 7+ messages in thread
From: dje--- via @ 2021-01-21  8:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Samuel Thibault, Doug Evans

Signed-off-by: Doug Evans <dje@google.com>
---
 hmp-commands.hx     |  29 ++++-
 include/net/slirp.h |   2 +
 net/slirp.c         | 311 +++++++++++++++++++++++++++++++++++---------
 qapi/net.json       |   1 +
 slirp               |   2 +-
 5 files changed, 279 insertions(+), 66 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 73e0832ea1..fa404123b7 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1398,6 +1398,34 @@ SRST
   Remove host-to-guest TCP or UDP redirection.
 ERST
 
+#ifdef CONFIG_SLIRP
+    {
+        .name       = "ipv6_hostfwd_add",
+        .args_type  = "arg1:s,arg2:s?",
+        .params     = "[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
+SRST
+``ipv6_hostfwd_add``
+  Redirect TCP6 or UDP6 connections from host to guest (requires -net user).
+ERST
+
+#ifdef CONFIG_SLIRP
+    {
+        .name       = "ipv6_hostfwd_remove",
+        .args_type  = "arg1:s,arg2:s?",
+        .params     = "[netdev_id] [tcp|udp]:[hostaddr6]:hostport",
+        .help       = "remove host-to-guest TCP6 or UDP6 redirection",
+        .cmd        = hmp_ipv6_hostfwd_remove,
+    },
+#endif
+SRST
+``ipv6_hostfwd_remove``
+  Remove host-to-guest TCP6 or UDP6 redirection.
+ERST
+
     {
         .name       = "balloon",
         .args_type  = "value:M",
@@ -1866,4 +1894,3 @@ ERST
         .sub_table  = hmp_info_cmds,
         .flags      = "p",
     },
-
diff --git a/include/net/slirp.h b/include/net/slirp.h
index bad3e1e241..4796a5cd39 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 8350c6d45f..5870a3284a 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -70,6 +70,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;
@@ -101,6 +102,8 @@ static QTAILQ_HEAD(, 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
@@ -586,6 +589,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;
@@ -631,15 +638,136 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *id)
     }
 }
 
-void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
+/*
+ * Parse a protocol name of the form "name<sep>".
+ * Valid protocols are "tcp" and "udp". An empty string means "tcp".
+ * Returns a pointer to the end of the parsed string on success, and stores
+ * the result in *is_udp.
+ * Otherwise returns NULL and stores the error message in *errmsg, which must
+ * be freed by the caller.
+ */
+static const char *parse_protocol(const char *str, int sep, int *is_udp,
+                                  char **errmsg)
+{
+    char buf[10];
+    const char *p = str;
+
+    if (get_str_sep(buf, sizeof(buf), &p, sep) < 0) {
+        *errmsg = g_strdup("Missing protcol name separator");
+        return NULL;
+    }
+
+    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+        *is_udp = 0;
+    } else if (!strcmp(buf, "udp")) {
+        *is_udp = 1;
+    } else {
+        *errmsg = g_strdup("Bad protcol name");
+        return NULL;
+    }
+
+    return p;
+}
+
+/*
+ * Parse an ipv4 address/port of the form "addr<addr_sep>port<port_sep>".
+ * "kind" is either "host" or "guest" and is included in error messages.
+ * An empty address means INADDR_ANY.
+ * Returns a pointer to the end of the parsed string on success, and stores
+ * the results in *addr, *port.
+ * Otherwise returns NULL and stores the error message in *errmsg, which must
+ * be freed by the caller.
+ */
+static const char *parse_in4_addr_port(const char *str, const char *kind,
+                                       int addr_sep, int port_sep,
+                                       struct in_addr *addr, int *port,
+                                       char **errmsg)
 {
-    struct in_addr host_addr = { .s_addr = INADDR_ANY };
-    int host_port;
     char buf[256];
-    const char *src_str, *p;
+    const char *p = str;
+
+    if (get_str_sep(buf, sizeof(buf), &p, addr_sep) < 0) {
+        *errmsg = g_strdup_printf("Missing %s address separator", kind);
+        return NULL;
+    }
+    if (buf[0] == '\0') {
+        addr->s_addr = INADDR_ANY;
+    } else if (!inet_aton(buf, addr)) {
+        *errmsg = g_strdup_printf("Bad %s address", kind);
+        return NULL;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, port_sep) < 0) {
+        *errmsg = g_strdup_printf("Missing %s port separator", kind);
+        return NULL;
+    }
+    if (qemu_strtoi(buf, NULL, 10, port) < 0 ||
+        *port < 0 || *port > 65535) {
+        *errmsg = g_strdup_printf("Bad %s port", kind);
+        return NULL;
+    }
+
+    return p;
+}
+
+/*
+ * Parse an ipv6 address/port of the form "addr<addr_sep>port<port_sep>".
+ * "kind" is either "host" or "guest" and is included in error messages.
+ * An empty address means in6addr_any.
+ * Returns a pointer to the end of the parsed string on success, and stores
+ * the results in *addr, *port.
+ * Otherwise returns NULL and stores the error message in *errmsg, which must
+ * be freed by the caller.
+ */
+static const char *parse_in6_addr_port(const char *str, const char *kind,
+                                       int addr_sep, int port_sep,
+                                       struct in6_addr *addr, int *port,
+                                       char **errmsg)
+{
+    char buf[256];
+    const char *p = str;
+
+    if (*(p++) != '[') {
+        *errmsg = g_strdup_printf("IPv6 %s address must be enclosed"
+                                  " in square brackets", kind);
+        return NULL;
+    }
+    if (get_str_sep(buf, sizeof(buf), &p, ']') < 0) {
+        *errmsg = g_strdup_printf("IPv6 %s address must be enclosed"
+                                  " in square brackets", kind);
+        return NULL;
+    }
+    if (buf[0] == '\0') {
+        *addr = in6addr_any;
+    } else if (!inet_pton(AF_INET6, buf, addr)) {
+        *errmsg = g_strdup_printf("Bad %s address", kind);
+        return NULL;
+    }
+
+    /* Ignore the part between the ']' and addr_sep. */
+    if (get_str_sep(buf, sizeof(buf), &p, addr_sep) < 0) {
+        *errmsg = g_strdup_printf("Missing %s address separator", kind);
+        return NULL;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, port_sep) < 0) {
+        *errmsg = g_strdup_printf("Missing %s port separator", kind);
+        return NULL;
+    }
+    if (qemu_strtoi(buf, NULL, 10, port) < 0 ||
+        *port < 0 || *port > 65535) {
+        *errmsg = g_strdup_printf("Bad %s port", kind);
+        return NULL;
+    }
+
+    return p;
+}
+
+static void hmp_hostfwd_remove_worker(Monitor *mon, const QDict *qdict,
+                                      int family)
+{
+    const char *src_str;
     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");
 
@@ -654,38 +782,52 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
         return;
     }
 
-    p = src_str;
-    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
-        goto fail_syntax;
-    }
+    int host_port;
+    int is_udp;
+    char *errmsg = NULL;
+    int err;
 
-    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
-        is_udp = 0;
-    } else if (!strcmp(buf, "udp")) {
-        is_udp = 1;
-    } else {
-        goto fail_syntax;
-    }
+    g_assert(src_str != NULL);
+    const char *p = src_str;
 
-    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
-        goto fail_syntax;
-    }
-    if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
+    p = parse_protocol(p, ':', &is_udp, &errmsg);
+    if (p == NULL) {
         goto fail_syntax;
     }
 
-    if (qemu_strtoi(p, NULL, 10, &host_port)) {
-        goto fail_syntax;
+    if (family == AF_INET) {
+        struct in_addr host_addr;
+        if (parse_in4_addr_port(p, "host", ':', '\0', &host_addr, &host_port,
+                                &errmsg) == NULL) {
+            goto fail_syntax;
+        }
+        err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port);
+    } else {
+        struct in6_addr host_addr;
+        if (parse_in6_addr_port(p, "host", ':', '\0', &host_addr, &host_port,
+                                &errmsg) == NULL) {
+            goto fail_syntax;
+        }
+        err = slirp_remove_ipv6_hostfwd(s->slirp, is_udp, host_addr, host_port);
     }
 
-    err = slirp_remove_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");
+    monitor_printf(mon, "Invalid format: %s\n", errmsg);
+    g_free(errmsg);
+}
+
+void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
+{
+    hmp_hostfwd_remove_worker(mon, qdict, AF_INET);
+}
+
+void hmp_ipv6_hostfwd_remove(Monitor *mon, const QDict *qdict)
+{
+    hmp_hostfwd_remove_worker(mon, qdict, AF_INET6);
 }
 
 static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
@@ -694,61 +836,83 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
     struct in_addr guest_addr = { .s_addr = 0 };
     int host_port, guest_port;
     const char *p;
-    char buf[256];
     int is_udp;
-    char *end;
-    const char *fail_reason = "Unknown reason";
+    char *errmsg = NULL;
 
+    g_assert(redir_str != NULL);
     p = redir_str;
-    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
-        fail_reason = "No : separators";
+
+    p = parse_protocol(p, ':', &is_udp, &errmsg);
+    if (p == NULL) {
         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";
+
+    p = parse_in4_addr_port(p, "host", ':', '-', &host_addr, &host_port,
+                            &errmsg);
+    if (p == NULL) {
         goto fail_syntax;
     }
 
-    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
-        fail_reason = "Missing : separator";
+    if (parse_in4_addr_port(p, "guest", ':', '\0', &guest_addr, &guest_port,
+                            &errmsg) == NULL) {
         goto fail_syntax;
     }
-    if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
-        fail_reason = "Bad host address";
+    if (guest_port == 0) {
+        errmsg = g_strdup("Bad guest port");
         goto fail_syntax;
     }
 
-    if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
-        fail_reason = "Bad host port separator";
-        goto fail_syntax;
+    if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
+                          guest_port) < 0) {
+        error_setg(errp, "Could not set up host forwarding rule '%s'",
+                   redir_str);
+        return -1;
     }
-    host_port = strtol(buf, &end, 0);
-    if (*end != '\0' || host_port < 0 || host_port > 65535) {
-        fail_reason = "Bad host port";
+    return 0;
+
+ fail_syntax:
+    error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str,
+               errmsg);
+    g_free(errmsg);
+    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;
+    int is_udp;
+    char *errmsg = NULL;
+
+    memset(&guest_addr, 0, sizeof(guest_addr));
+    g_assert(redir_str != NULL);
+    p = redir_str;
+
+    p = parse_protocol(p, ':', &is_udp, &errmsg);
+    if (p == NULL) {
         goto fail_syntax;
     }
 
-    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
-        fail_reason = "Missing guest address";
+    p = parse_in6_addr_port(p, "host", ':', '-', &host_addr, &host_port,
+                            &errmsg);
+    if (p == NULL) {
         goto fail_syntax;
     }
-    if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
-        fail_reason = "Bad guest address";
+
+    if (parse_in6_addr_port(p, "guest", ':', '\0', &guest_addr, &guest_port,
+                            &errmsg) == NULL) {
         goto fail_syntax;
     }
-
-    guest_port = strtol(p, &end, 0);
-    if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
-        fail_reason = "Bad guest port";
+    if (guest_port == 0) {
+        errmsg = g_strdup("Bad guest port");
         goto fail_syntax;
     }
 
-    if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
-                          guest_port) < 0) {
+    if (slirp_add_ipv6_hostfwd(s->slirp, is_udp, host_addr, host_port,
+                               guest_addr, guest_port) < 0) {
         error_setg(errp, "Could not set up host forwarding rule '%s'",
                    redir_str);
         return -1;
@@ -757,11 +921,12 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
 
  fail_syntax:
     error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str,
-               fail_reason);
+               errmsg);
+    g_free(errmsg);
     return -1;
 }
 
-void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
+static void hmp_hostfwd_add_worker(Monitor *mon, const QDict *qdict, int family)
 {
     const char *redir_str;
     SlirpState *s;
@@ -775,13 +940,30 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
         s = slirp_lookup(mon, NULL);
         redir_str = arg1;
     }
-    if (s) {
-        Error *err = NULL;
-        if (slirp_hostfwd(s, redir_str, &err) < 0) {
-            error_report_err(err);
-        }
+    if (!s) {
+        return;
+    }
+
+    Error *err = NULL;
+    int rc;
+    if (family == AF_INET) {
+        rc = slirp_hostfwd(s, redir_str, &err);
+    } else {
+        rc = slirp_ipv6_hostfwd(s, redir_str, &err);
+    }
+    if (rc < 0) {
+        error_report_err(err);
     }
+}
+
+void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
+{
+    hmp_hostfwd_add_worker(mon, qdict, AF_INET);
+}
 
+void hmp_ipv6_hostfwd_add(Monitor *mon, const QDict *qdict)
+{
+    hmp_hostfwd_add_worker(mon, qdict, AF_INET6);
 }
 
 #ifndef _WIN32
@@ -1090,6 +1272,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 c31748c87f..34ea92277c 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -189,6 +189,7 @@
     '*smb':       'str',
     '*smbserver': 'str',
     '*hostfwd':   ['String'],
+    '*ipv6-hostfwd': ['String'],
     '*guestfwd':  ['String'],
     '*tftp-server-name': 'str' } }
 
diff --git a/slirp b/slirp
index 8f43a99191..6c0db866ef 160000
--- a/slirp
+++ b/slirp
@@ -1 +1 @@
-Subproject commit 8f43a99191afb47ca3f3c6972f6306209f367ece
+Subproject commit 6c0db866ef5e11a3b87640e57769fbf9b0a1e3b8
-- 
2.30.0.296.g2bfb1c46d8-goog



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add ipv6 hostfwd support
  2021-01-21  8:23 [PATCH 0/2] Add ipv6 hostfwd support dje--- via
  2021-01-21  8:23 ` [PATCH 1/2] slirp: " dje--- via
  2021-01-21  8:23 ` [PATCH 2/2] net: Add ipv6_hostfwd option dje--- via
@ 2021-01-21  9:41 ` Marc-André Lureau
  2021-01-21 20:28   ` dje--- via
  2 siblings, 1 reply; 7+ messages in thread
From: Marc-André Lureau @ 2021-01-21  9:41 UTC (permalink / raw)
  To: Doug Evans; +Cc: Samuel Thibault, QEMU

Hi Doug,

On Thu, Jan 21, 2021 at 12:24 PM dje--- via <qemu-devel@nongnu.org> wrote:
>
> Hi. This patchset takes the original patch from Maxim,
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg569573.html
> and updates it.
>
> The first patch is the slirp additions, the second patch adds the u/i.

libslirp is maintained on gitlab. Can you open a merge request?
https://gitlab.freedesktop.org/slirp/libslirp/-/merge_requests

thanks

>
> Doug Evans (2):
>   slirp: Add ipv6 hostfwd support
>   net: Add ipv6_hostfwd option
>
> Slirp:
>  src/libslirp.h |   6 +++
>  src/slirp.c    |  51 +++++++++++++++++++++--
>  src/socket.c   | 109 +++++++++++++++++++++++++++++++++++--------------
>  src/socket.h   |   9 ++++
>  src/udp.c      |  73 ++++++++++++++++++++++++++-------
>  src/udp.h      |   2 +
>  6 files changed, 202 insertions(+), 48 deletions(-)
>
> QEMU:
>  hmp-commands.hx     |  29 ++++-
>  include/net/slirp.h |   2 +
>  net/slirp.c         | 311 +++++++++++++++++++++++++++++++++++---------
>  qapi/net.json       |   1 +
>  slirp               |   2 +-
>  5 files changed, 279 insertions(+), 66 deletions(-)
>
> --
> 2.30.0.296.g2bfb1c46d8-goog
>
>


-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add ipv6 hostfwd support
  2021-01-21  9:41 ` [PATCH 0/2] Add ipv6 hostfwd support Marc-André Lureau
@ 2021-01-21 20:28   ` dje--- via
  2021-01-21 20:40     ` Marc-André Lureau
  0 siblings, 1 reply; 7+ messages in thread
From: dje--- via @ 2021-01-21 20:28 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Samuel Thibault, QEMU

[-- Attachment #1: Type: text/plain, Size: 1757 bytes --]

On Thu, Jan 21, 2021 at 1:41 AM Marc-André Lureau <
marcandre.lureau@gmail.com> wrote:

> Hi Doug,
>
> On Thu, Jan 21, 2021 at 12:24 PM dje--- via <qemu-devel@nongnu.org> wrote:
> >
> > Hi. This patchset takes the original patch from Maxim,
> > https://www.mail-archive.com/qemu-devel@nongnu.org/msg569573.html
> > and updates it.
> >
> > The first patch is the slirp additions, the second patch adds the u/i.
>
> libslirp is maintained on gitlab. Can you open a merge request?
> https://gitlab.freedesktop.org/slirp/libslirp/-/merge_requests
>
> thanks
>


Hi. Sure, no problem.
I wasn't sure what the procedure is but figured it would come out during
the review.
How does review of libslirp patches work?
I gather Samuel is a libslirp maintainer so I'm guessing it just needs to
be approved here (after appropriate review) and then afterwards I should
file the merge request?



>
> >
> > Doug Evans (2):
> >   slirp: Add ipv6 hostfwd support
> >   net: Add ipv6_hostfwd option
> >
> > Slirp:
> >  src/libslirp.h |   6 +++
> >  src/slirp.c    |  51 +++++++++++++++++++++--
> >  src/socket.c   | 109 +++++++++++++++++++++++++++++++++++--------------
> >  src/socket.h   |   9 ++++
> >  src/udp.c      |  73 ++++++++++++++++++++++++++-------
> >  src/udp.h      |   2 +
> >  6 files changed, 202 insertions(+), 48 deletions(-)
> >
> > QEMU:
> >  hmp-commands.hx     |  29 ++++-
> >  include/net/slirp.h |   2 +
> >  net/slirp.c         | 311 +++++++++++++++++++++++++++++++++++---------
> >  qapi/net.json       |   1 +
> >  slirp               |   2 +-
> >  5 files changed, 279 insertions(+), 66 deletions(-)
> >
> > --
> > 2.30.0.296.g2bfb1c46d8-goog
> >
> >
>
>
> --
> Marc-André Lureau
>

[-- Attachment #2: Type: text/html, Size: 3022 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] Add ipv6 hostfwd support
  2021-01-21 20:28   ` dje--- via
@ 2021-01-21 20:40     ` Marc-André Lureau
  0 siblings, 0 replies; 7+ messages in thread
From: Marc-André Lureau @ 2021-01-21 20:40 UTC (permalink / raw)
  To: Doug Evans; +Cc: Samuel Thibault, QEMU

Hi Doug

On Fri, Jan 22, 2021 at 12:29 AM Doug Evans <dje@google.com> wrote:
>
> On Thu, Jan 21, 2021 at 1:41 AM Marc-André Lureau <marcandre.lureau@gmail.com> wrote:
>>
>> Hi Doug,
>>
>> On Thu, Jan 21, 2021 at 12:24 PM dje--- via <qemu-devel@nongnu.org> wrote:
>> >
>> > Hi. This patchset takes the original patch from Maxim,
>> > https://www.mail-archive.com/qemu-devel@nongnu.org/msg569573.html
>> > and updates it.
>> >
>> > The first patch is the slirp additions, the second patch adds the u/i.
>>
>> libslirp is maintained on gitlab. Can you open a merge request?
>> https://gitlab.freedesktop.org/slirp/libslirp/-/merge_requests
>>
>> thanks
>
>
>
> Hi. Sure, no problem.
> I wasn't sure what the procedure is but figured it would come out during the review.
> How does review of libslirp patches work?

We do the reviews on the gitlab MR (of the individual patches).

> I gather Samuel is a libslirp maintainer so I'm guessing it just needs to be approved here (after appropriate review) and then afterwards I should file the merge request?

Any of the libslirp maintainer can merge after reviews (including me).

Then anybody can propose a patch to update libslirp in qemu, get
reviews/comments, and Samuel can send the pull request. Sometimes
someone else sends it.

Note that libslirp is packaged in a number of distributions these days
(https://repology.org/project/libslirp/versions), and that qemu may
link to it depending on how it is built.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] net: Add ipv6_hostfwd option
  2021-01-21  8:23 ` [PATCH 2/2] net: Add ipv6_hostfwd option dje--- via
@ 2021-01-21 22:30   ` Eric Blake
  0 siblings, 0 replies; 7+ messages in thread
From: Eric Blake @ 2021-01-21 22:30 UTC (permalink / raw)
  To: Doug Evans, qemu-devel; +Cc: Samuel Thibault

On 1/21/21 2:23 AM, dje--- via wrote:
> Signed-off-by: Doug Evans <dje@google.com>
> ---

Rather light on the commit message description.  The one-line summary
does a good job of saying "what" the commit does, but the rest of the
commit body should say "why" the commit is worthwhile, rather than being
silent.

>  hmp-commands.hx     |  29 ++++-
>  include/net/slirp.h |   2 +
>  net/slirp.c         | 311 +++++++++++++++++++++++++++++++++++---------
>  qapi/net.json       |   1 +
>  slirp               |   2 +-
>  5 files changed, 279 insertions(+), 66 deletions(-)
> 

> +++ b/qapi/net.json
> @@ -189,6 +189,7 @@
>      '*smb':       'str',
>      '*smbserver': 'str',
>      '*hostfwd':   ['String'],
> +    '*ipv6-hostfwd': ['String'],

Missing documentation, including a 'since 6.0' tag on the new member.

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



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-01-21 22:32 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-21  8:23 [PATCH 0/2] Add ipv6 hostfwd support dje--- via
2021-01-21  8:23 ` [PATCH 1/2] slirp: " dje--- via
2021-01-21  8:23 ` [PATCH 2/2] net: Add ipv6_hostfwd option dje--- via
2021-01-21 22:30   ` Eric Blake
2021-01-21  9:41 ` [PATCH 0/2] Add ipv6 hostfwd support Marc-André Lureau
2021-01-21 20:28   ` dje--- via
2021-01-21 20:40     ` Marc-André Lureau

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.