aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Evans <dje@google.com>2021-02-03 10:26:38 -0800
committerDoug Evans <dje@google.com>2021-02-03 10:26:38 -0800
commit0624fbe7d39b5433d7084a5096d1effc1df74e39 (patch)
treec479391b21f8b8389e29a3269e3479734243c410
parent72654d84113c8b241028342b1b79d70220ff92bd (diff)
downloadslirp-0624fbe7d39b5433d7084a5096d1effc1df74e39.zip
slirp-0624fbe7d39b5433d7084a5096d1effc1df74e39.tar.gz
slirp-0624fbe7d39b5433d7084a5096d1effc1df74e39.tar.bz2
Add ipv6 host forward support
Two exported functions are added which are the ipv6 versions of their ipv4 counterparts: slirp_add_ipv6_hostfwd, slirp_remove_ipv6_hostfwd. Signed-off-by: Doug Evans <dje@google.com>
-rw-r--r--src/libslirp.h6
-rw-r--r--src/libslirp.map5
-rw-r--r--src/slirp.c51
-rw-r--r--src/socket.c92
-rw-r--r--src/socket.h9
-rw-r--r--src/udp.c70
-rw-r--r--src/udp.h2
7 files changed, 191 insertions, 44 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/libslirp.map b/src/libslirp.map
index 72aab91..c0113c9 100644
--- a/src/libslirp.map
+++ b/src/libslirp.map
@@ -28,3 +28,8 @@ SLIRP_4.2 {
slirp_add_unix;
slirp_remove_guestfwd;
} SLIRP_4.1;
+
+SLIRP_4.5 {
+ slirp_add_ipv6_hostfwd;
+ slirp_remove_ipv6_hostfwd;
+} SLIRP_4.2;
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..4979f09 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -736,20 +736,26 @@ 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 slirp_sockaddr 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_CALL("tcpx_listen");
+ /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
+ char addrstr[INET6_ADDRSTRLEN];
+ const char *str = inet_ntop(family, &haddr, addrstr, sizeof(addrstr));
+ g_assert(str != NULL);
+ DEBUG_ARG("haddr = %s", str);
DEBUG_ARG("hport = %d", ntohs(hport));
- DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){ .s_addr = laddr }));
+ str = inet_ntop(family, &laddr, addrstr, sizeof(addrstr));
+ g_assert(str != NULL);
+ DEBUG_ARG("laddr = %s", str);
DEBUG_ARG("lport = %d", ntohs(lport));
DEBUG_ARG("flags = %x", flags);
@@ -770,20 +776,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 = 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.sin.sin_family = family;
+ addr.sin.sin_addr = haddr.addr4;
+ addr.sin.sin_port = hport;
+ addrlen = sizeof(addr.sin);
+ } else {
+ addr.sin6.sin6_family = family;
+ addr.sin6.sin6_addr = haddr.addr6;
+ addr.sin6.sin6_port = hport;
+ addrlen = sizeof(addr.sin6);
+ }
- 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 +818,41 @@ 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.sin;
+ } else {
+ so->fhost.sin6 = addr.sin6;
+ }
+ 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)
+{
+ in4or6_addr haddr4, laddr4;
+
+ haddr4.addr4.s_addr = haddr;
+ laddr4.addr4.s_addr = 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)
+{
+ 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..ac9a57e 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 {
+ struct in_addr 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..e59fe8e 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -353,17 +353,17 @@ 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 slirp_sockaddr 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 +371,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.sin.sin_family = family;
+ addr.sin.sin_addr = haddr.addr4;
+ addr.sin.sin_port = hport;
+ addrlen = sizeof(addr.sin);
+ } else {
+ addr.sin6.sin6_family = family;
+ addr.sin6.sin6_addr = haddr.addr6;
+ addr.sin6.sin6_port = hport;
+ addrlen = sizeof(addr.sin6);
+ }
if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) {
udp_detach(so);
@@ -382,16 +391,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.sin;
+ } else {
+ so->fhost.sin6 = addr.sin6;
+ }
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 = 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.s_addr = haddr;
+ laddr4.addr4.s_addr = 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);