aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@noconroy.net>2020-05-23 01:44:13 +1000
committerMatt Dunwoodie <ncon@noconroy.net>2020-05-23 01:44:13 +1000
commit6ceedeca852e73878377bbeb233bf08c6b117414 (patch)
treec214d8623cda6b70d372abd18d2c3991e102fa25
parentUse atomic counter operation when available (diff)
downloadwireguard-openbsd-6ceedeca852e73878377bbeb233bf08c6b117414.tar.xz
wireguard-openbsd-6ceedeca852e73878377bbeb233bf08c6b117414.zip
Refactor binding interface.
I always like it when we take away lines, and remove bugs at the same time Some things that are here now: * Don't update sockets until new ones are ready. This means if you try to set the port to an address already in use, then it won't kill the current sockets. * Retry AF_INET6 if the port that AF_INET chose was in use for AF_INET6. This copies the retries (100) from Linux, not sure where that came from but is a reasonable number. Personally, I'd prefer a power of 2 number, and for it to be a bit smaller, but there isn't much difference by this point. If you can't bind to a port 64 vs 100 times in a row, something else is the issue. Bugfixes: * Need to create a new socket if changing rtable, will return EBUSY if socket is bind'ed and you attempt to change the rtable.
-rw-r--r--src/if_wg.c221
1 files changed, 103 insertions, 118 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index 4750d60..952b412 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -273,7 +273,9 @@ int wg_aip_remove(struct wg_softc *, struct wg_peer *,
struct wg_aip_io *);
/* wg_socket */
-int wg_bind(struct wg_softc *);
+int wg_socket_open(struct socket **, int, in_port_t *, int *, void *);
+void wg_socket_close(struct socket **);
+int wg_bind(struct wg_softc *, in_port_t *, int *);
void wg_unbind(struct wg_softc *);
int wg_send(struct wg_softc *, struct wg_endpoint *, struct mbuf *);
void wg_send_buf(struct wg_softc *, struct wg_endpoint *, uint8_t *,
@@ -665,106 +667,106 @@ wg_aip_remove(struct wg_softc *sc, struct wg_peer *peer, struct wg_aip_io *d)
}
int
-wg_bind(struct wg_softc *sc)
+wg_socket_open(struct socket **so, int af, in_port_t *port,
+ int *rtable, void *upcall_arg)
{
- struct mbuf hostnam, rtable;
- struct socket *so4, *so6;
- struct sockaddr_in *sin;
+ struct mbuf mhostnam, mrtable;
struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
int ret, s;
- rw_enter_write(&sc->sc_so_lock);
+ m_inithdr(&mhostnam);
+ m_inithdr(&mrtable);
+
+ bzero(mtod(&mrtable, u_int *), sizeof(u_int));
+ *mtod(&mrtable, u_int *) = *rtable;
+ mrtable.m_len = sizeof(u_int);
+
+ if (af == AF_INET) {
+ sin = mtod(&mhostnam, struct sockaddr_in *);
+ bzero(sin, sizeof(*sin));
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = *port;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ mhostnam.m_len = sin->sin_len;
+ } else if (af == AF_INET6) {
+ sin6 = mtod(&mhostnam, struct sockaddr_in6 *);
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = *port;
+ sin6->sin6_addr = (struct in6_addr) { .s6_addr = { 0 } };
+ mhostnam.m_len = sin6->sin6_len;
+ } else {
+ return EAFNOSUPPORT;
+ }
- if ((ret = socreate(AF_INET, &so4, SOCK_DGRAM, 0)) != 0)
- goto error;
- if ((ret = socreate(AF_INET6, &so6, SOCK_DGRAM, 0)) != 0)
- goto error;
+ if ((ret = socreate(af, so, SOCK_DGRAM, 0)) != 0)
+ return ret;
- m_inithdr(&hostnam);
- m_inithdr(&rtable);
+ s = solock(*so);
+ sotoinpcb(*so)->inp_upcall = wg_input;
+ sotoinpcb(*so)->inp_upcall_arg = upcall_arg;
- bzero(mtod(&rtable, u_int *), sizeof(u_int));
- *mtod(&rtable, u_int *) = sc->sc_udp_rtable;
- rtable.m_len = sizeof(u_int);
+ if ((ret = sosetopt(*so, SOL_SOCKET, SO_RTABLE, &mrtable)) == 0) {
+ if ((ret = sobind(*so, &mhostnam, curproc)) == 0) {
+ *port = sotoinpcb(*so)->inp_lport;
+ *rtable = sotoinpcb(*so)->inp_rtableid;
+ }
+ }
+ sounlock(*so, s);
- /* Listen v4 */
- sin = mtod(&hostnam, struct sockaddr_in *);
- bzero(sin, sizeof(*sin));
- sin->sin_len = sizeof(*sin);
- sin->sin_family = AF_INET;
- sin->sin_port = sc->sc_udp_port;
- sin->sin_addr.s_addr = INADDR_ANY;
- hostnam.m_len = sin->sin_len;
+ if (ret != 0)
+ wg_socket_close(so);
- s = solock(so4);
- sotoinpcb(so4)->inp_upcall = wg_input;
- sotoinpcb(so4)->inp_upcall_arg = sc;
+ return ret;
+}
- if ((ret = sosetopt(so4, SOL_SOCKET, SO_RTABLE, &rtable)) != 0) {
- sounlock(so4, s);
- goto error;
- }
+void
+wg_socket_close(struct socket **so)
+{
+ if (*so != NULL && soclose(*so, 0) != 0)
+ panic("Unable to close wg socket");
+ *so = NULL;
+}
- ret = sobind(so4, &hostnam, curproc);
- /* Update port to whatever v4 chose */
- sc->sc_udp_port = sotoinpcb(so4)->inp_lport;
- sc->sc_udp_rtable = sotoinpcb(so4)->inp_rtableid;
- sounlock(so4, s);
- if (ret)
- goto error;
+int
+wg_bind(struct wg_softc *sc, in_port_t *portp, int *rtablep)
+{
+ int ret = 0, retries = 0, rtable = *rtablep;
+ in_port_t port = *portp;
+ struct socket *so4, *so6;
- /* Listen v6 */
- sin6 = mtod(&hostnam, struct sockaddr_in6 *);
- bzero(sin6, sizeof(*sin6));
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = sc->sc_udp_port;
- sin6->sin6_addr = (struct in6_addr) { .s6_addr = { 0 } };
- hostnam.m_len = sin6->sin6_len;
-
- s = solock(so6);
- sotoinpcb(so6)->inp_upcall = wg_input;
- sotoinpcb(so6)->inp_upcall_arg = sc;
-
- if ((ret = sosetopt(so6, SOL_SOCKET, SO_RTABLE, &rtable)) != 0) {
- sounlock(so6, s);
- goto error;
- }
+retry:
+ if ((ret = wg_socket_open(&so4, AF_INET, &port, &rtable, sc)) != 0)
+ return ret;
- ret = sobind(so6, &hostnam, curproc);
- sc->sc_udp_rtable = sotoinpcb(so6)->inp_rtableid;
- sounlock(so6, s);
- if (ret)
- goto error;
+ if ((ret = wg_socket_open(&so6, AF_INET6, &port, &rtable, sc)) != 0) {
+ if (ret == EADDRINUSE && *portp == 0 && retries++ < 100)
+ goto retry;
+ wg_socket_close(&so4);
+ return ret;
+ }
- /* Set wg_softc sockets to new values */
+ rw_enter_write(&sc->sc_so_lock);
+ wg_socket_close(&sc->sc_so4);
+ wg_socket_close(&sc->sc_so6);
sc->sc_so4 = so4;
sc->sc_so6 = so6;
-
rw_exit_write(&sc->sc_so_lock);
+
+ *portp = port;
+ *rtablep = rtable;
return 0;
-error:
- rw_exit_write(&sc->sc_so_lock);
- wg_unbind(sc);
- return ret;
}
void
wg_unbind(struct wg_softc *sc)
{
- struct socket *so4;
- struct socket *so6;
-
rw_enter_write(&sc->sc_so_lock);
- so4 = sc->sc_so4;
- so6 = sc->sc_so6;
- sc->sc_so4 = NULL;
- sc->sc_so6 = NULL;
-
- if (so4 != NULL && soclose(so4, 0) != 0)
- panic("Unable to close wg socket");
- if (so6 != NULL && soclose(so6, 0) != 0)
- panic("Unable to close wg socket");
+ wg_socket_close(&sc->sc_so4);
+ wg_socket_close(&sc->sc_so6);
rw_exit_write(&sc->sc_so_lock);
}
@@ -2081,9 +2083,12 @@ wg_ioctl_set(struct wg_softc *sc, struct wg_data_io *data)
struct wg_peer *peer, *tpeer;
struct wg_aip *aip, *taip;
+ in_port_t port;
+ int rtable;
+
uint8_t public[WG_KEY_SIZE], private[WG_KEY_SIZE];
size_t i, j;
- int ret, has_identity, already_set_rtable = false;
+ int ret, has_identity;
if ((ret = suser(curproc)) != 0)
return ret;
@@ -2118,47 +2123,26 @@ wg_ioctl_set(struct wg_softc *sc, struct wg_data_io *data)
noise_local_unlock_identity(&sc->sc_local);
}
- if (iface_o.i_flags & WG_INTERFACE_HAS_PORT &&
- sc->sc_udp_port != htons(iface_o.i_port)) {
- sc->sc_udp_port = htons(iface_o.i_port);
- if (iface_o.i_flags & WG_INTERFACE_HAS_RTABLE)
- sc->sc_udp_rtable = iface_o.i_rtable;
- if (ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
- wg_unbind(sc);
- if ((ret = wg_bind(sc)) != 0)
- goto error;
- already_set_rtable = true;
+ if (iface_o.i_flags & WG_INTERFACE_HAS_PORT)
+ port = htons(iface_o.i_port);
+ else
+ port = sc->sc_udp_port;
- }
- }
- if (iface_o.i_flags & WG_INTERFACE_HAS_RTABLE &&
- sc->sc_udp_rtable != iface_o.i_rtable && !already_set_rtable) {
- sc->sc_udp_rtable = iface_o.i_rtable;
- if (ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
- struct socket *so4, *so6;
- struct mbuf rtable;
- m_inithdr(&rtable);
- bzero(mtod(&rtable, u_int *), sizeof(u_int));
- *mtod(&rtable, u_int *) = sc->sc_udp_rtable;
- rtable.m_len = sizeof(u_int);
- NET_LOCK();
- so4 = sc->sc_so4;
- so6 = sc->sc_so6;
- ret = 0;
- if (so4)
- ret = sosetopt(so4, SOL_SOCKET, SO_RTABLE, &rtable);
- if (so6 && !ret)
- ret = sosetopt(so6, SOL_SOCKET, SO_RTABLE, &rtable);
- if (so4)
- sc->sc_udp_rtable = sotoinpcb(so4)->inp_rtableid;
- if (so6)
- sc->sc_udp_rtable = sotoinpcb(so6)->inp_rtableid;
- NET_UNLOCK();
- WG_PEERS_FOREACH(peer, sc)
- wg_peer_clear_src(peer);
- if (ret)
+ if (iface_o.i_flags & WG_INTERFACE_HAS_PORT)
+ rtable = iface_o.i_rtable;
+ else
+ rtable = sc->sc_udp_rtable;
+
+ if (port != sc->sc_udp_port || rtable != sc->sc_udp_rtable) {
+ WG_PEERS_FOREACH(peer, sc)
+ wg_peer_clear_src(peer);
+
+ if (sc->sc_if.if_flags & IFF_RUNNING)
+ if ((ret = wg_bind(sc, &port, &rtable)) != 0)
goto error;
- }
+
+ sc->sc_udp_port = port;
+ sc->sc_udp_rtable = rtable;
}
peer_p = &iface_p->i_peers[0];
@@ -2409,7 +2393,8 @@ wg_up(struct wg_softc *sc)
/* If we successfully bind the socket, then enable the timers
* for the peer. This will send all staged packets and a
* keepalive if necessary. */
- if ((ret = wg_bind(sc)) == 0) {
+ ret = wg_bind(sc, &sc->sc_udp_port, &sc->sc_udp_rtable);
+ if (ret == 0) {
WG_PEERS_FOREACH(peer, sc) {
wg_timers_enable(&peer->p_timers);
wg_queue_out(sc, peer);