aboutsummaryrefslogtreecommitdiff
path: root/src/socket.c
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2021-04-12 22:32:37 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2021-04-12 22:32:37 +0000
commit4e6444e842695a6bfb00e15a8d0edfceb5c4628d (patch)
tree7d86421e4c125989f5ba265f1fb309b909b2e715 /src/socket.c
parentfcf2568d40baefbd6a83d919d6028d94b4172665 (diff)
parentf8bc26ad23213bd21ff7b540e97216f65969463a (diff)
downloadslirp-4e6444e842695a6bfb00e15a8d0edfceb5c4628d.zip
slirp-4e6444e842695a6bfb00e15a8d0edfceb5c4628d.tar.gz
slirp-4e6444e842695a6bfb00e15a8d0edfceb5c4628d.tar.bz2
Merge branch 'lazy-ipv6-resolution' into 'master'
Perform lazy guest address resolution for IPv6 See merge request slirp/libslirp!81
Diffstat (limited to 'src/socket.c')
-rw-r--r--src/socket.c81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/socket.c b/src/socket.c
index bf50058..2c1b789 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -680,6 +680,28 @@ void sorecvfrom(struct socket *so)
*/
saddr = addr;
sotranslate_in(so, &saddr);
+
+ /* Perform lazy guest IP address resolution if needed. */
+ if (so->so_state & SS_HOSTFWD) {
+ if (soassign_guest_addr_if_needed(so) < 0) {
+ DEBUG_MISC(" guest address not available yet");
+ switch (so->so_lfamily) {
+ case AF_INET:
+ icmp_send_error(so->so_m, ICMP_UNREACH,
+ ICMP_UNREACH_HOST, 0,
+ "guest address not available yet");
+ break;
+ case AF_INET6:
+ icmp6_send_error(so->so_m, ICMP6_UNREACH,
+ ICMP6_UNREACH_ADDRESS);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ m_free(m);
+ return;
+ }
+ }
daddr = so->lhost.ss;
switch (so->so_ffamily) {
@@ -761,6 +783,15 @@ struct socket *tcpx_listen(Slirp *slirp,
DEBUG_ARG("lport = %s", portstr);
DEBUG_ARG("flags = %x", flags);
+ /*
+ * SS_HOSTFWD sockets can be accepted multiple times, so they can't be
+ * SS_FACCEPTONCE. Also, SS_HOSTFWD connections can be accepted and
+ * immediately closed if the guest address isn't available yet, which is
+ * incompatible with the "accept once" concept. Correct code will never
+ * request both, so disallow their combination by assertion.
+ */
+ g_assert(!((flags & SS_HOSTFWD) && (flags & SS_FACCEPTONCE)));
+
so = socreate(slirp);
/* Don't tcp_attach... we don't need so_snd nor so_rcv */
@@ -1021,3 +1052,53 @@ void sodrop(struct socket *s, int num)
s->slirp->cb->notify(s->slirp->opaque);
}
}
+
+/*
+ * Translate "addr-any" in so->lhost to the guest's actual address.
+ * Returns 0 for success, or -1 if the guest doesn't have an address yet
+ * with errno set to EHOSTUNREACH.
+ *
+ * The guest address is taken from the first entry in the ARP table for IPv4
+ * and the first entry in the NDP table for IPv6.
+ * Note: The IPv4 path isn't exercised yet as all hostfwd "" guest translations
+ * are handled immediately by using slirp->vdhcp_startaddr.
+ */
+int soassign_guest_addr_if_needed(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+ /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
+ char addrstr[INET6_ADDRSTRLEN];
+ char portstr[6];
+
+ g_assert(so->so_state & SS_HOSTFWD);
+
+ switch (so->so_ffamily) {
+ case AF_INET:
+ if (so->so_laddr.s_addr == INADDR_ANY) {
+ g_assert_not_reached();
+ }
+ break;
+
+ case AF_INET6:
+ if (in6_zero(&so->so_laddr6)) {
+ int ret;
+ if (in6_zero(&slirp->ndp_table.guest_in6_addr)) {
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+ so->so_laddr6 = slirp->ndp_table.guest_in6_addr;
+ ret = getnameinfo((const struct sockaddr *) &so->lhost.ss,
+ sizeof(so->lhost.ss), addrstr, sizeof(addrstr),
+ portstr, sizeof(portstr),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ g_assert(ret == 0);
+ DEBUG_MISC("%s: new ip = [%s]:%s", __func__, addrstr, portstr);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}