diff options
author | Doug Evans <dje@google.com> | 2021-02-03 10:26:38 -0800 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2021-02-03 10:26:38 -0800 |
commit | 0624fbe7d39b5433d7084a5096d1effc1df74e39 (patch) | |
tree | c479391b21f8b8389e29a3269e3479734243c410 | |
parent | 72654d84113c8b241028342b1b79d70220ff92bd (diff) | |
download | slirp-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.h | 6 | ||||
-rw-r--r-- | src/libslirp.map | 5 | ||||
-rw-r--r-- | src/slirp.c | 51 | ||||
-rw-r--r-- | src/socket.c | 92 | ||||
-rw-r--r-- | src/socket.h | 9 | ||||
-rw-r--r-- | src/udp.c | 70 | ||||
-rw-r--r-- | src/udp.h | 2 |
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 *); @@ -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); +} @@ -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); |