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);
 
