aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/Makefile.vm20
-rw-r--r--release/tools/basic-ci.conf4
-rw-r--r--release/tools/vmimage.subr44
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math.h2
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/sound/pcm/mixer.c2
-rw-r--r--sys/dev/usb/controller/usb_controller.c2
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/wlan_gcmp/Makefile9
-rw-r--r--sys/net80211/ieee80211_crypto_gcm.c363
-rw-r--r--sys/net80211/ieee80211_crypto_gcm.h58
-rw-r--r--sys/net80211/ieee80211_crypto_gcmp.c682
-rw-r--r--sys/net80211/ieee80211_hostap.c42
-rw-r--r--sys/net80211/ieee80211_ioctl.h7
-rw-r--r--sys/net80211/ieee80211_vht.c4
-rw-r--r--usr.sbin/wlanstats/wlanstats.c24
16 files changed, 1247 insertions, 19 deletions
diff --git a/release/Makefile.vm b/release/Makefile.vm
index ec3e734285ce..1f56bdb03f97 100644
--- a/release/Makefile.vm
+++ b/release/Makefile.vm
@@ -21,6 +21,10 @@ VMDK_DESC= VMWare, VirtualBox disk image
QCOW2_DESC= Qemu, KVM disk image
RAW_DESC= Unformatted raw disk image
+.if defined(WITHOUT_QEMU)
+CLOUDWARE_TYPES?= \
+ BASIC-CI
+.else
CLOUDWARE_TYPES?= AZURE \
BASIC-CI \
BASIC-CLOUDINIT \
@@ -28,6 +32,7 @@ CLOUDWARE_TYPES?= AZURE \
GCE \
ORACLE \
VAGRANT
+.endif
AZURE_FORMAT= vhdf
AZURE_FSLIST?= ufs zfs
AZURE_DESC= Microsoft Azure platform image
@@ -94,7 +99,7 @@ emulator-portinstall:
.endif
touch ${.TARGET}
-.if ${TARGET_ARCH} != ${MACHINE_ARCH}
+.if ${TARGET_ARCH} != ${MACHINE_ARCH} && !defined(WITHOUT_QEMU)
.if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" )
QEMUSTATIC=/usr/local/bin/qemu-${TARGET_ARCH}-static
QEMUTGT=emulator-portinstall
@@ -103,6 +108,10 @@ QEMUTGT=emulator-portinstall
QEMUTGT?=
.if defined(WITH_CLOUDWARE) && !empty(WITH_CLOUDWARE) && !empty(CLOUDWARE)
+.if (defined(WITHOUT_QEMU) && !defined(NO_ROOT)) || \
+ (!defined(WITHOUT_QEMU) && defined(NO_ROOT))
+.error WITHOUT_QEMU requires NO_ROOT (and vice versa)
+.endif
. for _CW in ${CLOUDWARE}
. if exists(${.CURDIR}/tools/${_CW:tl}.conf) && !defined(${_CW:tu}CONF)
${_CW:tu}CONF?= ${.CURDIR}/tools/${_CW:tl}.conf
@@ -120,6 +129,8 @@ cw-${_CW:tl}-${_FS}-${_FMT}: ${QEMUTGT}
mkdir -p ${.OBJDIR}/${.TARGET}
env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \
QEMUSTATIC=${QEMUSTATIC} \
+ WITHOUT_QEMU=${WITHOUT_QEMU:Dtrue} \
+ NO_ROOT=${NO_ROOT:Dtrue} \
${.CURDIR}/scripts/mk-vmimage.sh \
-C ${.CURDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${_FS} \
-i ${.OBJDIR}/${_CW:tl}.${_FS}.${_FMT}.img -s ${VMSIZE} -f ${_FMT} \
@@ -169,6 +180,7 @@ vm-image: ${QEMUTGT}
mkdir -p ${.OBJDIR}/${.TARGET}-${FORMAT}-${FS}
env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \
QEMUSTATIC=${QEMUSTATIC} \
+ WITHOUT_QEMU=${WITHOUT_QEMU:Dtrue} \
${.CURDIR}/scripts/mk-vmimage.sh \
-C ${.CURDIR}/tools/vmimage.subr \
-d ${.OBJDIR}/${.TARGET}-${FORMAT}-${FS} -F ${FS} \
@@ -210,7 +222,7 @@ vm-install:
. for FORMAT in ${VMFORMATS}
. for FS in ${VMFSLIST}
# Don't keep the originals. There is a copy in ${.OBJDIR} if needed.
- ${XZ_CMD} ${DESTDIR}/vmimages/${OSRELEASE}-${FS}.${FORMAT}
+ ${XZ_CMD} -f ${DESTDIR}/vmimages/${OSRELEASE}-${FS}.${FORMAT}
. endfor
. endfor
. endif
@@ -226,7 +238,7 @@ cloud-install-BASIC-CI:
. for _FMT in ${BASIC-CI_FORMAT}
cp -p ${.OBJDIR}/${BASIC-CI${_FS:tu}${_FMT:tu}IMAGE} \
${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT}
- ${XZ_CMD} ${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT}
+ ${XZ_CMD} -f ${DESTDIR}/ciimages/${OSRELEASE}-BASIC-CI-${_FS}.${_FMT}
. endfor
.endfor
cd ${DESTDIR}/ciimages && sha512 ${OSRELEASE}* > \
@@ -240,7 +252,7 @@ cloud-install-BASIC-CLOUDINIT:
. for _FMT in ${BASIC-CLOUDINIT_FORMAT}
cp -p ${.OBJDIR}/${BASIC-CLOUDINIT${_FS:tu}${_FMT:tu}IMAGE} \
${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT}
- ${XZ_CMD} ${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT}
+ ${XZ_CMD} -f ${DESTDIR}/vmimages/${OSRELEASE}-BASIC-CLOUDINIT-${_FS}.${_FMT}
. endfor
.endfor
cd ${DESTDIR}/vmimages && sha512 ${OSRELEASE}* > \
diff --git a/release/tools/basic-ci.conf b/release/tools/basic-ci.conf
index 5acc04b1e530..12c9abb15fb1 100644
--- a/release/tools/basic-ci.conf
+++ b/release/tools/basic-ci.conf
@@ -21,6 +21,8 @@ beastie_disable="YES"
loader_logo="none"
console="comconsole,vidconsole"
EOF
+ metalog_add_data ./boot/loader.conf
+
cat <<EOF >> ${DESTDIR}/etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
@@ -28,7 +30,7 @@ PermitEmptyPasswords yes
UsePAM no
EOF
- touch ${DESTDIR}/firstboot
+ touch_firstboot
return 0
}
diff --git a/release/tools/vmimage.subr b/release/tools/vmimage.subr
index 7ebf2144efe4..c647097e8ced 100644
--- a/release/tools/vmimage.subr
+++ b/release/tools/vmimage.subr
@@ -37,6 +37,13 @@ cleanup() {
return 0
}
+metalog_add_data() {
+ if [ -n "${NO_ROOT}" ]; then
+ echo "$1 type=file uname=root gname=wheel mode=0644" >> \
+ ${DESTDIR}/METALOG
+ fi
+}
+
vm_create_base() {
mkdir -p ${DESTDIR}
@@ -61,7 +68,14 @@ vm_install_base() {
etcupdate extract -B \
-M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \
-s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate \
- -L /dev/stdout
+ -L /dev/stdout ${NO_ROOT:+-N}
+ if [ -n "${NO_ROOT}" ]; then
+ # Reroot etcupdate's internal METALOG to the whole tree
+ sed -n 's,^\.,./var/db/etcupdate/current,p' \
+ ${DESTDIR}/var/db/etcupdate/current/METALOG | \
+ env -i LC_COLLATE=C sort >> ${DESTDIR}/METALOG
+ rm ${DESTDIR}/var/db/etcupdate/current/METALOG
+ fi
echo '# Custom /etc/fstab for FreeBSD VM images' \
> ${DESTDIR}/etc/fstab
@@ -73,22 +87,28 @@ vm_install_base() {
echo '/dev/gpt/swapfs none swap sw 0 0' \
>> ${DESTDIR}/etc/fstab
fi
+ metalog_add_data ./etc/fstab
local hostname
hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')"
echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf
+ metalog_add_data ./etc/rc.conf
if [ "${VMFS}" = zfs ]; then
echo "zfs_enable=\"YES\"" >> ${DESTDIR}/etc/rc.conf
echo "zpool_reguid=\"zroot\"" >> ${DESTDIR}/etc/rc.conf
echo "zpool_upgrade=\"zroot\"" >> ${DESTDIR}/etc/rc.conf
echo "kern.geom.label.disk_ident.enable=0" >> ${DESTDIR}/boot/loader.conf
echo "zfs_load=YES" >> ${DESTDIR}/boot/loader.conf
+ metalog_add_data ./boot/loader.conf
fi
return 0
}
vm_emulation_setup() {
+ if [ -n "${WITHOUT_QEMU}" ]; then
+ return 0
+ fi
if ! [ -z "${QEMUSTATIC}" ]; then
export EMULATOR=/qemu
cp ${QEMUSTATIC} ${DESTDIR}/${EMULATOR}
@@ -130,6 +150,10 @@ vm_extra_enable_services() {
}
vm_extra_install_packages() {
+ if [ -n "${WITHOUT_QEMU}" ]; then
+ return 0
+ fi
+
if [ -z "${VM_EXTRA_PACKAGES}" ]; then
return 0
fi
@@ -158,6 +182,10 @@ vm_extra_pre_umount() {
}
vm_emulation_cleanup() {
+ if [ -n "${WITHOUT_QEMU}" ]; then
+ return 0
+ fi
+
if ! [ -z "${QEMUSTATIC}" ]; then
rm -f ${DESTDIR}/${EMULATOR}
fi
@@ -180,11 +208,11 @@ buildfs() {
case "${VMFS}" in
ufs)
- makefs ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \
- ${VMBASE} ${DESTDIR}
+ cd ${DESTDIR} && makefs ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \
+ ${VMBASE} .${NO_ROOT:+/METALOG}
;;
zfs)
- makefs -t zfs ${MAKEFSARGS} \
+ cd ${DESTDIR} && makefs -t zfs ${MAKEFSARGS} \
-o poolname=zroot -o bootfs=zroot/ROOT/default -o rootpath=/ \
-o fs=zroot\;mountpoint=none \
-o fs=zroot/ROOT\;mountpoint=none \
@@ -201,7 +229,7 @@ buildfs() {
-o fs=zroot/var/log\;setuid=off\;exec=off \
-o fs=zroot/var/mail\;atime=on \
-o fs=zroot/var/tmp\;setuid=off \
- ${VMBASE} ${DESTDIR}
+ ${VMBASE} .${NO_ROOT:+/METALOG}
;;
*)
echo "Unexpected VMFS value '${VMFS}'"
@@ -265,7 +293,7 @@ vm_create_disk() {
WITH_UNIFIED_OBJDIR=yes \
make -C ${WORLDDIR}/stand -V .OBJDIR)"
BOOTFILES="$(realpath ${BOOTFILES})"
- MAKEFSARGS="-s ${VMSIZE}"
+ MAKEFSARGS="-s ${VMSIZE} -D"
case "${TARGET}:${TARGET_ARCH}" in
amd64:amd64 | i386:i386)
@@ -334,3 +362,7 @@ vm_extra_create_disk() {
return 0
}
+touch_firstboot() {
+ touch ${DESTDIR}/firstboot
+ metalog_add_data ./firstboot
+}
diff --git a/sys/compat/linuxkpi/common/include/linux/math.h b/sys/compat/linuxkpi/common/include/linux/math.h
index dad7fa7fbe9c..5a348a57747b 100644
--- a/sys/compat/linuxkpi/common/include/linux/math.h
+++ b/sys/compat/linuxkpi/common/include/linux/math.h
@@ -46,7 +46,7 @@
#define DIV_ROUND_UP(x, n) howmany(x, n)
#define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n))
-#define DIV_ROUND_DOWN_ULL(x, n) (((unsigned long long)(x) / (n)) * (n))
+#define DIV_ROUND_DOWN_ULL(x, n) ((unsigned long long)(x) / (n))
#define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor))
#define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \
diff --git a/sys/conf/files b/sys/conf/files
index 157cda2c4b60..9173fc9b81e5 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4201,6 +4201,8 @@ net80211/ieee80211_amrr.c optional wlan | wlan_amrr
net80211/ieee80211_crypto.c optional wlan \
compile-with "${NORMAL_C} -Wno-unused-function"
net80211/ieee80211_crypto_ccmp.c optional wlan wlan_ccmp
+net80211/ieee80211_crypto_gcmp.c optional wlan wlan_gcmp
+net80211/ieee80211_crypto_gcm.c optional wlan wlan_gcmp
net80211/ieee80211_crypto_none.c optional wlan
net80211/ieee80211_crypto_tkip.c optional wlan wlan_tkip
net80211/ieee80211_crypto_wep.c optional wlan wlan_wep
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index b5b5814e667b..7bd0a2e14c46 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -1365,7 +1365,7 @@ mixer_clone(void *arg,
bus_topo_lock();
d = devclass_get_softc(pcm_devclass, snd_unit);
/* See related comment in dsp_clone(). */
- if (d != NULL && PCM_REGISTERED(d) && d->mixer_dev != NULL) {
+ if (PCM_REGISTERED(d) && d->mixer_dev != NULL) {
*dev = d->mixer_dev;
dev_ref(*dev);
}
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
index d717ad56adea..7e89a5ab0155 100644
--- a/sys/dev/usb/controller/usb_controller.c
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -438,7 +438,7 @@ usb_bus_detach(struct usb_proc_msg *pm)
/* detach children first */
bus_topo_lock();
- bus_generic_detach(dev);
+ bus_detach_children(dev);
bus_topo_unlock();
/*
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 294cb5a224de..fda662286cde 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -420,6 +420,7 @@ SUBDIR= \
wlan_acl \
wlan_amrr \
wlan_ccmp \
+ wlan_gcmp \
wlan_rssadapt \
wlan_tkip \
wlan_wep \
diff --git a/sys/modules/wlan_gcmp/Makefile b/sys/modules/wlan_gcmp/Makefile
new file mode 100644
index 000000000000..fb0a94b0a9df
--- /dev/null
+++ b/sys/modules/wlan_gcmp/Makefile
@@ -0,0 +1,9 @@
+.PATH: ${SRCTOP}/sys/net80211
+.PATH: ${SRCTOP}/sys/crypto/rijndael
+
+KMOD= wlan_gcmp
+SRCS= ieee80211_crypto_gcmp.c ieee80211_crypto_gcm.c
+SRCS+= rijndael-alg-fst.c rijndael-api.c
+SRCS+= opt_wlan.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/net80211/ieee80211_crypto_gcm.c b/sys/net80211/ieee80211_crypto_gcm.c
new file mode 100644
index 000000000000..daf02cb96298
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_gcm.c
@@ -0,0 +1,363 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * All rights reserved.
+ *
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * Originally sourced from hostapd 2.11 (src/crypto/aes-gcm.c).
+ *
+ * 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 ``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 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.
+ */
+
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <crypto/rijndael/rijndael.h>
+#include <net80211/ieee80211_crypto_gcm.h>
+
+#define AES_BLOCK_LEN 16
+
+#define BIT(x) (1U << (x))
+
+static __inline void
+xor_block(uint8_t *b, const uint8_t *a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+static inline
+void WPA_PUT_BE64(uint8_t *a, uint64_t val)
+{
+ a[0] = val >> 56;
+ a[1] = val >> 48;
+ a[2] = val >> 40;
+ a[3] = val >> 32;
+ a[4] = val >> 24;
+ a[5] = val >> 16;
+ a[6] = val >> 8;
+ a[7] = val & 0xff;
+}
+
+static inline void
+WPA_PUT_BE32(uint8_t *a, uint32_t val)
+{
+ a[0] = (val >> 24) & 0xff;
+ a[1] = (val >> 16) & 0xff;
+ a[2] = (val >> 8) & 0xff;
+ a[3] = val & 0xff;
+}
+
+static inline uint32_t
+WPA_GET_BE32(const uint8_t *a)
+{
+ return (((uint32_t) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]);
+}
+
+static void
+inc32(uint8_t *block)
+{
+ uint32_t val;
+
+ val = WPA_GET_BE32(block + AES_BLOCK_LEN - 4);
+ val++;
+ WPA_PUT_BE32(block + AES_BLOCK_LEN - 4, val);
+}
+
+static void
+shift_right_block(uint8_t *v)
+{
+ uint32_t val;
+
+ val = WPA_GET_BE32(v + 12);
+ val >>= 1;
+ if (v[11] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 12, val);
+
+ val = WPA_GET_BE32(v + 8);
+ val >>= 1;
+ if (v[7] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 8, val);
+
+ val = WPA_GET_BE32(v + 4);
+ val >>= 1;
+ if (v[3] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 4, val);
+
+ val = WPA_GET_BE32(v);
+ val >>= 1;
+ WPA_PUT_BE32(v, val);
+}
+
+
+/* Multiplication in GF(2^128) */
+static void
+gf_mult(const uint8_t *x, const uint8_t *y, uint8_t *z)
+{
+ uint8_t v[16];
+ int i, j;
+
+ memset(z, 0, 16); /* Z_0 = 0^128 */
+ memcpy(v, y, 16); /* V_0 = Y */
+
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 8; j++) {
+ if (x[i] & BIT(7 - j)) {
+ /* Z_(i + 1) = Z_i XOR V_i */
+ xor_block(z, v, AES_BLOCK_LEN);
+ } else {
+ /* Z_(i + 1) = Z_i */
+ }
+
+ if (v[15] & 0x01) {
+ /* V_(i + 1) = (V_i >> 1) XOR R */
+ shift_right_block(v);
+ /* R = 11100001 || 0^120 */
+ v[0] ^= 0xe1;
+ } else {
+ /* V_(i + 1) = V_i >> 1 */
+ shift_right_block(v);
+ }
+ }
+ }
+}
+
+static void
+ghash_start(uint8_t *y)
+{
+ /* Y_0 = 0^128 */
+ memset(y, 0, 16);
+}
+
+static void
+ghash(const uint8_t *h, const uint8_t *x, size_t xlen, uint8_t *y)
+{
+ size_t m, i;
+ const uint8_t *xpos = x;
+ uint8_t tmp[16];
+
+ m = xlen / 16;
+
+ for (i = 0; i < m; i++) {
+ /* Y_i = (Y^(i-1) XOR X_i) dot H */
+ xor_block(y, xpos, AES_BLOCK_LEN);
+ xpos += 16;
+
+ /* dot operation:
+ * multiplication operation for binary Galois (finite) field of
+ * 2^128 elements */
+ gf_mult(y, h, tmp);
+ memcpy(y, tmp, 16);
+ }
+
+ if (x + xlen > xpos) {
+ /* Add zero padded last block */
+ size_t last = x + xlen - xpos;
+ memcpy(tmp, xpos, last);
+ memset(tmp + last, 0, sizeof(tmp) - last);
+
+ /* Y_i = (Y^(i-1) XOR X_i) dot H */
+ xor_block(y, tmp, AES_BLOCK_LEN);
+
+ /* dot operation:
+ * multiplication operation for binary Galois (finite) field of
+ * 2^128 elements */
+ gf_mult(y, h, tmp);
+ memcpy(y, tmp, 16);
+ }
+
+ /* Return Y_m */
+}
+
+/*
+ * Execute the GCTR call with the counter block icb
+ * on payload x (size len), output into y.
+ */
+static void
+aes_gctr(rijndael_ctx *aes, const uint8_t *icb,
+ const uint8_t *x, size_t xlen, uint8_t *y)
+{
+ size_t i, n, last;
+ uint8_t cb[AES_BLOCK_LEN], tmp[AES_BLOCK_LEN];
+ const uint8_t *xpos = x;
+ uint8_t *ypos = y;
+
+ if (xlen == 0)
+ return;
+
+ n = xlen / 16;
+
+ memcpy(cb, icb, AES_BLOCK_LEN);
+
+ /* Full blocks */
+ for (i = 0; i < n; i++) {
+ rijndael_encrypt(aes, cb, ypos);
+ xor_block(ypos, xpos, AES_BLOCK_LEN);
+ xpos += AES_BLOCK_LEN;
+ ypos += AES_BLOCK_LEN;
+ inc32(cb);
+ }
+
+ last = x + xlen - xpos;
+ if (last) {
+ /* Last, partial block */
+ rijndael_encrypt(aes, cb, tmp);
+ for (i = 0; i < last; i++)
+ *ypos++ = *xpos++ ^ tmp[i];
+ }
+}
+
+static void
+aes_gcm_init_hash_subkey(rijndael_ctx *aes, uint8_t *H)
+{
+ /* Generate hash subkey H = AES_K(0^128) */
+ memset(H, 0, AES_BLOCK_LEN);
+
+ rijndael_encrypt(aes, H, H);
+}
+
+static void
+aes_gcm_prepare_j0(const uint8_t *iv, size_t iv_len, const uint8_t *H,
+ uint8_t *J0)
+{
+ uint8_t len_buf[16];
+
+ if (iv_len == 12) {
+ /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
+ memcpy(J0, iv, iv_len);
+ memset(J0 + iv_len, 0, AES_BLOCK_LEN - iv_len);
+ J0[AES_BLOCK_LEN - 1] = 0x01;
+ } else {
+ /*
+ * s = 128 * ceil(len(IV)/128) - len(IV)
+ * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
+ */
+ ghash_start(J0);
+ ghash(H, iv, iv_len, J0);
+ WPA_PUT_BE64(len_buf, 0);
+ WPA_PUT_BE64(len_buf + 8, iv_len * 8);
+ ghash(H, len_buf, sizeof(len_buf), J0);
+ }
+}
+
+static void
+aes_gcm_gctr(rijndael_ctx *aes, const uint8_t *J0, const uint8_t *in,
+ size_t len, uint8_t *out)
+{
+ uint8_t J0inc[AES_BLOCK_LEN];
+
+ if (len == 0)
+ return;
+
+ memcpy(J0inc, J0, AES_BLOCK_LEN);
+ inc32(J0inc);
+
+ aes_gctr(aes, J0inc, in, len, out);
+}
+
+static void
+aes_gcm_ghash(const uint8_t *H, const uint8_t *aad, size_t aad_len,
+ const uint8_t *crypt, size_t crypt_len, uint8_t *S)
+{
+ uint8_t len_buf[16];
+
+ /*
+ * u = 128 * ceil[len(C)/128] - len(C)
+ * v = 128 * ceil[len(A)/128] - len(A)
+ * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
+ * (i.e., zero padded to block size A || C and lengths of each in bits)
+ */
+ ghash_start(S);
+ ghash(H, aad, aad_len, S);
+ ghash(H, crypt, crypt_len, S);
+ WPA_PUT_BE64(len_buf, aad_len * 8);
+ WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
+ ghash(H, len_buf, sizeof(len_buf), S);
+}
+
+/**
+ * aes_gcm_ae - GCM-AE_K(IV, P, A)
+ */
+void
+ieee80211_crypto_aes_gcm_ae(rijndael_ctx *aes, const uint8_t *iv, size_t iv_len,
+ const uint8_t *plain, size_t plain_len,
+ const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *tag)
+{
+ uint8_t H[AES_BLOCK_LEN];
+ uint8_t J0[AES_BLOCK_LEN];
+ uint8_t S[GCMP_MIC_LEN];
+
+ aes_gcm_init_hash_subkey(aes, H);
+
+ aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+ /* C = GCTR_K(inc_32(J_0), P) */
+ aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
+
+ aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
+
+ /* T = MSB_t(GCTR_K(J_0, S)) */
+ aes_gctr(aes, J0, S, sizeof(S), tag);
+
+ /* Return (C, T) */
+}
+
+/**
+ * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
+ *
+ * Return 0 if OK, -1 if decrypt failure.
+ */
+int
+ieee80211_crypto_aes_gcm_ad(rijndael_ctx *aes, const uint8_t *iv, size_t iv_len,
+ const uint8_t *crypt, size_t crypt_len,
+ const uint8_t *aad, size_t aad_len, const uint8_t *tag, uint8_t *plain)
+{
+ uint8_t H[AES_BLOCK_LEN];
+ uint8_t J0[AES_BLOCK_LEN];
+ uint8_t S[16], T[GCMP_MIC_LEN];
+
+ aes_gcm_init_hash_subkey(aes, H);
+
+ aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+ /* P = GCTR_K(inc_32(J_0), C) */
+ aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
+
+ aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
+
+ /* T' = MSB_t(GCTR_K(J_0, S)) */
+ aes_gctr(aes, J0, S, sizeof(S), T);
+
+ if (memcmp(tag, T, 16) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/sys/net80211/ieee80211_crypto_gcm.h b/sys/net80211/ieee80211_crypto_gcm.h
new file mode 100644
index 000000000000..bdb41bfaee4c
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_gcm.h
@@ -0,0 +1,58 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * All rights reserved.
+ *
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * 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 ``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 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.
+ */
+#ifndef __IEEE80211_CRYPTO_GCM_H__
+#define __IEEE80211_CRYPTO_GCM_H__
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+
+#include <crypto/rijndael/rijndael.h>
+
+#define AES_BLOCK_LEN 16
+
+/*
+ * buffer is 2x the AES_BLOCK_LEN, but the AAD contents may be variable
+ * and are padded.
+ */
+#define GCM_AAD_LEN (AES_BLOCK_LEN * 2)
+
+/* GCMP is always 128 bit / 16 byte MIC */
+#define GCMP_MIC_LEN 16
+
+void ieee80211_crypto_aes_gcm_ae(rijndael_ctx *aes, const uint8_t *iv,
+ size_t iv_len, const uint8_t *plain, size_t plain_len,
+ const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *tag);
+
+int ieee80211_crypto_aes_gcm_ad(rijndael_ctx *aes, const uint8_t *iv,
+ size_t iv_len, const uint8_t *crypt, size_t crypt_len,
+ const uint8_t *aad, size_t aad_len, const uint8_t *tag,
+ uint8_t *plain);
+
+#endif /* __KERNEL__ */
+
+#endif /* __IEEE80211_CRYPTO_GCM_H__ */
diff --git a/sys/net80211/ieee80211_crypto_gcmp.c b/sys/net80211/ieee80211_crypto_gcmp.c
new file mode 100644
index 000000000000..290111235b4e
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_gcmp.c
@@ -0,0 +1,682 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ * Copyright (c) 2025 Adrian Chadd <adrian@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 ``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 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.
+ */
+
+/*
+ * IEEE 802.11 AES-GCMP crypto support.
+ *
+ * The AES-GCM crypto routines in sys/net80211/ieee80211_crypto_gcm.[ch]
+ * are derived from similar code in hostapd 2.11 (src/crypto/aes-gcm.c).
+ * The code is used with the consent of the author and its licence is
+ * included in the above source files.
+ */
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_crypto_gcm.h>
+
+#include <crypto/rijndael/rijndael.h>
+
+#define AES_BLOCK_LEN 16
+
+/*
+ * Note: GCMP_MIC_LEN defined in ieee80211_crypto_gcm.h, as it is also
+ * used by the AES-GCM routines for sizing the S and T hashes which are
+ * used by GCMP as the MIC.
+ */
+#define GCMP_PN_LEN 6
+#define GCMP_IV_LEN 12
+
+struct gcmp_ctx {
+ struct ieee80211vap *cc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *cc_ic;
+ rijndael_ctx cc_aes;
+};
+
+static void *gcmp_attach(struct ieee80211vap *, struct ieee80211_key *);
+static void gcmp_detach(struct ieee80211_key *);
+static int gcmp_setkey(struct ieee80211_key *);
+static void gcmp_setiv(struct ieee80211_key *, uint8_t *);
+static int gcmp_encap(struct ieee80211_key *, struct mbuf *);
+static int gcmp_decap(struct ieee80211_key *, struct mbuf *, int);
+static int gcmp_enmic(struct ieee80211_key *, struct mbuf *, int);
+static int gcmp_demic(struct ieee80211_key *, struct mbuf *, int);
+
+static const struct ieee80211_cipher gcmp = {
+ .ic_name = "AES-GCMP",
+ .ic_cipher = IEEE80211_CIPHER_AES_GCM_128,
+ .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ .ic_trailer = GCMP_MIC_LEN,
+ .ic_miclen = 0,
+ .ic_attach = gcmp_attach,
+ .ic_detach = gcmp_detach,
+ .ic_setkey = gcmp_setkey,
+ .ic_setiv = gcmp_setiv,
+ .ic_encap = gcmp_encap,
+ .ic_decap = gcmp_decap,
+ .ic_enmic = gcmp_enmic,
+ .ic_demic = gcmp_demic,
+};
+
+static const struct ieee80211_cipher gcmp_256 = {
+ .ic_name = "AES-GCMP-256",
+ .ic_cipher = IEEE80211_CIPHER_AES_GCM_256,
+ .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ .ic_trailer = GCMP_MIC_LEN,
+ .ic_miclen = 0,
+ .ic_attach = gcmp_attach,
+ .ic_detach = gcmp_detach,
+ .ic_setkey = gcmp_setkey,
+ .ic_setiv = gcmp_setiv,
+ .ic_encap = gcmp_encap,
+ .ic_decap = gcmp_decap,
+ .ic_enmic = gcmp_enmic,
+ .ic_demic = gcmp_demic,
+};
+
+
+static int gcmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+static int gcmp_decrypt(struct ieee80211_key *, u_int64_t pn,
+ struct mbuf *, int hdrlen);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+static void *
+gcmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ struct gcmp_ctx *ctx;
+
+ ctx = (struct gcmp_ctx *) IEEE80211_MALLOC(sizeof(struct gcmp_ctx),
+ M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (ctx == NULL) {
+ vap->iv_stats.is_crypto_nomem++;
+ return (NULL);
+ }
+ ctx->cc_vap = vap;
+ ctx->cc_ic = vap->iv_ic;
+ nrefs++; /* NB: we assume caller locking */
+ return (ctx);
+}
+
+static void
+gcmp_detach(struct ieee80211_key *k)
+{
+ struct gcmp_ctx *ctx = k->wk_private;
+
+ IEEE80211_FREE(ctx, M_80211_CRYPTO);
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+}
+
+static int
+gcmp_get_trailer_len(struct ieee80211_key *k)
+{
+ return (k->wk_cipher->ic_trailer);
+}
+
+static int
+gcmp_get_header_len(struct ieee80211_key *k)
+{
+ return (k->wk_cipher->ic_header);
+}
+
+static int
+gcmp_setkey(struct ieee80211_key *k)
+{
+ uint32_t keylen;
+
+ struct gcmp_ctx *ctx = k->wk_private;
+
+ switch (k->wk_cipher->ic_cipher) {
+ case IEEE80211_CIPHER_AES_GCM_128:
+ keylen = 128;
+ break;
+ case IEEE80211_CIPHER_AES_GCM_256:
+ keylen = 256;
+ break;
+ default:
+ IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ "%s: Unexpected cipher (%u)",
+ __func__, k->wk_cipher->ic_cipher);
+ return (0);
+ }
+
+ if (k->wk_keylen != (keylen/NBBY)) {
+ IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ "%s: Invalid key length %u, expecting %u\n",
+ __func__, k->wk_keylen, keylen/NBBY);
+ return (0);
+ }
+ if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)
+ rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
+ return (1);
+}
+
+static void
+gcmp_setiv(struct ieee80211_key *k, uint8_t *ivp)
+{
+ struct gcmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
+ uint8_t keyid;
+
+ keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
+
+ k->wk_keytsc++;
+ ivp[0] = k->wk_keytsc >> 0; /* PN0 */
+ ivp[1] = k->wk_keytsc >> 8; /* PN1 */
+ ivp[2] = 0; /* Reserved */
+ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
+ ivp[4] = k->wk_keytsc >> 16; /* PN2 */
+ ivp[5] = k->wk_keytsc >> 24; /* PN3 */
+ ivp[6] = k->wk_keytsc >> 32; /* PN4 */
+ ivp[7] = k->wk_keytsc >> 40; /* PN5 */
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+gcmp_encap(struct ieee80211_key *k, struct mbuf *m)
+{
+ const struct ieee80211_frame *wh;
+ struct gcmp_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->cc_ic;
+ uint8_t *ivp;
+ int hdrlen;
+ int is_mgmt;
+
+ hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+ wh = mtod(m, const struct ieee80211_frame *);
+ is_mgmt = IEEE80211_IS_MGMT(wh);
+
+ /*
+ * Check to see if we need to insert IV/MIC.
+ *
+ * Some offload devices don't require the IV to be inserted
+ * as part of the hardware encryption.
+ */
+ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+ return (1);
+ if (!is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIV))
+ return (1);
+
+ /*
+ * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+ */
+ M_PREPEND(m, gcmp_get_header_len(k), IEEE80211_M_NOWAIT);
+ if (m == NULL)
+ return (0);
+ ivp = mtod(m, uint8_t *);
+ ovbcopy(ivp + gcmp_get_header_len(k), ivp, hdrlen);
+ ivp += hdrlen;
+
+ gcmp_setiv(k, ivp);
+
+ /*
+ * Finally, do software encrypt if needed.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
+ !gcmp_encrypt(k, m, hdrlen))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+gcmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+ return (1);
+}
+
+static __inline uint64_t
+READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
+{
+ uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+ uint16_t iv16 = (b4 << 0) | (b5 << 8);
+ return ((((uint64_t)iv16) << 32) | iv32);
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+gcmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+ const struct ieee80211_rx_stats *rxs;
+ struct gcmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
+ struct ieee80211_frame *wh;
+ uint8_t *ivp, tid;
+ uint64_t pn;
+ bool noreplaycheck;
+
+ rxs = ieee80211_get_rx_params_ptr(m);
+
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0)
+ goto finish;
+
+ /*
+ * Header should have extended IV and sequence number;
+ * verify the former and validate the latter.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ ivp = mtod(m, uint8_t *) + hdrlen;
+ if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+ /*
+ * No extended IV; discard frame.
+ */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for AES-GCM cipher");
+ vap->iv_stats.is_rx_gcmpformat++;
+ return (0);
+ }
+ tid = ieee80211_gettid(wh);
+ pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+
+ noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0;
+ noreplaycheck |= (rxs != NULL) &&
+ (rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0;
+ if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) {
+ /*
+ * Replay violation.
+ */
+ ieee80211_notify_replay_failure(vap, wh, k, pn, tid);
+ vap->iv_stats.is_rx_gcmpreplay++;
+ return (0);
+ }
+
+ /*
+ * Check if the device handled the decrypt in hardware.
+ * If so we just strip the header; otherwise we need to
+ * handle the decrypt in software. Note that for the
+ * latter we leave the header in place for use in the
+ * decryption work.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
+ !gcmp_decrypt(k, pn, m, hdrlen))
+ return (0);
+
+finish:
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) {
+ ovbcopy(mtod(m, void *), mtod(m, uint8_t *) +
+ gcmp_get_header_len(k), hdrlen);
+ m_adj(m, gcmp_get_header_len(k));
+ }
+
+ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0)
+ m_adj(m, -gcmp_get_trailer_len(k));
+
+ /*
+ * Ok to update rsc now.
+ */
+ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) {
+ /*
+ * Do not go backwards in the IEEE80211_KEY_NOREPLAY cases
+ * or in case hardware has checked but frames are arriving
+ * reordered (e.g., LinuxKPI drivers doing RSS which we are
+ * not prepared for at all).
+ */
+ if (pn > k->wk_keyrsc[tid])
+ k->wk_keyrsc[tid] = pn;
+ }
+
+ return (1);
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+gcmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+ return (1);
+}
+
+/**
+ * @brief Calculate the AAD required for this frame for AES-GCM.
+ *
+ * Note: This code was first copied over from ieee80211_crypto_ccmp.c, so
+ * it has some CCMP-isms.
+ *
+ * NOTE: the first two bytes are a 16 bit big-endian length, which are used
+ * by AES-CCM. AES-GCM doesn't require the length at the beginning.
+ *
+ * @param wh 802.11 frame to calculate the AAD over
+ * @param aad AAD buffer, GCM_AAD_LEN bytes
+ * @param The AAD length in bytes.
+ */
+static int
+gcmp_init_aad(const struct ieee80211_frame *wh, uint8_t *aad)
+{
+ int aad_len;
+
+ memset(aad, 0, GCM_AAD_LEN);
+
+#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh)
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ aad[0] = 0; /* AAD length >> 8 */
+ /* NB: aad[1] set below */
+
+ /*
+ * TODO: go back over this in 802.11-2020 and triple check
+ * the AAD assembly with regards to packet flags.
+ */
+
+ aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */
+ /*
+ * TODO: 12.5.3.3.3 - bit 14 should always be set; bit 15 masked to 0
+ * if QoS control field, unmasked otherwise
+ */
+ aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */
+ /* NB: we know 3 addresses are contiguous */
+ memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
+ aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
+ aad[23] = 0; /* all bits masked */
+ /*
+ * Construct variable-length portion of AAD based
+ * on whether this is a 4-address frame/QOS frame.
+ * We always zero-pad to 32 bytes before running it
+ * through the cipher.
+ */
+ if (IEEE80211_IS_DSTODS(wh)) {
+ IEEE80211_ADDR_COPY(aad + 24,
+ ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
+ if (IS_QOS_DATA(wh)) {
+ const struct ieee80211_qosframe_addr4 *qwh4 =
+ (const struct ieee80211_qosframe_addr4 *) wh;
+ aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
+ aad[31] = 0;
+ aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
+ } else {
+ *(uint16_t *)&aad[30] = 0;
+ aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN;
+ }
+ } else {
+ if (IS_QOS_DATA(wh)) {
+ const struct ieee80211_qosframe *qwh =
+ (const struct ieee80211_qosframe*) wh;
+ aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
+ aad[25] = 0;
+ aad_len = aad[1] = 22 + 2;
+ } else {
+ *(uint16_t *)&aad[24] = 0;
+ aad_len = aad[1] = 22;
+ }
+ *(uint16_t *)&aad[26] = 0;
+ *(uint32_t *)&aad[28] = 0;
+ }
+#undef IS_QOS_DATA
+
+ return (aad_len);
+}
+
+/*
+ * Populate the 12 byte / 96 bit IV buffer.
+ */
+static int
+gcmp_init_iv(uint8_t *iv, const struct ieee80211_frame *wh, u_int64_t pn)
+{
+ uint8_t j_pn[GCMP_PN_LEN];
+
+ /* Construct the pn buffer */
+ j_pn[0] = pn >> 40;
+ j_pn[1] = pn >> 32;
+ j_pn[2] = pn >> 24;
+ j_pn[3] = pn >> 16;
+ j_pn[4] = pn >> 8;
+ j_pn[5] = pn >> 0;
+
+ memcpy(iv, wh->i_addr2, IEEE80211_ADDR_LEN);
+ memcpy(iv + IEEE80211_ADDR_LEN, j_pn, GCMP_PN_LEN);
+
+ return (GCMP_IV_LEN); /* 96 bits */
+}
+
+/*
+ * @brief Encrypt an mbuf.
+ *
+ * This uses a temporary memory buffer to encrypt; the
+ * current AES-GCM code expects things in a contiguous buffer
+ * and this avoids the need of breaking out the GCTR and
+ * GHASH routines into using mbuf iterators.
+ *
+ * @param key ieee80211_key to use
+ * @param mbuf 802.11 frame to encrypt
+ * @param hdrlen the length of the 802.11 header, including any padding
+ * @returns 0 if error, > 0 if OK.
+ */
+static int
+gcmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+ struct gcmp_ctx *ctx = key->wk_private;
+ struct ieee80211_frame *wh;
+ struct mbuf *m = m0;
+ int data_len, aad_len, iv_len, ret;
+ uint8_t aad[GCM_AAD_LEN];
+ uint8_t T[GCMP_MIC_LEN];
+ uint8_t iv[GCMP_IV_LEN];
+ uint8_t *p_pktbuf = NULL;
+ uint8_t *c_pktbuf = NULL;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ data_len = m->m_pkthdr.len - (hdrlen + gcmp_get_header_len(key));
+
+ ctx->cc_vap->iv_stats.is_crypto_gcmp++;
+
+ p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (p_pktbuf == NULL) {
+ IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr2, "%s",
+ "AES-GCM encrypt failed; couldn't allocate buffer");
+ ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;
+ return (0);
+ }
+ c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (c_pktbuf == NULL) {
+ IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr2, "%s",
+ "AES-GCM encrypt failed; couldn't allocate buffer");
+ ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;
+ IEEE80211_FREE(p_pktbuf, M_TEMP);
+ return (0);
+ }
+
+ /* Initialise AAD */
+ aad_len = gcmp_init_aad(wh, aad);
+
+ /* Initialise local Nonce to work on */
+ /* TODO: rename iv stuff here to nonce */
+ iv_len = gcmp_init_iv(iv, wh, key->wk_keytsc);
+
+ /* Copy mbuf data part into plaintext pktbuf */
+ m_copydata(m0, hdrlen + gcmp_get_header_len(key), data_len,
+ p_pktbuf);
+
+ /* Run encrypt */
+ /*
+ * Note: aad + 2 to skip over the 2 byte length populated
+ * at the beginning, since it's based on the AAD code in CCMP.
+ */
+ ieee80211_crypto_aes_gcm_ae(&ctx->cc_aes, iv, iv_len,
+ p_pktbuf, data_len, aad + 2, aad_len, c_pktbuf, T);
+
+ /* Copy data back over mbuf */
+ m_copyback(m0, hdrlen + gcmp_get_header_len(key), data_len,
+ c_pktbuf);
+
+ /* Append MIC */
+ ret = m_append(m0, gcmp_get_trailer_len(key), T);
+ if (ret == 0) {
+ IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr2, "%s",
+ "AES-GCM encrypt failed; couldn't append T");
+ ctx->cc_vap->iv_stats.is_crypto_gcmp_nospc++;
+ }
+
+ IEEE80211_FREE(p_pktbuf, M_TEMP);
+ IEEE80211_FREE(c_pktbuf, M_TEMP);
+
+ return (ret);
+}
+
+/*
+ * @brief Decrypt an mbuf.
+ *
+ * This uses a temporary memory buffer to decrypt; the
+ * current AES-GCM code expects things in a contiguous buffer
+ * and this avoids the need of breaking out the GCTR and
+ * GHASH routines into using mbuf iterators.
+ *
+ * @param key ieee80211_key to use
+ * @param mbuf 802.11 frame to decrypt
+ * @param hdrlen the length of the 802.11 header, including any padding
+ * @returns 0 if error, > 0 if OK.
+ */
+static int
+gcmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m,
+ int hdrlen)
+{
+ const struct ieee80211_rx_stats *rxs;
+ struct gcmp_ctx *ctx = key->wk_private;
+ struct ieee80211_frame *wh;
+ int data_len, aad_len, iv_len, ret;
+ uint8_t aad[GCM_AAD_LEN];
+ uint8_t T[GCMP_MIC_LEN];
+ uint8_t iv[GCMP_IV_LEN];
+ uint8_t *p_pktbuf = NULL;
+ uint8_t *c_pktbuf = NULL;
+
+ rxs = ieee80211_get_rx_params_ptr(m);
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0)
+ return (1);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /* Data length doesn't include the MIC at the end */
+ data_len = m->m_pkthdr.len -
+ (hdrlen + gcmp_get_header_len(key) + GCMP_MIC_LEN);
+
+ ctx->cc_vap->iv_stats.is_crypto_gcmp++;
+
+ p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (p_pktbuf == NULL) {
+ ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;
+ return (0);
+ }
+ c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (c_pktbuf == NULL) {
+ ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;
+ IEEE80211_FREE(p_pktbuf, M_TEMP);
+ return (0);
+ }
+
+ /* Initialise AAD */
+ aad_len = gcmp_init_aad(wh, aad);
+
+ /* Initialise local IV copy to work on */
+ iv_len = gcmp_init_iv(iv, wh, pn);
+
+ /* Copy mbuf into ciphertext pktbuf */
+ m_copydata(m, hdrlen + gcmp_get_header_len(key), data_len,
+ c_pktbuf);
+
+ /* Copy the MIC into the tag buffer */
+ m_copydata(m, hdrlen + gcmp_get_header_len(key) + data_len,
+ GCMP_MIC_LEN, T);
+
+ /* Run decrypt */
+ /*
+ * Note: aad + 2 to skip over the 2 byte length populated
+ * at the beginning, since it's based on the AAD code in CCMP.
+ */
+ ret = ieee80211_crypto_aes_gcm_ad(&ctx->cc_aes, iv, iv_len,
+ c_pktbuf, data_len, aad + 2, aad_len, T, p_pktbuf);
+
+ /* If the MIC was stripped by HW/driver we are done. */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0)
+ goto skip_ok;
+
+ if (ret != 0) {
+ /* Decrypt failure */
+ ctx->cc_vap->iv_stats.is_rx_gcmpmic++;
+ IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr2, "%s", "AES-GCM decrypt failed; MIC mismatch");
+ IEEE80211_FREE(p_pktbuf, M_TEMP);
+ IEEE80211_FREE(c_pktbuf, M_TEMP);
+ return (0);
+ }
+
+skip_ok:
+ /* Copy data back over mbuf */
+ m_copyback(m, hdrlen + gcmp_get_header_len(key), data_len,
+ p_pktbuf);
+
+ IEEE80211_FREE(p_pktbuf, M_TEMP);
+ IEEE80211_FREE(c_pktbuf, M_TEMP);
+
+ return (1);
+}
+
+/*
+ * Module glue.
+ */
+IEEE80211_CRYPTO_MODULE(gcmp, 1);
+IEEE80211_CRYPTO_MODULE_ADD(gcmp_256);
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
index 76c419f5bdf8..c9e2c4896f15 100644
--- a/sys/net80211/ieee80211_hostap.c
+++ b/sys/net80211/ieee80211_hostap.c
@@ -1206,6 +1206,7 @@ wpa_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
case WPA_SEL(WPA_CSE_CCMP):
*cipher = IEEE80211_CIPHER_AES_CCM;
break;
+ /* Note: no GCM cipher in the legacy WPA1 OUI */
default:
return (EINVAL);
}
@@ -1384,6 +1385,9 @@ rsn_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
case RSN_SEL(RSN_CSE_WRAP):
*cipher = IEEE80211_CIPHER_AES_OCB;
break;
+ case RSN_SEL(RSN_CSE_GCMP_128):
+ *cipher = IEEE80211_CIPHER_AES_GCM_128;
+ break;
default:
return (EINVAL);
}
@@ -1496,8 +1500,10 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
frm += 4, len -= 4;
}
- if (w & (1 << IEEE80211_CIPHER_AES_CCM))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+ if (w & (1 << IEEE80211_CIPHER_AES_GCM_128))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_GCM_128;
+ else if (w & (1 << IEEE80211_CIPHER_AES_CCM))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
else if (w & (1 << IEEE80211_CIPHER_AES_OCB))
rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_OCB;
else if (w & (1 << IEEE80211_CIPHER_TKIP))
@@ -1756,6 +1762,29 @@ is11bclient(const uint8_t *rates, const uint8_t *xrates)
return 1;
}
+/**
+ * Check if the given cipher is valid for 802.11 HT operation.
+ *
+ * The 802.11 specification only allows HT A-MPDU to be performed
+ * on CCMP / GCMP encrypted frames. The WEP/TKIP hardware crypto
+ * implementations may not meet the timing required for A-MPDU
+ * operation.
+ *
+ * @param cipher the IEEE80211_CIPHER_ value to check
+ * @returns true if the cipher is valid for HT A-MPDU, false otherwise
+ */
+static bool
+hostapd_validate_cipher_for_ht_ampdu(uint8_t cipher)
+{
+ switch (cipher) {
+ case IEEE80211_CIPHER_AES_CCM:
+ case IEEE80211_CIPHER_AES_GCM_128:
+ return true;
+ default:
+ return false;
+ }
+}
+
static void
hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
@@ -2222,13 +2251,16 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
#endif
/*
* Allow AMPDU operation only with unencrypted traffic
- * or AES-CCM; the 11n spec only specifies these ciphers
- * so permitting any others is undefined and can lead
+ * or AES-CCM / AES-GCM; the 802.11n spec only specifies these
+ * ciphers so permitting any others is undefined and can lead
* to interoperability problems.
+ *
+ * TODO: before landing, find exactly where in 802.11-2020 this
+ * is called out!
*/
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
(((vap->iv_flags & IEEE80211_F_WPA) &&
- rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) ||
+ !hostapd_validate_cipher_for_ht_ampdu(rsnparms.rsn_ucastcipher)) ||
(vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index 18152495c499..6064f586c923 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -253,6 +253,13 @@ struct ieee80211_stats {
uint32_t is_ff_encapfail; /* failed FF encap */
uint32_t is_amsdu_encapfail; /* failed A-MSDU encap */
+ uint32_t is_crypto_gcmp; /* gcmp crypto done in s/w */
+ uint32_t is_rx_gcmpreplay; /* rx seq# violation (GCMP) */
+ uint32_t is_rx_gcmpformat; /* rx format bad (GCMP) */
+ uint32_t is_rx_gcmpmic; /* rx MIC check failed (GCMP) */
+ uint32_t is_crypto_gcmp_nomem; /* gcmp crypto failed; no mem */
+ uint32_t is_crypto_gcmp_nospc; /* gcmp crypto failed; no mbuf space */
+
uint32_t is_spare[5];
};
diff --git a/sys/net80211/ieee80211_vht.c b/sys/net80211/ieee80211_vht.c
index 703447945845..0df95d87d01a 100644
--- a/sys/net80211/ieee80211_vht.c
+++ b/sys/net80211/ieee80211_vht.c
@@ -237,6 +237,8 @@ ieee80211_vht_node_cleanup(struct ieee80211_node *ni)
/*
* Parse an 802.11ac VHT operation IE.
+ *
+ * 802.11-2020 9.4.2.158 (VHT Operation element)
*/
void
ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie)
@@ -256,6 +258,8 @@ ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie)
/*
* Parse an 802.11ac VHT capability IE.
+ *
+ * 802.11-2020 9.4.2.157 (VHT Capabilities element)
*/
void
ieee80211_parse_vhtcap(struct ieee80211_node *ni, const uint8_t *ie)
diff --git a/usr.sbin/wlanstats/wlanstats.c b/usr.sbin/wlanstats/wlanstats.c
index 8795333ccb17..588b67ddd417 100644
--- a/usr.sbin/wlanstats/wlanstats.c
+++ b/usr.sbin/wlanstats/wlanstats.c
@@ -382,6 +382,18 @@ static const struct fmt wlanstats[] = {
{ 9, "ampdu_bartxfail", "bartx_fail", "BAR frames failed to send" },
#define S_AMPDU_BARTX_RETRY AFTER(S_AMPDU_BARTX_FAIL)
{ 10, "ampdu_bartxretry", "bartx_retry", "BAR frames retried" },
+#define S_CRYPTO_GCMP AFTER(S_AMPDU_BARTX_RETRY)
+ { 11, "crypto_gcmp", "crypto_gcmp", "gcmp crypto done in s/w" },
+#define S_RX_GCMPREPLAY AFTER(S_CRYPTO_GCMP)
+ { 10, "rx_gcmpreplay", "gcmpreplay", "rx seq# violation (GCMP)" },
+#define S_RX_GCMPFORMAT AFTER(S_RX_GCMPREPLAY)
+ { 10, "rx_gcmpformat", "gcmpformat", "rx format bad (GCMP)" },
+#define S_RX_GCMPMIC AFTER(S_RX_GCMPFORMAT)
+ { 7, "rx_gcmpmic", "gcmpmic", "rx MIC check failed (GCMP)" },
+#define S_RX_GCMPNOMEM AFTER(S_RX_GCMPMIC)
+ { 9, "gcmp_nomem", "gcmpnomem", "No memory available (GCMP)" },
+#define S_RX_GCMPNOSPC AFTER(S_RX_GCMPNOMEM)
+ { 9, "gcmp_nospc", "gcmpnospc", "No mbuf space available (GCMP)" },
};
struct wlanstatfoo_p {
@@ -830,6 +842,12 @@ wlan_get_curstat(struct bsdstat *sf, int s, char b[], size_t bs)
case S_AMPDU_BARTX: STAT(ampdu_bar_tx);
case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry);
case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail);
+ case S_CRYPTO_GCMP: STAT(crypto_gcmp);
+ case S_RX_GCMPREPLAY: STAT(rx_gcmpreplay);
+ case S_RX_GCMPFORMAT: STAT(rx_gcmpformat);
+ case S_RX_GCMPMIC: STAT(rx_gcmpmic);
+ case S_RX_GCMPNOMEM: STAT(crypto_gcmp_nomem);
+ case S_RX_GCMPNOSPC: STAT(crypto_gcmp_nospc);
}
return wlan_getinfo(wf, s, b, bs);
#undef NSTAT
@@ -995,6 +1013,12 @@ wlan_get_totstat(struct bsdstat *sf, int s, char b[], size_t bs)
case S_AMPDU_BARTX: STAT(ampdu_bar_tx);
case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry);
case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail);
+ case S_CRYPTO_GCMP: STAT(crypto_gcmp);
+ case S_RX_GCMPREPLAY: STAT(rx_gcmpreplay);
+ case S_RX_GCMPFORMAT: STAT(rx_gcmpformat);
+ case S_RX_GCMPMIC: STAT(rx_gcmpmic);
+ case S_RX_GCMPNOMEM: STAT(crypto_gcmp_nomem);
+ case S_RX_GCMPNOSPC: STAT(crypto_gcmp_nospc);
}
return wlan_getinfo(wf, s, b, bs);
#undef NSTAT