aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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;
}