Import the WireGuard driver from zx2c4.com.

This commit brings back the driver from FreeBSD commit
f187d6dfbf plus subsequent fixes from
upstream.

Relative to upstream this commit includes a few other small fixes such
as additional INET and INET6 #ifdef's, #include cleanups, and updates
for recent API changes in main.

Reviewed by:	pauamma, gbe, kevans, emaste
Obtained from:	git@git.zx2c4.com:wireguard-freebsd @ 3cc22b2
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D36909
This commit is contained in:
John Baldwin 2022-10-28 13:36:12 -07:00
parent 9e0aaedd70
commit 744bfb2131
23 changed files with 7613 additions and 6 deletions

View File

@ -136,6 +136,8 @@
..
vkbd
..
wg
..
wi
..
..

View File

@ -49,7 +49,7 @@ LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \
dev/hwpmc dev/hyperv \
dev/ic dev/iicbus dev/io dev/mfi dev/mmc dev/nvme \
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd \
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wg \
fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/nullfs \
fs/procfs fs/smbfs fs/udf fs/unionfs \
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
@ -225,6 +225,10 @@ NVPAIRDIR= ${INCLUDEDIR}/sys
MLX5= mlx5io.h
MLX5DIR= ${INCLUDEDIR}/dev/mlx5
.PATH: ${SRCTOP}/sys/dev/wg
WG= if_wg.h
WGDIR= ${INCLUDEDIR}/dev/wg
INCSGROUPS= INCS \
ACPICA \
AGP \
@ -244,7 +248,8 @@ INCSGROUPS= INCS \
RPC \
SECAUDIT \
TEKEN \
VERIEXEC
VERIEXEC \
WG
.if ${MK_IPFILTER} != "no"
INCSGROUPS+= IPFILTER

View File

@ -584,6 +584,7 @@ MAN= aac.4 \
vtnet.4 \
watchdog.4 \
${_wbwd.4} \
wg.4 \
witness.4 \
wlan.4 \
wlan_acl.4 \
@ -761,6 +762,7 @@ MLINKS+=vr.4 if_vr.4
MLINKS+=vte.4 if_vte.4
MLINKS+=vtnet.4 if_vtnet.4
MLINKS+=watchdog.4 SW_WATCHDOG.4
MLINKS+=wg.4 if_wg.4
MLINKS+=${_wpi.4} ${_if_wpi.4}
MLINKS+=xl.4 if_xl.4

213
share/man/man4/wg.4 Normal file
View File

@ -0,0 +1,213 @@
.\" Copyright (c) 2020 Gordon Bergling <gbe@FreeBSD.org>
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd October 28, 2022
.Dt WG 4
.Os
.Sh NAME
.Nm wg
.Nd "WireGuard - pseudo-device"
.Sh SYNOPSIS
To load the driver as a module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_wg_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides Virtual Private Network (VPN) interfaces for the secure
exchange of layer 3 traffic with other WireGuard peers using the WireGuard
protocol.
.Pp
A
.Nm
interface recognises one or more peers, establishes a secure tunnel with
each on demand, and tracks each peer's UDP endpoint for exchanging encrypted
traffic with.
.Pp
The interfaces can be created at runtime using the
.Ic ifconfig Cm wg Ns Ar N Cm create
command.
The interface itself can be configured with
.Xr wg 8 .
.Pp
The following glossary provides a brief overview of WireGuard
terminology:
.Bl -tag -width indent -offset 3n
.It Peer
Peers exchange IPv4 or IPv6 traffic over secure tunnels.
Each
.Nm
interface may be configured to recognise one or more peers.
.It Key
Each peer uses its private key and corresponding public key to
identify itself to others.
A peer configures a
.Nm
interface with its own private key and with the public keys of its peers.
.It Pre-shared key
In addition to the public keys, each peer pair may be configured with a
unique pre-shared symmetric key.
This is used in their handshake to guard against future compromise of the
peers' encrypted tunnel if a quantum-computational attack on their
Diffie-Hellman exchange becomes feasible.
It is optional, but recommended.
.It Allowed IPs
A single
.Nm
interface may maintain concurrent tunnels connecting diverse networks.
The interface therefore implements rudimentary routing and reverse-path
filtering functions for its tunneled traffic.
These functions reference a set of allowed IP ranges configured against
each peer.
.Pp
The interface will route outbound tunneled traffic to the peer configured
with the most specific matching allowed IP address range, or drop it
if no such match exists.
.Pp
The interface will accept tunneled traffic only from the peer
configured with the most specific matching allowed IP address range
for the incoming traffic, or drop it if no such match exists.
That is, tunneled traffic routed to a given peer cannot return through
another peer of the same
.Nm
interface.
This ensures that peers cannot spoof another's traffic.
.It Handshake
Two peers handshake to mutually authenticate each other and to
establish a shared series of secret ephemeral encryption keys.
Any peer may initiate a handshake.
Handshakes occur only when there is traffic to send, and recur every
two minutes during transfers.
.It Connectionless
Due to the handshake behavior, there is no connected or disconnected
state.
.El
.Ss Keys
Private keys for WireGuard can be generated from any sufficiently
secure random source.
The Curve25519 keys and the pre-shared keys are both 32 bytes
long and are commonly encoded in base64 for ease of use.
.Pp
Keys can be generated with
.Xr wg 8
as follows:
.Pp
.Dl $ wg genkey
.Pp
Although a valid Curve25519 key must have 5 bits set to
specific values, this is done by the interface and so it
will accept any random 32-byte base64 string.
.Sh EXAMPLES
Create a
.Nm
interface and set random private key.
.Bd -literal -offset indent
# ifconfig wg0 create
# wg genkey | wg set wg0 listen-port 54321 private-key /dev/stdin
.Ed
.Pp
Retrieve the associated public key from a
.Nm
interface.
.Bd -literal -offset indent
$ wg show wg0 public-key
.Ed
.Pp
Connect to a specific endpoint using its public-key and set the allowed IP address
.Bd -literal -offset indent
# wg set wg0 peer '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' endpoint 10.0.1.100:54321 allowed-ips 192.168.2.100/32
.Ed
.Pp
Remove a peer
.Bd -literal -offset indent
# wg set wg0 peer '7lWtsDdqaGB3EY9WNxRN3hVaHMtu1zXw71+bOjNOVUw=' remove
.Ed
.Sh DIAGNOSTICS
The
.Nm
interface supports runtime debugging, which can be enabled with:
.Pp
.D1 Ic ifconfig Cm wg Ns Ar N Cm debug
.Pp
Some common error messages include:
.Bl -diag
.It "Handshake for peer X did not complete after 5 seconds, retrying"
Peer X did not reply to our initiation packet, for example because:
.Bl -bullet
.It
The peer does not have the local interface configured as a peer.
Peers must be able to mutually authenticate each other.
.It
The peer endpoint IP address is incorrectly configured.
.It
There are firewall rules preventing communication between hosts.
.El
.It "Invalid handshake initiation"
The incoming handshake packet could not be processed.
This is likely due to the local interface not containing
the correct public key for the peer.
.It "Invalid initiation MAC"
The incoming handshake initiation packet had an invalid MAC.
This is likely because the initiation sender has the wrong public key
for the handshake receiver.
.It "Packet has unallowed src IP from peer X"
After decryption, an incoming data packet has a source IP address that
is not assigned to the allowed IPs of Peer X.
.El
.Sh SEE ALSO
.Xr inet 4 ,
.Xr ip 4 ,
.Xr netintro 4 ,
.Xr ipf 5 ,
.Xr pf.conf 5 ,
.Xr ifconfig 8 ,
.Xr ipfw 8 ,
.Xr wg 8
.Rs
.%T WireGuard whitepaper
.%U https://www.wireguard.com/papers/wireguard.pdf
.Re
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx 14.0 .
.Sh AUTHORS
The
.Nm
device driver written by
.An Jason A. Donenfeld Aq Mt Jason@zx2c4.com ,
.An Matt Dunwoodie Aq Mt ncon@nconroy.net ,
and
.An Kyle Evans Aq Mt kevans@FreeBSD.org .
.Pp
This manual page was written by
.An Gordon Bergling Aq Mt gbe@FreeBSD.org
and is based on the
.Ox
manual page written by
.An David Gwynne Aq Mt dlg@openbsd.org .

View File

@ -961,6 +961,9 @@ device enc
# Link aggregation interface.
device lagg
# WireGuard interface.
device wg
#
# Internet family options:
#

View File

@ -750,8 +750,8 @@ crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | \
crypto/sha2/sha512c.c optional crypto | geom_bde | zfs
crypto/skein/skein.c optional crypto | zfs
crypto/skein/skein_block.c optional crypto | zfs
crypto/siphash/siphash.c optional inet | inet6
crypto/siphash/siphash_test.c optional inet | inet6
crypto/siphash/siphash.c optional inet | inet6 | wg
crypto/siphash/siphash_test.c optional inet | inet6 | wg
ddb/db_access.c optional ddb
ddb/db_break.c optional ddb
ddb/db_capture.c optional ddb
@ -3480,6 +3480,14 @@ dev/vt/vt_font.c optional vt
dev/vt/vt_sysmouse.c optional vt
dev/vte/if_vte.c optional vte pci
dev/watchdog/watchdog.c standard
dev/wg/if_wg.c optional wg \
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
dev/wg/wg_cookie.c optional wg \
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
dev/wg/wg_crypto.c optional wg \
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
dev/wg/wg_noise.c optional wg \
compile-with "${NORMAL_C} -include $S/dev/wg/compat.h"
dev/wpi/if_wpi.c optional wpi pci
wpifw.c optional wpifw \
compile-with "${AWK} -f $S/tools/fw_stub.awk wpi.fw:wpifw:153229 -mwpi -c${.TARGET}" \

118
sys/dev/wg/compat.h Normal file
View File

@ -0,0 +1,118 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (c) 2022 The FreeBSD Foundation
*
* compat.h contains code that is backported from FreeBSD's main branch.
* It is different from support.h, which is for code that is not _yet_ upstream.
*/
#include <sys/param.h>
#if (__FreeBSD_version < 1400036 && __FreeBSD_version >= 1400000) || __FreeBSD_version < 1300519
#define COMPAT_NEED_CHACHA20POLY1305_MBUF
#endif
#if __FreeBSD_version < 1400048
#define COMPAT_NEED_CHACHA20POLY1305
#endif
#if __FreeBSD_version < 1400049
#define COMPAT_NEED_CURVE25519
#endif
#if __FreeBSD_version < 0x7fffffff /* TODO: update this when implemented */
#define COMPAT_NEED_BLAKE2S
#endif
#if __FreeBSD_version < 1400059
#include <sys/sockbuf.h>
#define sbcreatecontrol(a, b, c, d, e) sbcreatecontrol(a, b, c, d)
#endif
#if __FreeBSD_version < 1300507
#include <sys/smp.h>
#include <sys/gtaskqueue.h>
struct taskqgroup_cpu {
LIST_HEAD(, grouptask) tgc_tasks;
struct gtaskqueue *tgc_taskq;
int tgc_cnt;
int tgc_cpu;
};
struct taskqgroup {
struct taskqgroup_cpu tqg_queue[MAXCPU];
/* Other members trimmed from compat. */
};
static inline void taskqgroup_drain_all(struct taskqgroup *tqg)
{
struct gtaskqueue *q;
for (int i = 0; i < mp_ncpus; i++) {
q = tqg->tqg_queue[i].tgc_taskq;
if (q == NULL)
continue;
gtaskqueue_drain_all(q);
}
}
#endif
#if __FreeBSD_version < 1300000
#define VIMAGE
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <net/vnet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <vm/uma.h>
#define taskqgroup_attach(a, b, c, d, e, f) taskqgroup_attach((a), (b), (c), -1, (f))
#define taskqgroup_attach_cpu(a, b, c, d, e, f, g) taskqgroup_attach_cpu((a), (b), (c), (d), -1, (g))
#undef NET_EPOCH_ENTER
#define NET_EPOCH_ENTER(et) NET_EPOCH_ENTER_ET(et)
#undef NET_EPOCH_EXIT
#define NET_EPOCH_EXIT(et) NET_EPOCH_EXIT_ET(et)
#define NET_EPOCH_CALL(f, c) epoch_call(net_epoch_preempt, (c), (f))
#define NET_EPOCH_ASSERT() MPASS(in_epoch(net_epoch_preempt))
#undef atomic_load_ptr
#define atomic_load_ptr(p) (*(volatile __typeof(*p) *)(p))
#endif
#if __FreeBSD_version < 1202000
static inline uint32_t arc4random_uniform(uint32_t bound)
{
uint32_t ret, max_mod_bound;
if (bound < 2)
return 0;
max_mod_bound = (1 + ~bound) % bound;
do {
ret = arc4random();
} while (ret < max_mod_bound);
return ret % bound;
}
typedef void callout_func_t(void *);
#ifndef CSUM_SND_TAG
#define CSUM_SND_TAG 0x80000000
#endif
#endif

182
sys/dev/wg/crypto.h Normal file
View File

@ -0,0 +1,182 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (c) 2022 The FreeBSD Foundation
*/
#ifndef _WG_CRYPTO
#define _WG_CRYPTO
#include <sys/param.h>
struct mbuf;
int crypto_init(void);
void crypto_deinit(void);
enum chacha20poly1305_lengths {
XCHACHA20POLY1305_NONCE_SIZE = 24,
CHACHA20POLY1305_KEY_SIZE = 32,
CHACHA20POLY1305_AUTHTAG_SIZE = 16
};
#ifdef COMPAT_NEED_CHACHA20POLY1305
void
chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
const uint8_t *ad, const size_t ad_len,
const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
bool
chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
const uint8_t *ad, const size_t ad_len,
const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
void
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
const size_t ad_len,
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
bool
xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
const size_t ad_len,
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
#else
#include <sys/endian.h>
#include <crypto/chacha20_poly1305.h>
static inline void
chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
const uint8_t *ad, const size_t ad_len,
const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
{
uint8_t nonce_bytes[8];
le64enc(nonce_bytes, nonce);
chacha20_poly1305_encrypt(dst, src, src_len, ad, ad_len,
nonce_bytes, sizeof(nonce_bytes), key);
}
static inline bool
chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
const uint8_t *ad, const size_t ad_len,
const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
{
uint8_t nonce_bytes[8];
le64enc(nonce_bytes, nonce);
return (chacha20_poly1305_decrypt(dst, src, src_len, ad, ad_len,
nonce_bytes, sizeof(nonce_bytes), key));
}
static inline void
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
const size_t ad_len,
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
{
xchacha20_poly1305_encrypt(dst, src, src_len, ad, ad_len, nonce, key);
}
static inline bool
xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
const size_t ad_len,
const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
{
return (xchacha20_poly1305_decrypt(dst, src, src_len, ad, ad_len, nonce, key));
}
#endif
int
chacha20poly1305_encrypt_mbuf(struct mbuf *, const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
int
chacha20poly1305_decrypt_mbuf(struct mbuf *, const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
enum blake2s_lengths {
BLAKE2S_BLOCK_SIZE = 64,
BLAKE2S_HASH_SIZE = 32,
BLAKE2S_KEY_SIZE = 32
};
#ifdef COMPAT_NEED_BLAKE2S
struct blake2s_state {
uint32_t h[8];
uint32_t t[2];
uint32_t f[2];
uint8_t buf[BLAKE2S_BLOCK_SIZE];
unsigned int buflen;
unsigned int outlen;
};
void blake2s_init(struct blake2s_state *state, const size_t outlen);
void blake2s_init_key(struct blake2s_state *state, const size_t outlen,
const uint8_t *key, const size_t keylen);
void blake2s_update(struct blake2s_state *state, const uint8_t *in, size_t inlen);
void blake2s_final(struct blake2s_state *state, uint8_t *out);
static inline void blake2s(uint8_t *out, const uint8_t *in, const uint8_t *key,
const size_t outlen, const size_t inlen, const size_t keylen)
{
struct blake2s_state state;
if (keylen)
blake2s_init_key(&state, outlen, key, keylen);
else
blake2s_init(&state, outlen);
blake2s_update(&state, in, inlen);
blake2s_final(&state, out);
}
#endif
#ifdef COMPAT_NEED_CURVE25519
enum curve25519_lengths {
CURVE25519_KEY_SIZE = 32
};
bool curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE],
const uint8_t secret[static CURVE25519_KEY_SIZE],
const uint8_t basepoint[static CURVE25519_KEY_SIZE]);
static inline bool
curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE],
const uint8_t secret[static CURVE25519_KEY_SIZE])
{
static const uint8_t basepoint[CURVE25519_KEY_SIZE] = { 9 };
return curve25519(pub, secret, basepoint);
}
static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE])
{
secret[0] &= 248;
secret[31] = (secret[31] & 127) | 64;
}
static inline void curve25519_generate_secret(uint8_t secret[CURVE25519_KEY_SIZE])
{
arc4random_buf(secret, CURVE25519_KEY_SIZE);
curve25519_clamp_secret(secret);
}
#else
#include <crypto/curve25519.h>
#endif
#endif

3055
sys/dev/wg/if_wg.c Normal file

File diff suppressed because it is too large Load Diff

37
sys/dev/wg/if_wg.h Normal file
View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019 Matt Dunwoodie <ncon@noconroy.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $FreeBSD$
*/
#ifndef __IF_WG_H__
#define __IF_WG_H__
#include <net/if.h>
#include <netinet/in.h>
struct wg_data_io {
char wgd_name[IFNAMSIZ];
void *wgd_data;
size_t wgd_size;
};
#define WG_KEY_SIZE 32
#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
#endif /* __IF_WG_H__ */

21
sys/dev/wg/support.h Normal file
View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: ISC
*
* Copyright (C) 2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
*
* support.h contains code that is not _yet_ upstream in FreeBSD's main branch.
* It is different from compat.h, which is strictly for backports.
*/
#ifndef _WG_SUPPORT
#define _WG_SUPPORT
#ifndef ck_pr_store_bool
#define ck_pr_store_bool(dst, val) ck_pr_store_8((uint8_t *)(dst), (uint8_t)(val))
#endif
#ifndef ck_pr_load_bool
#define ck_pr_load_bool(src) ((bool)ck_pr_load_8((uint8_t *)(src)))
#endif
#endif

1
sys/dev/wg/version.h Normal file
View File

@ -0,0 +1 @@
#define WIREGUARD_VERSION 20220615

500
sys/dev/wg/wg_cookie.c Normal file
View File

@ -0,0 +1,500 @@
/* SPDX-License-Identifier: ISC
*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <crypto/siphash/siphash.h>
#include <netinet/in.h>
#include <vm/uma.h>
#include "wg_cookie.h"
#define COOKIE_MAC1_KEY_LABEL "mac1----"
#define COOKIE_COOKIE_KEY_LABEL "cookie--"
#define COOKIE_SECRET_MAX_AGE 120
#define COOKIE_SECRET_LATENCY 5
/* Constants for initiation rate limiting */
#define RATELIMIT_SIZE (1 << 13)
#define RATELIMIT_MASK (RATELIMIT_SIZE - 1)
#define RATELIMIT_SIZE_MAX (RATELIMIT_SIZE * 8)
#define INITIATIONS_PER_SECOND 20
#define INITIATIONS_BURSTABLE 5
#define INITIATION_COST (SBT_1S / INITIATIONS_PER_SECOND)
#define TOKEN_MAX (INITIATION_COST * INITIATIONS_BURSTABLE)
#define ELEMENT_TIMEOUT 1
#define IPV4_MASK_SIZE 4 /* Use all 4 bytes of IPv4 address */
#define IPV6_MASK_SIZE 8 /* Use top 8 bytes (/64) of IPv6 address */
struct ratelimit_key {
struct vnet *vnet;
uint8_t ip[IPV6_MASK_SIZE];
};
struct ratelimit_entry {
LIST_ENTRY(ratelimit_entry) r_entry;
struct ratelimit_key r_key;
sbintime_t r_last_time; /* sbinuptime */
uint64_t r_tokens;
};
struct ratelimit {
uint8_t rl_secret[SIPHASH_KEY_LENGTH];
struct mtx rl_mtx;
struct callout rl_gc;
LIST_HEAD(, ratelimit_entry) rl_table[RATELIMIT_SIZE];
size_t rl_table_num;
};
static void precompute_key(uint8_t *,
const uint8_t[COOKIE_INPUT_SIZE], const char *);
static void macs_mac1(struct cookie_macs *, const void *, size_t,
const uint8_t[COOKIE_KEY_SIZE]);
static void macs_mac2(struct cookie_macs *, const void *, size_t,
const uint8_t[COOKIE_COOKIE_SIZE]);
static int timer_expired(sbintime_t, uint32_t, uint32_t);
static void make_cookie(struct cookie_checker *,
uint8_t[COOKIE_COOKIE_SIZE], struct sockaddr *);
static void ratelimit_init(struct ratelimit *);
static void ratelimit_deinit(struct ratelimit *);
static void ratelimit_gc_callout(void *);
static void ratelimit_gc_schedule(struct ratelimit *);
static void ratelimit_gc(struct ratelimit *, bool);
static int ratelimit_allow(struct ratelimit *, struct sockaddr *, struct vnet *);
static uint64_t siphash13(const uint8_t [SIPHASH_KEY_LENGTH], const void *, size_t);
static struct ratelimit ratelimit_v4;
#ifdef INET6
static struct ratelimit ratelimit_v6;
#endif
static uma_zone_t ratelimit_zone;
/* Public Functions */
int
cookie_init(void)
{
if ((ratelimit_zone = uma_zcreate("wg ratelimit",
sizeof(struct ratelimit_entry), NULL, NULL, NULL, NULL, 0, 0)) == NULL)
return ENOMEM;
ratelimit_init(&ratelimit_v4);
#ifdef INET6
ratelimit_init(&ratelimit_v6);
#endif
return (0);
}
void
cookie_deinit(void)
{
ratelimit_deinit(&ratelimit_v4);
#ifdef INET6
ratelimit_deinit(&ratelimit_v6);
#endif
uma_zdestroy(ratelimit_zone);
}
void
cookie_checker_init(struct cookie_checker *cc)
{
bzero(cc, sizeof(*cc));
rw_init(&cc->cc_key_lock, "cookie_checker_key");
mtx_init(&cc->cc_secret_mtx, "cookie_checker_secret", NULL, MTX_DEF);
}
void
cookie_checker_free(struct cookie_checker *cc)
{
rw_destroy(&cc->cc_key_lock);
mtx_destroy(&cc->cc_secret_mtx);
explicit_bzero(cc, sizeof(*cc));
}
void
cookie_checker_update(struct cookie_checker *cc,
const uint8_t key[COOKIE_INPUT_SIZE])
{
rw_wlock(&cc->cc_key_lock);
if (key) {
precompute_key(cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
precompute_key(cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
} else {
bzero(cc->cc_mac1_key, sizeof(cc->cc_mac1_key));
bzero(cc->cc_cookie_key, sizeof(cc->cc_cookie_key));
}
rw_wunlock(&cc->cc_key_lock);
}
void
cookie_checker_create_payload(struct cookie_checker *cc,
struct cookie_macs *macs, uint8_t nonce[COOKIE_NONCE_SIZE],
uint8_t ecookie[COOKIE_ENCRYPTED_SIZE], struct sockaddr *sa)
{
uint8_t cookie[COOKIE_COOKIE_SIZE];
make_cookie(cc, cookie, sa);
arc4random_buf(nonce, COOKIE_NONCE_SIZE);
rw_rlock(&cc->cc_key_lock);
xchacha20poly1305_encrypt(ecookie, cookie, COOKIE_COOKIE_SIZE,
macs->mac1, COOKIE_MAC_SIZE, nonce, cc->cc_cookie_key);
rw_runlock(&cc->cc_key_lock);
explicit_bzero(cookie, sizeof(cookie));
}
void
cookie_maker_init(struct cookie_maker *cm, const uint8_t key[COOKIE_INPUT_SIZE])
{
bzero(cm, sizeof(*cm));
precompute_key(cm->cm_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
precompute_key(cm->cm_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
rw_init(&cm->cm_lock, "cookie_maker");
}
void
cookie_maker_free(struct cookie_maker *cm)
{
rw_destroy(&cm->cm_lock);
explicit_bzero(cm, sizeof(*cm));
}
int
cookie_maker_consume_payload(struct cookie_maker *cm,
uint8_t nonce[COOKIE_NONCE_SIZE], uint8_t ecookie[COOKIE_ENCRYPTED_SIZE])
{
uint8_t cookie[COOKIE_COOKIE_SIZE];
int ret;
rw_rlock(&cm->cm_lock);
if (!cm->cm_mac1_sent) {
ret = ETIMEDOUT;
goto error;
}
if (!xchacha20poly1305_decrypt(cookie, ecookie, COOKIE_ENCRYPTED_SIZE,
cm->cm_mac1_last, COOKIE_MAC_SIZE, nonce, cm->cm_cookie_key)) {
ret = EINVAL;
goto error;
}
rw_runlock(&cm->cm_lock);
rw_wlock(&cm->cm_lock);
memcpy(cm->cm_cookie, cookie, COOKIE_COOKIE_SIZE);
cm->cm_cookie_birthdate = getsbinuptime();
cm->cm_cookie_valid = true;
cm->cm_mac1_sent = false;
rw_wunlock(&cm->cm_lock);
return 0;
error:
rw_runlock(&cm->cm_lock);
return ret;
}
void
cookie_maker_mac(struct cookie_maker *cm, struct cookie_macs *macs, void *buf,
size_t len)
{
rw_wlock(&cm->cm_lock);
macs_mac1(macs, buf, len, cm->cm_mac1_key);
memcpy(cm->cm_mac1_last, macs->mac1, COOKIE_MAC_SIZE);
cm->cm_mac1_sent = true;
if (cm->cm_cookie_valid &&
!timer_expired(cm->cm_cookie_birthdate,
COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY, 0)) {
macs_mac2(macs, buf, len, cm->cm_cookie);
} else {
bzero(macs->mac2, COOKIE_MAC_SIZE);
cm->cm_cookie_valid = false;
}
rw_wunlock(&cm->cm_lock);
}
int
cookie_checker_validate_macs(struct cookie_checker *cc, struct cookie_macs *macs,
void *buf, size_t len, bool check_cookie, struct sockaddr *sa, struct vnet *vnet)
{
struct cookie_macs our_macs;
uint8_t cookie[COOKIE_COOKIE_SIZE];
/* Validate incoming MACs */
rw_rlock(&cc->cc_key_lock);
macs_mac1(&our_macs, buf, len, cc->cc_mac1_key);
rw_runlock(&cc->cc_key_lock);
/* If mac1 is invald, we want to drop the packet */
if (timingsafe_bcmp(our_macs.mac1, macs->mac1, COOKIE_MAC_SIZE) != 0)
return EINVAL;
if (check_cookie) {
make_cookie(cc, cookie, sa);
macs_mac2(&our_macs, buf, len, cookie);
/* If the mac2 is invalid, we want to send a cookie response */
if (timingsafe_bcmp(our_macs.mac2, macs->mac2, COOKIE_MAC_SIZE) != 0)
return EAGAIN;
/* If the mac2 is valid, we may want rate limit the peer.
* ratelimit_allow will return either 0 or ECONNREFUSED,
* implying there is no ratelimiting, or we should ratelimit
* (refuse) respectively. */
if (sa->sa_family == AF_INET)
return ratelimit_allow(&ratelimit_v4, sa, vnet);
#ifdef INET6
else if (sa->sa_family == AF_INET6)
return ratelimit_allow(&ratelimit_v6, sa, vnet);
#endif
else
return EAFNOSUPPORT;
}
return 0;
}
/* Private functions */
static void
precompute_key(uint8_t *key, const uint8_t input[COOKIE_INPUT_SIZE],
const char *label)
{
struct blake2s_state blake;
blake2s_init(&blake, COOKIE_KEY_SIZE);
blake2s_update(&blake, label, strlen(label));
blake2s_update(&blake, input, COOKIE_INPUT_SIZE);
blake2s_final(&blake, key);
}
static void
macs_mac1(struct cookie_macs *macs, const void *buf, size_t len,
const uint8_t key[COOKIE_KEY_SIZE])
{
struct blake2s_state state;
blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
blake2s_update(&state, buf, len);
blake2s_final(&state, macs->mac1);
}
static void
macs_mac2(struct cookie_macs *macs, const void *buf, size_t len,
const uint8_t key[COOKIE_COOKIE_SIZE])
{
struct blake2s_state state;
blake2s_init_key(&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
blake2s_update(&state, buf, len);
blake2s_update(&state, macs->mac1, COOKIE_MAC_SIZE);
blake2s_final(&state, macs->mac2);
}
static __inline int
timer_expired(sbintime_t timer, uint32_t sec, uint32_t nsec)
{
sbintime_t now = getsbinuptime();
return (now > (timer + sec * SBT_1S + nstosbt(nsec))) ? ETIMEDOUT : 0;
}
static void
make_cookie(struct cookie_checker *cc, uint8_t cookie[COOKIE_COOKIE_SIZE],
struct sockaddr *sa)
{
struct blake2s_state state;
mtx_lock(&cc->cc_secret_mtx);
if (timer_expired(cc->cc_secret_birthdate,
COOKIE_SECRET_MAX_AGE, 0)) {
arc4random_buf(cc->cc_secret, COOKIE_SECRET_SIZE);
cc->cc_secret_birthdate = getsbinuptime();
}
blake2s_init_key(&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
COOKIE_SECRET_SIZE);
mtx_unlock(&cc->cc_secret_mtx);
if (sa->sa_family == AF_INET) {
blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_addr,
sizeof(struct in_addr));
blake2s_update(&state, (uint8_t *)&satosin(sa)->sin_port,
sizeof(in_port_t));
blake2s_final(&state, cookie);
#ifdef INET6
} else if (sa->sa_family == AF_INET6) {
blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_addr,
sizeof(struct in6_addr));
blake2s_update(&state, (uint8_t *)&satosin6(sa)->sin6_port,
sizeof(in_port_t));
blake2s_final(&state, cookie);
#endif
} else {
arc4random_buf(cookie, COOKIE_COOKIE_SIZE);
}
}
static void
ratelimit_init(struct ratelimit *rl)
{
size_t i;
mtx_init(&rl->rl_mtx, "ratelimit_lock", NULL, MTX_DEF);
callout_init_mtx(&rl->rl_gc, &rl->rl_mtx, 0);
arc4random_buf(rl->rl_secret, sizeof(rl->rl_secret));
for (i = 0; i < RATELIMIT_SIZE; i++)
LIST_INIT(&rl->rl_table[i]);
rl->rl_table_num = 0;
}
static void
ratelimit_deinit(struct ratelimit *rl)
{
mtx_lock(&rl->rl_mtx);
callout_stop(&rl->rl_gc);
ratelimit_gc(rl, true);
mtx_unlock(&rl->rl_mtx);
mtx_destroy(&rl->rl_mtx);
}
static void
ratelimit_gc_callout(void *_rl)
{
/* callout will lock rl_mtx for us */
ratelimit_gc(_rl, false);
}
static void
ratelimit_gc_schedule(struct ratelimit *rl)
{
/* Trigger another GC if needed. There is no point calling GC if there
* are no entries in the table. We also want to ensure that GC occurs
* on a regular interval, so don't override a currently pending GC.
*
* In the case of a forced ratelimit_gc, there will be no entries left
* so we will will not schedule another GC. */
if (rl->rl_table_num > 0 && !callout_pending(&rl->rl_gc))
callout_reset(&rl->rl_gc, ELEMENT_TIMEOUT * hz,
ratelimit_gc_callout, rl);
}
static void
ratelimit_gc(struct ratelimit *rl, bool force)
{
size_t i;
struct ratelimit_entry *r, *tr;
sbintime_t expiry;
mtx_assert(&rl->rl_mtx, MA_OWNED);
if (rl->rl_table_num == 0)
return;
expiry = getsbinuptime() - ELEMENT_TIMEOUT * SBT_1S;
for (i = 0; i < RATELIMIT_SIZE; i++) {
LIST_FOREACH_SAFE(r, &rl->rl_table[i], r_entry, tr) {
if (r->r_last_time < expiry || force) {
rl->rl_table_num--;
LIST_REMOVE(r, r_entry);
uma_zfree(ratelimit_zone, r);
}
}
}
ratelimit_gc_schedule(rl);
}
static int
ratelimit_allow(struct ratelimit *rl, struct sockaddr *sa, struct vnet *vnet)
{
uint64_t bucket, tokens;
sbintime_t diff, now;
struct ratelimit_entry *r;
int ret = ECONNREFUSED;
struct ratelimit_key key = { .vnet = vnet };
size_t len = sizeof(key);
if (sa->sa_family == AF_INET) {
memcpy(key.ip, &satosin(sa)->sin_addr, IPV4_MASK_SIZE);
len -= IPV6_MASK_SIZE - IPV4_MASK_SIZE;
}
#ifdef INET6
else if (sa->sa_family == AF_INET6)
memcpy(key.ip, &satosin6(sa)->sin6_addr, IPV6_MASK_SIZE);
#endif
else
return ret;
bucket = siphash13(rl->rl_secret, &key, len) & RATELIMIT_MASK;
mtx_lock(&rl->rl_mtx);
LIST_FOREACH(r, &rl->rl_table[bucket], r_entry) {
if (bcmp(&r->r_key, &key, len) != 0)
continue;
/* If we get to here, we've found an entry for the endpoint.
* We apply standard token bucket, by calculating the time
* lapsed since our last_time, adding that, ensuring that we
* cap the tokens at TOKEN_MAX. If the endpoint has no tokens
* left (that is tokens <= INITIATION_COST) then we block the
* request, otherwise we subtract the INITITIATION_COST and
* return OK. */
now = getsbinuptime();
diff = now - r->r_last_time;
r->r_last_time = now;
tokens = r->r_tokens + diff;
if (tokens > TOKEN_MAX)
tokens = TOKEN_MAX;
if (tokens >= INITIATION_COST) {
r->r_tokens = tokens - INITIATION_COST;
goto ok;
} else {
r->r_tokens = tokens;
goto error;
}
}
/* If we get to here, we didn't have an entry for the endpoint, let's
* add one if we have space. */
if (rl->rl_table_num >= RATELIMIT_SIZE_MAX)
goto error;
/* Goto error if out of memory */
if ((r = uma_zalloc(ratelimit_zone, M_NOWAIT | M_ZERO)) == NULL)
goto error;
rl->rl_table_num++;
/* Insert entry into the hashtable and ensure it's initialised */
LIST_INSERT_HEAD(&rl->rl_table[bucket], r, r_entry);
r->r_key = key;
r->r_last_time = getsbinuptime();
r->r_tokens = TOKEN_MAX - INITIATION_COST;
/* If we've added a new entry, let's trigger GC. */
ratelimit_gc_schedule(rl);
ok:
ret = 0;
error:
mtx_unlock(&rl->rl_mtx);
return ret;
}
static uint64_t siphash13(const uint8_t key[SIPHASH_KEY_LENGTH], const void *src, size_t len)
{
SIPHASH_CTX ctx;
return (SipHashX(&ctx, 1, 3, key, src, len));
}
#ifdef SELFTESTS
#include "selftest/cookie.c"
#endif /* SELFTESTS */

72
sys/dev/wg/wg_cookie.h Normal file
View File

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: ISC
*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
*/
#ifndef __COOKIE_H__
#define __COOKIE_H__
#include "crypto.h"
#define COOKIE_MAC_SIZE 16
#define COOKIE_KEY_SIZE 32
#define COOKIE_NONCE_SIZE XCHACHA20POLY1305_NONCE_SIZE
#define COOKIE_COOKIE_SIZE 16
#define COOKIE_SECRET_SIZE 32
#define COOKIE_INPUT_SIZE 32
#define COOKIE_ENCRYPTED_SIZE (COOKIE_COOKIE_SIZE + COOKIE_MAC_SIZE)
struct vnet;
struct cookie_macs {
uint8_t mac1[COOKIE_MAC_SIZE];
uint8_t mac2[COOKIE_MAC_SIZE];
};
struct cookie_maker {
uint8_t cm_mac1_key[COOKIE_KEY_SIZE];
uint8_t cm_cookie_key[COOKIE_KEY_SIZE];
struct rwlock cm_lock;
bool cm_cookie_valid;
uint8_t cm_cookie[COOKIE_COOKIE_SIZE];
sbintime_t cm_cookie_birthdate; /* sbinuptime */
bool cm_mac1_sent;
uint8_t cm_mac1_last[COOKIE_MAC_SIZE];
};
struct cookie_checker {
struct rwlock cc_key_lock;
uint8_t cc_mac1_key[COOKIE_KEY_SIZE];
uint8_t cc_cookie_key[COOKIE_KEY_SIZE];
struct mtx cc_secret_mtx;
sbintime_t cc_secret_birthdate; /* sbinuptime */
uint8_t cc_secret[COOKIE_SECRET_SIZE];
};
int cookie_init(void);
void cookie_deinit(void);
void cookie_checker_init(struct cookie_checker *);
void cookie_checker_free(struct cookie_checker *);
void cookie_checker_update(struct cookie_checker *,
const uint8_t[COOKIE_INPUT_SIZE]);
void cookie_checker_create_payload(struct cookie_checker *,
struct cookie_macs *cm, uint8_t[COOKIE_NONCE_SIZE],
uint8_t [COOKIE_ENCRYPTED_SIZE], struct sockaddr *);
void cookie_maker_init(struct cookie_maker *, const uint8_t[COOKIE_INPUT_SIZE]);
void cookie_maker_free(struct cookie_maker *);
int cookie_maker_consume_payload(struct cookie_maker *,
uint8_t[COOKIE_NONCE_SIZE], uint8_t[COOKIE_ENCRYPTED_SIZE]);
void cookie_maker_mac(struct cookie_maker *, struct cookie_macs *,
void *, size_t);
int cookie_checker_validate_macs(struct cookie_checker *,
struct cookie_macs *, void *, size_t, bool, struct sockaddr *,
struct vnet *);
#ifdef SELFTESTS
bool cookie_selftest(void);
#endif /* SELFTESTS */
#endif /* __COOKIE_H__ */

1830
sys/dev/wg/wg_crypto.c Normal file

File diff suppressed because it is too large Load Diff

1410
sys/dev/wg/wg_noise.c Normal file

File diff suppressed because it is too large Load Diff

131
sys/dev/wg/wg_noise.h Normal file
View File

@ -0,0 +1,131 @@
/* SPDX-License-Identifier: ISC
*
* Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
*/
#ifndef __NOISE_H__
#define __NOISE_H__
#include "crypto.h"
#define NOISE_PUBLIC_KEY_LEN CURVE25519_KEY_SIZE
#define NOISE_SYMMETRIC_KEY_LEN CHACHA20POLY1305_KEY_SIZE
#define NOISE_TIMESTAMP_LEN (sizeof(uint64_t) + sizeof(uint32_t))
#define NOISE_AUTHTAG_LEN CHACHA20POLY1305_AUTHTAG_SIZE
#define NOISE_HASH_LEN BLAKE2S_HASH_SIZE
#define REJECT_AFTER_TIME 180
#define REKEY_TIMEOUT 5
#define KEEPALIVE_TIMEOUT 10
struct noise_local;
struct noise_remote;
struct noise_keypair;
/* Local configuration */
struct noise_local *
noise_local_alloc(void *);
struct noise_local *
noise_local_ref(struct noise_local *);
void noise_local_put(struct noise_local *);
void noise_local_free(struct noise_local *, void (*)(struct noise_local *));
void * noise_local_arg(struct noise_local *);
void noise_local_private(struct noise_local *,
const uint8_t[NOISE_PUBLIC_KEY_LEN]);
int noise_local_keys(struct noise_local *,
uint8_t[NOISE_PUBLIC_KEY_LEN],
uint8_t[NOISE_PUBLIC_KEY_LEN]);
/* Remote configuration */
struct noise_remote *
noise_remote_alloc(struct noise_local *, void *,
const uint8_t[NOISE_PUBLIC_KEY_LEN]);
int noise_remote_enable(struct noise_remote *);
void noise_remote_disable(struct noise_remote *);
struct noise_remote *
noise_remote_lookup(struct noise_local *, const uint8_t[NOISE_PUBLIC_KEY_LEN]);
struct noise_remote *
noise_remote_index(struct noise_local *, uint32_t);
struct noise_remote *
noise_remote_ref(struct noise_remote *);
void noise_remote_put(struct noise_remote *);
void noise_remote_free(struct noise_remote *, void (*)(struct noise_remote *));
struct noise_local *
noise_remote_local(struct noise_remote *);
void * noise_remote_arg(struct noise_remote *);
void noise_remote_set_psk(struct noise_remote *,
const uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
int noise_remote_keys(struct noise_remote *,
uint8_t[NOISE_PUBLIC_KEY_LEN],
uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
int noise_remote_initiation_expired(struct noise_remote *);
void noise_remote_handshake_clear(struct noise_remote *);
void noise_remote_keypairs_clear(struct noise_remote *);
/* Keypair functions */
struct noise_keypair *
noise_keypair_lookup(struct noise_local *, uint32_t);
struct noise_keypair *
noise_keypair_current(struct noise_remote *);
struct noise_keypair *
noise_keypair_ref(struct noise_keypair *);
int noise_keypair_received_with(struct noise_keypair *);
void noise_keypair_put(struct noise_keypair *);
struct noise_remote *
noise_keypair_remote(struct noise_keypair *);
int noise_keypair_nonce_next(struct noise_keypair *, uint64_t *);
int noise_keypair_nonce_check(struct noise_keypair *, uint64_t);
int noise_keep_key_fresh_send(struct noise_remote *);
int noise_keep_key_fresh_recv(struct noise_remote *);
int noise_keypair_encrypt(
struct noise_keypair *,
uint32_t *r_idx,
uint64_t nonce,
struct mbuf *);
int noise_keypair_decrypt(
struct noise_keypair *,
uint64_t nonce,
struct mbuf *);
/* Handshake functions */
int noise_create_initiation(
struct noise_remote *,
uint32_t *s_idx,
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN],
uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]);
int noise_consume_initiation(
struct noise_local *,
struct noise_remote **,
uint32_t s_idx,
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN],
uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]);
int noise_create_response(
struct noise_remote *,
uint32_t *s_idx,
uint32_t *r_idx,
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
uint8_t en[0 + NOISE_AUTHTAG_LEN]);
int noise_consume_response(
struct noise_local *,
struct noise_remote **,
uint32_t s_idx,
uint32_t r_idx,
uint8_t ue[NOISE_PUBLIC_KEY_LEN],
uint8_t en[0 + NOISE_AUTHTAG_LEN]);
#ifdef SELFTESTS
bool noise_counter_selftest(void);
#endif /* SELFTESTS */
#endif /* __NOISE_H__ */

View File

@ -3758,6 +3758,7 @@ prison_priv_check(struct ucred *cred, int priv)
case PRIV_NET_SETIFFIB:
case PRIV_NET_OVPN:
case PRIV_NET_ME:
case PRIV_NET_WG:
/*
* 802.11-related privileges.

View File

@ -164,6 +164,7 @@ SUBDIR= \
if_tuntap \
if_vlan \
if_vxlan \
${_if_wg} \
iflib \
${_igc} \
imgact_binmisc \
@ -449,6 +450,9 @@ _toecore= toecore
_if_enc= if_enc
_if_gif= if_gif
_if_gre= if_gre
.if ${MK_CRYPT} != "no" || defined(ALL_MODULES)
_if_wg= if_wg
.endif
_ipfw_pmod= ipfw_pmod
.if ${KERN_OPTS:MIPSEC_SUPPORT} && !${KERN_OPTS:MIPSEC}
_ipsec= ipsec

View File

@ -0,0 +1,10 @@
.PATH: ${SRCTOP}/sys/dev/wg
KMOD= if_wg
SRCS= if_wg.c wg_cookie.c wg_crypto.c wg_noise.c
SRCS+= opt_inet.h opt_inet6.h device_if.h bus_if.h
.include <bsd.kmod.mk>
CFLAGS+= -include ${SRCTOP}/sys/dev/wg/compat.h

View File

@ -256,6 +256,7 @@ typedef enum {
IFT_ENC = 0xf4, /* Encapsulating interface */
IFT_PFLOG = 0xf6, /* PF packet filter logging */
IFT_PFSYNC = 0xf7, /* PF packet filter synchronization */
IFT_WIREGUARD = 0xf8, /* WireGuard tunnel */
} ifType;
/*

View File

@ -284,8 +284,8 @@ nd6_ifattach(struct ifnet *ifp)
* default regardless of the V_ip6_auto_linklocal configuration to
* give a reasonable default behavior.
*/
if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) ||
(ifp->if_flags & IFF_LOOPBACK))
if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE &&
ifp->if_type != IFT_WIREGUARD) || (ifp->if_flags & IFF_LOOPBACK))
nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
/*
* A loopback interface does not need to accept RTADV.

View File

@ -350,6 +350,7 @@
#define PRIV_NET_SETVLANPCP PRIV_NET_SETLANPCP /* Alias Set VLAN priority */
#define PRIV_NET_OVPN 422 /* Administer OpenVPN DCO. */
#define PRIV_NET_ME 423 /* Administer ME interface. */
#define PRIV_NET_WG 424 /* Administer WireGuard interface. */
/*
* 802.11-related privileges.