aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2021-04-18 18:15:49 -0600
committerJason A. Donenfeld <Jason@zx2c4.com>2021-04-19 15:59:21 -0600
commitab0f1b70b7e4b5250e010b7e794d1a16e5895fc3 (patch)
tree0ba07e9586014665eab417d40fdbfb4bc14303d3 /src
parentcrypto: chacha and poly in same loop (diff)
downloadwireguard-freebsd-ab0f1b70b7e4b5250e010b7e794d1a16e5895fc3.tar.xz
wireguard-freebsd-ab0f1b70b7e4b5250e010b7e794d1a16e5895fc3.zip
if_wg: allow v4 xor v6 socket binding to fail with EADDRNOTAVAIL
This happens if a jail does not have an interface with a configured v4 or v6 address. In that case, we just fall back to only having one socket for the address family that does exist. In the case that neither socket can be created, fail as before. Closes: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254212 Reported-by: Mark Johnston <markj@FreeBSD.org> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'src')
-rw-r--r--src/if_wg.c61
1 files changed, 40 insertions, 21 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index ae2a4b8..ab7645a 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -297,7 +297,7 @@ struct wg_timespec64 {
};
static int wg_socket_init(struct wg_softc *, in_port_t);
-static int wg_socket_bind(struct socket *, struct socket *, in_port_t *);
+static int wg_socket_bind(struct socket **, struct socket **, in_port_t *);
static void wg_socket_set(struct wg_softc *, struct socket *, struct socket *);
static void wg_socket_uninit(struct wg_softc *);
static int wg_socket_set_sockopt(struct socket *, struct socket *, int, void *, size_t);
@@ -699,7 +699,7 @@ wg_socket_init(struct wg_softc *sc, in_port_t port)
if (rc)
goto out;
- rc = wg_socket_bind(so4, so6, &port);
+ rc = wg_socket_bind(&so4, &so6, &port);
if (!rc) {
sc->sc_socket.so_port = port;
wg_socket_set(sc, so4, so6);
@@ -786,9 +786,10 @@ wg_socket_set(struct wg_softc *sc, struct socket *new_so4, struct socket *new_so
}
static int
-wg_socket_bind(struct socket *so4, struct socket *so6, in_port_t *requested_port)
+wg_socket_bind(struct socket **in_so4, struct socket **in_so6, in_port_t *requested_port)
{
- int ret;
+ struct socket *so4 = *in_so4, *so6 = *in_so6;
+ int ret4 = 0, ret6 = 0;
in_port_t port = *requested_port;
struct sockaddr_in sin = {
.sin_len = sizeof(struct sockaddr_in),
@@ -801,27 +802,45 @@ wg_socket_bind(struct socket *so4, struct socket *so6, in_port_t *requested_port
.sin6_port = htons(port)
};
- ret = sobind(so4, (struct sockaddr *)&sin, curthread);
- if (ret)
- return ret;
-
- if (port == 0) {
- struct sockaddr_in *bound_sin;
- ret = sogetsockaddr(so4, (struct sockaddr **)&bound_sin);
- if (ret)
- return ret;
- port = ntohs(bound_sin->sin_port);
- sin6.sin6_port = bound_sin->sin_port;
- free(bound_sin, M_SONAME);
+ if (so4) {
+ ret4 = sobind(so4, (struct sockaddr *)&sin, curthread);
+ if (ret4 && ret4 != EADDRNOTAVAIL)
+ return ret4;
+ if (!ret4 && !sin.sin_port) {
+ struct sockaddr_in *bound_sin;
+ int ret = sogetsockaddr(so4, (struct sockaddr **)&bound_sin);
+ if (ret)
+ return ret;
+ port = ntohs(bound_sin->sin_port);
+ sin6.sin6_port = bound_sin->sin_port;
+ free(bound_sin, M_SONAME);
+ }
}
-#ifdef INET6
- ret = sobind(so6, (struct sockaddr *)&sin6, curthread);
- if (ret)
- return ret;
-#endif
+ if (so6) {
+ ret6 = sobind(so6, (struct sockaddr *)&sin6, curthread);
+ if (ret6 && ret6 != EADDRNOTAVAIL)
+ return ret6;
+ if (!ret6 && !sin6.sin6_port) {
+ struct sockaddr_in6 *bound_sin6;
+ int ret = sogetsockaddr(so6, (struct sockaddr **)&bound_sin6);
+ if (ret)
+ return ret;
+ port = ntohs(bound_sin6->sin6_port);
+ free(bound_sin6, M_SONAME);
+ }
+ }
+ if (ret4 && ret6)
+ return ret4;
*requested_port = port;
+ if (ret4 && !ret6 && so4) {
+ soclose(so4);
+ *in_so4 = NULL;
+ } else if (ret6 && !ret4 && so6) {
+ soclose(so6);
+ *in_so6 = NULL;
+ }
return 0;
}