aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2021-03-08 06:06:28 +0000
committerKyle Evans <kevans@FreeBSD.org>2021-03-09 11:16:30 +0000
commite80e371d79d825712a9078e0d1a14b62567291c4 (patch)
treef46093c385f3d26554c530eddf9239972ecd8286
parentbae59285f932d59ee9fd9d6a7c41d34ef8e51186 (diff)
downloadsrc-e80e371d79d825712a9078e0d1a14b62567291c4.tar.gz
src-e80e371d79d825712a9078e0d1a14b62567291c4.zip
if_wg: avoid sleeping under the net epoch
No sleeping allowed here, so avoid it. Collect the subset of data we want inside of the epoch, as we'll need extra allocations when we add items to the nvlist. Reviewed by: grehan (earlier version), markj MFC after: 3 days Differential Revision: https://reviews.freebsd.org/D29124
-rw-r--r--sys/dev/if_wg/module/module.c136
1 files changed, 99 insertions, 37 deletions
diff --git a/sys/dev/if_wg/module/module.c b/sys/dev/if_wg/module/module.c
index a40a304616c7..6ae3bf9db022 100644
--- a/sys/dev/if_wg/module/module.c
+++ b/sys/dev/if_wg/module/module.c
@@ -69,6 +69,14 @@ MALLOC_DEFINE(M_WG, "WG", "wireguard");
TASKQGROUP_DECLARE(if_io_tqg);
+struct wg_peer_export {
+ struct sockaddr_storage endpoint;
+ uint8_t public_key[WG_KEY_SIZE];
+ size_t endpoint_sz;
+ struct wg_allowedip *aip;
+ int aip_count;
+};
+
static int clone_count;
uma_zone_t ratelimit_zone;
@@ -386,37 +394,70 @@ wg_stop(if_ctx_t ctx)
wg_socket_reinit(sc, NULL, NULL);
}
-static nvlist_t *
-wg_peer_to_nvl(struct wg_peer *peer)
+static int
+wg_peer_to_export(struct wg_peer *peer, struct wg_peer_export *exp)
{
- struct wg_route *rt;
- int i, count;
- nvlist_t *nvl;
- caddr_t key;
- size_t sa_sz;
- struct wg_allowedip *aip;
struct wg_endpoint *ep;
+ struct wg_route *rt;
+ int i;
- if ((nvl = nvlist_create(0)) == NULL)
- return (NULL);
- key = peer->p_remote.r_public;
- nvlist_add_binary(nvl, "public-key", key, WG_KEY_SIZE);
+ /* Non-sleepable context. */
+ NET_EPOCH_ASSERT();
+
+ bzero(&exp->endpoint, sizeof(exp->endpoint));
ep = &peer->p_endpoint;
if (ep->e_remote.r_sa.sa_family != 0) {
- sa_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ?
- sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
- nvlist_add_binary(nvl, "endpoint", &ep->e_remote, sa_sz);
+ exp->endpoint_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ?
+ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+
+ memcpy(&exp->endpoint, &ep->e_remote, exp->endpoint_sz);
}
- i = count = 0;
+
+ memcpy(exp->public_key, peer->p_remote.r_public,
+ sizeof(exp->public_key));
+
+ exp->aip_count = 0;
CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
- count++;
+ exp->aip_count++;
}
- aip = malloc(count*sizeof(*aip), M_TEMP, M_WAITOK);
+
+ /* Early success; no allowed-ips to copy out. */
+ if (exp->aip_count == 0)
+ return (0);
+
+ exp->aip = malloc(exp->aip_count * sizeof(*exp->aip), M_TEMP, M_NOWAIT);
+ if (exp->aip == NULL)
+ return (ENOMEM);
+
+ i = 0;
CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
- memcpy(&aip[i++], &rt->r_cidr, sizeof(*aip));
+ memcpy(&exp->aip[i++], &rt->r_cidr, sizeof(*exp->aip));
+ if (i == exp->aip_count)
+ break;
}
- nvlist_add_binary(nvl, "allowed-ips", aip, count*sizeof(*aip));
- free(aip, M_TEMP);
+
+ /* Again, AllowedIPs might have shrank; update it. */
+ exp->aip_count = i;
+
+ return (0);
+}
+
+static nvlist_t *
+wg_peer_export_to_nvl(struct wg_peer_export *exp)
+{
+ nvlist_t *nvl;
+
+ if ((nvl = nvlist_create(0)) == NULL)
+ return (NULL);
+
+ nvlist_add_binary(nvl, "public-key", exp->public_key, WG_KEY_SIZE);
+ if (exp->endpoint_sz != 0)
+ nvlist_add_binary(nvl, "endpoint", &exp->endpoint,
+ exp->endpoint_sz);
+
+ nvlist_add_binary(nvl, "allowed-ips", exp->aip,
+ exp->aip_count * sizeof(*exp->aip));
+
return (nvl);
}
@@ -427,10 +468,8 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
int err, i, peer_count;
nvlist_t *nvl, **nvl_array;
struct epoch_tracker et;
-#ifdef INVARIANTS
- void *packed;
- size_t size;
-#endif
+ struct wg_peer_export *wpe;
+
nvl = NULL;
nvl_array = NULL;
if (nvl_arrayp)
@@ -447,34 +486,44 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
if (nvlp && (nvl = nvlist_create(0)) == NULL)
return (ENOMEM);
+
err = i = 0;
nvl_array = malloc(peer_count*sizeof(void*), M_TEMP, M_WAITOK);
+ wpe = malloc(peer_count*sizeof(*wpe), M_TEMP, M_WAITOK | M_ZERO);
+
NET_EPOCH_ENTER(et);
CK_LIST_FOREACH(peer, &sc->sc_hashtable.h_peers_list, p_entry) {
- nvl_array[i] = wg_peer_to_nvl(peer);
- if (nvl_array[i] == NULL) {
- printf("wg_peer_to_nvl failed on %d peer\n", i);
+ if ((err = wg_peer_to_export(peer, &wpe[i])) != 0) {
+ printf("wg_peer_to_export failed on %d peer, error %d\n",
+ i, err);
break;
}
-#ifdef INVARIANTS
- packed = nvlist_pack(nvl_array[i], &size);
- if (packed == NULL) {
- printf("nvlist_pack(%p, %p) => %d",
- nvl_array[i], &size, nvlist_error(nvl));
- }
- free(packed, M_NVLIST);
-#endif
+
i++;
if (i == peer_count)
break;
}
NET_EPOCH_EXIT(et);
+
+ if (err != 0)
+ goto out;
+
+ /* Update the peer count, in case we found fewer entries. */
*peer_countp = peer_count = i;
if (peer_count == 0) {
printf("no peers found in list\n");
err = ENOENT;
goto out;
}
+
+ for (i = 0; i < peer_count; i++) {
+ nvl_array[i] = wg_peer_export_to_nvl(&wpe[i]);
+ if (nvl_array[i] == NULL) {
+ printf("wg_peer_export_to_nvl failed on %d peer\n", i);
+ break;
+ }
+ }
+
if (nvl) {
nvlist_add_nvlist_array(nvl, "peer-list",
(const nvlist_t * const *)nvl_array, peer_count);
@@ -486,8 +535,21 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
*nvlp = nvl;
}
*nvl_arrayp = nvl_array;
- return (0);
+ err = 0;
out:
+ if (err != 0) {
+ for (i = 0; i < peer_count; i++) {
+ nvlist_destroy(nvl_array[i]);
+ }
+
+ free(nvl_array, M_TEMP);
+ if (nvl != NULL)
+ nvlist_destroy(nvl);
+ }
+
+ for (i = 0; i < peer_count; i++)
+ free(wpe[i].aip, M_TEMP);
+ free(wpe, M_TEMP);
return (err);
}