2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019-2020 Rubicon Communications, LLC (Netgate)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 #include "opt_inet6.h"
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
39 #include <sys/mutex.h>
41 #include <sys/module.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/queue.h>
49 #include <net/ethernet.h>
50 #include <net/if_var.h>
51 #include <net/iflib.h>
52 #include <net/if_clone.h>
53 #include <net/radix.h>
55 #include <net/mp_ring.h>
59 #include <sys/wg_module.h>
60 #include <crypto/zinc.h>
61 #include <sys/wg_noise.h>
62 #include <sys/if_wg_session_vars.h>
63 #include <sys/if_wg_session.h>
65 MALLOC_DEFINE(M_WG, "WG", "wireguard");
67 #define WG_CAPS IFCAP_LINKSTATE
68 #define ph_family PH_loc.eight[5]
70 TASKQGROUP_DECLARE(if_io_tqg);
72 static int clone_count;
73 uma_zone_t ratelimit_zone;
76 wg_encrypt_dispatch(struct wg_softc *sc)
78 for (int i = 0; i < mp_ncpus; i++) {
79 if (sc->sc_encrypt[i].gt_task.ta_flags & TASK_ENQUEUED)
81 GROUPTASK_ENQUEUE(&sc->sc_encrypt[i]);
86 wg_decrypt_dispatch(struct wg_softc *sc)
88 for (int i = 0; i < mp_ncpus; i++) {
89 if (sc->sc_decrypt[i].gt_task.ta_flags & TASK_ENQUEUED)
91 GROUPTASK_ENQUEUE(&sc->sc_decrypt[i]);
96 crypto_taskq_setup(struct wg_softc *sc)
98 device_t dev = iflib_get_dev(sc->wg_ctx);
100 sc->sc_encrypt = malloc(sizeof(struct grouptask)*mp_ncpus, M_WG, M_WAITOK);
101 sc->sc_decrypt = malloc(sizeof(struct grouptask)*mp_ncpus, M_WG, M_WAITOK);
103 for (int i = 0; i < mp_ncpus; i++) {
104 GROUPTASK_INIT(&sc->sc_encrypt[i], 0,
105 (gtask_fn_t *)wg_softc_encrypt, sc);
106 taskqgroup_attach_cpu(qgroup_if_io_tqg, &sc->sc_encrypt[i], sc, i, dev, NULL, "wg encrypt");
107 GROUPTASK_INIT(&sc->sc_decrypt[i], 0,
108 (gtask_fn_t *)wg_softc_decrypt, sc);
109 taskqgroup_attach_cpu(qgroup_if_io_tqg, &sc->sc_decrypt[i], sc, i, dev, NULL, "wg decrypt");
114 crypto_taskq_destroy(struct wg_softc *sc)
116 for (int i = 0; i < mp_ncpus; i++) {
117 taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_encrypt[i]);
118 taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_decrypt[i]);
120 free(sc->sc_encrypt, M_WG);
121 free(sc->sc_decrypt, M_WG);
125 wg_cloneattach(if_ctx_t ctx, struct if_clone *ifc, const char *name, caddr_t params)
127 struct wg_softc *sc = iflib_get_softc(ctx);
128 if_softc_ctx_t scctx;
133 struct noise_local *local;
134 uint8_t public[WG_KEY_SIZE];
135 struct noise_upcall noise_upcall;
137 uint16_t listen_port;
142 dev = iflib_get_dev(ctx);
143 if (params == NULL) {
150 if (copyin(params, &iov, sizeof(iov)))
152 /* check that this is reasonable */
154 packed = malloc(size, M_TEMP, M_WAITOK);
155 if (copyin(iov.iov_base, packed, size)) {
159 nvl = nvlist_unpack(packed, size, 0);
161 device_printf(dev, "%s nvlist_unpack failed\n", __func__);
165 if (!nvlist_exists_number(nvl, "listen-port")) {
166 device_printf(dev, "%s listen-port not set\n", __func__);
170 listen_port = nvlist_get_number(nvl, "listen-port");
172 if (!nvlist_exists_binary(nvl, "private-key")) {
173 device_printf(dev, "%s private-key not set\n", __func__);
177 key = nvlist_get_binary(nvl, "private-key", &size);
178 if (size != CURVE25519_KEY_SIZE) {
179 device_printf(dev, "%s bad length for private-key %zu\n", __func__, size);
184 local = &sc->sc_local;
185 noise_upcall.u_arg = sc;
186 noise_upcall.u_remote_get =
187 (struct noise_remote *(*)(void *, uint8_t *))wg_remote_get;
188 noise_upcall.u_index_set =
189 (uint32_t (*)(void *, struct noise_remote *))wg_index_set;
190 noise_upcall.u_index_drop =
191 (void (*)(void *, uint32_t))wg_index_drop;
192 noise_local_init(local, &noise_upcall);
193 cookie_checker_init(&sc->sc_cookie, ratelimit_zone);
195 sc->sc_socket.so_port = listen_port;
198 noise_local_set_private(local, __DECONST(uint8_t *, key));
199 noise_local_keys(local, public, NULL);
200 cookie_checker_update(&sc->sc_cookie, public);
202 atomic_add_int(&clone_count, 1);
203 scctx = sc->shared = iflib_get_softc_ctx(ctx);
204 scctx->isc_capenable = WG_CAPS;
205 scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_TSO | CSUM_IP6_TCP \
206 | CSUM_IP6_UDP | CSUM_IP6_TCP;
208 sc->sc_ifp = iflib_get_ifp(ctx);
210 mbufq_init(&sc->sc_handshake_queue, MAX_QUEUED_INCOMING_HANDSHAKES);
211 mtx_init(&sc->sc_mtx, NULL, "wg softc lock", MTX_DEF);
212 rw_init(&sc->sc_index_lock, "wg index lock");
213 sc->sc_encap_ring = buf_ring_alloc(MAX_QUEUED_PACKETS, M_WG, M_WAITOK, &sc->sc_mtx);
214 sc->sc_decap_ring = buf_ring_alloc(MAX_QUEUED_PACKETS, M_WG, M_WAITOK, &sc->sc_mtx);
215 GROUPTASK_INIT(&sc->sc_handshake, 0,
216 (gtask_fn_t *)wg_softc_handshake_receive, sc);
217 taskqgroup_attach(qgroup_if_io_tqg, &sc->sc_handshake, sc, dev, NULL, "wg tx initiation");
218 crypto_taskq_setup(sc);
223 free(packed, M_TEMP);
228 wg_transmit(struct ifnet *ifp, struct mbuf *m)
232 struct epoch_tracker et;
233 struct wg_peer *peer;
240 * Work around lifetime issue in the ipv6 mld code.
242 if (__predict_false(ifp->if_flags & IFF_DYING))
246 sc = iflib_get_softc(ifp->if_softc);
247 if ((t = wg_tag_get(m)) == NULL) {
251 af = m->m_pkthdr.ph_family;
252 BPF_MTAP2(ifp, &af, sizeof(af), m);
255 peer = wg_route_lookup(&sc->sc_routes, m, OUT);
256 if (__predict_false(peer == NULL)) {
262 family = atomic_load_acq(peer->p_endpoint.e_remote.r_sa.sa_family);
263 if (__predict_false(family != AF_INET && family != AF_INET6)) {
271 t->t_mtu = ifp->if_mtu;
273 rc = wg_queue_out(peer, m);
275 wg_encrypt_dispatch(peer->p_sc);
281 if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1);
282 /* XXX send ICMP unreachable */
288 wg_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, struct route *rt)
290 m->m_pkthdr.ph_family = sa->sa_family;
291 return (wg_transmit(ifp, m));
295 wg_attach_post(if_ctx_t ctx)
300 sc = iflib_get_softc(ctx);
301 ifp = iflib_get_ifp(ctx);
302 if_setmtu(ifp, ETHERMTU - 80);
304 if_setflagbits(ifp, IFF_NOARP, IFF_POINTOPOINT);
305 ifp->if_transmit = wg_transmit;
306 ifp->if_output = wg_output;
308 wg_hashtable_init(&sc->sc_hashtable);
309 sc->sc_index = hashinit(HASHTABLE_INDEX_SIZE, M_DEVBUF, &sc->sc_index_mask);
310 wg_route_init(&sc->sc_routes);
316 wg_mtu_set(if_ctx_t ctx, uint32_t mtu)
323 wg_set_promisc(if_ctx_t ctx, int flags)
330 wg_detach(if_ctx_t ctx)
334 sc = iflib_get_softc(ctx);
335 if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN);
337 wg_socket_reinit(sc, NULL, NULL);
338 taskqgroup_drain_all(qgroup_if_io_tqg);
339 pause("link_down", hz/4);
340 wg_peer_remove_all(sc);
341 pause("link_down", hz);
342 mtx_destroy(&sc->sc_mtx);
343 rw_destroy(&sc->sc_index_lock);
344 taskqgroup_detach(qgroup_if_io_tqg, &sc->sc_handshake);
345 crypto_taskq_destroy(sc);
346 buf_ring_free(sc->sc_encap_ring, M_WG);
347 buf_ring_free(sc->sc_decap_ring, M_WG);
349 wg_route_destroy(&sc->sc_routes);
350 wg_hashtable_destroy(&sc->sc_hashtable);
351 atomic_add_int(&clone_count, -1);
356 wg_init(if_ctx_t ctx)
362 if (iflib_in_detach(ctx))
365 sc = iflib_get_softc(ctx);
366 ifp = iflib_get_ifp(ctx);
367 if (sc->sc_socket.so_so4 != NULL)
368 printf("XXX wg_init, socket non-NULL %p\n",
369 sc->sc_socket.so_so4);
370 wg_socket_reinit(sc, NULL, NULL);
371 rc = wg_socket_init(sc);
374 if_link_state_change(ifp, LINK_STATE_UP);
378 wg_stop(if_ctx_t ctx)
383 sc = iflib_get_softc(ctx);
384 ifp = iflib_get_ifp(ctx);
385 if_link_state_change(ifp, LINK_STATE_DOWN);
386 wg_socket_reinit(sc, NULL, NULL);
390 wg_peer_to_nvl(struct wg_peer *peer)
397 struct wg_allowedip *aip;
398 struct wg_endpoint *ep;
400 if ((nvl = nvlist_create(0)) == NULL)
402 key = peer->p_remote.r_public;
403 nvlist_add_binary(nvl, "public-key", key, WG_KEY_SIZE);
404 ep = &peer->p_endpoint;
405 if (ep->e_remote.r_sa.sa_family != 0) {
406 sa_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ?
407 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
408 nvlist_add_binary(nvl, "endpoint", &ep->e_remote, sa_sz);
411 CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
414 aip = malloc(count*sizeof(*aip), M_TEMP, M_WAITOK);
415 CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
416 memcpy(&aip[i++], &rt->r_cidr, sizeof(*aip));
418 nvlist_add_binary(nvl, "allowed-ips", aip, count*sizeof(*aip));
424 wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, int *peer_countp)
426 struct wg_peer *peer;
427 int err, i, peer_count;
428 nvlist_t *nvl, **nvl_array;
429 struct epoch_tracker et;
442 peer_count = sc->sc_hashtable.h_num_peers;
443 if (peer_count == 0) {
444 printf("no peers found\n");
448 if (nvlp && (nvl = nvlist_create(0)) == NULL)
451 nvl_array = malloc(peer_count*sizeof(void*), M_TEMP, M_WAITOK);
453 CK_LIST_FOREACH(peer, &sc->sc_hashtable.h_peers_list, p_entry) {
454 nvl_array[i] = wg_peer_to_nvl(peer);
455 if (nvl_array[i] == NULL) {
456 printf("wg_peer_to_nvl failed on %d peer\n", i);
460 packed = nvlist_pack(nvl_array[i], &size);
461 if (packed == NULL) {
462 printf("nvlist_pack(%p, %p) => %d",
463 nvl_array[i], &size, nvlist_error(nvl));
465 free(packed, M_NVLIST);
472 *peer_countp = peer_count = i;
473 if (peer_count == 0) {
474 printf("no peers found in list\n");
479 nvlist_add_nvlist_array(nvl, "peer-list",
480 (const nvlist_t * const *)nvl_array, peer_count);
481 if ((err = nvlist_error(nvl))) {
482 printf("nvlist_add_nvlist_array(%p, \"peer-list\", %p, %d) => %d\n",
483 nvl, nvl_array, peer_count, err);
488 *nvl_arrayp = nvl_array;
495 wgc_get(struct wg_softc *sc, struct ifdrv *ifd)
497 nvlist_t *nvl, **nvl_array;
502 nvl = nvlist_create(0);
508 if (sc->sc_socket.so_port != 0)
509 nvlist_add_number(nvl, "listen-port", sc->sc_socket.so_port);
510 if (sc->sc_local.l_has_identity) {
511 nvlist_add_binary(nvl, "public-key", sc->sc_local.l_public, WG_KEY_SIZE);
512 if (curthread->td_ucred->cr_uid == 0)
513 nvlist_add_binary(nvl, "private-key", sc->sc_local.l_private, WG_KEY_SIZE);
515 if (sc->sc_hashtable.h_num_peers > 0) {
516 err = wg_marshal_peers(sc, NULL, &nvl_array, &peer_count);
519 nvlist_add_nvlist_array(nvl, "peer-list",
520 (const nvlist_t * const *)nvl_array, peer_count);
522 packed = nvlist_pack(nvl, &size);
525 if (ifd->ifd_len == 0) {
529 if (ifd->ifd_len < size) {
533 if (ifd->ifd_data == NULL) {
537 err = copyout(packed, ifd->ifd_data, size);
541 free(packed, M_NVLIST);
546 wg_allowedip_valid(const struct wg_allowedip *wip)
553 wg_peer_add(struct wg_softc *sc, const nvlist_t *nvl)
555 uint8_t public[WG_KEY_SIZE];
557 const struct sockaddr *endpoint;
558 int i, err, allowedip_count;
561 struct wg_peer *peer = NULL;
562 bool need_insert = false;
563 dev = iflib_get_dev(sc->wg_ctx);
565 if (!nvlist_exists_binary(nvl, "public-key")) {
566 device_printf(dev, "peer has no public-key\n");
569 pub_key = nvlist_get_binary(nvl, "public-key", &size);
570 if (size != CURVE25519_KEY_SIZE) {
571 device_printf(dev, "%s bad length for public-key %zu\n", __func__, size);
574 if (noise_local_keys(&sc->sc_local, public, NULL) == 0 &&
575 bcmp(public, pub_key, WG_KEY_SIZE) == 0) {
576 device_printf(dev, "public-key for peer already in use by host\n");
579 peer = wg_peer_lookup(sc, pub_key);
580 if (nvlist_exists_bool(nvl, "peer-remove") &&
581 nvlist_get_bool(nvl, "peer-remove")) {
583 wg_hashtable_peer_remove(&sc->sc_hashtable, peer);
584 wg_peer_destroy(peer);
586 printf("peer removed\n");
590 if (nvlist_exists_bool(nvl, "replace-allowedips") &&
591 nvlist_get_bool(nvl, "replace-allowedips") &&
594 wg_route_delete(&peer->p_sc->sc_routes, peer);
598 peer = wg_peer_alloc(sc);
599 noise_remote_init(&peer->p_remote, pub_key, &sc->sc_local);
600 cookie_maker_init(&peer->p_cookie, pub_key);
602 if (nvlist_exists_binary(nvl, "endpoint")) {
603 endpoint = nvlist_get_binary(nvl, "endpoint", &size);
604 if (size > sizeof(peer->p_endpoint.e_remote)) {
605 device_printf(dev, "%s bad length for endpoint %zu\n", __func__, size);
609 memcpy(&peer->p_endpoint.e_remote, endpoint, size);
611 if (nvlist_exists_binary(nvl, "pre-shared-key")) {
614 key = nvlist_get_binary(nvl, "pre-shared-key", &size);
615 noise_remote_set_psk(&peer->p_remote, key);
617 if (nvlist_exists_number(nvl, "persistent-keepalive-interval")) {
620 pki = nvlist_get_number(nvl, "persistent-keepalive-interval");
621 wg_timers_set_persistent_keepalive(&peer->p_timers, pki);
623 if (nvlist_exists_binary(nvl, "allowed-ips")) {
624 const struct wg_allowedip *aip, *aip_base;
626 aip = aip_base = nvlist_get_binary(nvl, "allowed-ips", &size);
627 if (size % sizeof(struct wg_allowedip) != 0) {
628 device_printf(dev, "%s bad length for allowed-ips %zu not integer multiple of struct size\n", __func__, size);
632 allowedip_count = size/sizeof(struct wg_allowedip);
633 for (i = 0; i < allowedip_count; i++) {
634 if (!wg_allowedip_valid(&aip_base[i])) {
635 device_printf(dev, "%s allowedip %d not valid\n", __func__, i);
640 for (int i = 0; i < allowedip_count; i++, aip++) {
641 if ((err = wg_route_add(&sc->sc_routes, peer, aip)) != 0) {
642 printf("route add %d failed -> %d\n", i, err);
647 wg_hashtable_peer_insert(&sc->sc_hashtable, peer);
651 wg_peer_destroy(peer);
656 wgc_set(struct wg_softc *sc, struct ifdrv *ifd)
658 uint8_t public[WG_KEY_SIZE];
665 if (ifd->ifd_len == 0 || ifd->ifd_data == NULL)
668 dev = iflib_get_dev(sc->wg_ctx);
669 nvlpacked = malloc(ifd->ifd_len, M_TEMP, M_WAITOK);
670 err = copyin(ifd->ifd_data, nvlpacked, ifd->ifd_len);
673 nvl = nvlist_unpack(nvlpacked, ifd->ifd_len, 0);
675 device_printf(dev, "%s nvlist_unpack failed\n", __func__);
679 if (nvlist_exists_bool(nvl, "replace-peers") &&
680 nvlist_get_bool(nvl, "replace-peers"))
681 wg_peer_remove_all(sc);
682 if (nvlist_exists_number(nvl, "listen-port")) {
683 int listen_port __unused = nvlist_get_number(nvl, "listen-port");
687 if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN);
688 pause("link_down", hz/4);
689 wg_socket_reinit(sc, NULL, NULL);
690 sc->sc_socket.so_port = listen_port;
691 if ((err = wg_socket_init(sc)) != 0)
693 if_link_state_change(sc->sc_ifp, LINK_STATE_UP);
695 if (nvlist_exists_binary(nvl, "private-key")) {
696 struct noise_local *local;
697 const void *key = nvlist_get_binary(nvl, "private-key", &size);
699 if (size != CURVE25519_KEY_SIZE) {
700 device_printf(dev, "%s bad length for private-key %zu\n", __func__, size);
707 local = &sc->sc_local;
708 noise_local_set_private(local, __DECONST(uint8_t *, key));
709 noise_local_keys(local, public, NULL);
710 cookie_checker_update(&sc->sc_cookie, public);
712 if (nvlist_exists_number(nvl, "user-cookie")) {
713 sc->sc_user_cookie = nvlist_get_number(nvl, "user-cookie");
718 if (nvlist_exists_nvlist_array(nvl, "peer-list")) {
720 const nvlist_t * const*nvl_peers;
722 nvl_peers = nvlist_get_nvlist_array(nvl, "peer-list", &peercount);
723 for (int i = 0; i < peercount; i++) {
724 wg_peer_add(sc, nvl_peers[i]);
730 free(nvlpacked, M_TEMP);
735 wg_priv_ioctl(if_ctx_t ctx, u_long command, caddr_t data)
737 struct wg_softc *sc = iflib_get_softc(ctx);
738 struct ifdrv *ifd = (struct ifdrv *)data;
744 ifd_cmd = ifd->ifd_cmd;
751 return (wgc_get(sc, ifd));
754 if (priv_check(curthread, PRIV_NET_HWIOCTL))
756 return (wgc_set(sc, ifd));
762 static device_method_t wg_if_methods[] = {
763 DEVMETHOD(ifdi_cloneattach, wg_cloneattach),
764 DEVMETHOD(ifdi_attach_post, wg_attach_post),
765 DEVMETHOD(ifdi_detach, wg_detach),
766 DEVMETHOD(ifdi_init, wg_init),
767 DEVMETHOD(ifdi_stop, wg_stop),
768 DEVMETHOD(ifdi_priv_ioctl, wg_priv_ioctl),
769 DEVMETHOD(ifdi_mtu_set, wg_mtu_set),
770 DEVMETHOD(ifdi_promisc_set, wg_set_promisc),
774 static driver_t wg_iflib_driver = {
775 "wg", wg_if_methods, sizeof(struct wg_softc)
778 char wg_driver_version[] = "0.0.1";
780 static struct if_shared_ctx wg_sctx_init = {
781 .isc_magic = IFLIB_MAGIC,
782 .isc_driver_version = wg_driver_version,
783 .isc_driver = &wg_iflib_driver,
784 .isc_flags = IFLIB_PSEUDO,
788 if_shared_ctx_t wg_sctx = &wg_sctx_init;
789 static if_pseudo_t wg_pseudo;
795 ratelimit_zone = uma_zcreate("wg ratelimit", sizeof(struct ratelimit),
796 NULL, NULL, NULL, NULL, 0, 0);
803 uma_zdestroy(ratelimit_zone);
811 if ((rc = wg_ctx_init()))
814 wg_pseudo = iflib_clone_register(wg_sctx);
815 if (wg_pseudo == NULL)
822 wg_module_deinit(void)
825 iflib_clone_deregister(wg_pseudo);
829 wg_module_event_handler(module_t mod, int what, void *arg)
835 if ((err = wg_module_init()) != 0)
839 if (clone_count == 0)
850 static moduledata_t wg_moduledata = {
852 wg_module_event_handler,
856 DECLARE_MODULE(wg, wg_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
857 MODULE_VERSION(wg, 1);
858 MODULE_DEPEND(wg, iflib, 1, 1, 1);
859 #if defined(__amd64__) || defined(__i386__)
860 /* Optimized blake2 implementations are only available on x86. */
861 MODULE_DEPEND(wg, blake2, 1, 1, 1);
863 MODULE_DEPEND(wg, crypto, 1, 1, 1);