mirror of
git://git.zx2c4.com/wireguard-freebsd
synced 2024-09-29 05:49:44 +02:00
if_wg: remove peer marshalling from get request
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
1a3412f78d
commit
0687984e44
5
TODO.md
5
TODO.md
@ -2,11 +2,6 @@
|
||||
|
||||
- Finish porting [this script](https://git.zx2c4.com/wireguard-linux/tree/tools/testing/selftests/wireguard/netns.sh)
|
||||
to `./tests/netns.sh` using vnets and epairs.
|
||||
- Marshall peers directly to nvlists without going through intermediate
|
||||
structures (`wg_peer_export`) and the like. This means grappling with the use
|
||||
of epoch there, and either making nvl allocations not sleep, or doing nvl
|
||||
allocations outside of epoch critical sections, or possibly deciding epoch
|
||||
doesn't actually matter there because we're holding an sx.
|
||||
- Rework locking and epoch lifetimes; come up with consistent set of rules.
|
||||
- Chop off padding on rx after verifying lengths, so that tcpdump doesn't see
|
||||
zeros.
|
||||
|
403
src/if_wg.c
403
src/if_wg.c
@ -359,19 +359,6 @@ struct wg_timespec64 {
|
||||
uint64_t tv_nsec;
|
||||
};
|
||||
|
||||
struct wg_peer_export {
|
||||
struct sockaddr_storage endpoint;
|
||||
struct timespec last_handshake;
|
||||
uint8_t public_key[WG_KEY_SIZE];
|
||||
uint8_t preshared_key[NOISE_SYMMETRIC_KEY_LEN];
|
||||
size_t endpoint_sz;
|
||||
struct wg_allowedip *aip;
|
||||
uint64_t rx_bytes;
|
||||
uint64_t tx_bytes;
|
||||
int aip_count;
|
||||
uint16_t persistent_keepalive;
|
||||
};
|
||||
|
||||
static struct wg_tag *wg_tag_get(struct mbuf *);
|
||||
static struct wg_endpoint *wg_mbuf_endpoint_get(struct mbuf *);
|
||||
static int wg_socket_init(struct wg_softc *, in_port_t);
|
||||
@ -403,7 +390,7 @@ static void wg_timers_init(struct wg_timers *);
|
||||
static void wg_timers_enable(struct wg_timers *);
|
||||
static void wg_timers_disable(struct wg_timers *);
|
||||
static void wg_timers_set_persistent_keepalive(struct wg_timers *, uint16_t);
|
||||
static void wg_timers_get_last_handshake(struct wg_timers *, struct timespec *);
|
||||
static void wg_timers_get_last_handshake(struct wg_timers *, struct wg_timespec64 *);
|
||||
static int wg_timers_expired_handshake_last_sent(struct wg_timers *);
|
||||
static int wg_timers_check_handshake_last_sent(struct wg_timers *);
|
||||
static void wg_queue_init(struct wg_queue *, const char *);
|
||||
@ -464,7 +451,6 @@ static void wg_qflush(struct ifnet *);
|
||||
static int wg_transmit(struct ifnet *, struct mbuf *);
|
||||
static int wg_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *);
|
||||
static void wg_clone_destroy(struct ifnet *);
|
||||
static int wg_peer_to_export(struct wg_peer *, struct wg_peer_export *);
|
||||
static bool wgc_privileged(struct wg_softc *);
|
||||
static int wgc_get(struct wg_softc *, struct wg_data_io *);
|
||||
static int wgc_set(struct wg_softc *, struct wg_data_io *);
|
||||
@ -1275,7 +1261,7 @@ wg_timers_set_persistent_keepalive(struct wg_timers *t, uint16_t interval)
|
||||
}
|
||||
|
||||
static void
|
||||
wg_timers_get_last_handshake(struct wg_timers *t, struct timespec *time)
|
||||
wg_timers_get_last_handshake(struct wg_timers *t, struct wg_timespec64 *time)
|
||||
{
|
||||
rw_rlock(&t->t_lock);
|
||||
time->tv_sec = t->t_handshake_complete.tv_sec;
|
||||
@ -2771,297 +2757,30 @@ wgc_set(struct wg_softc *sc, struct wg_data_io *wgd)
|
||||
|
||||
nvlist_destroy(nvl);
|
||||
out:
|
||||
explicit_bzero(nvlpacked, wgd->wgd_size);
|
||||
free(nvlpacked, M_TEMP);
|
||||
sx_xunlock(&sc->sc_lock);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
in_mask2len(struct in_addr *mask)
|
||||
{
|
||||
unsigned int x, y;
|
||||
uint8_t *p;
|
||||
|
||||
p = (uint8_t *)mask;
|
||||
for (x = 0; x < sizeof(*mask); x++) {
|
||||
if (p[x] != 0xff)
|
||||
break;
|
||||
}
|
||||
y = 0;
|
||||
if (x < sizeof(*mask)) {
|
||||
for (y = 0; y < NBBY; y++) {
|
||||
if ((p[x] & (0x80 >> y)) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return x * NBBY + y;
|
||||
}
|
||||
|
||||
static int
|
||||
wg_peer_to_export(struct wg_peer *peer, struct wg_peer_export *exp)
|
||||
{
|
||||
struct wg_endpoint *ep;
|
||||
struct wg_aip *rt;
|
||||
struct noise_remote *remote;
|
||||
int i;
|
||||
|
||||
/* Non-sleepable context. */
|
||||
NET_EPOCH_ASSERT();
|
||||
|
||||
bzero(&exp->endpoint, sizeof(exp->endpoint));
|
||||
remote = &peer->p_remote;
|
||||
ep = &peer->p_endpoint;
|
||||
if (ep->e_remote.r_sa.sa_family != 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* We always export it. */
|
||||
(void)noise_remote_keys(remote, exp->public_key, exp->preshared_key);
|
||||
exp->persistent_keepalive =
|
||||
peer->p_timers.t_persistent_keepalive_interval;
|
||||
wg_timers_get_last_handshake(&peer->p_timers, &exp->last_handshake);
|
||||
exp->rx_bytes = counter_u64_fetch(peer->p_rx_bytes);
|
||||
exp->tx_bytes = counter_u64_fetch(peer->p_tx_bytes);
|
||||
|
||||
exp->aip_count = 0;
|
||||
CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) {
|
||||
exp->aip_count++;
|
||||
}
|
||||
|
||||
/* Early success; no allowed-ips to copy out. */
|
||||
if (exp->aip_count == 0)
|
||||
return (0);
|
||||
|
||||
exp->aip = mallocarray(exp->aip_count, sizeof(*exp->aip), M_TEMP, M_NOWAIT);
|
||||
if (exp->aip == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
i = 0;
|
||||
CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) {
|
||||
exp->aip[i].family = rt->r_addr.ss_family;
|
||||
if (exp->aip[i].family == AF_INET) {
|
||||
struct sockaddr_in *sin =
|
||||
(struct sockaddr_in *)&rt->r_addr;
|
||||
|
||||
exp->aip[i].ip4 = sin->sin_addr;
|
||||
|
||||
sin = (struct sockaddr_in *)&rt->r_mask;
|
||||
exp->aip[i].cidr = in_mask2len(&sin->sin_addr);
|
||||
} else if (exp->aip[i].family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 =
|
||||
(struct sockaddr_in6 *)&rt->r_addr;
|
||||
|
||||
exp->aip[i].ip6 = sin6->sin6_addr;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *)&rt->r_mask;
|
||||
exp->aip[i].cidr = in6_mask2len(&sin6->sin6_addr, NULL);
|
||||
}
|
||||
i++;
|
||||
if (i == exp->aip_count)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Again, AllowedIPs might have shrank; update it. */
|
||||
exp->aip_count = i;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static nvlist_t *
|
||||
wg_peer_export_to_nvl(struct wg_softc *sc, struct wg_peer_export *exp)
|
||||
{
|
||||
struct wg_timespec64 ts64;
|
||||
nvlist_t *nvl, **nvl_aips;
|
||||
size_t i;
|
||||
uint16_t family;
|
||||
|
||||
nvl_aips = NULL;
|
||||
if ((nvl = nvlist_create(0)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
nvlist_add_binary(nvl, "public-key", exp->public_key,
|
||||
sizeof(exp->public_key));
|
||||
if (wgc_privileged(sc))
|
||||
nvlist_add_binary(nvl, "preshared-key", exp->preshared_key,
|
||||
sizeof(exp->preshared_key));
|
||||
if (exp->endpoint_sz != 0)
|
||||
nvlist_add_binary(nvl, "endpoint", &exp->endpoint,
|
||||
exp->endpoint_sz);
|
||||
|
||||
if (exp->aip_count != 0) {
|
||||
nvl_aips = mallocarray(exp->aip_count, sizeof(*nvl_aips),
|
||||
M_WG, M_WAITOK | M_ZERO);
|
||||
}
|
||||
|
||||
for (i = 0; i < exp->aip_count; i++) {
|
||||
nvl_aips[i] = nvlist_create(0);
|
||||
if (nvl_aips[i] == NULL)
|
||||
goto err;
|
||||
family = exp->aip[i].family;
|
||||
nvlist_add_number(nvl_aips[i], "cidr", exp->aip[i].cidr);
|
||||
if (family == AF_INET)
|
||||
nvlist_add_binary(nvl_aips[i], "ipv4",
|
||||
&exp->aip[i].ip4, sizeof(exp->aip[i].ip4));
|
||||
else if (family == AF_INET6)
|
||||
nvlist_add_binary(nvl_aips[i], "ipv6",
|
||||
&exp->aip[i].ip6, sizeof(exp->aip[i].ip6));
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
nvlist_add_nvlist_array(nvl, "allowed-ips",
|
||||
(const nvlist_t *const *)nvl_aips, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < exp->aip_count; ++i)
|
||||
nvlist_destroy(nvl_aips[i]);
|
||||
|
||||
free(nvl_aips, M_WG);
|
||||
nvl_aips = NULL;
|
||||
|
||||
ts64.tv_sec = exp->last_handshake.tv_sec;
|
||||
ts64.tv_nsec = exp->last_handshake.tv_nsec;
|
||||
nvlist_add_binary(nvl, "last-handshake-time", &ts64, sizeof(ts64));
|
||||
|
||||
if (exp->persistent_keepalive != 0)
|
||||
nvlist_add_number(nvl, "persistent-keepalive-interval",
|
||||
exp->persistent_keepalive);
|
||||
|
||||
if (exp->rx_bytes != 0)
|
||||
nvlist_add_number(nvl, "rx-bytes", exp->rx_bytes);
|
||||
if (exp->tx_bytes != 0)
|
||||
nvlist_add_number(nvl, "tx-bytes", exp->tx_bytes);
|
||||
|
||||
return (nvl);
|
||||
err:
|
||||
for (i = 0; i < exp->aip_count && nvl_aips[i] != NULL; i++) {
|
||||
nvlist_destroy(nvl_aips[i]);
|
||||
}
|
||||
|
||||
free(nvl_aips, M_WG);
|
||||
nvlist_destroy(nvl);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, int *peer_countp)
|
||||
{
|
||||
struct wg_peer *peer;
|
||||
int err, i, peer_count;
|
||||
nvlist_t *nvl, **nvl_array;
|
||||
struct epoch_tracker et;
|
||||
struct wg_peer_export *wpe;
|
||||
|
||||
nvl = NULL;
|
||||
nvl_array = NULL;
|
||||
if (nvl_arrayp)
|
||||
*nvl_arrayp = NULL;
|
||||
if (nvlp)
|
||||
*nvlp = NULL;
|
||||
if (peer_countp)
|
||||
*peer_countp = 0;
|
||||
peer_count = sc->sc_hashtable.h_num_peers;
|
||||
if (peer_count == 0) {
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
if (nvlp && (nvl = nvlist_create(0)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
err = i = 0;
|
||||
nvl_array = mallocarray(peer_count, sizeof(void *), M_TEMP, M_WAITOK | M_ZERO);
|
||||
wpe = mallocarray(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) {
|
||||
if ((err = wg_peer_to_export(peer, &wpe[i])) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
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) {
|
||||
err = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < peer_count; i++) {
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Peers are added to the list in reverse order, effectively,
|
||||
* because it's simpler/quicker to add at the head every time.
|
||||
*
|
||||
* Export them in reverse order. No worries if we fail mid-way
|
||||
* through, the cleanup below will DTRT.
|
||||
*/
|
||||
idx = peer_count - i - 1;
|
||||
nvl_array[idx] = wg_peer_export_to_nvl(sc, &wpe[i]);
|
||||
if (nvl_array[idx] == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < peer_count) {
|
||||
/* Error! */
|
||||
*peer_countp = 0;
|
||||
err = ENOMEM;
|
||||
} else if (nvl) {
|
||||
nvlist_add_nvlist_array(nvl, "peers",
|
||||
(const nvlist_t * const *)nvl_array, peer_count);
|
||||
if ((err = nvlist_error(nvl))) {
|
||||
goto out;
|
||||
}
|
||||
*nvlp = nvl;
|
||||
}
|
||||
*nvl_arrayp = nvl_array;
|
||||
out:
|
||||
if (err != 0) {
|
||||
if (nvl_array) {
|
||||
/* Note that nvl_array is populated in reverse order. */
|
||||
for (i = 0; i < peer_count; i++) {
|
||||
nvlist_destroy(nvl_array[i]);
|
||||
}
|
||||
free(nvl_array, M_TEMP);
|
||||
}
|
||||
if (nvl)
|
||||
nvlist_destroy(nvl);
|
||||
}
|
||||
if (wpe) {
|
||||
for (i = 0; i < peer_count; i++)
|
||||
free(wpe[i].aip, M_TEMP);
|
||||
free(wpe, M_TEMP);
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
wgc_get(struct wg_softc *sc, struct wg_data_io *wgd)
|
||||
{
|
||||
nvlist_t *nvl, **nvl_array;
|
||||
uint8_t public_key[WG_KEY_SIZE] = { 0 }, preshared_key[NOISE_SYMMETRIC_KEY_LEN] = { 0 };
|
||||
nvlist_t *nvl, *nvl_peer, *nvl_aip, **nvl_peers, **nvl_aips;
|
||||
size_t size, peer_count, aip_count, i, j;
|
||||
struct wg_timespec64 ts64;
|
||||
struct wg_peer *peer;
|
||||
struct wg_aip *rt;
|
||||
void *packed;
|
||||
size_t size;
|
||||
int peer_count, err;
|
||||
int err = 0;
|
||||
|
||||
nvl = nvlist_create(0);
|
||||
if (nvl == NULL)
|
||||
return (ENOMEM);
|
||||
if (!nvl)
|
||||
return ENOMEM;
|
||||
|
||||
sx_slock(&sc->sc_lock);
|
||||
|
||||
err = 0;
|
||||
packed = NULL;
|
||||
if (sc->sc_socket.so_port != 0)
|
||||
nvlist_add_number(nvl, "listen-port", sc->sc_socket.so_port);
|
||||
if (sc->sc_socket.so_user_cookie != 0)
|
||||
@ -3071,39 +2790,107 @@ wgc_get(struct wg_softc *sc, struct wg_data_io *wgd)
|
||||
if (wgc_privileged(sc))
|
||||
nvlist_add_binary(nvl, "private-key", sc->sc_local.l_private, WG_KEY_SIZE);
|
||||
}
|
||||
if (sc->sc_hashtable.h_num_peers > 0) {
|
||||
err = wg_marshal_peers(sc, NULL, &nvl_array, &peer_count);
|
||||
peer_count = sc->sc_hashtable.h_num_peers;
|
||||
if (peer_count) {
|
||||
nvl_peers = mallocarray(peer_count, sizeof(void *), M_NVLIST, M_WAITOK | M_ZERO);
|
||||
i = 0;
|
||||
CK_LIST_FOREACH(peer, &sc->sc_hashtable.h_peers_list, p_entry) {
|
||||
if (i >= peer_count)
|
||||
panic("peers changed from under us");
|
||||
|
||||
nvl_peers[i++] = nvl_peer = nvlist_create(0);
|
||||
if (!nvl_peer) {
|
||||
err = ENOMEM;
|
||||
goto err_peer;
|
||||
}
|
||||
|
||||
(void)noise_remote_keys(&peer->p_remote, public_key, preshared_key);
|
||||
nvlist_add_binary(nvl_peer, "public-key", public_key, sizeof(public_key));
|
||||
if (wgc_privileged(sc))
|
||||
nvlist_add_binary(nvl_peer, "preshared-key", preshared_key, sizeof(preshared_key));
|
||||
explicit_bzero(preshared_key, sizeof(preshared_key));
|
||||
if (peer->p_endpoint.e_remote.r_sa.sa_family == AF_INET)
|
||||
nvlist_add_binary(nvl_peer, "endpoint", &peer->p_endpoint.e_remote, sizeof(struct sockaddr_in));
|
||||
else if (peer->p_endpoint.e_remote.r_sa.sa_family == AF_INET6)
|
||||
nvlist_add_binary(nvl_peer, "endpoint", &peer->p_endpoint.e_remote, sizeof(struct sockaddr_in6));
|
||||
wg_timers_get_last_handshake(&peer->p_timers, &ts64);
|
||||
nvlist_add_binary(nvl_peer, "last-handshake-time", &ts64, sizeof(ts64));
|
||||
nvlist_add_number(nvl_peer, "persistent-keepalive-interval", peer->p_timers.t_persistent_keepalive_interval);
|
||||
nvlist_add_number(nvl_peer, "rx-bytes", counter_u64_fetch(peer->p_rx_bytes));
|
||||
nvlist_add_number(nvl_peer, "tx-bytes", counter_u64_fetch(peer->p_tx_bytes));
|
||||
|
||||
aip_count = 0;
|
||||
CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) {
|
||||
++aip_count;
|
||||
}
|
||||
if (aip_count) {
|
||||
nvl_aips = mallocarray(aip_count, sizeof(void *), M_NVLIST, M_WAITOK | M_ZERO);
|
||||
j = 0;
|
||||
CK_LIST_FOREACH(rt, &peer->p_aips, r_entry) {
|
||||
if (j >= aip_count)
|
||||
panic("aips changed from under us");
|
||||
|
||||
nvl_aips[j++] = nvl_aip = nvlist_create(0);
|
||||
if (!nvl_aip) {
|
||||
err = ENOMEM;
|
||||
goto err_aip;
|
||||
}
|
||||
if (rt->r_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&rt->r_addr;
|
||||
nvlist_add_binary(nvl_aip, "ipv4", &sin->sin_addr, sizeof(sin->sin_addr));
|
||||
sin = (struct sockaddr_in *)&rt->r_mask;
|
||||
nvlist_add_number(nvl_aip, "cidr", __builtin_popcount(sin->sin_addr.s_addr));
|
||||
} else if (rt->r_addr.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&rt->r_addr;
|
||||
nvlist_add_binary(nvl_aip, "ipv6", &sin6->sin6_addr, sizeof(sin6->sin6_addr));
|
||||
sin6 = (struct sockaddr_in6 *)&rt->r_mask;
|
||||
nvlist_add_number(nvl_aip, "cidr", in6_mask2len(&sin6->sin6_addr, NULL));
|
||||
}
|
||||
}
|
||||
nvlist_add_nvlist_array(nvl_peer, "allowed-ips", (const nvlist_t *const *)nvl_aips, aip_count);
|
||||
err_aip:
|
||||
for (j = 0; j < aip_count; ++j)
|
||||
nvlist_destroy(nvl_aips[j]);
|
||||
free(nvl_aips, M_NVLIST);
|
||||
if (err)
|
||||
goto err_peer;
|
||||
}
|
||||
}
|
||||
nvlist_add_nvlist_array(nvl, "peers", (const nvlist_t * const *)nvl_peers, peer_count);
|
||||
err_peer:
|
||||
for (i = 0; i < peer_count; ++i)
|
||||
nvlist_destroy(nvl_peers[i]);
|
||||
free(nvl_peers, M_NVLIST);
|
||||
if (err)
|
||||
goto out_nvl;
|
||||
nvlist_add_nvlist_array(nvl, "peers",
|
||||
(const nvlist_t * const *)nvl_array, peer_count);
|
||||
goto err;
|
||||
}
|
||||
packed = nvlist_pack(nvl, &size);
|
||||
if (packed == NULL) {
|
||||
if (!packed) {
|
||||
err = ENOMEM;
|
||||
goto out_nvl;
|
||||
goto err;
|
||||
}
|
||||
if (wgd->wgd_size == 0) {
|
||||
if (!wgd->wgd_size) {
|
||||
wgd->wgd_size = size;
|
||||
goto out_packed;
|
||||
goto out;
|
||||
}
|
||||
if (wgd->wgd_size < size) {
|
||||
err = ENOSPC;
|
||||
goto out_packed;
|
||||
goto out;
|
||||
}
|
||||
if (wgd->wgd_data == NULL) {
|
||||
if (!wgd->wgd_data) {
|
||||
err = EFAULT;
|
||||
goto out_packed;
|
||||
goto out;
|
||||
}
|
||||
err = copyout(packed, wgd->wgd_data, size);
|
||||
wgd->wgd_size = size;
|
||||
|
||||
out_packed:
|
||||
out:
|
||||
explicit_bzero(packed, size);
|
||||
free(packed, M_NVLIST);
|
||||
out_nvl:
|
||||
err:
|
||||
nvlist_destroy(nvl);
|
||||
sx_sunlock(&sc->sc_lock);
|
||||
return (err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
|
Loading…
Reference in New Issue
Block a user