From b9b9eea0f4656a51843a75af16e004a2061162c4 Mon Sep 17 00:00:00 2001
From: Gordon Tetlow
Date: Tue, 14 May 2019 23:48:52 +0000
Subject: [PATCH] Add SA-19:03 to SA-19:07 and EN-19:08 to EN-19:10.
Approved by: so
---
.../advisories/FreeBSD-EN-19:08.tzdata.asc | 146 +
.../advisories/FreeBSD-EN-19:09.xinstall.asc | 128 +
.../advisories/FreeBSD-EN-19:10.scp.asc | 125 +
.../advisories/FreeBSD-SA-19:03.wpa.asc | 154 +
.../advisories/FreeBSD-SA-19:04.ntp.asc | 146 +
.../advisories/FreeBSD-SA-19:05.pf.asc | 134 +
.../advisories/FreeBSD-SA-19:06.pf.asc | 134 +
.../advisories/FreeBSD-SA-19:07.mds.asc | 198 +
.../patches/EN-19:08/tzdata-2019a.patch | 590 +
.../patches/EN-19:08/tzdata-2019a.patch.asc | 18 +
.../security/patches/EN-19:09/xinstall.patch | 71 +
.../patches/EN-19:09/xinstall.patch.asc | 18 +
share/security/patches/EN-19:10/scp.patch | 462 +
share/security/patches/EN-19:10/scp.patch.asc | 18 +
share/security/patches/SA-19:03/wpa-11.patch | 192652 +++++++++++++++
.../patches/SA-19:03/wpa-11.patch.asc | 18 +
share/security/patches/SA-19:03/wpa-12.patch | 133835 ++++++++++
.../patches/SA-19:03/wpa-12.patch.asc | 18 +
.../security/patches/SA-19:04/ntp-11.2.patch | 43036 ++++
.../patches/SA-19:04/ntp-11.2.patch.asc | 18 +
share/security/patches/SA-19:04/ntp.patch | 35712 +++
share/security/patches/SA-19:04/ntp.patch.asc | 18 +
share/security/patches/SA-19:05/pf.patch | 16 +
share/security/patches/SA-19:05/pf.patch.asc | 18 +
share/security/patches/SA-19:06/pf.patch | 69 +
share/security/patches/SA-19:06/pf.patch.asc | 18 +
.../patches/SA-19:07/mds.11-stable.patch | 835 +
.../patches/SA-19:07/mds.11-stable.patch.asc | 18 +
.../security/patches/SA-19:07/mds.11.2.patch | 849 +
.../patches/SA-19:07/mds.11.2.patch.asc | 18 +
.../patches/SA-19:07/mds.12-stable.patch | 854 +
.../patches/SA-19:07/mds.12-stable.patch.asc | 18 +
.../security/patches/SA-19:07/mds.12.0.patch | 868 +
.../patches/SA-19:07/mds.12.0.patch.asc | 18 +
share/xml/advisories.xml | 30 +
share/xml/notices.xml | 21 +
36 files changed, 411299 insertions(+)
create mode 100644 share/security/advisories/FreeBSD-EN-19:08.tzdata.asc
create mode 100644 share/security/advisories/FreeBSD-EN-19:09.xinstall.asc
create mode 100644 share/security/advisories/FreeBSD-EN-19:10.scp.asc
create mode 100644 share/security/advisories/FreeBSD-SA-19:03.wpa.asc
create mode 100644 share/security/advisories/FreeBSD-SA-19:04.ntp.asc
create mode 100644 share/security/advisories/FreeBSD-SA-19:05.pf.asc
create mode 100644 share/security/advisories/FreeBSD-SA-19:06.pf.asc
create mode 100644 share/security/advisories/FreeBSD-SA-19:07.mds.asc
create mode 100644 share/security/patches/EN-19:08/tzdata-2019a.patch
create mode 100644 share/security/patches/EN-19:08/tzdata-2019a.patch.asc
create mode 100644 share/security/patches/EN-19:09/xinstall.patch
create mode 100644 share/security/patches/EN-19:09/xinstall.patch.asc
create mode 100644 share/security/patches/EN-19:10/scp.patch
create mode 100644 share/security/patches/EN-19:10/scp.patch.asc
create mode 100644 share/security/patches/SA-19:03/wpa-11.patch
create mode 100644 share/security/patches/SA-19:03/wpa-11.patch.asc
create mode 100644 share/security/patches/SA-19:03/wpa-12.patch
create mode 100644 share/security/patches/SA-19:03/wpa-12.patch.asc
create mode 100644 share/security/patches/SA-19:04/ntp-11.2.patch
create mode 100644 share/security/patches/SA-19:04/ntp-11.2.patch.asc
create mode 100644 share/security/patches/SA-19:04/ntp.patch
create mode 100644 share/security/patches/SA-19:04/ntp.patch.asc
create mode 100644 share/security/patches/SA-19:05/pf.patch
create mode 100644 share/security/patches/SA-19:05/pf.patch.asc
create mode 100644 share/security/patches/SA-19:06/pf.patch
create mode 100644 share/security/patches/SA-19:06/pf.patch.asc
create mode 100644 share/security/patches/SA-19:07/mds.11-stable.patch
create mode 100644 share/security/patches/SA-19:07/mds.11-stable.patch.asc
create mode 100644 share/security/patches/SA-19:07/mds.11.2.patch
create mode 100644 share/security/patches/SA-19:07/mds.11.2.patch.asc
create mode 100644 share/security/patches/SA-19:07/mds.12-stable.patch
create mode 100644 share/security/patches/SA-19:07/mds.12-stable.patch.asc
create mode 100644 share/security/patches/SA-19:07/mds.12.0.patch
create mode 100644 share/security/patches/SA-19:07/mds.12.0.patch.asc
diff --git a/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc b/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc
new file mode 100644
index 0000000000..a9a2584008
--- /dev/null
+++ b/share/security/advisories/FreeBSD-EN-19:08.tzdata.asc
@@ -0,0 +1,146 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-EN-19:08.tzdata Errata Notice
+ The FreeBSD Project
+
+Topic: Timezone database information update
+
+Category: contrib
+Module: zoneinfo
+Announced: 2019-01-09
+Affects: All supported versions of FreeBSD.
+Corrected: 2019-03-29 01:39:20 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 22:48:36 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-01-01 01:40:44 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 22:48:36 UTC (releng/11.2, 11.2-RELEASE-p10)
+
+For general information regarding FreeBSD Errata Notices and Security
+Advisories, including descriptions of the fields above, security
+branches, and the following sections, please visit
+ .
+
+I. Background
+
+The tzsetup(8) program allows the user to specify the default local timezone.
+Based on the selected timezone, tzsetup(8) copies one of the files from
+/usr/share/zoneinfo to /etc/localtime. This file actually controls the
+conversion.
+
+II. Problem Description
+
+Several changes in Daylight Savings Time happened after previous FreeBSD
+releases were released that would affect many people who live in different
+countries. Because of these changes, the data in the zoneinfo files need to
+be updated, and if the local timezone on the running system is affected,
+tzsetup(8) needs to be run so the /etc/localtime is updated.
+
+III. Impact
+
+An incorrect time will be displayed on a system configured to use one of the
+affected timezones if the /usr/share/zoneinfo and /etc/localtime files are
+not updated, and all applications on the system that rely on the system time,
+such as cron(8) and syslog(8), will be affected.
+
+IV. Workaround
+
+The system administrator can install an updated timezone database from the
+misc/zoneinfo port and run tzsetup(8) to get the timezone database corrected.
+
+Applications that store and display times in Coordinated Universal Time (UTC)
+are not affected.
+
+V. Solution
+
+Please note that some third party software, for instance PHP, Ruby, Java and
+Perl, may be using different zoneinfo data source, in such cases this
+software must be updated separately. For software packages that is installed
+via binary packages, they can be upgraded by executing `pkg upgrade'.
+
+Following the instructions in this Errata Notice will update all of the
+zoneinfo files to be the same as what was released with FreeBSD release.
+
+Perform one of the following:
+
+1) Upgrade your system to a supported FreeBSD stable or release / security
+branch (releng) dated after the correction date. Restart all the affected
+applications and daemons, or reboot the system.
+
+2) To update your system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Restart all the affected applications and daemons, or reboot the system.
+
+3) To update your system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+# fetch https://security.FreeBSD.org/patches/EN-19:08/tzdata-2019a.patch
+# fetch https://security.FreeBSD.org/patches/EN-19:08/tzdata-2019a.patch.asc
+# gpg --verify tzdata-2019a.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in .
+
+Restart all the affected applications and daemons, or reboot the system.
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r345669
+releng/12.0/ r347584
+stable/11/ r345670
+releng/11.2/ r347584
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTplfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cK4Dw//Y28mXrmzitCE3RclEPrP90hcRYOzknKv8xLYNo3SnCOfSnJCQqoeNw/X
+HoAgX5Blm1sSYJ7GvK+AmKVn6FLoRGyd2tLzK5lofpbuExqrIZM6crHUx7HrblfO
+4EfUJsIPr70y0+DeD4lBgZtpV5umOVFVWz8plgyeffGwTG3qNEES8RLI62uMrtpW
+bkp+/l90eo2P9Wo34DqZSwW4V7JUwmFqooF4akZ0NBJnGpyz0iK+EZjluiRnsZxT
+ueG5yqh5BpPPQ4UTxkTMoFrF2cKP18cDzQ2e1Z27JF+MpfW3Ki4zBLcmbFrVdHhR
+1vlw1uIVKzusntEYX05oJUG8nkXckf6b7Wr6i1hD8tC7xgg4uBvTU4k/nLuGOHE/
+Oe6pAfLHvFS2ISk97FtImJd3UHR62+ZVX544dOxnY8N86tTU8p9vaO2AnfvTxzMR
+5lyqIHgDd1RWH41aASin2fM3jeXUTubq5UsTiujaFUM5Cqoe8u5UrDAzFjxx8y2H
+Uci9zi0IggRp7z8HbiXLtmoqqzwuUkXIk36j2CT7JLwH/QiP2w34Euh2wrWAeblG
+tpITlvvMl9B1+zljUCxs1+8++Q/jLbhmsH1U+r7Qj6CKAg/9hCmNYZp5WAmwDHfY
+V1JMNu6eaZpbCscJu9/QTsnvWiZZFBdHFubUueFsBNoKyQGVDkw=
+=69LY
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc b/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc
new file mode 100644
index 0000000000..49d63b9921
--- /dev/null
+++ b/share/security/advisories/FreeBSD-EN-19:09.xinstall.asc
@@ -0,0 +1,128 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-EN-19:09.xinstall Errata Notice
+ The FreeBSD Project
+
+Topic: install(1) broken with partially matching relative paths
+
+Category: core
+Module: xinstall
+Announced: 2019-05-14
+Affects: All supported versions of FreeBSD
+Corrected: 2019-02-16 04:48:30 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 22:51:49 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-02-16 04:49:10 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 22:51:49 UTC (releng/11.2, 11.2-RELEASE-p10)
+
+For general information regarding FreeBSD Errata Notices and Security
+Advisories, including descriptions of the fields above, security
+branches, and the following sections, please visit
+ .
+
+I. Background
+
+The install(1) utility installs files and links, optionally calculating
+relative paths for an installed symbolic link.
+
+II. Problem Description
+
+Due to an issue in the way install(1) determines common components of the
+source and target paths, the relative link may be incorrectly calculated and
+drop a component of the link because a partial match existed on that
+component.
+
+III. Impact
+
+The ports tree and other software very frequently use install(1) to create
+relative symlinks without checking whether a partial match of the path
+exists that would result in such a truncation.
+
+IV. Workaround
+
+No workaround is available, but using install(1) to install non-relative
+links and files is unaffected.
+
+V. Solution
+
+Perform one of the following:
+
+1) Upgrade your system to a supported FreeBSD stable or release / security
+branch (releng) dated after the correction date.
+
+2) To update your system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+3) To update your system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+# fetch https://security.FreeBSD.org/patches/EN-19:09/xinstall.patch
+# fetch https://security.FreeBSD.org/patches/EN-19:09/xinstall.patch.asc
+# gpg --verify xinstall.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in .
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r344205
+releng/12.0/ r347585
+stable/11/ r344206
+releng/11.2/ r347585
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTqhfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cJV2RAAjFslsJRGQlL5piJPcAixaQO3gEgmaAp+q79whcsN3O8cqQpApU0BApTA
+cT7cNnm3/sMteHFd6wCTLsssBnDsTWYxqccOeUIiCIgpXXkP67XYpLxxjBZqq5Tn
+egFesjpZdu2yr+0gdRrpf54msed7ts8E0dDVoGIYeGhU7omIqlYWJGJfsZ4tg1La
+Mod40JgxXcHMTca7Et46LBu/j/cF5MeQhzIepRrj1awiElQY/dMesmJwD9AuYL9m
+cuS7yTH4eC6A/b7TdhUXBqBTbNipUCmwUuIWJ6OxpcrKPrtv/qGhUCEDdsNvMxpA
+i8ciQY4YD06wdmZP+9Ugp/qXMXpLlxzwHrUYPe/Xn6/NvUgMp+KyMWgfkmtPBuIl
+YKRTp5S4ZAs6U7RPSOMUWmQ2bWh0yZqEaQXAgzzNwIpqdghrZj73krr99pCeWc81
+1MWv6K9/ZMdm8i31Iur3Mz/4hkv5WQSObU9SdjigtvFGu5ldVEJzE5f3Zu9Vr5ja
+keCB1HVYtU25ekngLYPdFiVf9B/HAWwHugOyeZNV2jPB6VVSeFkyeicm8zZ95G63
+Ww0BQbc830AFYlhb6DpciaP1Epokywr+wO4O+I3DRN3K6Zi47ODv7881milM8KQO
+jWYn0kemMIgnz0R0ZluU/I5SU1cnXbWZuKvsw9efd++irqEHrBw=
+=t05i
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-EN-19:10.scp.asc b/share/security/advisories/FreeBSD-EN-19:10.scp.asc
new file mode 100644
index 0000000000..bd1e18dd6f
--- /dev/null
+++ b/share/security/advisories/FreeBSD-EN-19:10.scp.asc
@@ -0,0 +1,125 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-EN-19:10.scp Errata Notice
+ The FreeBSD Project
+
+Topic: Insufficient filename validation in scp(1) client
+
+Category: contrib
+Module: scp
+Announced: 2019-05-14
+Affects: All supported versions of FreeBSD.
+Corrected: 2019-05-07 19:48:39 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 22:54:17 UTC (releng/12.0, 12.0-RELEASE-p10)
+CVE Name: CVE-2019-6111
+
+For general information regarding FreeBSD Errata Notices and Security
+Advisories, including descriptions of the fields above, security
+branches, and the following sections, please visit
+ .
+
+I. Background
+
+scp(1) is a file transfer protocol running over an SSH session.
+
+II. Problem Description
+
+The scp(1) client implementation fails to verify if the objects returned by
+the server match what was requested.
+
+III. Impact
+
+A malicious scp server can write arbitrary files to the client.
+
+IV. Workaround
+
+Switch to using the sftp(1) client, if possible.
+
+V. Solution
+
+Note: While stable/11 and its release branches are currently affected by this
+errata, due to the lack of patches, no fix is currently available for
+stable/11. We are currently evaluating a backport for these fixes to
+stable/11.
+
+Perform one of the following:
+
+1) Upgrade your system to a supported FreeBSD stable or release / security
+branch (releng) dated after the correction date.
+
+2) To update your system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+3) To update your system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 12.0]
+# fetch https://security.FreeBSD.org/patches/EN-19:10/scp.patch
+# fetch https://security.FreeBSD.org/patches/EN-19:10/scp.patch.asc
+# gpg --verify scp.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in .
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r347232
+releng/12.0/ r347586
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTq1fFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cJXGQ/+Ii19QUq6MdSeNPPOHVTtW8G/FIlsaYYlCFooIvzxYxvcqDcCyabVlX/a
+Lt815YY7+EbKcSbA0Gh/YFm9S05rwUg4Dnj8nIQwMVp9OEtziIdY6TVU0JhRoUpe
++YVG9e5eh8wK7FFJ/jIaZbAcr2MfMYV2KPouA1HZdqsMBkAkr8xuS3HrmkeE0nxo
+6QHTWaaD7qvr8foUSHS1hJsAX3+1eIsdytGUTJIGeL6g7DWsLYYiX7v2k+eZuSe1
+dkt7/3J+RqpyJAv+LfGh3QnILC52fO7jOVlnOBt5H/HefX+xRdb8lwHfoBeyxIFc
+N4v4Ecypewci6Hv4moTeZF+FtIETHj3EfPIe04eiikiGhrpGQ4cCveK6+kk49x4m
+RR7TE+y7klGIfoSuxoooaJ1/UyFJ9T0eICmBUh1B5rcrnwbbhgpXVPpbbee7IFL2
+HYiEuDECPN45zek+bL0M5D0wHZc823e7p1Ioxl1NNzawdts7hWwIpNmFTlfWNczQ
+KZ9y0bDFffK3nuUkMHORLagCM6ou/wAPunsnWXY3Xg3X61svYIvZThDIeeOi9SbF
+d1ve8/H/t5yHRQBpqWk51FfO4RdPmQAo6Y9w9WzhnkETsNXeTruQq7D8SnOaWgXG
+JUh9PAVQKcJRWPXVwDTPEsqRgaDVB0gpaPCt5IS2j2tyB8UuAd4=
+=2h+W
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-SA-19:03.wpa.asc b/share/security/advisories/FreeBSD-SA-19:03.wpa.asc
new file mode 100644
index 0000000000..219ebfd1d9
--- /dev/null
+++ b/share/security/advisories/FreeBSD-SA-19:03.wpa.asc
@@ -0,0 +1,154 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-19:03.wpa Security Advisory
+ The FreeBSD Project
+
+Topic: Multiple vulnerabilities in hostapd and wpa_supplicant
+
+Category: contrib
+Module: wpa
+Announced: 2019-05-14
+Affects: All supported versions of FreeBSD.
+Corrected: 2019-05-01 01:42:38 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 22:57:29 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-05-01 01:43:17 UTC (stable/11, 11.2-STABLE)
+ 2019-05-14 22:59:32 UTC (releng/11.2, 11.2-RELEASE-p10)
+CVE Name: CVE-2019-9494, CVE-2019-9495, CVE-2019-9496, CVE-2019-9497,
+ CVE-2019-9498, CVE-2019-9499, CVE-2019-11555
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit .
+
+I. Background
+
+Wi-Fi Protected Access II (WPA2) is a security protocol developed by the
+Wi-Fi Alliance to secure wireless computer networks.
+
+hostapd(8) and wpa_supplicant(8) are implementations of user space daemon for
+access points and wireless client that implements the WPA2 protocol.
+
+II. Problem Description
+
+Multiple vulnerabilities exist in the hostapd(8) and wpa_supplicant(8)
+implementations. For more details, please see the reference URLs in the
+References section below.
+
+III. Impact
+
+Security of the wireless network may be compromised. For more details,
+please see the reference URLS in the References section below.
+
+IV. Workaround
+
+No workaround is available, but systems not using hostapd(8) or
+wpa_supplicant(8) are not affected.
+
+V. Solution
+
+Perform one of the following:
+
+1) Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+
+Afterwards, restart hostapd(8) or wpa_supplicant(8).
+
+2) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Afterwards, restart hostapd(8) or wpa_supplicant(8).
+
+3) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 12.0]
+# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-12.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-12.patch.asc
+# gpg --verify wpa-12.patch.asc
+
+[FreeBSD 11.2]
+# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-11.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:03/wpa-11.patch.asc
+# gpg --verify wpa-11.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in .
+
+Restart the applicable daemons, or reboot the system.
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r346980
+releng/12.0/ r347587
+stable/11/ r346981
+releng/11.2/ r347588
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTrVfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cLsaA/9EB577JYdYdwFCOQ6TiOVhyluLJzgrhG3aiXeBntj8ytkRjcXKnP0aega
+3G2R1do7pixVYUF52OWJwaNO3Hm+LHMngiOqujcLI+49ISI3T/APaU/D2dqmXVb8
+nN/Pd+0HDGj3R3MwyyHT8/3fX0pJ395vcQhYb61M6PUSrwr8uiBbILT57iCadZoL
+F4KOCvRv7I4EFWXvqngGfeohZbbeHPBga2DwuebWR/E/1uWrMKEOF2pvh4b6ZSN2
+pdr7ZHMiL1cZt+p+2gwWoqDWyD93u2lTC7Gmo3Vom+meH7eaQ79obXEN541aiQ04
+CYhjkwuW5uNGUWCO/Xsfn5gqICeB1G5A/aBHQlAyVgUGia8jukL1jn3ga4AQgKrN
+h9aTmvrQs17PjMVtq81ZS0xm0ztW0Y6t2A9fRgGcnOOw+uy5tHMbJaKSMy8x97NT
+gUyXtoyu47tjjMrzsQcma2t6/+iCEDuW1P1LybSmv/v59gro9uveCdl0busgM9GS
+M5bpWK/qYQS1HYmYeTKMRynmD8ntRbflYoUP/SpijHsz+56rgyeJO12WyltyT32f
+j5fgnKaznW/UPtgmK0wnPIG9XEj3Nzs4C4cypO5t8OiuLEli4wRdb6MYlvEjq4la
+R3lnCzmTd9sg+K6cod2qWWSYdsdEwizcpQDp7M9lRqomiANLqJ4=
+=MXma
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-SA-19:04.ntp.asc b/share/security/advisories/FreeBSD-SA-19:04.ntp.asc
new file mode 100644
index 0000000000..de2dd01682
--- /dev/null
+++ b/share/security/advisories/FreeBSD-SA-19:04.ntp.asc
@@ -0,0 +1,146 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-19:04.ntp Security Advisory
+ The FreeBSD Project
+
+Topic: Authenticated denial of service in ntpd
+
+Category: contrib
+Module: ntp
+Announced: 2019-05-14
+Credits: Magnus Stubman
+Affects: All supported versions of FreeBSD
+Corrected: 2019-03-07 13:45:36 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 23:02:56 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-03-07 13:45:36 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 23:06:26 UTC (releng/11.2, 11.2-RELEASE-p10)
+CVE Name: CVE-2019-8936
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit .
+
+I. Background
+
+The ntpd(8) daemon is an implementation of the Network Time Protocol
+(NTP) used to synchronize the time of a computer system to a reference
+time source. The ntpd(8) daemon uses a protocol called mode 6 to both get
+status information from the running ntpd(8) daemon and configure it on the
+fly. This protocol is typically used by the ntpq(8) program, among others.
+
+II. Problem Description
+
+A crafted malicious authenticated mode 6 packet from a permitted network
+address can trigger a NULL pointer dereference.
+
+Note for this attack to work, the sending system must be on an address from
+which the target ntpd(8) accepts mode 6 packets, and must use a private key
+that is specifically listed as being used for mode 6 authorization.
+
+III. Impact
+
+The ntpd daemon can crash due to the NULL pointer dereference, causing a
+denial of service.
+
+IV. Workaround
+
+Use 'restrict noquery' in the ntpd configuration to limit addresses that
+can send mode 6 queries.
+
+V. Solution
+
+Perform one of the following:
+
+1) Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+
+2) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Afterwards, restart the ntpd service:
+# service ntpd restart
+
+3) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 12.0]
+# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp.patch.asc
+# gpg --verify ntp.patch.asc
+
+[FreeBSD 11.2-RELEASE/11.3-PRERELEASE]
+# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp-11.2.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:04/ntp-11.2.patch.asc
+# gpg --verify ntp-11.2.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in .
+
+Restart the ntpd service, or reboot the system.
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r344884
+releng/12.0/ r347589
+stable/11/ r344884
+releng/11.2/ r347590
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTrdfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cLGtw/8CNAYnLxARrMUK1QeC9sE7EaboYInSOgaunfK2Uw5tJk9b4GwWWjCSE0C
+hSWg4a9xv3pks2ppfEJzRuy0eoYmiU0MYblnAnCwCmE2d3WYlExO7hZJa1iK3uPO
+WvHre5q80kF8TJhS9rbph+6oyLaPun8f9PDIo4Oc2knTppNfrfzbB/HEuzP27KMp
+gCXD/Nk/5tHbXjkIGamWCf9wgYuw/typYRV3W6sWDuPhug2sAvWk1TMo0cMJ4BHL
+wL7Qh00rZ+nHWdk5GKFslga9gNjVPqD2DzRKCQO2bj4o+7ly2d+yk4jUpMKBq2r4
+eQcQQnk9xj60NQ5cHGprOv6xwulBYycugF57iouNAP241cvVf+XZd4b/GthJODgz
+fhP0aquusmtkawida3ZWWIVCjkM5NmHQsY5VTQLvTudtemb3kdmRMy3dFDN7oyXZ
+PqP6JJUqamxNHilxRVytNCZLiSuy1P2MnJamyLZIqcDiT6yvMVBqwuGdQrSTSKyu
+g/sR+vUohuJrP2i3pCCEfGtH5Nfq6GpY6Swxec81wUoqReGVCGmSFSEaas21TFYf
+ZzAEAhywveGegkhqvsGP9A1zrTs6ZTCRzun32MhSo4xH/YZaArMvRa6JiSWTA1fG
+ctwXEwIBj0XNEWBsCPgVvaF9bglmQZ2Iqn4iOiHlRGT7KxgjT7w=
+=o9t5
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-SA-19:05.pf.asc b/share/security/advisories/FreeBSD-SA-19:05.pf.asc
new file mode 100644
index 0000000000..90a1fe7706
--- /dev/null
+++ b/share/security/advisories/FreeBSD-SA-19:05.pf.asc
@@ -0,0 +1,134 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-19:05.pf Security Advisory
+ The FreeBSD Project
+
+Topic: IPv6 fragment reassembly panic in pf(4)
+
+Category: contrib
+Module: pf
+Announced: 2019-05-14
+Credits: Synacktiv
+Affects: All supported versions of FreeBSD
+Corrected: 2019-03-01 18:12:05 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 23:10:21 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-03-01 18:12:07 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 23:10:21 UTC (releng/11.2, 11.2-RELEASE-p10)
+CVE Name: CVE-2019-5597
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit .
+
+I. Background
+
+pf(4) is an Internet Protocol packet filter originally written for OpenBSD.
+In addition to filtering packets, it also has packet normalization
+capabilities.
+
+II. Problem Description
+
+A bug in the pf(4) IPv6 fragment reassembly logic incorrectly uses the last
+extension header offset from the last received packet instead of from the
+first packet.
+
+III. Impact
+
+Malicious IPv6 packets with different IPv6 extensions could cause a kernel
+panic or potentially a filtering rule bypass.
+
+IV. Workaround
+
+Only systems leveraging the pf(4) firewall and include packet scrubbing using
+the recommended 'scrub all in' or similar are affected.
+
+V. Solution
+
+Perform one of the following:
+
+1) Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+Afterwards, reboot the system.
+
+2) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Afterwards, reboot the system.
+
+3) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+# fetch https://security.FreeBSD.org/patches/SA-19:05/pf.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:05/pf.patch.asc
+# gpg --verify pf.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile your kernel as described in
+ and reboot the
+system.
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r344706
+releng/12.0/ r347591
+stable/11/ r344707
+releng/11.2/ r347591
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTsNfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cL1cxAAjYy90WBfuBkU/FddQWMJkXOn2YqABFxY/BfFpJEbGrnXXuxz9YJByK3b
+6ikWq5HcxgL/9ek6QULwEOoNvms8tT4m4waJOLa3hZPoPlgD2ArgvdcEI00R/8T9
+Z+k1YlT0oLOY4XbVynPGNmiFNTAcsg7Ognp9yam3kmPZTMGYm6cKIBy1idrzCCmI
+nj0SscyoL4Z09kSWe3UOitjh8cpxqGuvGosCb7YGPl6yTSalBUgP44Lyg7jS4nrZ
+xjZxqhAfp7tk9peF4rov8apZIsrBF5GMaahnIGIwZzmRn/E1pND9qx1lB1Uh7rfR
+nb8OmwbshJTWdnS1GXyLxRGJOd0zmh+YZ10ygZAQTM5sNaxfn6pWJFmr2S/mR+kN
+RG/Bhj+lN7jh1eUNdwk/pAm0aZZ+J8GX4/QOrqPfGDko/s/S7YwJB/DKR/14uPY7
+Fwcgv4tvgoRstSKHdIe45d7/N0SgQCS/EfzVIO5XPQtkrk9/zalQubionijObr1Q
+ARVl7H5M7m7kP8PJz/vRNvhar0c0xTk9ov2JDxKHKTd+7D78LQEAFvEGPIFREBsY
+VBW8BqZbuVcsgrhr/YWFE3TEw4O0YbnY5g9wmVv+d/pdDngLuTsfbNEsAQewWcu/
+dYefeBMKBukyLUKtLYHjVAhUlL3hF3j/aBu498F6LRCzFcaoIOQ=
+=0alQ
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-SA-19:06.pf.asc b/share/security/advisories/FreeBSD-SA-19:06.pf.asc
new file mode 100644
index 0000000000..e137475380
--- /dev/null
+++ b/share/security/advisories/FreeBSD-SA-19:06.pf.asc
@@ -0,0 +1,134 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-19:06.pf Security Advisory
+ The FreeBSD Project
+
+Topic: ICMP/ICMP6 packet filter bypass in pf
+
+Category: contrib
+Module: pf
+Announced: 2019-05-14
+Credits: Synacktiv
+Affects: All supported versions of FreeBSD
+Corrected: 2019-03-21 14:17:10 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 23:12:22 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-03-21 14:17:12 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 23:12:22 UTC (releng/11.2, 11.2-RELEASE-p10)
+CVE Name: CVE-2019-5598
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit .
+
+I. Background
+
+pf(4) is an Internet Protocol packet filter originally written for OpenBSD.
+In addition to filtering packets, it also has packet normalization
+capabilities.
+
+II. Problem Description
+
+States in pf(4) let ICMP and ICMP6 packets pass if they have a packet in
+their payload matching an existing condition. pf(4) does not check if the
+outer ICMP or ICMP6 packet has the same destination IP as the source IP of
+the inner protocol packet.
+
+III. Impact
+
+A maliciously crafted ICMP/ICMP6 packet could bypass the packet filter rules
+and be passed to a host that would otherwise be unavailable.
+
+IV. Workaround
+
+No workaround is available.
+
+V. Solution
+
+Perform one of the following:
+
+1) Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+Afterwards, reboot the system.
+
+2) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Afterwards, reboot the system.
+
+3) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+# fetch https://security.FreeBSD.org/patches/SA-19:06/pf.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:06/pf.patch.asc
+# gpg --verify pf.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile your kernel as described in
+ and reboot the
+system.
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r345377
+releng/12.0/ r347593
+stable/11/ r345378
+releng/11.2/ r347593
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTsdfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cIjXA/9FevC+Ygihzb0J9MN0znEM883dk5sPCSvMwiivsNRkDMXreYqPXU+Fkt0
+iV1OZ8tKwKAihm+iGJ5mzS5l40wWF1oDcqJrC0myICdvreraoJKZvTLhgGIBqKkE
+b8yIuzPueWdnnudoAzTV38RhyaP2aOb44OMUNPQZsEB/6hHsNvp9m6yAua/F+x9+
+N9J38Y/C6udsNfhqDeuCI4G8yiN33XfFiRbF+31rt3s0rUm6KGNsJanJe8dNAEvE
+DN4tA4+MORnQ7QTLgOobGuLFhWJ2urC6psH8duO72hcSTzSkTZpxrC3f6SW8RlZ+
+Pbr4LZ6FA3bZp/sCmWPOot94hotBDr03MZwrxURokeDHZU1nUBsw0rmTG4aypujl
+JrGPOAp89TtqrR0zV8DhpGO/RWoBeMDf7ZGvIplOIEF5rijQWEyC5pnYlBKPfSdm
+UTxcN9RoJCfz7O4KLAAqhHiuu6xc+CqlQH1dvyLbqGVv9LzUQlziTNsbQ4cGryuj
+g1TztU0VfpvHDkAKBh0iHwkoUqDSut3K19rFAQ3zkM/EodqSTkE1OG77pmsjYaVq
+AfcnN/se8lklq0lKi3BwNvVIWTjhMAwY63otVxvVD4wrJrgQH8NKgOeYuGBreXeW
+Uv569bIhR0/vsyGJK/SMKxBiAGfzkE7LqDMJqdXLsompX97nOwI=
+=m3as
+-----END PGP SIGNATURE-----
diff --git a/share/security/advisories/FreeBSD-SA-19:07.mds.asc b/share/security/advisories/FreeBSD-SA-19:07.mds.asc
new file mode 100644
index 0000000000..42ca84a7c8
--- /dev/null
+++ b/share/security/advisories/FreeBSD-SA-19:07.mds.asc
@@ -0,0 +1,198 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-19:07.mds Security Advisory
+ The FreeBSD Project
+
+Topic: Microarchitectural Data Sampling (MDS)
+
+Category: core
+Module: kernel
+Announced: 2019-05-14
+Credits: Refer to Intel's security advisory at the URL below for
+ detailed acknowledgements.
+Affects: All supported versions of FreeBSD.
+Corrected: 2019-05-14 17:04:00 UTC (stable/12, 12.0-STABLE)
+ 2019-05-14 23:19:08 UTC (releng/12.0, 12.0-RELEASE-p4)
+ 2019-05-14 17:05:02 UTC (stable/11, 11.3-PRERELEASE)
+ 2019-05-14 23:20:16 UTC (releng/11.2, 11.2-RELEASE-p10)
+CVE Name: CVE-2018-12126, CVE-2018-12127, CVE-2018-12130,
+ CVE-2019-11091
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit .
+
+I. Background
+
+Modern processors make use of speculative execution, an optimization
+technique which performs some action in advance of knowing whether the
+result will actually be used.
+
+II. Problem Description
+
+On some Intel processors utilizing speculative execution a local process may
+be able to infer stale information from microarchitectural buffers to obtain
+a memory disclosure.
+
+III. Impact
+
+An attacker may be able to read secret data from the kernel or from a
+process when executing untrusted code (for example, in a web browser).
+
+IV. Workaround
+
+No workaround is available.
+
+Systems with users or processors in different trust domains should disable
+Hyper-Threading by setting the machdep.hyperthreading_allowed tunable to 0:
+
+# echo 'machdep.hyperthreading_allowed=0 >> /boot/loader.conf'
+# shutdown
+
+V. Solution
+
+Perform one of the following:
+
+Update CPU microcode, upgrade your vulnerable system to a supported FreeBSD
+stable or release / security branch (releng) dated after the correction date,
+evaluate mitigation and Hyper Threading controls, and reboot the system.
+
+New CPU microcode may be available in a BIOS update from your system vendor,
+or by installing the devcpu-data package or sysutils/devcpu-data port.
+Ensure that the BIOS update or devcpu-data package is dated after 2014-05-14.
+
+If using the package or port the microcode update can be applied at boot time
+by adding the following lines to the system's /boot/loader.conf:
+
+cpu_microcode_load="YES"
+cpu_microcode_name="/boot/firmware/intel-ucode.bin"
+
+Microcode updates can also be applied while the system is running. See
+cpucontrol(8) for details.
+
+1) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the i386 or amd64
+platforms can be updated via the freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Follow additional details under "Mitigation Configuration" below.
+
+2) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 12.0-STABLE]
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12-stable.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12-stable.patch.asc
+# gpg --verify mds.12-stable.patch.asc
+
+[FreeBSD 12.0-RELEASE]
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12.0.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.12.0.patch.asc
+# gpg --verify mds.12.0.patch.asc
+
+[FreeBSD 11.3-PRERELEASE]
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11-stable.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11-stable.patch.asc
+# gpg --verify mds.11-stable.patch.asc
+
+[FreeBSD 11.2-RELEASE]
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11.2.patch
+# fetch https://security.FreeBSD.org/patches/SA-19:07/mds.11.2.patch.asc
+# gpg --verify mds.11.2.patch.asc
+
+b) Apply the patch. Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile your kernel as described in
+.
+
+Mitigation Configuration
+
+Systems with users, processes, or virtual machines in different trust
+domains should disable Hyper-Threading by setting the
+machdep.hyperthreading_allowed tunable to 0:
+
+# echo machdep.hyperthreading_allowed=0 >> /boot/loader.conf
+
+To activate the MDS mitigation set the hw.mds_disable sysctl. The settings
+are:
+
+0 - mitigation disabled
+1 - VERW instruction (microcode) mitigation enabled
+2 - Software sequence mitigation enabled (not recommended)
+3 - Automatic VERW or Software selection
+
+Automatic mode uses the VERW instruction if supported by the CPU / microcode,
+or software sequences if not. To enable automatic mode at boot:
+
+# echo hw.mds_disable=3 >> /etc/sysctl.conf
+
+Reboot the system:
+
+# shutdown -r +10min "Security update"
+
+Check the mitigation status:
+
+# sysctl hw.mds_disable_state
+hw.mds_disable_state: software Silvermont
+
+VI. Correction details
+
+The following list contains the correction revision numbers for each
+affected branch.
+
+Branch/path Revision
+- -------------------------------------------------------------------------
+stable/12/ r347567
+releng/12.0/ r346594
+stable/11/ r347568
+releng/11.2/ r347595
+- -------------------------------------------------------------------------
+
+To see which files were modified by a particular revision, run the
+following command, replacing NNNNNN with the revision number, on a
+machine with Subversion installed:
+
+# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base
+
+Or visit the following URL, replacing NNNNNN with the revision number:
+
+
+
+VII. References
+
+
+
+
+The latest revision of this advisory is available at
+
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAEBCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTspfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cKcyA//ZlJa5eoNt0L2pcWAjukf1X+/iTjHv/t3wWclEfuPv2S9lO5SDlwxUV5x
+woGkxcIj7Tp51HJZRBjn62x/cwd6CjbpxsYPUvRs1Nkruj82/p6Yj5nSYrDCqqj1
+k84hyCj0Y6V2NwbBEPTNXqqPbOmid0R3GrQJk1JXZ1zTf8VHGxrquXp1xP7PIPSX
+GWYup0k4edMCY2mbBb8QQQmQSg6S2k6eZnvF9AZUga5pM7FGYLo0rPHNVHx+te83
+THvmnrJXnCR5AEjqmsubxwF/p+HneJke7HJxj1GjokzFgzTz3C9X3vUWHedwlVoD
+BzeqSgWD0icgJMYl8xGabeRzXj49tIzrC+twdXMtTLiDIKGxaRxqGVTMHYHgh44h
+GilgZ60X4m8e4Nuzf8xcQ1X2/QLvfWwZR+zUzQwOiKVoNp7nPJ5m8nr1s9anqDdl
+n1fJw3tqw+8ant58k71IKD5lCV0KhJXgD/Kd3TZWu9a4mnMlvuJWYbEKEvxSlvTh
+ghORCSg+OBEgN//t9a/3UaAOzqKijkN6Iau1JpMrFNtBOXgOO17B1jQGz1R2VKKb
+mu5gotDQqkdQocN+94sB8T3fouSa6ub2cUox34+DngqxuFeMv6Ffg1o/Z4C0mRUu
+bVdzPrsUai/Z7O/kBpUF6ddsBGsDXWElfo9flfbJonLcYndWyWc=
+=QUYl
+-----END PGP SIGNATURE-----
diff --git a/share/security/patches/EN-19:08/tzdata-2019a.patch b/share/security/patches/EN-19:08/tzdata-2019a.patch
new file mode 100644
index 0000000000..50037dffec
--- /dev/null
+++ b/share/security/patches/EN-19:08/tzdata-2019a.patch
@@ -0,0 +1,590 @@
+--- contrib/tzdata/Makefile.orig
++++ contrib/tzdata/Makefile
+@@ -12,7 +12,10 @@
+ # Email address for bug reports.
+ BUGEMAIL= tz@iana.org
+
+-# Choose source data features. To get new features right away, use:
++# DATAFORM selects the data format.
++# Available formats represent essentially the same data, albeit
++# possibly with minor discrepancies that users are not likely to notice.
++# To get new features and the best data right away, use:
+ # DATAFORM= vanguard
+ # To wait a while before using new features, to give downstream users
+ # time to upgrade zic (the default), use:
+@@ -33,11 +36,11 @@
+ LOCALTIME= GMT
+
+ # If you want something other than Eastern United States time as a template
+-# for handling POSIX-style timezone environment variables,
++# for handling ruleless POSIX-style timezone environment variables,
+ # change the line below (after finding the timezone you want in the
+ # one of the $(TDATA) source files, or adding it to a source file).
+-# When a POSIX-style environment variable is handled, the rules in the
+-# template file are used to determine "spring forward" and "fall back" days and
++# A ruleless environment setting like TZ='CST6CDT' uses the rules in the
++# template file to determine "spring forward" and "fall back" days and
+ # times; the environment variable itself specifies UT offsets of standard and
+ # daylight saving time.
+ # Alternatively, if you discover you've got the wrong timezone, you can just
+@@ -46,7 +49,6 @@
+ # Use the command
+ # make zonenames
+ # to get a list of the values you can use for POSIXRULES.
+-# If you want POSIX compatibility, use "America/New_York".
+
+ POSIXRULES= America/New_York
+
+@@ -113,8 +115,8 @@
+ TIME_T_ALTERNATIVES_HEAD = int64_t
+ TIME_T_ALTERNATIVES_TAIL = int32_t uint32_t uint64_t
+
+-# What kind of TZif data files to generate.
+-# (TZif is the binary time zone data format that zic generates.)
++# What kind of TZif data files to generate. (TZif is the binary time
++# zone data format that zic generates; see Internet RFC 8536.)
+ # If you want only POSIX time, with time values interpreted as
+ # seconds since the epoch (not counting leap seconds), use
+ # REDO= posix_only
+@@ -360,6 +362,9 @@
+ zic= ./zic
+ ZIC= $(zic) $(ZFLAGS)
+
++# To shrink the size of installed TZif files,
++# append "-r @N" to omit data before N-seconds-after-the-Epoch.
++# See the zic man page for more about -r.
+ ZFLAGS=
+
+ # How to use zic to install TZif files.
+@@ -491,7 +496,8 @@
+ COMMON= calendars CONTRIBUTING LICENSE Makefile \
+ NEWS README theory.html version
+ WEB_PAGES= tz-art.html tz-how-to.html tz-link.html
+-CHECK_WEB_PAGES=check_tz-art.html check_tz-how-to.html check_tz-link.html
++CHECK_WEB_PAGES=check_theory.html check_tz-art.html \
++ check_tz-how-to.html check_tz-link.html
+ DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES)
+ PRIMARY_YDATA= africa antarctica asia australasia \
+ europe northamerica southamerica
+@@ -804,9 +810,10 @@
+ touch $@
+
+ check_web: $(CHECK_WEB_PAGES)
++check_theory.html: theory.html
+ check_tz-art.html: tz-art.html
+ check_tz-link.html: tz-link.html
+-check_tz-art.html check_tz-link.html:
++check_theory.html check_tz-art.html check_tz-link.html:
+ $(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \
+ -F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \
+ test ! -s $@.out || { cat $@.out; exit 1; }
+@@ -840,11 +847,13 @@
+ touch $@
+
+ clean_misc:
++ rm -fr check_*.dir
+ rm -f *.o *.out $(TIME_T_ALTERNATIVES) \
+ check_* core typecheck_* \
+ date tzselect version.h zdump zic yearistype libtz.a
+ clean: clean_misc
+- rm -fr *.dir *.zi tzdb-*/ $(TZS_NEW)
++ rm -fr *.dir tzdb-*/
++ rm -f *.zi $(TZS_NEW)
+
+ maintainer-clean: clean
+ @echo 'This command is intended for maintainers to use; it'
+--- contrib/tzdata/NEWS.orig
++++ contrib/tzdata/NEWS
+@@ -1,5 +1,53 @@
+ News for the tz database
+
++Release 20198 - 2019-03-25 22:01:33 -0700
++
++ Briefly:
++ Palestine "springs forward" on 2019-03-30 instead of 2019-03-23.
++ Metlakatla "fell back" to rejoin Alaska Time on 2019-01-20 at 02:00.
++
++ Changes to past and future timestamps
++
++ Palestine will not start DST until 2019-03-30, instead of 2019-03-23 as
++ previously predicted. Adjust our prediction by guessing that spring
++ transitions will be between 24 and 30 March, which matches recent practice
++ since 2016. (Thanks to Even Scharning and Tim Parenti.)
++
++ Metlakatla ended its observance of Pacific standard time,
++ rejoining Alaska Time, on 2019-01-20 at 02:00. (Thanks to Ryan
++ Stanley and Tim Parenti.)
++
++ Changes to past timestamps
++
++ Israel observed DST in 1980 (08-02/09-13) and 1984 (05-05/08-25).
++ (Thanks to Alois Treindl and Isaac Starkman.)
++
++ Changes to time zone abbreviations
++
++ Etc/UCT is now a backward-compatibility link to Etc/UTC, instead
++ of being a separate zone that generates the abbreviation "UCT",
++ which nowadays is typically a typo. (Problem reported by Isiah
++ Meadows.)
++
++ Changes to code
++
++ zic now has an -r option to limit the time range of output data.
++ For example, 'zic -r @1000000000' limits the output data to
++ timestamps starting 1000000000 seconds after the Epoch.
++ This helps shrink output size and can be useful for applications
++ not needing the full timestamp history, such as TZDIST truncation;
++ see Internet RFC 8536 section 5.1. (Inspired by a feature request
++ from Christopher Wong, helped along by bug reports from Wong and
++ from Tim Parenti.)
++
++ Changes to documentation
++
++ Mention Internet RFC 8536 (February 2019), which documents TZif.
++
++ tz-link.html now cites tzdata-meta
++ .
++
++
+ Release 2018i - 2018-12-30 11:05:43 -0800
+
+ Briefly:
+@@ -400,8 +448,9 @@
+ downstream parsers do not support it.
+
+ * The build procedure constructs three files vanguard.zi, main.zi,
+- and rearguard.zi, one for each format. The files represent the
+- same data as closely as the formats allow. These three files
++ and rearguard.zi, one for each format. Although the files
++ represent essentially the same data, they may have minor
++ discrepancies that users are not likely to notice. The files
+ are intended for downstream data consumers and are not
+ installed. Zoneinfo parsers that do not support negative SAVE values
+ should start using rearguard.zi, so that they will be unaffected
+--- contrib/tzdata/README.orig
++++ contrib/tzdata/README
+@@ -1,7 +1,7 @@
+ README for the tz distribution
+
+-"What time is it?" -- Richard Deacon as The King
+-"Any time you want it to be." -- Frank Baxter as The Scientist
++"Where do I set the hands of the clock?" -- Les Tremayne as The King
++"Oh that--you can set them any place you want." -- Frank Baxter as The Scientist
+ (from the Bell System film "About Time")
+
+ The Time Zone Database (called tz, tzdb or zoneinfo) contains code and
+--- contrib/tzdata/africa.orig
++++ contrib/tzdata/africa
+@@ -364,6 +364,11 @@
+ # See Africa/Lagos.
+
+ # Eritrea
++# See Africa/Nairobi.
++
++# Eswatini (formerly Swaziland)
++# See Africa/Johannesburg.
++
+ # Ethiopia
+ # See Africa/Nairobi.
+ #
+@@ -1188,7 +1193,7 @@
+ 1:30 - SAST 1903 Mar
+ 2:00 SA SAST
+ Link Africa/Johannesburg Africa/Maseru # Lesotho
+-Link Africa/Johannesburg Africa/Mbabane # Swaziland
++Link Africa/Johannesburg Africa/Mbabane # Eswatini
+ #
+ # Marion and Prince Edward Is
+ # scientific station since 1947
+@@ -1230,9 +1235,6 @@
+ 2:00 Sudan CA%sT 2000 Jan 15 12:00
+ 3:00 - EAT
+
+-# Swaziland
+-# See Africa/Johannesburg.
+-
+ # Tanzania
+ # See Africa/Nairobi.
+
+--- contrib/tzdata/asia.orig
++++ contrib/tzdata/asia
+@@ -1620,6 +1620,24 @@
+ Rule Zion 1974 only - Oct 13 0:00 0 S
+ Rule Zion 1975 only - Apr 20 0:00 1:00 D
+ Rule Zion 1975 only - Aug 31 0:00 0 S
++
++# From Alois Treindl (2019-03-06):
++# http://www.moin.gov.il/Documents/שעון קיץ/clock-50-years-7-2014.pdf
++# From Isaac Starkman (2019-03-06):
++# Summer time was in that period in 1980 and 1984, see
++# https://www.ynet.co.il/articles/0,7340,L-3951073,00.html
++# You can of course read it in translation.
++# I checked the local newspapers for that years.
++# It started on midnight and end at 01.00 am.
++# From Paul Eggert (2019-03-06):
++# Also see this thread about the moin.gov.il URL:
++# https://mm.icann.org/pipermail/tz/2018-November/027194.html
++Rule Zion 1980 only - Aug 2 0:00 1:00 D
++Rule Zion 1980 only - Sep 13 1:00 0 S
++Rule Zion 1984 only - May 5 0:00 1:00 D
++Rule Zion 1984 only - Aug 25 1:00 0 S
++
++# From Shanks & Pottenger:
+ Rule Zion 1985 only - Apr 14 0:00 1:00 D
+ Rule Zion 1985 only - Sep 15 0:00 0 S
+ Rule Zion 1986 only - May 18 0:00 1:00 D
+@@ -3071,9 +3089,15 @@
+ # the official website, though the decree did not specify the exact
+ # time of the time shift.
+ # http://www.palestinecabinet.gov.ps/Website/AR/NDecrees/ViewFile.ashx?ID=e7a42ab7-ee23-435a-b9c8-a4f7e81f3817
++
++# From Even Scharning (2019-03-23):
++# DST in Palestine will start on 30 March this year, not 23 March as the time
++# zone database predicted.
++# https://ramallah.news/post/123610
+ #
+-# From Paul Eggert (2018-03-16):
+-# For 2016 on, predict spring transitions on March's fourth Saturday at 01:00.
++# From Tim Parenti (2019-03-23):
++# Combining this with the rules observed since 2016, adjust our spring
++# transition guess to Mar Sat>=24.
+
+ # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
+ Rule EgyptAsia 1957 only - May 10 0:00 1:00 S
+@@ -3104,7 +3128,7 @@
+ Rule Palestine 2013 only - Sep Fri>=21 0:00 0 -
+ Rule Palestine 2014 2015 - Oct Fri>=21 0:00 0 -
+ Rule Palestine 2015 only - Mar lastFri 24:00 1:00 S
+-Rule Palestine 2016 max - Mar Sat>=22 1:00 1:00 S
++Rule Palestine 2016 max - Mar Sat>=24 1:00 1:00 S
+ Rule Palestine 2016 max - Oct lastSat 1:00 0 -
+
+ # Zone NAME GMTOFF RULES FORMAT [UNTIL]
+@@ -3596,5 +3620,17 @@
+ 8:00 - +08 1975 Jun 13
+ 7:00 - +07
+
++# From Paul Eggert (2019-02-19):
++#
++# The Ho Chi Minh entry suffices for most purposes as it agrees with all of
++# Vietnam since 1975-06-13. Presumably clocks often changed in south Vietnam
++# in the early 1970s as locations changed hands during the war; however the
++# details are unknown and would likely be too voluminous for this database.
++#
++# For timestamps in north Vietnam back to 1970 (the tzdb cutoff),
++# use Asia/Bangkok; see the VN entries in the file zone1970.tab.
++# For timestamps before 1970, see Asia/Hanoi in the file 'backzone'.
++
++
+ # Yemen
+ # See Asia/Riyadh.
+--- contrib/tzdata/backward.orig
++++ contrib/tzdata/backward
+@@ -77,6 +77,7 @@
+ Link America/Havana Cuba
+ Link Africa/Cairo Egypt
+ Link Europe/Dublin Eire
++Link Etc/UTC Etc/UCT
+ Link Europe/London Europe/Belfast
+ Link Europe/Chisinau Europe/Tiraspol
+ Link Europe/London GB
+@@ -111,7 +112,7 @@
+ Link Asia/Seoul ROK
+ Link Asia/Singapore Singapore
+ Link Europe/Istanbul Turkey
+-Link Etc/UCT UCT
++Link Etc/UTC UCT
+ Link America/Anchorage US/Alaska
+ Link America/Adak US/Aleutian
+ Link America/Phoenix US/Arizona
+--- contrib/tzdata/backzone.orig
++++ contrib/tzdata/backzone
+@@ -204,7 +204,7 @@
+ 2:00 1:00 SAST 1944 Mar 19 2:00
+ 2:00 - SAST
+
+-# Swaziland
++# Eswatini (formerly Swaziland)
+ Zone Africa/Mbabane 2:04:24 - LMT 1903 Mar
+ 2:00 - SAST
+
+@@ -625,7 +625,7 @@
+ 1:00 - CET 1982 Nov 27
+ 1:00 EU CE%sT
+
+-# Macedonia
++# North Macedonia
+ Zone Europe/Skopje 1:25:44 - LMT 1884
+ 1:00 - CET 1941 Apr 18 23:00
+ 1:00 C-Eur CE%sT 1945 May 8 2:00s
+--- contrib/tzdata/etcetera.orig
++++ contrib/tzdata/etcetera
+@@ -19,7 +19,6 @@
+
+ Zone Etc/GMT 0 - GMT
+ Zone Etc/UTC 0 - UTC
+-Zone Etc/UCT 0 - UCT
+
+ # The following link uses older naming conventions,
+ # but it belongs here, not in the file 'backward',
+--- contrib/tzdata/europe.orig
++++ contrib/tzdata/europe
+@@ -1855,7 +1855,7 @@
+ 1:00 Belgium CE%sT 1977
+ 1:00 EU CE%sT
+
+-# Macedonia
++# North Macedonia
+ # See Europe/Belgrade.
+
+ # Malta
+@@ -3359,7 +3359,7 @@
+ Link Europe/Belgrade Europe/Ljubljana # Slovenia
+ Link Europe/Belgrade Europe/Podgorica # Montenegro
+ Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina
+-Link Europe/Belgrade Europe/Skopje # Macedonia
++Link Europe/Belgrade Europe/Skopje # North Macedonia
+ Link Europe/Belgrade Europe/Zagreb # Croatia
+
+ # Slovakia
+--- contrib/tzdata/leap-seconds.list.orig
++++ contrib/tzdata/leap-seconds.list
+@@ -204,10 +204,10 @@
+ # current -- the update time stamp, the data and the name of the file
+ # will not change.
+ #
+-# Updated through IERS Bulletin C56
+-# File expires on: 28 June 2019
++# Updated through IERS Bulletin C57
++# File expires on: 28 December 2019
+ #
+-#@ 3770668800
++#@ 3786480000
+ #
+ 2272060800 10 # 1 Jan 1972
+ 2287785600 11 # 1 Jul 1972
+@@ -252,4 +252,4 @@
+ # the hash line is also ignored in the
+ # computation.
+ #
+-#h 62ca19f6 96a4ae0a 3708451c 9f8693f4 016604eb
++#h 83c68138 d3650221 07dbbbcd 11fcc859 ced1106a
+--- contrib/tzdata/leapseconds.orig
++++ contrib/tzdata/leapseconds
+@@ -63,7 +63,7 @@
+
+ # POSIX timestamps for the data in this file:
+ #updated 1467936000
+-#expires 1561680000
++#expires 1577491200
+
+-# Updated through IERS Bulletin C56
+-# File expires on: 28 June 2019
++# Updated through IERS Bulletin C57
++# File expires on: 28 December 2019
+--- contrib/tzdata/northamerica.orig
++++ contrib/tzdata/northamerica
+@@ -609,6 +609,15 @@
+ # In a 2018-12-11 special election, Metlakatla voted to go back to
+ # Alaska time (including daylight saving time) starting next year.
+ # https://www.krbd.org/2018/12/12/metlakatla-to-follow-alaska-standard-time-allow-liquor-sales/
++#
++# From Ryan Stanley (2019-01-11):
++# The community will be changing back on the 20th of this month...
++# From Tim Parenti (2019-01-11):
++# Per an announcement on the Metlakatla community's official Facebook page, the
++# "fall back" will be on Sunday 2019-01-20 at 02:00:
++# https://www.facebook.com/141055983004923/photos/607150969728753/
++# So they won't be waiting for Alaska to join them on 2019-03-10, but will
++# rather change their clocks twice in seven weeks.
+
+ # Zone NAME GMTOFF RULES FORMAT [UNTIL]
+ Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 15:33:32
+@@ -637,7 +646,7 @@
+ -8:00 US P%sT 1983 Oct 30 2:00
+ -8:00 - PST 2015 Nov 1 2:00
+ -9:00 US AK%sT 2018 Nov 4 2:00
+- -8:00 - PST 2019 Mar Sun>=8 3:00
++ -8:00 - PST 2019 Jan 20 2:00
+ -9:00 US AK%sT
+ Zone America/Yakutat 14:41:05 - LMT 1867 Oct 19 15:12:18
+ -9:18:55 - LMT 1900 Aug 20 12:00
+--- contrib/tzdata/theory.html.orig
++++ contrib/tzdata/theory.html
+@@ -15,7 +15,7 @@
+
+ Scope of the tz
+ database
+- Names of timezones
++ Timezone identifiers
+ Time zone abbreviations
+ Accuracy of the tz
+ database
+@@ -107,9 +107,9 @@
+
+
+
+- Names of timezones
++ Timezone identifiers
+
+-Each timezone has a unique name.
++Each timezone has a name that uniquely identifies the timezone.
+ Inexperienced users are not expected to select these names unaided.
+ Distributors should provide documentation and/or a simple selection
+ interface that explains each name via a map or via descriptive text like
+@@ -142,10 +142,12 @@
+
+
+ Be robust in the presence of political changes.
+- For example, names of countries are ordinarily not used, to avoid
++ For example, names are typically not tied to countries, to avoid
+ incompatibilities when countries change their name (e.g.,
+- Zaire→Congo) or when locations change countries (e.g., Hong
++ Swaziland→Eswatini) or when locations change countries (e.g., Hong
+ Kong from UK colony to China).
++ There is no requirement that every country or national
++ capital must have a timezone name.
+
+
+ Be portable to a wide variety of implementations.
+@@ -215,13 +217,6 @@
+ do not need locations, since local time is not defined there.
+
+
+- There should typically be at least one name for each ISO
+- 3166-1 officially assigned two-letter code for an inhabited
+- country or territory.
+-
+-
+ If all the clocks in a timezone have agreed since 1970,
+ do not bother to include more than one timezone
+ even if some of the clocks disagreed before 1970.
+@@ -228,6 +223,12 @@
+ Otherwise these tables would become annoyingly large.
+
+
++ If boundaries between regions are fluid, such as during a war or
++ insurrection, do not bother to create a new timezone merely
++ because of yet another boundary change. This helps prevent table
++ bloat and simplifies maintenance.
++
++
+ If a name is ambiguous, use a less ambiguous alternative;
+ e.g., many cities are named San José and Georgetown, so
+ prefer America/Costa_Rica
to
+@@ -299,29 +300,23 @@
+
+
+
+-The file 'zone1970.tab
' lists geographical locations used
+-to name timezones.
+-It is intended to be an exhaustive list of names for geographic
+-regions as described above; this is a subset of the timezones in the data.
+-Although a 'zone1970.tab
' location's
+-longitude
+-corresponds to
+-its local mean
+-time (LMT ) offset with one hour for every 15°
+-east longitude, this relationship is not exact.
++Guidelines have evolved with time, and names following old versions of
++this guideline might not follow the current version. When guidelines
++have changed, old names continue to be supported. Guideline changes
++have included the following:
+
+
+-
+-Older versions of this package used a different naming scheme,
+-and these older names are still supported.
++
++
++Older versions of this package used a different naming scheme.
+ See the file 'backward
' for most of these older names
+ (e.g., 'US/Eastern
' instead of 'America/New_York
').
+ The other old-fashioned names still supported are
+ 'WET
', 'CET
', 'MET
', and
+ 'EET
' (see the file 'europe
').
+-
++
+
+-
++
+ Older versions of this package defined legacy names that are
+ incompatible with the first guideline of location names, but which are
+ still supported.
+@@ -332,6 +327,31 @@
+ and the file 'northamerica
' defines the legacy names
+ 'EST5EDT
', 'CST6CDT
',
+ 'MST7MDT
', and 'PST8PDT
'.
++
++
++
++Older versions of this guideline said that
++there should typically be at least one name for each ISO
++3166-1 officially assigned two-letter code for an inhabited
++country or territory.
++This old guideline has been dropped, as it was not needed to handle
++timestamps correctly and it increased maintenance burden.
++
++
++
++
++The file 'zone1970.tab
' lists geographical locations used
++to name timezones.
++It is intended to be an exhaustive list of names for geographic
++regions as described above; this is a subset of the timezones in the data.
++Although a 'zone1970.tab
' location's
++longitude
++corresponds to
++its local mean
++time (LMT ) offset with one hour for every 15°
++east longitude, this relationship is not exact.
+
+
+
+@@ -983,7 +1003,9 @@
+ constrained to be a string containing abbreviations
+ and numeric data as described above .
+ The file's format is TZif ,
+- a timezone information format that contains binary data.
++ a timezone information format that contains binary data; see
++ Internet
++ RFC 8536 .
+ The daylight saving time rules to be used for a
+ particular timezone are encoded in the
+ TZif file; the format of the file allows US ,
+@@ -1166,7 +1188,7 @@
+
+
+ A set of timezone names as per
+- "Names of timezones " above.
++ "Timezone identifiers " above.
+
+
+ Library functions described in "Time and date
+@@ -1213,6 +1235,17 @@
+ offsets or abbreviations for timestamps, as data entries are often
+ based on guesswork and these guesses may be corrected or improved.
+
++
++
++Timezone boundaries are not part of the stable interface.
++For example, even though the Asia/Bangkok timezone
++currently includes Chang Mai, Hanoi, and Phnom Penh, this is not part
++of the stable interface and the timezone can split at any time.
++If a calendar application records a future event in some location other
++than Bangkok by putting "Asia/Bangkok " in the event's record,
++the application should be robust in the presence of timezone splits
++between now and the future time.
++
+
+
+
+--- contrib/tzdata/version.orig
++++ contrib/tzdata/version
+@@ -1 +1 @@
+-2018i
++2019a
diff --git a/share/security/patches/EN-19:08/tzdata-2019a.patch.asc b/share/security/patches/EN-19:08/tzdata-2019a.patch.asc
new file mode 100644
index 0000000000..ea8804921e
--- /dev/null
+++ b/share/security/patches/EN-19:08/tzdata-2019a.patch.asc
@@ -0,0 +1,18 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTt9fFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cLylBAAnxBCTvmO6feqKS+TKuC7duoJ/46iaEss1qLU3Co+C53djVU/tGqdHpaV
+ZP69xexBkxSthV+Hkifps8LIaD1rF3KYXWEe7icbVvBeSe5mBX079VgTkGtQFaJm
+UgSY7aY7fDh5phDF5XjRIl3XgEbKyBTd8f+pDO0rJ3+LehoAqR5F3UXJEGtNvqXm
+ZOGZVZoOxD7+8OqJFH7Zb7O70iLhN5U+FEFnfIPGrYywwDn04BVh0pYMryBcp4sr
+SiENYNxoByEpN2oNDWq+JkWfi3woZmQHAMc5xhQgDCNiMma7eIAmANdMkz1Q0Hdy
+9Tt5Dt7vYkhbfXLqsmUqGXKqGwasWm5jSUT8sXsJ9+DkMnzAGIg8FrZygYIIn/XO
+aO5SLRQujKEltnCTEBJygVsjDQB0ZZUIGBER9njasnedlqBJPPG3Yf16aU6kO/LS
+jq8xi/ymskE/kYrl9L1G2yiyR83p6BeZu8L8Y68dH9xGpLPmfcVYLs3yYB1Yb7AN
+L3eDPfkF/PFN2rTB41m8NoEwQqkqABaIGDaolU5z/Cvs3sUvdpOhJ1PWLzJTeCYV
+eDNbu7NqrbKSa79oE6atLSb+JKyv1zWtqivPVJ47LaL3+LYfwTnRHVBcgmb1RFfO
+qvSENI9vGNkYrobxlglSi/av8P9TaD5nAk8xJRk6zn3F6cXLHto=
+=6OYE
+-----END PGP SIGNATURE-----
diff --git a/share/security/patches/EN-19:09/xinstall.patch b/share/security/patches/EN-19:09/xinstall.patch
new file mode 100644
index 0000000000..bd4882e772
--- /dev/null
+++ b/share/security/patches/EN-19:09/xinstall.patch
@@ -0,0 +1,71 @@
+--- usr.bin/xinstall/tests/install_test.sh.orig
++++ usr.bin/xinstall/tests/install_test.sh
+@@ -377,6 +377,29 @@
+ atf_check install -d dir1/dir2/dir3
+ }
+
++atf_test_case symbolic_link_relative_absolute_common
++symbolic_link_relative_absolute_common_head() {
++ atf_set "descr" "Verify -l rs with absolute paths having common components"
++}
++symbolic_link_relative_absolute_common_body() {
++ filename=foo.so
++ src_path=lib
++ src_path_prefixed=$PWD/$src_path
++ dest_path=$PWD/libexec/
++ src_file=$src_path_prefixed/$filename
++ dest_file=$dest_path/$filename
++
++ atf_check mkdir $src_path_prefixed $dest_path
++ atf_check touch $src_file
++ atf_check install -l sr $src_file $dest_path
++
++ dest_path_relative=$(readlink $dest_file)
++ src_path_relative="../lib/$filename"
++ if [ "$src_path_relative" != "$dest_path_relative" ]; then
++ atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')"
++ fi
++}
++
+ atf_init_test_cases() {
+ atf_add_test_case copy_to_nonexistent
+ atf_add_test_case copy_to_nonexistent_safe
+@@ -415,5 +438,6 @@
+ atf_add_test_case symbolic_link_relative_absolute_source_and_dest1
+ atf_add_test_case symbolic_link_relative_absolute_source_and_dest1_double_slash
+ atf_add_test_case symbolic_link_relative_absolute_source_and_dest2
++ atf_add_test_case symbolic_link_relative_absolute_common
+ atf_add_test_case mkdir_simple
+ }
+--- usr.bin/xinstall/xinstall.c.orig
++++ usr.bin/xinstall/xinstall.c
+@@ -673,7 +673,7 @@
+ }
+
+ if (dolink & LN_RELATIVE) {
+- char *to_name_copy, *cp, *d, *s;
++ char *to_name_copy, *cp, *d, *ld, *ls, *s;
+
+ if (*from_name != '/') {
+ /* this is already a relative link */
+@@ -709,8 +709,19 @@
+ free(to_name_copy);
+
+ /* Trim common path components. */
+- for (s = src, d = dst; *s == *d; s++, d++)
++ ls = ld = NULL;
++ for (s = src, d = dst; *s == *d; ls = s, ld = d, s++, d++)
+ continue;
++ /*
++ * If we didn't end after a directory separator, then we've
++ * falsely matched the last component. For example, if one
++ * invoked install -lrs /lib/foo.so /libexec/ then the source
++ * would terminate just after the separator while the
++ * destination would terminate in the middle of 'libexec',
++ * leading to a full directory getting falsely eaten.
++ */
++ if ((ls != NULL && *ls != '/') || (ld != NULL && *ld != '/'))
++ s--, d--;
+ while (*s != '/')
+ s--, d--;
+
diff --git a/share/security/patches/EN-19:09/xinstall.patch.asc b/share/security/patches/EN-19:09/xinstall.patch.asc
new file mode 100644
index 0000000000..ac57a4093b
--- /dev/null
+++ b/share/security/patches/EN-19:09/xinstall.patch.asc
@@ -0,0 +1,18 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTulfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cLCnhAApUMnH4ylN0QvYr/1DvbRVYXXijKJ6BgmHb93wXO1hlA+6lt9rvitx6Vy
+uhId9HDbb4p2Mt4G9mJS8oV1Sb+DKJ0l9cqtvKqLWqtlrwaNWf5Wnnksh0eE5aH/
+44L6ei4jbX6akDhdHhlhvrtwUupZ5MmlbdL5iM/hxtv8yWim4gV+GD4g0wIKCCEu
+sZ4cRSmlVO673e1jGYZ5bTtvP24SSDUKivijjswLXoS3mBmWp4n00o0qVR8WIRqJ
+95Dy/RKMOnkPDEpbSj6Gnv4GIO/4W/kHhpD/2Su3ga9dUq7fpwYqJKY9wOrF8qz1
+Mc8UTk3uZyfjmd05Kz7R9uTuv7GbYeWFIwTdLyAo3ImwfZIqPMYZqxE8M815Wvlo
+V1PBlkj7v4ZTEm4Z7SsjWUTHz7ILHxChGoBRzQkjS3VcItJniz5XIwh8JvQjYRtv
+Oco1N7zhuPHE7AHTo8huH/saGjlv4CgzZlgWpfj8zWNPttKKMgPjei+Wle5AToLH
+6Zx4AIYU5RdhG6gLxnjdRaj6xF6+PuRSzrym+2sRy89s0ksCtDLJYK2Ehzz5Uocu
+d+UMDsYIzE1bNA1Blj1oEulU7yWBsLAq5WznuoP5WeXaLx8PWcIgUHgtbnlPIIES
+vG+WFBxOrB9bW6VjfbHQNFf07/rQQ2M7Q6AB/ycbUrUz3/STfxE=
+=qyyv
+-----END PGP SIGNATURE-----
diff --git a/share/security/patches/EN-19:10/scp.patch b/share/security/patches/EN-19:10/scp.patch
new file mode 100644
index 0000000000..ddbd9b7cc4
--- /dev/null
+++ b/share/security/patches/EN-19:10/scp.patch
@@ -0,0 +1,462 @@
+--- crypto/openssh/scp.1.orig
++++ crypto/openssh/scp.1
+@@ -18,7 +18,7 @@
+ .Nd secure copy (remote file copy program)
+ .Sh SYNOPSIS
+ .Nm scp
+-.Op Fl 346BCpqrv
++.Op Fl 346BCpqrTv
+ .Op Fl c Ar cipher
+ .Op Fl F Ar ssh_config
+ .Op Fl i Ar identity_file
+@@ -207,6 +207,16 @@
+ The program must understand
+ .Xr ssh 1
+ options.
++.It Fl T
++Disable strict filename checking.
++By default when copying files from a remote host to a local directory
++.Nm
++checks that the received filenames match those requested on the command-line
++to prevent the remote end from sending unexpected or unwanted files.
++Because of differences in how various operating systems and shells interpret
++filename wildcards, these checks may cause wanted files to be rejected.
++This option disables these checks at the expense of fully trusting that
++the server will not send unexpected filenames.
+ .It Fl v
+ Verbose mode.
+ Causes
+--- crypto/openssh/scp.c.orig
++++ crypto/openssh/scp.c
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: scp.c,v 1.197 2018/06/01 04:31:48 dtucker Exp $ */
++/* $OpenBSD: scp.c,v 1.204 2019/02/10 11:15:52 djm Exp $ */
+ /*
+ * scp - secure remote copy. This is basically patched BSD rcp which
+ * uses ssh to do the data transfer (instead of using rcmd).
+@@ -94,6 +94,7 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+@@ -375,7 +376,7 @@
+ struct passwd *pwd;
+ uid_t userid;
+ int errs, remin, remout;
+-int pflag, iamremote, iamrecursive, targetshouldbedirectory;
++int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
+
+ #define CMDNEEDS 64
+ char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
+@@ -382,7 +383,7 @@
+
+ int response(void);
+ void rsource(char *, struct stat *);
+-void sink(int, char *[]);
++void sink(int, char *[], const char *);
+ void source(int, char *[]);
+ void tolocal(int, char *[]);
+ void toremote(int, char *[]);
+@@ -421,8 +422,9 @@
+ addargs(&args, "-oRemoteCommand=none");
+ addargs(&args, "-oRequestTTY=no");
+
+- fflag = tflag = 0;
+- while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
++ fflag = Tflag = tflag = 0;
++ while ((ch = getopt(argc, argv,
++ "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) {
+ switch (ch) {
+ /* User-visible flags. */
+ case '1':
+@@ -501,9 +503,13 @@
+ setmode(0, O_BINARY);
+ #endif
+ break;
++ case 'T':
++ Tflag = 1;
++ break;
+ default:
+ usage();
+ }
++ }
+ argc -= optind;
+ argv += optind;
+
+@@ -534,7 +540,7 @@
+ }
+ if (tflag) {
+ /* Receive data. */
+- sink(argc, argv);
++ sink(argc, argv, NULL);
+ exit(errs != 0);
+ }
+ if (argc < 2)
+@@ -620,6 +626,253 @@
+ return r;
+ }
+
++/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
++static int
++append(char *cp, char ***ap, size_t *np)
++{
++ char **tmp;
++
++ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
++ return -1;
++ tmp[(*np)] = cp;
++ (*np)++;
++ *ap = tmp;
++ return 0;
++}
++
++/*
++ * Finds the start and end of the first brace pair in the pattern.
++ * returns 0 on success or -1 for invalid patterns.
++ */
++static int
++find_brace(const char *pattern, int *startp, int *endp)
++{
++ int i;
++ int in_bracket, brace_level;
++
++ *startp = *endp = -1;
++ in_bracket = brace_level = 0;
++ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
++ switch (pattern[i]) {
++ case '\\':
++ /* skip next character */
++ if (pattern[i + 1] != '\0')
++ i++;
++ break;
++ case '[':
++ in_bracket = 1;
++ break;
++ case ']':
++ in_bracket = 0;
++ break;
++ case '{':
++ if (in_bracket)
++ break;
++ if (pattern[i + 1] == '}') {
++ /* Protect a single {}, for find(1), like csh */
++ i++; /* skip */
++ break;
++ }
++ if (*startp == -1)
++ *startp = i;
++ brace_level++;
++ break;
++ case '}':
++ if (in_bracket)
++ break;
++ if (*startp < 0) {
++ /* Unbalanced brace */
++ return -1;
++ }
++ if (--brace_level <= 0)
++ *endp = i;
++ break;
++ }
++ }
++ /* unbalanced brackets/braces */
++ if (*endp < 0 && (*startp >= 0 || in_bracket))
++ return -1;
++ return 0;
++}
++
++/*
++ * Assembles and records a successfully-expanded pattern, returns -1 on
++ * alloc failure.
++ */
++static int
++emit_expansion(const char *pattern, int brace_start, int brace_end,
++ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
++{
++ char *cp;
++ int o = 0, tail_len = strlen(pattern + brace_end + 1);
++
++ if ((cp = malloc(brace_start + (sel_end - sel_start) +
++ tail_len + 1)) == NULL)
++ return -1;
++
++ /* Pattern before initial brace */
++ if (brace_start > 0) {
++ memcpy(cp, pattern, brace_start);
++ o = brace_start;
++ }
++ /* Current braced selection */
++ if (sel_end - sel_start > 0) {
++ memcpy(cp + o, pattern + sel_start,
++ sel_end - sel_start);
++ o += sel_end - sel_start;
++ }
++ /* Remainder of pattern after closing brace */
++ if (tail_len > 0) {
++ memcpy(cp + o, pattern + brace_end + 1, tail_len);
++ o += tail_len;
++ }
++ cp[o] = '\0';
++ if (append(cp, patternsp, npatternsp) != 0) {
++ free(cp);
++ return -1;
++ }
++ return 0;
++}
++
++/*
++ * Expand the first encountered brace in pattern, appending the expanded
++ * patterns it yielded to the *patternsp array.
++ *
++ * Returns 0 on success or -1 on allocation failure.
++ *
++ * Signals whether expansion was performed via *expanded and whether
++ * pattern was invalid via *invalid.
++ */
++static int
++brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
++ int *expanded, int *invalid)
++{
++ int i;
++ int in_bracket, brace_start, brace_end, brace_level;
++ int sel_start, sel_end;
++
++ *invalid = *expanded = 0;
++
++ if (find_brace(pattern, &brace_start, &brace_end) != 0) {
++ *invalid = 1;
++ return 0;
++ } else if (brace_start == -1)
++ return 0;
++
++ in_bracket = brace_level = 0;
++ for (i = sel_start = brace_start + 1; i < brace_end; i++) {
++ switch (pattern[i]) {
++ case '{':
++ if (in_bracket)
++ break;
++ brace_level++;
++ break;
++ case '}':
++ if (in_bracket)
++ break;
++ brace_level--;
++ break;
++ case '[':
++ in_bracket = 1;
++ break;
++ case ']':
++ in_bracket = 0;
++ break;
++ case '\\':
++ if (i < brace_end - 1)
++ i++; /* skip */
++ break;
++ }
++ if (pattern[i] == ',' || i == brace_end - 1) {
++ if (in_bracket || brace_level > 0)
++ continue;
++ /* End of a selection, emit an expanded pattern */
++
++ /* Adjust end index for last selection */
++ sel_end = (i == brace_end - 1) ? brace_end : i;
++ if (emit_expansion(pattern, brace_start, brace_end,
++ sel_start, sel_end, patternsp, npatternsp) != 0)
++ return -1;
++ /* move on to the next selection */
++ sel_start = i + 1;
++ continue;
++ }
++ }
++ if (in_bracket || brace_level > 0) {
++ *invalid = 1;
++ return 0;
++ }
++ /* success */
++ *expanded = 1;
++ return 0;
++}
++
++/* Expand braces from pattern. Returns 0 on success, -1 on failure */
++static int
++brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
++{
++ char *cp, *cp2, **active = NULL, **done = NULL;
++ size_t i, nactive = 0, ndone = 0;
++ int ret = -1, invalid = 0, expanded = 0;
++
++ *patternsp = NULL;
++ *npatternsp = 0;
++
++ /* Start the worklist with the original pattern */
++ if ((cp = strdup(pattern)) == NULL)
++ return -1;
++ if (append(cp, &active, &nactive) != 0) {
++ free(cp);
++ return -1;
++ }
++ while (nactive > 0) {
++ cp = active[nactive - 1];
++ nactive--;
++ if (brace_expand_one(cp, &active, &nactive,
++ &expanded, &invalid) == -1) {
++ free(cp);
++ goto fail;
++ }
++ if (invalid)
++ fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
++ if (expanded) {
++ /*
++ * Current entry expanded to new entries on the
++ * active list; discard the progenitor pattern.
++ */
++ free(cp);
++ continue;
++ }
++ /*
++ * Pattern did not expand; append the finename component to
++ * the completed list
++ */
++ if ((cp2 = strrchr(cp, '/')) != NULL)
++ *cp2++ = '\0';
++ else
++ cp2 = cp;
++ if (append(xstrdup(cp2), &done, &ndone) != 0) {
++ free(cp);
++ goto fail;
++ }
++ free(cp);
++ }
++ /* success */
++ *patternsp = done;
++ *npatternsp = ndone;
++ done = NULL;
++ ndone = 0;
++ ret = 0;
++ fail:
++ for (i = 0; i < nactive; i++)
++ free(active[i]);
++ free(active);
++ for (i = 0; i < ndone; i++)
++ free(done[i]);
++ free(done);
++ return ret;
++}
++
+ void
+ toremote(int argc, char **argv)
+ {
+@@ -791,7 +1044,7 @@
+ continue;
+ }
+ free(bp);
+- sink(1, argv + argc - 1);
++ sink(1, argv + argc - 1, src);
+ (void) close(remin);
+ remin = remout = -1;
+ }
+@@ -967,7 +1220,7 @@
+ (sizeof(type) != 4 && sizeof(type) != 8))
+
+ void
+-sink(int argc, char **argv)
++sink(int argc, char **argv, const char *src)
+ {
+ static BUF buffer;
+ struct stat stb;
+@@ -983,6 +1236,8 @@
+ unsigned long long ull;
+ int setimes, targisdir, wrerrno = 0;
+ char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
++ char **patterns = NULL;
++ size_t n, npatterns = 0;
+ struct timeval tv[2];
+
+ #define atime tv[0]
+@@ -1007,10 +1262,18 @@
+ (void) atomicio(vwrite, remout, "", 1);
+ if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
+ targisdir = 1;
++ if (src != NULL && !iamrecursive && !Tflag) {
++ /*
++ * Prepare to try to restrict incoming filenames to match
++ * the requested destination file glob.
++ */
++ if (brace_expand(src, &patterns, &npatterns) != 0)
++ fatal("%s: could not expand pattern", __func__);
++ }
+ for (first = 1;; first = 0) {
+ cp = buf;
+ if (atomicio(read, remin, cp, 1) != 1)
+- return;
++ goto done;
+ if (*cp++ == '\n')
+ SCREWUP("unexpected ");
+ do {
+@@ -1036,7 +1299,7 @@
+ }
+ if (buf[0] == 'E') {
+ (void) atomicio(vwrite, remout, "", 1);
+- return;
++ goto done;
+ }
+ if (ch == '\n')
+ *--cp = 0;
+@@ -1106,10 +1369,19 @@
+ SCREWUP("size out of range");
+ size = (off_t)ull;
+
+- if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
++ if (*cp == '\0' || strchr(cp, '/') != NULL ||
++ strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) {
+ run_err("error: unexpected filename: %s", cp);
+ exit(1);
+ }
++ if (npatterns > 0) {
++ for (n = 0; n < npatterns; n++) {
++ if (fnmatch(patterns[n], cp, 0) == 0)
++ break;
++ }
++ if (n >= npatterns)
++ SCREWUP("filename does not match request");
++ }
+ if (targisdir) {
+ static char *namebuf;
+ static size_t cursize;
+@@ -1147,7 +1419,7 @@
+ goto bad;
+ }
+ vect[0] = xstrdup(np);
+- sink(1, vect);
++ sink(1, vect, src);
+ if (setimes) {
+ setimes = 0;
+ if (utimes(vect[0], tv) < 0)
+@@ -1268,7 +1540,15 @@
+ break;
+ }
+ }
++done:
++ for (n = 0; n < npatterns; n++)
++ free(patterns[n]);
++ free(patterns);
++ return;
+ screwup:
++ for (n = 0; n < npatterns; n++)
++ free(patterns[n]);
++ free(patterns);
+ run_err("protocol error: %s", why);
+ exit(1);
+ }
+@@ -1315,7 +1595,7 @@
+ usage(void)
+ {
+ (void) fprintf(stderr,
+- "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
++ "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
+ " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n");
+ exit(1);
+ }
diff --git a/share/security/patches/EN-19:10/scp.patch.asc b/share/security/patches/EN-19:10/scp.patch.asc
new file mode 100644
index 0000000000..831f54aefc
--- /dev/null
+++ b/share/security/patches/EN-19:10/scp.patch.asc
@@ -0,0 +1,18 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQKTBAABCgB9FiEE/A6HiuWv54gCjWNV05eS9J6n5cIFAlzbTvVfFIAAAAAALgAo
+aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldEZD
+MEU4NzhBRTVBRkU3ODgwMjhENjM1NUQzOTc5MkY0OUVBN0U1QzIACgkQ05eS9J6n
+5cKO+Q//TGSAM8N0dqIAi8AsD08fyJqsza70mF0PUq3w4Why0Se6Lm/XvBddR04N
+oCP0dDELlcklB3OAj/TFO0IqnozL5FHsRPuE376bjy8i3mK85LvHbC9vxHPGD69A
+OoKTgHAe62TqoSSkmJL66FIxbZlb9hh75k4KP2jyhhyD3o9YwuXPjF4vDbjD04s0
+JW2CjDhv+KnTfhjhL/iM/GTHDUl6upv7rWd/6gaH5hr6XDnfiXkcl0fLvOCndw9l
+asyXI/MjwkxxK25PKOX3/SixbiVR7oJTfafo6X/Jmw0ROACn3gxo3jaA9Rp/oGSJ
+v5BmH9iimAC3o5B9/r2/NIeY0qfZ2DGA8SxOabeVUSppcfc9IvYFyf0FQB6A5Kb3
+otWga6EGg9LutRGT3MX8DMjJ1CMIUIjWlC/szLMLHXGjw6XzL/VSA8W4A+X017eK
+6IKs/EVxK7NIrxl2HX3hRC1Slx0MSqWGB3a9eQ4NY3n3C0medadUolKG6whfx7ru
+qqBRavYT1C7JPKgNjWd+1x+fngslxlbBJCn8sSSk3pszvL7qcfJJAJbgwGYg+0t1
+c9VdsdsKZBU+Eqe7rVdDxtiI80AR0j7AN8Ph0j2Zm4Ecd5HIj5DsVAwOB6aXz7Kv
++3dbvvrpkFWIpXtOMQ+Qs6YsuneeMIwl2wZ1bXhLBmEtWk94rro=
+=rWoT
+-----END PGP SIGNATURE-----
diff --git a/share/security/patches/SA-19:03/wpa-11.patch b/share/security/patches/SA-19:03/wpa-11.patch
new file mode 100644
index 0000000000..e2ffe0f751
--- /dev/null
+++ b/share/security/patches/SA-19:03/wpa-11.patch
@@ -0,0 +1,192652 @@
+--- contrib/wpa/CONTRIBUTIONS.orig
++++ contrib/wpa/CONTRIBUTIONS
+@@ -29,6 +29,34 @@
+ unfortunately be accepted.
+
+
++The preferred method of submitting the contribution to the project is by
++email to the hostap mailing list:
++hostap@lists.infradead.org
++Note that the list may require subscription before accepting message
++without moderation. You can subscribe to the list at this address:
++http://lists.infradead.org/mailman/listinfo/hostap
++
++The message should contain an inlined patch against the current
++development branch (i.e., the master branch of
++git://w1.fi/hostap.git). Please make sure the software you use for
++sending the patch does not corrupt whitespace. If that cannot be fixed
++for some reason, it is better to include an attached version of the
++patch file than just send a whitespace damaged version in the message
++body.
++
++The patches should be separate logical changes rather than doing
++everything in a single patch. In other words, please keep cleanup, new
++features, and bug fixes all in their own patches. Each patch needs a
++commit log that describes the changes (what the changes fix, what
++functionality is added, why the changes are useful, etc.).
++
++Please try to follow the coding style used in the project.
++
++In general, the best way of generating a suitable formatted patch file
++is by committing the changes to a cloned git repository and using git
++format-patch. The patch can then be sent, e.g., with git send-email.
++
++
+ History of license and contributions terms
+ ------------------------------------------
+
+@@ -112,7 +140,7 @@
+
+ Modified BSD license (no advertisement clause):
+
+-Copyright (c) 2002-2015, Jouni Malinen and contributors
++Copyright (c) 2002-2019, Jouni Malinen and contributors
+ All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+--- contrib/wpa/COPYING.orig
++++ contrib/wpa/COPYING
+@@ -1,7 +1,7 @@
+ wpa_supplicant and hostapd
+ --------------------------
+
+-Copyright (c) 2002-2015, Jouni Malinen and contributors
++Copyright (c) 2002-2019, Jouni Malinen and contributors
+ All Rights Reserved.
+
+
+--- contrib/wpa/README.orig
++++ contrib/wpa/README
+@@ -1,7 +1,7 @@
+ wpa_supplicant and hostapd
+ --------------------------
+
+-Copyright (c) 2002-2015, Jouni Malinen and contributors
++Copyright (c) 2002-2019, Jouni Malinen and contributors
+ All Rights Reserved.
+
+ These programs are licensed under the BSD license (the one with
+--- contrib/wpa/hostapd/ChangeLog.orig
++++ contrib/wpa/hostapd/ChangeLog
+@@ -1,5 +1,188 @@
+ ChangeLog for hostapd
+
++2019-04-21 - v2.8
++ * SAE changes
++ - added support for SAE Password Identifier
++ - changed default configuration to enable only group 19
++ (i.e., disable groups 20, 21, 25, 26 from default configuration) and
++ disable all unsuitable groups completely based on REVmd changes
++ - improved anti-clogging token mechanism and SAE authentication
++ frame processing during heavy CPU load; this mitigates some issues
++ with potential DoS attacks trying to flood an AP with large number
++ of SAE messages
++ - added Finite Cyclic Group field in status code 77 responses
++ - reject use of unsuitable groups based on new implementation guidance
++ in REVmd (allow only FFC groups with prime >= 3072 bits and ECC
++ groups with prime >= 256)
++ - minimize timing and memory use differences in PWE derivation
++ [https://w1.fi/security/2019-1/] (CVE-2019-9494)
++ - fixed confirm message validation in error cases
++ [https://w1.fi/security/2019-3/] (CVE-2019-9496)
++ * EAP-pwd changes
++ - minimize timing and memory use differences in PWE derivation
++ [https://w1.fi/security/2019-2/] (CVE-2019-9495)
++ - verify peer scalar/element
++ [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498)
++ - fix message reassembly issue with unexpected fragment
++ [https://w1.fi/security/2019-5/]
++ - enforce rand,mask generation rules more strictly
++ - fix a memory leak in PWE derivation
++ - disallow ECC groups with a prime under 256 bits (groups 25, 26, and
++ 27)
++ * Hotspot 2.0 changes
++ - added support for release number 3
++ - reject release 2 or newer association without PMF
++ * added support for RSN operating channel validation
++ (CONFIG_OCV=y and configuration parameter ocv=1)
++ * added Multi-AP protocol support
++ * added FTM responder configuration
++ * fixed build with LibreSSL
++ * added FT/RRB workaround for short Ethernet frame padding
++ * fixed KEK2 derivation for FILS+FT
++ * added RSSI-based association rejection from OCE
++ * extended beacon reporting functionality
++ * VLAN changes
++ - allow local VLAN management with remote RADIUS authentication
++ - add WPA/WPA2 passphrase/PSK -based VLAN assignment
++ * OpenSSL: allow systemwide policies to be overridden
++ * extended PEAP to derive EMSK to enable use with ERP/FILS
++ * extended WPS to allow SAE configuration to be added automatically
++ for PSK (wps_cred_add_sae=1)
++ * fixed FT and SA Query Action frame with AP-MLME-in-driver cases
++ * OWE: allow Diffie-Hellman Parameter element to be included with DPP
++ in preparation for DPP protocol extension
++ * RADIUS server: started to accept ERP keyName-NAI as user identity
++ automatically without matching EAP database entry
++ * fixed PTK rekeying with FILS and FT
++
++2018-12-02 - v2.7
++ * fixed WPA packet number reuse with replayed messages and key
++ reinstallation
++ [http://w1.fi/security/2017-1/] (CVE-2017-13082)
++ * added support for FILS (IEEE 802.11ai) shared key authentication
++ * added support for OWE (Opportunistic Wireless Encryption, RFC 8110;
++ and transition mode defined by WFA)
++ * added support for DPP (Wi-Fi Device Provisioning Protocol)
++ * FT:
++ - added local generation of PMK-R0/PMK-R1 for FT-PSK
++ (ft_psk_generate_local=1)
++ - replaced inter-AP protocol with a cleaner design that is more
++ easily extensible; this breaks backward compatibility and requires
++ all APs in the ESS to be updated at the same time to maintain FT
++ functionality
++ - added support for wildcard R0KH/R1KH
++ - replaced r0_key_lifetime (minutes) parameter with
++ ft_r0_key_lifetime (seconds)
++ - fixed wpa_psk_file use for FT-PSK
++ - fixed FT-SAE PMKID matching
++ - added expiration to PMK-R0 and PMK-R1 cache
++ - added IEEE VLAN support (including tagged VLANs)
++ - added support for SHA384 based AKM
++ * SAE
++ - fixed some PMKSA caching cases with SAE
++ - added support for configuring SAE password separately of the
++ WPA2 PSK/passphrase
++ - added option to require MFP for SAE associations
++ (sae_require_pmf=1)
++ - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection
++ for SAE;
++ note: this is not backwards compatible, i.e., both the AP and
++ station side implementations will need to be update at the same
++ time to maintain interoperability
++ - added support for Password Identifier
++ * hostapd_cli: added support for command history and completion
++ * added support for requesting beacon report
++ * large number of other fixes, cleanup, and extensions
++ * added option to configure EAPOL-Key retry limits
++ (wpa_group_update_count and wpa_pairwise_update_count)
++ * removed all PeerKey functionality
++ * fixed nl80211 AP mode configuration regression with Linux 4.15 and
++ newer
++ * added support for using wolfSSL cryptographic library
++ * fixed some 20/40 MHz coexistence cases where the BSS could drop to
++ 20 MHz even when 40 MHz would be allowed
++ * Hotspot 2.0
++ - added support for setting Venue URL ANQP-element (venue_url)
++ - added support for advertising Hotspot 2.0 operator icons
++ - added support for Roaming Consortium Selection element
++ - added support for Terms and Conditions
++ - added support for OSEN connection in a shared RSN BSS
++ * added support for using OpenSSL 1.1.1
++ * added EAP-pwd server support for salted passwords
++
++2016-10-02 - v2.6
++ * fixed EAP-pwd last fragment validation
++ [http://w1.fi/security/2015-7/] (CVE-2015-5314)
++ * fixed WPS configuration update vulnerability with malformed passphrase
++ [http://w1.fi/security/2016-1/] (CVE-2016-4476)
++ * extended channel switch support for VHT bandwidth changes
++ * added support for configuring new ANQP-elements with
++ anqp_elem=:
++ * fixed Suite B 192-bit AKM to use proper PMK length
++ (note: this makes old releases incompatible with the fixed behavior)
++ * added no_probe_resp_if_max_sta=1 parameter to disable Probe Response
++ frame sending for not-associated STAs if max_num_sta limit has been
++ reached
++ * added option (-S as command line argument) to request all interfaces
++ to be started at the same time
++ * modified rts_threshold and fragm_threshold configuration parameters
++ to allow -1 to be used to disable RTS/fragmentation
++ * EAP-pwd: added support for Brainpool Elliptic Curves
++ (with OpenSSL 1.0.2 and newer)
++ * fixed EAPOL reauthentication after FT protocol run
++ * fixed FTIE generation for 4-way handshake after FT protocol run
++ * fixed and improved various FST operations
++ * TLS server
++ - support SHA384 and SHA512 hashes
++ - support TLS v1.2 signature algorithm with SHA384 and SHA512
++ - support PKCS #5 v2.0 PBES2
++ - support PKCS #5 with PKCS #12 style key decryption
++ - minimal support for PKCS #12
++ - support OCSP stapling (including ocsp_multi)
++ * added support for OpenSSL 1.1 API changes
++ - drop support for OpenSSL 0.9.8
++ - drop support for OpenSSL 1.0.0
++ * EAP-PEAP: support fast-connect crypto binding
++ * RADIUS
++ - fix Called-Station-Id to not escape SSID
++ - add Event-Timestamp to all Accounting-Request packets
++ - add Acct-Session-Id to Accounting-On/Off
++ - add Acct-Multi-Session-Id ton Access-Request packets
++ - add Service-Type (= Frames)
++ - allow server to provide PSK instead of passphrase for WPA-PSK
++ Tunnel_password case
++ - update full message for interim accounting updates
++ - add Acct-Delay-Time into Accounting messages
++ - add require_message_authenticator configuration option to require
++ CoA/Disconnect-Request packets to be authenticated
++ * started to postpone WNM-Notification frame sending by 100 ms so that
++ the STA has some more time to configure the key before this frame is
++ received after the 4-way handshake
++ * VHT: added interoperability workaround for 80+80 and 160 MHz channels
++ * extended VLAN support (per-STA vif, etc.)
++ * fixed PMKID derivation with SAE
++ * nl80211
++ - added support for full station state operations
++ - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use
++ unencrypted EAPOL frames
++ * added initial MBO support; number of extensions to WNM BSS Transition
++ Management
++ * added initial functionality for location related operations
++ * added assocresp_elements parameter to allow vendor specific elements
++ to be added into (Re)Association Response frames
++ * improved Public Action frame addressing
++ - use Address 3 = wildcard BSSID in GAS response if a query from an
++ unassociated STA used that address
++ - fix TX status processing for Address 3 = wildcard BSSID
++ - add gas_address3 configuration parameter to control Address 3
++ behavior
++ * added command line parameter -i to override interface parameter in
++ hostapd.conf
++ * added command completion support to hostapd_cli
++ * added passive client taxonomy determination (CONFIG_TAXONOMY=y
++ compile option and "SIGNATURE " control interface command)
++ * number of small fixes
++
+ 2015-09-27 - v2.5
+ * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+ [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+--- contrib/wpa/hostapd/README.orig
++++ contrib/wpa/hostapd/README
+@@ -2,7 +2,7 @@
+ Authenticator and RADIUS authentication server
+ ================================================================
+
+-Copyright (c) 2002-2015, Jouni Malinen and contributors
++Copyright (c) 2002-2019, Jouni Malinen and contributors
+ All Rights Reserved.
+
+ This program is licensed under the BSD license (the one with
+@@ -70,7 +70,7 @@
+ Current hardware/software requirements:
+ - drivers:
+ Host AP driver for Prism2/2.5/3.
+- (http://hostap.epitest.fi/)
++ (http://w1.fi/hostap-driver.html)
+ Please note that station firmware version needs to be 1.7.0 or newer
+ to work in WPA mode.
+
+@@ -81,8 +81,7 @@
+ Any wired Ethernet driver for wired IEEE 802.1X authentication
+ (experimental code)
+
+- FreeBSD -current (with some kernel mods that have not yet been
+- committed when hostapd v0.3.0 was released)
++ FreeBSD -current
+ BSD net80211 layer (e.g., Atheros driver)
+
+
+@@ -186,24 +185,14 @@
+ the Authentication Server. Other than this, the functionality is similar
+ to the case with the co-located Authentication Server.
+
+-Authentication Server and Supplicant
+-------------------------------------
++Authentication Server
++---------------------
+
+ Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
+ Authentication Server with hostapd Authenticator. FreeRADIUS
+ (http://www.freeradius.org/) has been successfully tested with hostapd
+-Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
+-XP Supplicants. EAP/TLS was used with Xsupplicant and
+-EAP/MD5-Challenge with Windows XP.
++Authenticator.
+
+-http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
+-about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
+-Cisco access point with Host AP driver, hostapd daemon, and a Prism2
+-card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
+-about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
+-configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
+-EAP/TLS use with WinXP Supplicant.
+-
+ Automatic WEP key configuration
+ -------------------------------
+
+@@ -243,8 +232,8 @@
+ of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+ to address the flaws of the base standard and has in practice
+ completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+-802.11 standard was approved in June 2004 and this amendment is likely
+-to be published in July 2004.
++802.11 standard was approved in June 2004 and this amendment was
++published in July 2004.
+
+ Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+ IEEE 802.11i work (draft 3.0) to define a subset of the security
+@@ -251,8 +240,7 @@
+ enhancements that can be implemented with existing wlan hardware. This
+ is called Wi-Fi Protected Access (WPA). This has now become a
+ mandatory component of interoperability testing and certification done
+-by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+-site (http://www.wi-fi.org/OpenSection/protected_access.asp).
++by Wi-Fi Alliance.
+
+ IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+ for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+--- contrib/wpa/hostapd/README-MULTI-AP.orig
++++ contrib/wpa/hostapd/README-MULTI-AP
+@@ -0,0 +1,160 @@
++hostapd, wpa_supplicant and the Multi-AP Specification
++======================================================
++
++This document describes how hostapd and wpa_supplicant can be configured to
++support the Multi-AP Specification.
++
++Introduction to Multi-AP
++------------------------
++
++The Wi-Fi Alliance Multi-AP Specification is the technical specification for
++Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for
++Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to
++join them into a network with centralized control and operation. It is targeted
++only at routers (repeaters, gateways, ...), not at clients. Clients are not
++involved at all in the protocols.
++
++Most of the Multi-AP specification falls outside of the scope of
++hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items
++summarized below. The rest of the protocol must be implemented by a separate
++daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd,
++e.g., to get a list of associated clients, but this can be done using the normal
++hostapd interfaces.
++
++hostapd/wpa_supplicant needs to be configured specifically to support:
++- the WPS onboarding process;
++- configuring backhaul links.
++
++The text below refers to "Multi-AP Specification v1.0" [3].
++
++
++Fronthaul and backhaul links
++----------------------------
++
++In a Multi-AP network, the central controller can configure the BSSs on the
++devices that are joined into the network. These are called fronthaul BSSs.
++From the point of view of hostapd, there is nothing special about these
++fronthaul BSSs.
++
++In addition to fronthaul BSSs, the controller can also configure backhaul
++links. A backhaul link is a link between two access point devices, giving
++internet access to access point devices that don't have a wired link. The
++Multi-AP specification doesn't dictate this, but typically the backhaul link
++will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the
++wired Ethernet ports.
++
++A backhaul link must be treated specially by hostapd and wpa_supplicant. One
++side of the backhaul link is configured through the Multi-AP protocol as the
++"backhaul STA", i.e., the client side of the link. A backhaul STA is like any
++station and is handled appropriately by wpa_supplicant, but two additional
++features are required. It must send an additional information element in each
++(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it
++must use 4-address mode for all frames sent over this link ([3], section 14).
++Therefore, wpa_supplicant must be configured explicitly as the backhaul STA
++role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block
++or when configuring the network profile through the control interface. When
++'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in
++(Re)Association Request frame and verifies that it is included in the
++(Re)Association Response frame. If it is not, association fails. If it is,
++wpa_supplicant sets 4-address mode for this interface through a driver
++callback.
++
++The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must
++be handled specially by hostapd, because it must add an additional information
++element in each (Re)Association Response frame, but only to stations that have
++identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6).
++This is important because it is possible to use the same BSS and SSID for
++fronthaul and backhaul at the same time. The additional information element must
++only be used for frames sent to a backhaul STA, not to a normal STA. Also,
++frames sent to a backhaul STA must use 4-address mode, while frames sent to a
++normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use
++3-address mode.
++
++A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap'
++configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3
++(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd
++parses the Multi-AP information element in the Association Request frame. If the
++station is a backhaul STA and the BSS is configured as a backhaul BSS,
++hostapd sets up 4-address mode. Since there may be multiple stations connected
++simultaneously, and each of them has a different RA (receiver address), a VLAN
++is created for each backhaul STA and it is automatically added to a bridge.
++This is the same behavior as for WDS, and the relevant option ('bridge' or
++'wds_bridge') applies here as well.
++
++If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate
++without the Multi-AP information element will be denied.
++
++If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate
++with the Multi-AP information element will be denied. That is also the only
++difference with 'multi_ap' set to 0: in the latter case, the Multi-AP
++information element is simply ignored.
++
++In summary, this is the end-to-end behavior for a backhaul BSS (i.e.,
++multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in
++hostapd on AP). Note that point 1 means that hostapd must not be configured
++with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for
++that.
++
++1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing
++ Multi-AP specific).
++2. STA sends Authentication frame (nothing Multi-AP specific).
++3. AP sends Authentication frame (nothing Multi-AP specific).
++4. STA sends Association Request frame with Multi-AP IE.
++5. AP sends Association Response frame with Multi-AP IE.
++6. STA and AP both use 4-address mode for Data frames.
++
++
++WPS support
++-----------
++
++WPS requires more special handling. WPS must only be advertised on fronthaul
++BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only
++BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS.
++When a WPS M1 message has an additional subelement that indicates a request for
++a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul
++BSS credentials; instead, it should respond with the (potentially different)
++backhaul BSS credentials.
++
++To support this, hostapd has the 'multi_ap_backhaul_ssid',
++'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options.
++When these are set on an BSS with WPS, they are used instead of the normal
++credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only
++WPA2-Personal is supported in the Multi-AP specification, so there is no need
++to specify authentication or encryption options. For the backhaul credentials,
++per-device PSK is not supported.
++
++If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to
++specify the backhaul credentials, since the backhaul and fronthaul credentials
++are identical.
++
++To enable the Multi-AP backhaul STA feature when it performs WPS, a new
++parameter has been introduced to the WPS_PBC control interface call. When this
++"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the
++Association Request frame and the M1 message. It then configures the new network
++profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does
++not follow the Multi-AP specification, wpa_supplicant will fail to associate.
++
++In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e.,
++multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2
++and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or
++multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS
++in hostapd on Registrar AP).
++
++1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP
++ specific).
++2. Enrollee sends Authentication frame (nothing Multi-AP specific).
++3. AP sends Authentication frame (nothing Multi-AP specific).
++4. Enrollee sends Association Request frame with Multi-AP IE.
++5. AP sends Association Response frame with Multi-AP IE.
++6. Enrollee sends M1 with additional Multi-AP subelement.
++7. AP sends M8 with backhaul instead of fronthaul credentials.
++8. Enrollee sends Deauthentication frame.
++
++
++References
++----------
++
++[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
++[2] https://github.com/prplfoundation/prplMesh
++[3] https://www.wi-fi.org/file/multi-ap-specification-v10
++ (requires registration)
+--- contrib/wpa/hostapd/config_file.c.orig
++++ contrib/wpa/hostapd/config_file.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / Configuration file parser
+- * Copyright (c) 2003-2015, Jouni Malinen
++ * Copyright (c) 2003-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -14,6 +14,8 @@
+ #include "utils/common.h"
+ #include "utils/uuid.h"
+ #include "common/ieee802_11_defs.h"
++#include "crypto/sha256.h"
++#include "crypto/tls.h"
+ #include "drivers/driver.h"
+ #include "eap_server/eap.h"
+ #include "radius/radius_client.h"
+@@ -35,7 +37,7 @@
+ const char *fname)
+ {
+ FILE *f;
+- char buf[128], *pos, *pos2;
++ char buf[128], *pos, *pos2, *pos3;
+ int line = 0, vlan_id;
+ struct hostapd_vlan *vlan;
+
+@@ -80,7 +82,10 @@
+ pos2 = pos;
+ while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
+ pos2++;
+- *pos2 = '\0';
++
++ if (*pos2 != '\0')
++ *(pos2++) = '\0';
++
+ if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d "
+ "in '%s'", line, fname);
+@@ -88,6 +93,13 @@
+ return -1;
+ }
+
++ while (*pos2 == ' ' || *pos2 == '\t')
++ pos2++;
++ pos3 = pos2;
++ while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0')
++ pos3++;
++ *pos3 = '\0';
++
+ vlan = os_zalloc(sizeof(*vlan));
+ if (vlan == NULL) {
+ wpa_printf(MSG_ERROR, "Out of memory while reading "
+@@ -97,7 +109,10 @@
+ }
+
+ vlan->vlan_id = vlan_id;
++ vlan->vlan_desc.untagged = vlan_id;
++ vlan->vlan_desc.notempty = !!vlan_id;
+ os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
++ os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge));
+ vlan->next = bss->vlan;
+ bss->vlan = vlan;
+ }
+@@ -109,7 +124,7 @@
+ #endif /* CONFIG_NO_VLAN */
+
+
+-static int hostapd_acl_comp(const void *a, const void *b)
++int hostapd_acl_comp(const void *a, const void *b)
+ {
+ const struct mac_acl_entry *aa = a;
+ const struct mac_acl_entry *bb = b;
+@@ -117,6 +132,44 @@
+ }
+
+
++int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
++ int vlan_id, const u8 *addr)
++{
++ struct mac_acl_entry *newacl;
++
++ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
++ if (!newacl) {
++ wpa_printf(MSG_ERROR, "MAC list reallocation failed");
++ return -1;
++ }
++
++ *acl = newacl;
++ os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
++ os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
++ (*acl)[*num].vlan_id.untagged = vlan_id;
++ (*acl)[*num].vlan_id.notempty = !!vlan_id;
++ (*num)++;
++
++ return 0;
++}
++
++
++void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
++ const u8 *addr)
++{
++ int i = 0;
++
++ while (i < *num) {
++ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
++ os_remove_in_array(*acl, *num, sizeof(**acl), i);
++ (*num)--;
++ } else {
++ i++;
++ }
++ }
++}
++
++
+ static int hostapd_config_read_maclist(const char *fname,
+ struct mac_acl_entry **acl, int *num)
+ {
+@@ -124,12 +177,8 @@
+ char buf[128], *pos;
+ int line = 0;
+ u8 addr[ETH_ALEN];
+- struct mac_acl_entry *newacl;
+ int vlan_id;
+
+- if (!fname)
+- return 0;
+-
+ f = fopen(fname, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
+@@ -137,7 +186,7 @@
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+- int i, rem = 0;
++ int rem = 0;
+
+ line++;
+
+@@ -167,16 +216,7 @@
+ }
+
+ if (rem) {
+- i = 0;
+- while (i < *num) {
+- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
+- 0) {
+- os_remove_in_array(*acl, *num,
+- sizeof(**acl), i);
+- (*num)--;
+- } else
+- i++;
+- }
++ hostapd_remove_acl_mac(acl, num, addr);
+ continue;
+ }
+ vlan_id = 0;
+@@ -188,22 +228,16 @@
+ if (*pos != '\0')
+ vlan_id = atoi(pos);
+
+- newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+- if (newacl == NULL) {
+- wpa_printf(MSG_ERROR, "MAC list reallocation failed");
++ if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) {
+ fclose(f);
+ return -1;
+ }
+-
+- *acl = newacl;
+- os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+- (*acl)[*num].vlan_id = vlan_id;
+- (*num)++;
+ }
+
+ fclose(f);
+
+- qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
++ if (*acl)
++ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+
+ return 0;
+ }
+@@ -210,6 +244,62 @@
+
+
+ #ifdef EAP_SERVER
++
++static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user,
++ const char *hash, size_t len,
++ char **pos, int line,
++ const char *fname)
++{
++ char *pos2 = *pos;
++
++ while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#')
++ pos2++;
++
++ if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */
++ wpa_printf(MSG_ERROR,
++ "Invalid salted %s hash on line %d in '%s'",
++ hash, line, fname);
++ return -1;
++ }
++
++ user->password = os_malloc(len);
++ if (!user->password) {
++ wpa_printf(MSG_ERROR,
++ "Failed to allocate memory for salted %s hash",
++ hash);
++ return -1;
++ }
++
++ if (hexstr2bin(*pos, user->password, len) < 0) {
++ wpa_printf(MSG_ERROR,
++ "Invalid salted password on line %d in '%s'",
++ line, fname);
++ return -1;
++ }
++ user->password_len = len;
++ *pos += 2 * len;
++
++ user->salt_len = (pos2 - *pos) / 2;
++ user->salt = os_malloc(user->salt_len);
++ if (!user->salt) {
++ wpa_printf(MSG_ERROR,
++ "Failed to allocate memory for salted %s hash",
++ hash);
++ return -1;
++ }
++
++ if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) {
++ wpa_printf(MSG_ERROR,
++ "Invalid salt for password on line %d in '%s'",
++ line, fname);
++ return -1;
++ }
++
++ *pos = pos2;
++ return 0;
++}
++
++
+ static int hostapd_config_read_eap_user(const char *fname,
+ struct hostapd_bss_config *conf)
+ {
+@@ -218,9 +308,6 @@
+ int line = 0, ret = 0, num_methods;
+ struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
+
+- if (!fname)
+- return 0;
+-
+ if (os_strncmp(fname, "sqlite:", 7) == 0) {
+ #ifdef CONFIG_SQLITE
+ os_free(conf->eap_user_sqlite);
+@@ -307,13 +394,12 @@
+ goto failed;
+ }
+
+- user->identity = os_malloc(pos - start);
++ user->identity = os_memdup(start, pos - start);
+ if (user->identity == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate "
+ "memory for EAP identity");
+ goto failed;
+ }
+- os_memcpy(user->identity, start, pos - start);
+ user->identity_len = pos - start;
+
+ if (pos[0] == '"' && pos[1] == '*') {
+@@ -431,13 +517,12 @@
+ goto failed;
+ }
+
+- user->password = os_malloc(pos - start);
++ user->password = os_memdup(start, pos - start);
+ if (user->password == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate "
+ "memory for EAP password");
+ goto failed;
+ }
+- os_memcpy(user->password, start, pos - start);
+ user->password_len = pos - start;
+
+ pos++;
+@@ -466,6 +551,24 @@
+ user->password_len = 16;
+ user->password_hash = 1;
+ pos = pos2;
++ } else if (os_strncmp(pos, "ssha1:", 6) == 0) {
++ pos += 6;
++ if (hostapd_config_eap_user_salted(user, "sha1", 20,
++ &pos,
++ line, fname) < 0)
++ goto failed;
++ } else if (os_strncmp(pos, "ssha256:", 8) == 0) {
++ pos += 8;
++ if (hostapd_config_eap_user_salted(user, "sha256", 32,
++ &pos,
++ line, fname) < 0)
++ goto failed;
++ } else if (os_strncmp(pos, "ssha512:", 8) == 0) {
++ pos += 8;
++ if (hostapd_config_eap_user_salted(user, "sha512", 64,
++ &pos,
++ line, fname) < 0)
++ goto failed;
+ } else {
+ pos2 = pos;
+ while (*pos2 != '\0' && *pos2 != ' ' &&
+@@ -517,19 +620,15 @@
+ fclose(f);
+
+ if (ret == 0) {
+- user = conf->eap_user;
+- while (user) {
+- struct hostapd_eap_user *prev;
+-
+- prev = user;
+- user = user->next;
+- hostapd_config_free_eap_user(prev);
+- }
++ hostapd_config_free_eap_users(conf->eap_user);
+ conf->eap_user = new_user;
++ } else {
++ hostapd_config_free_eap_users(new_user);
+ }
+
+ return ret;
+ }
++
+ #endif /* EAP_SERVER */
+
+
+@@ -631,8 +730,7 @@
+ }
+
+
+-static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
+- const char *val)
++static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val)
+ {
+ char *secret;
+
+@@ -640,7 +738,7 @@
+ if (secret == NULL)
+ return -1;
+
+- secret++;
++ *secret++ = '\0';
+
+ if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
+ return -1;
+@@ -680,12 +778,16 @@
+ val |= WPA_KEY_MGMT_PSK;
+ else if (os_strcmp(start, "WPA-EAP") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ else if (os_strcmp(start, "FT-PSK") == 0)
+ val |= WPA_KEY_MGMT_FT_PSK;
+ else if (os_strcmp(start, "FT-EAP") == 0)
+ val |= WPA_KEY_MGMT_FT_IEEE8021X;
+-#endif /* CONFIG_IEEE80211R */
++#ifdef CONFIG_SHA384
++ else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
++ val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
++#endif /* CONFIG_SHA384 */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+ else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
+ val |= WPA_KEY_MGMT_PSK_SHA256;
+@@ -706,6 +808,30 @@
+ else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ #endif /* CONFIG_SUITEB192 */
++#ifdef CONFIG_FILS
++ else if (os_strcmp(start, "FILS-SHA256") == 0)
++ val |= WPA_KEY_MGMT_FILS_SHA256;
++ else if (os_strcmp(start, "FILS-SHA384") == 0)
++ val |= WPA_KEY_MGMT_FILS_SHA384;
++#ifdef CONFIG_IEEE80211R_AP
++ else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
++ val |= WPA_KEY_MGMT_FT_FILS_SHA256;
++ else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
++ val |= WPA_KEY_MGMT_FT_FILS_SHA384;
++#endif /* CONFIG_IEEE80211R_AP */
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_OWE
++ else if (os_strcmp(start, "OWE") == 0)
++ val |= WPA_KEY_MGMT_OWE;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ else if (os_strcmp(start, "DPP") == 0)
++ val |= WPA_KEY_MGMT_DPP;
++#endif /* CONFIG_DPP */
++#ifdef CONFIG_HS20
++ else if (os_strcmp(start, "OSEN") == 0)
++ val |= WPA_KEY_MGMT_OSEN;
++#endif /* CONFIG_HS20 */
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+ line, start);
+@@ -751,17 +877,34 @@
+ {
+ size_t len = os_strlen(val);
+
+- if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL)
++ if (keyidx < 0 || keyidx > 3)
+ return -1;
+
++ if (len == 0) {
++ int i, set = 0;
++
++ bin_clear_free(wep->key[keyidx], wep->len[keyidx]);
++ wep->key[keyidx] = NULL;
++ wep->len[keyidx] = 0;
++ for (i = 0; i < NUM_WEP_KEYS; i++) {
++ if (wep->key[i])
++ set++;
++ }
++ if (!set)
++ wep->keys_set = 0;
++ return 0;
++ }
++
++ if (wep->key[keyidx] != NULL)
++ return -1;
++
+ if (val[0] == '"') {
+ if (len < 2 || val[len - 1] != '"')
+ return -1;
+ len -= 2;
+- wep->key[keyidx] = os_malloc(len);
++ wep->key[keyidx] = os_memdup(val + 1, len);
+ if (wep->key[keyidx] == NULL)
+ return -1;
+- os_memcpy(wep->key[keyidx], val + 1, len);
+ wep->len[keyidx] = len;
+ } else {
+ if (len & 1)
+@@ -974,7 +1117,27 @@
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
++
++static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
++{
++ u8 oldkey[16];
++ int ret;
++
++ if (!hexstr2bin(pos, key, key_len))
++ return 0;
++
++ /* Try to use old short key for backwards compatibility */
++ if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
++ return -1;
++
++ ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
++ key, key_len);
++ os_memset(oldkey, 0, sizeof(oldkey));
++ return ret;
++}
++
++
+ static int add_r0kh(struct hostapd_bss_config *bss, char *value)
+ {
+ struct ft_remote_r0kh *r0kh;
+@@ -1008,7 +1171,7 @@
+ os_memcpy(r0kh->id, pos, r0kh->id_len);
+
+ pos = next;
+- if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
++ if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
+ wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
+ os_free(r0kh);
+ return -1;
+@@ -1053,7 +1216,7 @@
+ }
+
+ pos = next;
+- if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
++ if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
+ wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
+ os_free(r1kh);
+ return -1;
+@@ -1064,7 +1227,7 @@
+
+ return 0;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+
+ #ifdef CONFIG_IEEE80211N
+@@ -1081,6 +1244,12 @@
+ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ conf->secondary_channel = 1;
+ }
++ if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) {
++ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++ conf->ht40_plus_minus_allowed = 1;
++ }
++ if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]"))
++ conf->secondary_channel = 0;
+ if (os_strstr(capab, "[SMPS-STATIC]")) {
+ conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
+ conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
+@@ -1210,6 +1379,30 @@
+ #endif /* CONFIG_IEEE80211AC */
+
+
++#ifdef CONFIG_IEEE80211AX
++
++static u8 find_bit_offset(u8 val)
++{
++ u8 res = 0;
++
++ for (; val; val >>= 1) {
++ if (val & 1)
++ break;
++ res++;
++ }
++
++ return res;
++}
++
++
++static u8 set_he_cap(int val, u8 mask)
++{
++ return (u8) (mask & (val << find_bit_offset(mask)));
++}
++
++#endif /* CONFIG_IEEE80211AX */
++
++
+ #ifdef CONFIG_INTERWORKING
+ static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
+ int line)
+@@ -1303,6 +1496,44 @@
+ }
+
+
++static int parse_venue_url(struct hostapd_bss_config *bss, char *pos,
++ int line)
++{
++ char *sep;
++ size_t nlen;
++ struct hostapd_venue_url *url;
++ int ret = -1;
++
++ sep = os_strchr(pos, ':');
++ if (!sep)
++ goto fail;
++ *sep++ = '\0';
++
++ nlen = os_strlen(sep);
++ if (nlen > 254)
++ goto fail;
++
++ url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1,
++ sizeof(struct hostapd_venue_url));
++ if (!url)
++ goto fail;
++
++ bss->venue_url = url;
++ url = &bss->venue_url[bss->venue_url_count++];
++
++ url->venue_number = atoi(pos);
++ url->url_len = nlen;
++ os_memcpy(url->url, sep, nlen);
++
++ ret = 0;
++fail:
++ if (ret)
++ wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'",
++ line, pos);
++ return ret;
++}
++
++
+ static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
+ int line)
+ {
+@@ -1519,6 +1750,54 @@
+ }
+
+
++static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
++{
++ char *delim;
++ u16 infoid;
++ size_t len;
++ struct wpabuf *payload;
++ struct anqp_element *elem;
++
++ delim = os_strchr(buf, ':');
++ if (!delim)
++ return -1;
++ delim++;
++ infoid = atoi(buf);
++ len = os_strlen(delim);
++ if (len & 1)
++ return -1;
++ len /= 2;
++ payload = wpabuf_alloc(len);
++ if (!payload)
++ return -1;
++ if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) {
++ wpabuf_free(payload);
++ return -1;
++ }
++
++ dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) {
++ if (elem->infoid == infoid) {
++ /* Update existing entry */
++ wpabuf_free(elem->payload);
++ elem->payload = payload;
++ return 0;
++ }
++ }
++
++ /* Add a new entry */
++ elem = os_zalloc(sizeof(*elem));
++ if (!elem) {
++ wpabuf_free(payload);
++ return -1;
++ }
++ elem->infoid = infoid;
++ elem->payload = payload;
++ dl_list_add(&bss->anqp_elem, &elem->list);
++
++ return 0;
++}
++
++
+ static int parse_qos_map_set(struct hostapd_bss_config *bss,
+ char *buf, int line)
+ {
+@@ -1805,6 +2084,24 @@
+ }
+
+
++static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
++ char *pos, int line)
++{
++ if (bss->last_osu == NULL) {
++ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
++ return -1;
++ }
++
++ os_free(bss->last_osu->osu_nai2);
++ bss->last_osu->osu_nai2 = os_strdup(pos);
++ if (bss->last_osu->osu_nai2 == NULL)
++ return -1;
++ bss->hs20_osu_providers_nai_count++;
++
++ return 0;
++}
++
++
+ static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
+ int line)
+ {
+@@ -1864,34 +2161,28 @@
+ return 0;
+ }
+
+-#endif /* CONFIG_HS20 */
+
+-
+-#ifdef CONFIG_WPS_NFC
+-static struct wpabuf * hostapd_parse_bin(const char *buf)
++static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
++ int line)
+ {
+- size_t len;
+- struct wpabuf *ret;
++ char **n;
+
+- len = os_strlen(buf);
+- if (len & 0x01)
+- return NULL;
+- len /= 2;
++ n = os_realloc_array(bss->hs20_operator_icon,
++ bss->hs20_operator_icon_count + 1, sizeof(char *));
++ if (!n)
++ return -1;
++ bss->hs20_operator_icon = n;
++ bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
++ if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
++ return -1;
++ bss->hs20_operator_icon_count++;
+
+- ret = wpabuf_alloc(len);
+- if (ret == NULL)
+- return NULL;
++ return 0;
++}
+
+- if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+- wpabuf_free(ret);
+- return NULL;
+- }
++#endif /* CONFIG_HS20 */
+
+- return ret;
+-}
+-#endif /* CONFIG_WPS_NFC */
+
+-
+ #ifdef CONFIG_ACS
+ static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
+ char *pos)
+@@ -1934,6 +2225,157 @@
+ #endif /* CONFIG_ACS */
+
+
++static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf,
++ const char *val)
++{
++ struct wpabuf *elems;
++
++ if (val[0] == '\0') {
++ wpabuf_free(*buf);
++ *buf = NULL;
++ return 0;
++ }
++
++ elems = wpabuf_parse_bin(val);
++ if (!elems) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'",
++ line, name, val);
++ return -1;
++ }
++
++ wpabuf_free(*buf);
++ *buf = elems;
++
++ return 0;
++}
++
++
++#ifdef CONFIG_FILS
++static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val)
++{
++ struct fils_realm *realm;
++ size_t len;
++
++ len = os_strlen(val);
++ realm = os_zalloc(sizeof(*realm) + len + 1);
++ if (!realm)
++ return -1;
++
++ os_memcpy(realm->realm, val, len);
++ if (fils_domain_name_hash(val, realm->hash) < 0) {
++ os_free(realm);
++ return -1;
++ }
++ dl_list_add_tail(&bss->fils_realms, &realm->list);
++
++ return 0;
++}
++#endif /* CONFIG_FILS */
++
++
++#ifdef EAP_SERVER
++static unsigned int parse_tls_flags(const char *val)
++{
++ unsigned int flags = 0;
++
++ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
++ * This can be enabled by default once the implementation has been fully
++ * completed and tested with other implementations. */
++ flags |= TLS_CONN_DISABLE_TLSv1_3;
++
++ if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
++ flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
++ if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
++ flags |= TLS_CONN_DISABLE_TIME_CHECKS;
++ if (os_strstr(val, "[DISABLE-TLSv1.0]"))
++ flags |= TLS_CONN_DISABLE_TLSv1_0;
++ if (os_strstr(val, "[ENABLE-TLSv1.0]"))
++ flags |= TLS_CONN_ENABLE_TLSv1_0;
++ if (os_strstr(val, "[DISABLE-TLSv1.1]"))
++ flags |= TLS_CONN_DISABLE_TLSv1_1;
++ if (os_strstr(val, "[ENABLE-TLSv1.1]"))
++ flags |= TLS_CONN_ENABLE_TLSv1_1;
++ if (os_strstr(val, "[DISABLE-TLSv1.2]"))
++ flags |= TLS_CONN_DISABLE_TLSv1_2;
++ if (os_strstr(val, "[ENABLE-TLSv1.2]"))
++ flags |= TLS_CONN_ENABLE_TLSv1_2;
++ if (os_strstr(val, "[DISABLE-TLSv1.3]"))
++ flags |= TLS_CONN_DISABLE_TLSv1_3;
++ if (os_strstr(val, "[ENABLE-TLSv1.3]"))
++ flags &= ~TLS_CONN_DISABLE_TLSv1_3;
++ if (os_strstr(val, "[SUITEB]"))
++ flags |= TLS_CONN_SUITEB;
++ if (os_strstr(val, "[SUITEB-NO-ECDH]"))
++ flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
++
++ return flags;
++}
++#endif /* EAP_SERVER */
++
++
++#ifdef CONFIG_SAE
++static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
++{
++ struct sae_password_entry *pw;
++ const char *pos = val, *pos2, *end = NULL;
++
++ pw = os_zalloc(sizeof(*pw));
++ if (!pw)
++ return -1;
++ os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */
++
++ pos2 = os_strstr(pos, "|mac=");
++ if (pos2) {
++ end = pos2;
++ pos2 += 5;
++ if (hwaddr_aton(pos2, pw->peer_addr) < 0)
++ goto fail;
++ pos = pos2 + ETH_ALEN * 3 - 1;
++ }
++
++ pos2 = os_strstr(pos, "|vlanid=");
++ if (pos2) {
++ if (!end)
++ end = pos2;
++ pos2 += 8;
++ pw->vlan_id = atoi(pos2);
++ }
++
++ pos2 = os_strstr(pos, "|id=");
++ if (pos2) {
++ if (!end)
++ end = pos2;
++ pos2 += 4;
++ pw->identifier = os_strdup(pos2);
++ if (!pw->identifier)
++ goto fail;
++ }
++
++ if (!end) {
++ pw->password = os_strdup(val);
++ if (!pw->password)
++ goto fail;
++ } else {
++ pw->password = os_malloc(end - val + 1);
++ if (!pw->password)
++ goto fail;
++ os_memcpy(pw->password, val, end - val);
++ pw->password[end - val] = '\0';
++ }
++
++ pw->next = bss->sae_passwords;
++ bss->sae_passwords = pw;
++
++ return 0;
++fail:
++ str_clear_free(pw->password);
++ os_free(pw->identifier);
++ os_free(pw);
++ return -1;
++}
++#endif /* CONFIG_SAE */
++
++
+ static int hostapd_config_fill(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss,
+ const char *buf, char *pos, int line)
+@@ -1949,20 +2391,21 @@
+ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+ } else if (os_strcmp(buf, "driver") == 0) {
+ int j;
+- /* clear to get error below if setting is invalid */
+- conf->driver = NULL;
++ const struct wpa_driver_ops *driver = NULL;
++
+ for (j = 0; wpa_drivers[j]; j++) {
+ if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
+- conf->driver = wpa_drivers[j];
++ driver = wpa_drivers[j];
+ break;
+ }
+ }
+- if (conf->driver == NULL) {
++ if (!driver) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid/unknown driver '%s'",
+ line, pos);
+ return 1;
+ }
++ conf->driver = driver;
+ } else if (os_strcmp(buf, "driver_params") == 0) {
+ os_free(conf->driver_params);
+ conf->driver_params = os_strdup(pos);
+@@ -2006,13 +2449,16 @@
+ } else if (os_strcmp(buf, "utf8_ssid") == 0) {
+ bss->ssid.utf8_ssid = atoi(pos) > 0;
+ } else if (os_strcmp(buf, "macaddr_acl") == 0) {
+- bss->macaddr_acl = atoi(pos);
+- if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
+- bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
+- bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
++ enum macaddr_acl acl = atoi(pos);
++
++ if (acl != ACCEPT_UNLESS_DENIED &&
++ acl != DENY_UNLESS_ACCEPTED &&
++ acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
+- line, bss->macaddr_acl);
++ line, acl);
++ return 1;
+ }
++ bss->macaddr_acl = acl;
+ } else if (os_strcmp(buf, "accept_mac_file") == 0) {
+ if (hostapd_config_read_maclist(pos, &bss->accept_mac,
+ &bss->num_accept_mac)) {
+@@ -2039,8 +2485,8 @@
+ bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "country_code") == 0) {
+ os_memcpy(conf->country, pos, 2);
+- /* FIX: make this configurable */
+- conf->country[2] = ' ';
++ } else if (os_strcmp(buf, "country3") == 0) {
++ conf->country[2] = strtol(pos, NULL, 16);
+ } else if (os_strcmp(buf, "ieee80211d") == 0) {
+ conf->ieee80211d = atoi(pos);
+ } else if (os_strcmp(buf, "ieee80211h") == 0) {
+@@ -2048,13 +2494,15 @@
+ } else if (os_strcmp(buf, "ieee8021x") == 0) {
+ bss->ieee802_1x = atoi(pos);
+ } else if (os_strcmp(buf, "eapol_version") == 0) {
+- bss->eapol_version = atoi(pos);
+- if (bss->eapol_version < 1 || bss->eapol_version > 2) {
++ int eapol_version = atoi(pos);
++
++ if (eapol_version < 1 || eapol_version > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid EAPOL version (%d): '%s'.",
+- line, bss->eapol_version, pos);
++ line, eapol_version, pos);
+ return 1;
+ }
++ bss->eapol_version = eapol_version;
+ wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
+ #ifdef EAP_SERVER
+ } else if (os_strcmp(buf, "eap_authenticator") == 0) {
+@@ -2077,13 +2525,32 @@
+ } else if (os_strcmp(buf, "private_key_passwd") == 0) {
+ os_free(bss->private_key_passwd);
+ bss->private_key_passwd = os_strdup(pos);
++ } else if (os_strcmp(buf, "check_cert_subject") == 0) {
++ if (!pos[0]) {
++ wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'",
++ line, pos);
++ return 1;
++ }
++ os_free(bss->check_cert_subject);
++ bss->check_cert_subject = os_strdup(pos);
++ if (!bss->check_cert_subject)
++ return 1;
+ } else if (os_strcmp(buf, "check_crl") == 0) {
+ bss->check_crl = atoi(pos);
++ } else if (os_strcmp(buf, "check_crl_strict") == 0) {
++ bss->check_crl_strict = atoi(pos);
++ } else if (os_strcmp(buf, "crl_reload_interval") == 0) {
++ bss->crl_reload_interval = atoi(pos);
+ } else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
+ bss->tls_session_lifetime = atoi(pos);
++ } else if (os_strcmp(buf, "tls_flags") == 0) {
++ bss->tls_flags = parse_tls_flags(pos);
+ } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
+ os_free(bss->ocsp_stapling_response);
+ bss->ocsp_stapling_response = os_strdup(pos);
++ } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) {
++ os_free(bss->ocsp_stapling_response_multi);
++ bss->ocsp_stapling_response_multi = os_strdup(pos);
+ } else if (os_strcmp(buf, "dh_file") == 0) {
+ os_free(bss->dh_file);
+ bss->dh_file = os_strdup(pos);
+@@ -2090,6 +2557,9 @@
+ } else if (os_strcmp(buf, "openssl_ciphers") == 0) {
+ os_free(bss->openssl_ciphers);
+ bss->openssl_ciphers = os_strdup(pos);
++ } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) {
++ os_free(bss->openssl_ecdh_curves);
++ bss->openssl_ecdh_curves = os_strdup(pos);
+ } else if (os_strcmp(buf, "fragment_size") == 0) {
+ bss->fragment_size = atoi(pos);
+ #ifdef EAP_SERVER_FAST
+@@ -2139,6 +2609,8 @@
+ } else if (os_strcmp(buf, "eap_sim_db") == 0) {
+ os_free(bss->eap_sim_db);
+ bss->eap_sim_db = os_strdup(pos);
++ } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) {
++ bss->eap_sim_db_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
+ bss->eap_sim_aka_result_ind = atoi(pos);
+ #endif /* EAP_SERVER_SIM */
+@@ -2150,8 +2622,10 @@
+ } else if (os_strcmp(buf, "pwd_group") == 0) {
+ bss->pwd_group = atoi(pos);
+ #endif /* EAP_SERVER_PWD */
++#ifdef CONFIG_ERP
+ } else if (os_strcmp(buf, "eap_server_erp") == 0) {
+ bss->eap_server_erp = atoi(pos);
++#endif /* CONFIG_ERP */
+ #endif /* EAP_SERVER */
+ } else if (os_strcmp(buf, "eap_message") == 0) {
+ char *term;
+@@ -2177,24 +2651,25 @@
+ os_free(bss->erp_domain);
+ bss->erp_domain = os_strdup(pos);
+ } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
+- bss->default_wep_key_len = atoi(pos);
+- if (bss->default_wep_key_len > 13) {
+- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
+- line,
+- (unsigned long) bss->default_wep_key_len,
+- (unsigned long)
+- bss->default_wep_key_len * 8);
++ int val = atoi(pos);
++
++ if (val < 0 || val > 13) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid WEP key len %d (= %d bits)",
++ line, val, val * 8);
+ return 1;
+ }
++ bss->default_wep_key_len = val;
+ } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
+- bss->individual_wep_key_len = atoi(pos);
+- if (bss->individual_wep_key_len < 0 ||
+- bss->individual_wep_key_len > 13) {
+- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
+- line, bss->individual_wep_key_len,
+- bss->individual_wep_key_len * 8);
++ int val = atoi(pos);
++
++ if (val < 0 || val > 13) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid WEP key len %d (= %d bits)",
++ line, val, val * 8);
+ return 1;
+ }
++ bss->individual_wep_key_len = val;
+ } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
+ bss->wep_rekeying_period = atoi(pos);
+ if (bss->wep_rekeying_period < 0) {
+@@ -2353,6 +2828,9 @@
+ bss->radius_das_time_window = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
+ bss->radius_das_require_event_timestamp = atoi(pos);
++ } else if (os_strcmp(buf, "radius_das_require_message_authenticator") ==
++ 0) {
++ bss->radius_das_require_message_authenticator = atoi(pos);
+ #endif /* CONFIG_NO_RADIUS */
+ } else if (os_strcmp(buf, "auth_algs") == 0) {
+ bss->auth_algs = atoi(pos);
+@@ -2373,6 +2851,7 @@
+ bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
+ bss->wpa_group_rekey = atoi(pos);
++ bss->wpa_group_rekey_set = 1;
+ } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
+ bss->wpa_strict_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
+@@ -2379,6 +2858,30 @@
+ bss->wpa_gmk_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
+ bss->wpa_ptk_rekey = atoi(pos);
++ } else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
++ char *endp;
++ unsigned long val = strtoul(pos, &endp, 0);
++
++ if (*endp || val < 1 || val > (u32) -1) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295",
++ line, val);
++ return 1;
++ }
++ bss->wpa_group_update_count = (u32) val;
++ } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) {
++ char *endp;
++ unsigned long val = strtoul(pos, &endp, 0);
++
++ if (*endp || val < 1 || val > (u32) -1) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295",
++ line, val);
++ return 1;
++ }
++ bss->wpa_pairwise_update_count = (u32) val;
++ } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
++ bss->wpa_disable_eapol_key_retries = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
+ int len = os_strlen(pos);
+ if (len < 8 || len > 63) {
+@@ -2437,7 +2940,7 @@
+ if (bss->wpa_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+- bss->wpa_pairwise, pos);
++ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
+@@ -2447,9 +2950,23 @@
+ if (bss->rsn_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+- bss->rsn_pairwise, pos);
++ line, pos);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "group_cipher") == 0) {
++ bss->group_cipher = hostapd_config_parse_cipher(line, pos);
++ if (bss->group_cipher == -1 || bss->group_cipher == 0)
++ return 1;
++ if (bss->group_cipher != WPA_CIPHER_TKIP &&
++ bss->group_cipher != WPA_CIPHER_CCMP &&
++ bss->group_cipher != WPA_CIPHER_GCMP &&
++ bss->group_cipher != WPA_CIPHER_GCMP_256 &&
++ bss->group_cipher != WPA_CIPHER_CCMP_256) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: unsupported group cipher suite '%s'",
++ line, pos);
++ return 1;
++ }
+ #ifdef CONFIG_RSN_PREAUTH
+ } else if (os_strcmp(buf, "rsn_preauth") == 0) {
+ bss->rsn_preauth = atoi(pos);
+@@ -2457,11 +2974,10 @@
+ os_free(bss->rsn_preauth_interfaces);
+ bss->rsn_preauth_interfaces = os_strdup(pos);
+ #endif /* CONFIG_RSN_PREAUTH */
+-#ifdef CONFIG_PEERKEY
+ } else if (os_strcmp(buf, "peerkey") == 0) {
+- bss->peerkey = atoi(pos);
+-#endif /* CONFIG_PEERKEY */
+-#ifdef CONFIG_IEEE80211R
++ wpa_printf(MSG_INFO,
++ "Line %d: Obsolete peerkey parameter ignored", line);
++#ifdef CONFIG_IEEE80211R_AP
+ } else if (os_strcmp(buf, "mobility_domain") == 0) {
+ if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+ hexstr2bin(pos, bss->mobility_domain,
+@@ -2480,9 +2996,22 @@
+ return 1;
+ }
+ } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
++ /* DEPRECATED: Use ft_r0_key_lifetime instead. */
++ bss->r0_key_lifetime = atoi(pos) * 60;
++ } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) {
+ bss->r0_key_lifetime = atoi(pos);
++ } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) {
++ bss->r1_max_key_lifetime = atoi(pos);
+ } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
+ bss->reassociation_deadline = atoi(pos);
++ } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
++ bss->rkh_pos_timeout = atoi(pos);
++ } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) {
++ bss->rkh_neg_timeout = atoi(pos);
++ } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) {
++ bss->rkh_pull_timeout = atoi(pos);
++ } else if (os_strcmp(buf, "rkh_pull_retries") == 0) {
++ bss->rkh_pull_retries = atoi(pos);
+ } else if (os_strcmp(buf, "r0kh") == 0) {
+ if (add_r0kh(bss, pos) < 0) {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
+@@ -2499,7 +3028,9 @@
+ bss->pmk_r1_push = atoi(pos);
+ } else if (os_strcmp(buf, "ft_over_ds") == 0) {
+ bss->ft_over_ds = atoi(pos);
+-#endif /* CONFIG_IEEE80211R */
++ } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
++ bss->ft_psk_generate_local = atoi(pos);
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifndef CONFIG_NO_CTRL_IFACE
+ } else if (os_strcmp(buf, "ctrl_interface") == 0) {
+ os_free(bss->ctrl_interface);
+@@ -2577,6 +3108,8 @@
+ line, pos);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
++ conf->acs_exclude_dfs = atoi(pos);
+ } else if (os_strcmp(buf, "channel") == 0) {
+ if (os_strcmp(pos, "acs_survey") == 0) {
+ #ifndef CONFIG_ACS
+@@ -2603,9 +3136,10 @@
+ * cause problems with the current implementation.
+ * Since it is unlikely that this small numbers are
+ * useful in real life scenarios, do not allow beacon
+- * period to be set below 15 TU. */
+- if (val < 15 || val > 65535) {
+- wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)",
++ * period to be set below 10 TU. */
++ if (val < 10 || val > 65535) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid beacon_int %d (expected 10..65535)",
+ line, val);
+ return 1;
+ }
+@@ -2627,24 +3161,37 @@
+ }
+ #endif /* CONFIG_ACS */
+ } else if (os_strcmp(buf, "dtim_period") == 0) {
+- bss->dtim_period = atoi(pos);
+- if (bss->dtim_period < 1 || bss->dtim_period > 255) {
++ int val = atoi(pos);
++
++ if (val < 1 || val > 255) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
+- line, bss->dtim_period);
++ line, val);
+ return 1;
+ }
++ bss->dtim_period = val;
+ } else if (os_strcmp(buf, "bss_load_update_period") == 0) {
+- bss->bss_load_update_period = atoi(pos);
+- if (bss->bss_load_update_period < 0 ||
+- bss->bss_load_update_period > 100) {
++ int val = atoi(pos);
++
++ if (val < 0 || val > 100) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid bss_load_update_period %d",
+- line, bss->bss_load_update_period);
++ line, val);
+ return 1;
+ }
++ bss->bss_load_update_period = val;
++ } else if (os_strcmp(buf, "chan_util_avg_period") == 0) {
++ int val = atoi(pos);
++
++ if (val < 0) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid chan_util_avg_period",
++ line);
++ return 1;
++ }
++ bss->chan_util_avg_period = val;
+ } else if (os_strcmp(buf, "rts_threshold") == 0) {
+ conf->rts_threshold = atoi(pos);
+- if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
++ if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid rts_threshold %d",
+ line, conf->rts_threshold);
+@@ -2652,8 +3199,10 @@
+ }
+ } else if (os_strcmp(buf, "fragm_threshold") == 0) {
+ conf->fragm_threshold = atoi(pos);
+- if (conf->fragm_threshold < 256 ||
+- conf->fragm_threshold > 2346) {
++ if (conf->fragm_threshold == -1) {
++ /* allow a value of -1 */
++ } else if (conf->fragm_threshold < 256 ||
++ conf->fragm_threshold > 2346) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid fragm_threshold %d",
+ line, conf->fragm_threshold);
+@@ -2666,7 +3215,7 @@
+ line, val);
+ return 1;
+ }
+- conf->send_probe_response = val;
++ bss->send_probe_response = val;
+ } else if (os_strcmp(buf, "supported_rates") == 0) {
+ if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+@@ -2679,6 +3228,40 @@
+ line);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "beacon_rate") == 0) {
++ int val;
++
++ if (os_strncmp(pos, "ht:", 3) == 0) {
++ val = atoi(pos + 3);
++ if (val < 0 || val > 31) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid beacon_rate HT-MCS %d",
++ line, val);
++ return 1;
++ }
++ conf->rate_type = BEACON_RATE_HT;
++ conf->beacon_rate = val;
++ } else if (os_strncmp(pos, "vht:", 4) == 0) {
++ val = atoi(pos + 4);
++ if (val < 0 || val > 9) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid beacon_rate VHT-MCS %d",
++ line, val);
++ return 1;
++ }
++ conf->rate_type = BEACON_RATE_VHT;
++ conf->beacon_rate = val;
++ } else {
++ val = atoi(pos);
++ if (val < 10 || val > 10000) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid legacy beacon_rate %d",
++ line, val);
++ return 1;
++ }
++ conf->rate_type = BEACON_RATE_LEGACY;
++ conf->beacon_rate = val;
++ }
+ } else if (os_strcmp(buf, "preamble") == 0) {
+ if (atoi(pos))
+ conf->preamble = SHORT_PREAMBLE;
+@@ -2686,6 +3269,8 @@
+ conf->preamble = LONG_PREAMBLE;
+ } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
+ bss->ignore_broadcast_ssid = atoi(pos);
++ } else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
++ bss->no_probe_resp_if_max_sta = atoi(pos);
+ } else if (os_strcmp(buf, "wep_default_key") == 0) {
+ bss->ssid.wep.idx = atoi(pos);
+ if (bss->ssid.wep.idx > 3) {
+@@ -2707,6 +3292,8 @@
+ #ifndef CONFIG_NO_VLAN
+ } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+ bss->ssid.dynamic_vlan = atoi(pos);
++ } else if (os_strcmp(buf, "per_sta_vif") == 0) {
++ bss->ssid.per_sta_vif = atoi(pos);
+ } else if (os_strcmp(buf, "vlan_file") == 0) {
+ if (hostapd_config_read_vlan_file(bss, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
+@@ -2762,6 +3349,8 @@
+ line);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
++ conf->use_driver_iface_addr = atoi(pos);
+ #ifdef CONFIG_IEEE80211W
+ } else if (os_strcmp(buf, "ieee80211w") == 0) {
+ bss->ieee80211w = atoi(pos);
+@@ -2794,6 +3383,12 @@
+ return 1;
+ }
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ } else if (os_strcmp(buf, "ocv") == 0) {
++ bss->ocv = atoi(pos);
++ if (bss->ocv && !bss->ieee80211w)
++ bss->ieee80211w = 1;
++#endif /* CONFIG_OCV */
+ #ifdef CONFIG_IEEE80211N
+ } else if (os_strcmp(buf, "ieee80211n") == 0) {
+ conf->ieee80211n = atoi(pos);
+@@ -2827,7 +3422,111 @@
+ conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+ } else if (os_strcmp(buf, "vendor_vht") == 0) {
+ bss->vendor_vht = atoi(pos);
++ } else if (os_strcmp(buf, "use_sta_nsts") == 0) {
++ bss->use_sta_nsts = atoi(pos);
+ #endif /* CONFIG_IEEE80211AC */
++#ifdef CONFIG_IEEE80211AX
++ } else if (os_strcmp(buf, "ieee80211ax") == 0) {
++ conf->ieee80211ax = atoi(pos);
++ } else if (os_strcmp(buf, "he_su_beamformer") == 0) {
++ conf->he_phy_capab.he_su_beamformer = atoi(pos);
++ } else if (os_strcmp(buf, "he_su_beamformee") == 0) {
++ conf->he_phy_capab.he_su_beamformee = atoi(pos);
++ } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
++ conf->he_phy_capab.he_mu_beamformer = atoi(pos);
++ } else if (os_strcmp(buf, "he_bss_color") == 0) {
++ conf->he_op.he_bss_color = atoi(pos);
++ } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
++ conf->he_op.he_default_pe_duration = atoi(pos);
++ } else if (os_strcmp(buf, "he_twt_required") == 0) {
++ conf->he_op.he_twt_required = atoi(pos);
++ } else if (os_strcmp(buf, "he_rts_threshold") == 0) {
++ conf->he_op.he_rts_threshold = atoi(pos);
++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) {
++ conf->he_mu_edca.he_qos_info |=
++ set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT);
++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) {
++ conf->he_mu_edca.he_qos_info |=
++ set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK);
++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) {
++ conf->he_mu_edca.he_qos_info |=
++ set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST);
++ } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) {
++ conf->he_mu_edca.he_qos_info |=
++ set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) {
++ conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] =
++ atoi(pos) & 0xff;
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) {
++ conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] =
++ atoi(pos) & 0xff;
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) {
++ conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] =
++ atoi(pos) & 0xff;
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
++ set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
++ } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) {
++ conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] =
++ atoi(pos) & 0xff;
++#endif /* CONFIG_IEEE80211AX */
+ } else if (os_strcmp(buf, "max_listen_interval") == 0) {
+ bss->max_listen_interval = atoi(pos);
+ } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
+@@ -2908,7 +3607,10 @@
+ }
+ } else if (os_strcmp(buf, "ap_pin") == 0) {
+ os_free(bss->ap_pin);
+- bss->ap_pin = os_strdup(pos);
++ if (*pos == '\0')
++ bss->ap_pin = NULL;
++ else
++ bss->ap_pin = os_strdup(pos);
+ } else if (os_strcmp(buf, "skip_cred_build") == 0) {
+ bss->skip_cred_build = atoi(pos);
+ } else if (os_strcmp(buf, "extra_cred") == 0) {
+@@ -2921,6 +3623,8 @@
+ }
+ } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
+ bss->wps_cred_processing = atoi(pos);
++ } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) {
++ bss->wps_cred_add_sae = atoi(pos);
+ } else if (os_strcmp(buf, "ap_settings") == 0) {
+ os_free(bss->ap_settings);
+ bss->ap_settings =
+@@ -2930,6 +3634,56 @@
+ line, pos);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) {
++ size_t slen;
++ char *str = wpa_config_parse_string(pos, &slen);
++
++ if (!str || slen < 1 || slen > SSID_MAX_LEN) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
++ line, pos);
++ os_free(str);
++ return 1;
++ }
++ os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen);
++ bss->multi_ap_backhaul_ssid.ssid_len = slen;
++ bss->multi_ap_backhaul_ssid.ssid_set = 1;
++ os_free(str);
++ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) {
++ int len = os_strlen(pos);
++
++ if (len < 8 || len > 63) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid WPA passphrase length %d (expected 8..63)",
++ line, len);
++ return 1;
++ }
++ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
++ bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos);
++ if (bss->multi_ap_backhaul_ssid.wpa_passphrase) {
++ hostapd_config_clear_wpa_psk(
++ &bss->multi_ap_backhaul_ssid.wpa_psk);
++ bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1;
++ }
++ } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) {
++ hostapd_config_clear_wpa_psk(
++ &bss->multi_ap_backhaul_ssid.wpa_psk);
++ bss->multi_ap_backhaul_ssid.wpa_psk =
++ os_zalloc(sizeof(struct hostapd_wpa_psk));
++ if (!bss->multi_ap_backhaul_ssid.wpa_psk)
++ return 1;
++ if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk,
++ PMK_LEN) ||
++ pos[PMK_LEN * 2] != '\0') {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
++ line, pos);
++ hostapd_config_clear_wpa_psk(
++ &bss->multi_ap_backhaul_ssid.wpa_psk);
++ return 1;
++ }
++ bss->multi_ap_backhaul_ssid.wpa_psk->group = 1;
++ os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
++ bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL;
++ bss->multi_ap_backhaul_ssid.wpa_psk_set = 1;
+ } else if (os_strcmp(buf, "upnp_iface") == 0) {
+ os_free(bss->upnp_iface);
+ bss->upnp_iface = os_strdup(pos);
+@@ -2965,15 +3719,15 @@
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_pubkey);
+- bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
++ bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_privkey);
+- bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
++ bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+ wpabuf_free(bss->wps_nfc_dev_pw);
+- bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
++ bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
+ #endif /* CONFIG_WPS_NFC */
+ #endif /* CONFIG_WPS */
+@@ -3019,12 +3773,14 @@
+ bss->time_zone = os_strdup(pos);
+ if (bss->time_zone == NULL)
+ return 1;
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+ bss->wnm_sleep_mode = atoi(pos);
++ } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) {
++ bss->wnm_sleep_mode_no_keys = atoi(pos);
+ } else if (os_strcmp(buf, "bss_transition") == 0) {
+ bss->bss_transition = atoi(pos);
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ #ifdef CONFIG_INTERWORKING
+ } else if (os_strcmp(buf, "interworking") == 0) {
+ bss->interworking = atoi(pos);
+@@ -3062,6 +3818,9 @@
+ } else if (os_strcmp(buf, "venue_name") == 0) {
+ if (parse_venue_name(bss, pos, line) < 0)
+ return 1;
++ } else if (os_strcmp(buf, "venue_url") == 0) {
++ if (parse_venue_url(bss, pos, line) < 0)
++ return 1;
+ } else if (os_strcmp(buf, "network_auth_type") == 0) {
+ u8 auth_type;
+ u16 redirect_url_len;
+@@ -3136,8 +3895,19 @@
+ } else if (os_strcmp(buf, "nai_realm") == 0) {
+ if (parse_nai_realm(bss, pos, line) < 0)
+ return 1;
++ } else if (os_strcmp(buf, "anqp_elem") == 0) {
++ if (parse_anqp_elem(bss, pos, line) < 0)
++ return 1;
+ } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+- bss->gas_frag_limit = atoi(pos);
++ int val = atoi(pos);
++
++ if (val <= 0) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Invalid gas_frag_limit '%s'",
++ line, pos);
++ return 1;
++ }
++ bss->gas_frag_limit = val;
+ } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+ bss->gas_comeback_delay = atoi(pos);
+ } else if (os_strcmp(buf, "qos_map_set") == 0) {
+@@ -3149,13 +3919,25 @@
+ os_free(bss->dump_msk_file);
+ bss->dump_msk_file = os_strdup(pos);
+ #endif /* CONFIG_RADIUS_TEST */
++#ifdef CONFIG_PROXYARP
++ } else if (os_strcmp(buf, "proxy_arp") == 0) {
++ bss->proxy_arp = atoi(pos);
++#endif /* CONFIG_PROXYARP */
+ #ifdef CONFIG_HS20
+ } else if (os_strcmp(buf, "hs20") == 0) {
+ bss->hs20 = atoi(pos);
++ } else if (os_strcmp(buf, "hs20_release") == 0) {
++ int val = atoi(pos);
++
++ if (val < 1 || val > (HS20_VERSION >> 4) + 1) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Unsupported hs20_release: %s",
++ line, pos);
++ return 1;
++ }
++ bss->hs20_release = val;
+ } else if (os_strcmp(buf, "disable_dgaf") == 0) {
+ bss->disable_dgaf = atoi(pos);
+- } else if (os_strcmp(buf, "proxy_arp") == 0) {
+- bss->proxy_arp = atoi(pos);
+ } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
+ bss->na_mcast_to_ucast = atoi(pos);
+ } else if (os_strcmp(buf, "osen") == 0) {
+@@ -3216,6 +3998,9 @@
+ } else if (os_strcmp(buf, "osu_nai") == 0) {
+ if (hs20_parse_osu_nai(bss, pos, line) < 0)
+ return 1;
++ } else if (os_strcmp(buf, "osu_nai2") == 0) {
++ if (hs20_parse_osu_nai2(bss, pos, line) < 0)
++ return 1;
+ } else if (os_strcmp(buf, "osu_method_list") == 0) {
+ if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+ return 1;
+@@ -3225,12 +4010,34 @@
+ } else if (os_strcmp(buf, "osu_service_desc") == 0) {
+ if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+ return 1;
++ } else if (os_strcmp(buf, "operator_icon") == 0) {
++ if (hs20_parse_operator_icon(bss, pos, line) < 0)
++ return 1;
+ } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
+ os_free(bss->subscr_remediation_url);
+ bss->subscr_remediation_url = os_strdup(pos);
+ } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
+ bss->subscr_remediation_method = atoi(pos);
++ } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
++ os_free(bss->t_c_filename);
++ bss->t_c_filename = os_strdup(pos);
++ } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) {
++ bss->t_c_timestamp = strtol(pos, NULL, 0);
++ } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
++ os_free(bss->t_c_server_url);
++ bss->t_c_server_url = os_strdup(pos);
++ } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
++ os_free(bss->hs20_sim_provisioning_url);
++ bss->hs20_sim_provisioning_url = os_strdup(pos);
+ #endif /* CONFIG_HS20 */
++#ifdef CONFIG_MBO
++ } else if (os_strcmp(buf, "mbo") == 0) {
++ bss->mbo_enabled = atoi(pos);
++ } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) {
++ bss->mbo_cell_data_conn_pref = atoi(pos);
++ } else if (os_strcmp(buf, "oce") == 0) {
++ bss->oce = atoi(pos);
++#endif /* CONFIG_MBO */
+ #ifdef CONFIG_TESTING_OPTIONS
+ #define PARSE_TEST_PROBABILITY(_val) \
+ } else if (os_strcmp(buf, #_val) == 0) { \
+@@ -3249,6 +4056,8 @@
+ PARSE_TEST_PROBABILITY(ignore_assoc_probability)
+ PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
+ PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
++ } else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
++ conf->ecsa_ie_only = atoi(pos);
+ } else if (os_strcmp(buf, "bss_load_test") == 0) {
+ WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
+ pos = os_strchr(pos, ':');
+@@ -3269,7 +4078,15 @@
+ WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
+ bss->bss_load_test_set = 1;
+ } else if (os_strcmp(buf, "radio_measurements") == 0) {
+- bss->radio_measurements = atoi(pos);
++ /*
++ * DEPRECATED: This parameter will be removed in the future.
++ * Use rrm_neighbor_report instead.
++ */
++ int val = atoi(pos);
++
++ if (val & BIT(0))
++ bss->radio_measurements[0] |=
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+ } else if (os_strcmp(buf, "own_ie_override") == 0) {
+ struct wpabuf *tmp;
+ size_t len = os_strlen(pos) / 2;
+@@ -3288,39 +4105,30 @@
+
+ wpabuf_free(bss->own_ie_override);
+ bss->own_ie_override = tmp;
++ } else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
++ bss->sae_reflection_attack = atoi(pos);
++ } else if (os_strcmp(buf, "sae_commit_override") == 0) {
++ wpabuf_free(bss->sae_commit_override);
++ bss->sae_commit_override = wpabuf_parse_bin(pos);
+ #endif /* CONFIG_TESTING_OPTIONS */
+- } else if (os_strcmp(buf, "vendor_elements") == 0) {
+- struct wpabuf *elems;
+- size_t len = os_strlen(pos);
+- if (len & 0x01) {
+- wpa_printf(MSG_ERROR,
+- "Line %d: Invalid vendor_elements '%s'",
+- line, pos);
++#ifdef CONFIG_SAE
++ } else if (os_strcmp(buf, "sae_password") == 0) {
++ if (parse_sae_password(bss, pos) < 0) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password",
++ line);
+ return 1;
+ }
+- len /= 2;
+- if (len == 0) {
+- wpabuf_free(bss->vendor_elements);
+- bss->vendor_elements = NULL;
+- return 0;
+- }
+-
+- elems = wpabuf_alloc(len);
+- if (elems == NULL)
++#endif /* CONFIG_SAE */
++ } else if (os_strcmp(buf, "vendor_elements") == 0) {
++ if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
+ return 1;
+-
+- if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+- wpabuf_free(elems);
+- wpa_printf(MSG_ERROR,
+- "Line %d: Invalid vendor_elements '%s'",
+- line, pos);
++ } else if (os_strcmp(buf, "assocresp_elements") == 0) {
++ if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos))
+ return 1;
+- }
+-
+- wpabuf_free(bss->vendor_elements);
+- bss->vendor_elements = elems;
+ } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+ bss->sae_anti_clogging_threshold = atoi(pos);
++ } else if (os_strcmp(buf, "sae_sync") == 0) {
++ bss->sae_sync = atoi(pos);
+ } else if (os_strcmp(buf, "sae_groups") == 0) {
+ if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
+ wpa_printf(MSG_ERROR,
+@@ -3328,6 +4136,8 @@
+ line, pos);
+ return 1;
+ }
++ } else if (os_strcmp(buf, "sae_require_mfp") == 0) {
++ bss->sae_require_mfp = atoi(pos);
+ } else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
+ int val = atoi(pos);
+ if (val < 0 || val > 255) {
+@@ -3391,7 +4201,8 @@
+ return -1;
+ }
+ val = strtol(pos, &endp, 0);
+- if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
++ if (*endp || val < 1 ||
++ (unsigned long int) val > FST_MAX_LLT_MS) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
+ line, val, pos, FST_MAX_LLT_MS);
+@@ -3409,6 +4220,135 @@
+ } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
+ os_free(bss->no_auth_if_seen_on);
+ bss->no_auth_if_seen_on = os_strdup(pos);
++ } else if (os_strcmp(buf, "lci") == 0) {
++ wpabuf_free(conf->lci);
++ conf->lci = wpabuf_parse_bin(pos);
++ if (conf->lci && wpabuf_len(conf->lci) == 0) {
++ wpabuf_free(conf->lci);
++ conf->lci = NULL;
++ }
++ } else if (os_strcmp(buf, "civic") == 0) {
++ wpabuf_free(conf->civic);
++ conf->civic = wpabuf_parse_bin(pos);
++ if (conf->civic && wpabuf_len(conf->civic) == 0) {
++ wpabuf_free(conf->civic);
++ conf->civic = NULL;
++ }
++ } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
++ if (atoi(pos))
++ bss->radio_measurements[0] |=
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
++ } else if (os_strcmp(buf, "rrm_beacon_report") == 0) {
++ if (atoi(pos))
++ bss->radio_measurements[0] |=
++ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
++ } else if (os_strcmp(buf, "gas_address3") == 0) {
++ bss->gas_address3 = atoi(pos);
++ } else if (os_strcmp(buf, "stationary_ap") == 0) {
++ conf->stationary_ap = atoi(pos);
++ } else if (os_strcmp(buf, "ftm_responder") == 0) {
++ bss->ftm_responder = atoi(pos);
++ } else if (os_strcmp(buf, "ftm_initiator") == 0) {
++ bss->ftm_initiator = atoi(pos);
++#ifdef CONFIG_FILS
++ } else if (os_strcmp(buf, "fils_cache_id") == 0) {
++ if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Invalid fils_cache_id '%s'",
++ line, pos);
++ return 1;
++ }
++ bss->fils_cache_id_set = 1;
++ } else if (os_strcmp(buf, "fils_realm") == 0) {
++ if (parse_fils_realm(bss, pos) < 0)
++ return 1;
++ } else if (os_strcmp(buf, "fils_dh_group") == 0) {
++ bss->fils_dh_group = atoi(pos);
++ } else if (os_strcmp(buf, "dhcp_server") == 0) {
++ if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid IP address '%s'",
++ line, pos);
++ return 1;
++ }
++ } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) {
++ bss->dhcp_rapid_commit_proxy = atoi(pos);
++ } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) {
++ bss->fils_hlp_wait_time = atoi(pos);
++ } else if (os_strcmp(buf, "dhcp_server_port") == 0) {
++ bss->dhcp_server_port = atoi(pos);
++ } else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
++ bss->dhcp_relay_port = atoi(pos);
++#endif /* CONFIG_FILS */
++ } else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
++ bss->multicast_to_unicast = atoi(pos);
++ } else if (os_strcmp(buf, "broadcast_deauth") == 0) {
++ bss->broadcast_deauth = atoi(pos);
++#ifdef CONFIG_DPP
++ } else if (os_strcmp(buf, "dpp_connector") == 0) {
++ os_free(bss->dpp_connector);
++ bss->dpp_connector = os_strdup(pos);
++ } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) {
++ if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos))
++ return 1;
++ } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) {
++ bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0);
++ } else if (os_strcmp(buf, "dpp_csign") == 0) {
++ if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
++ return 1;
++#endif /* CONFIG_DPP */
++#ifdef CONFIG_OWE
++ } else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
++ if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: invalid owe_transition_bssid",
++ line);
++ return 1;
++ }
++ } else if (os_strcmp(buf, "owe_transition_ssid") == 0) {
++ size_t slen;
++ char *str = wpa_config_parse_string(pos, &slen);
++
++ if (!str || slen < 1 || slen > SSID_MAX_LEN) {
++ wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
++ line, pos);
++ os_free(str);
++ return 1;
++ }
++ os_memcpy(bss->owe_transition_ssid, str, slen);
++ bss->owe_transition_ssid_len = slen;
++ os_free(str);
++ } else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
++ os_strlcpy(bss->owe_transition_ifname, pos,
++ sizeof(bss->owe_transition_ifname));
++ } else if (os_strcmp(buf, "owe_groups") == 0) {
++ if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
++ wpa_printf(MSG_ERROR,
++ "Line %d: Invalid owe_groups value '%s'",
++ line, pos);
++ return 1;
++ }
++ } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
++ bss->coloc_intf_reporting = atoi(pos);
++#endif /* CONFIG_OWE */
++ } else if (os_strcmp(buf, "multi_ap") == 0) {
++ int val = atoi(pos);
++
++ if (val < 0 || val > 3) {
++ wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'",
++ line, buf);
++ return -1;
++ }
++
++ bss->multi_ap = val;
++ } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) {
++ conf->rssi_reject_assoc_rssi = atoi(pos);
++ } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
++ conf->rssi_reject_assoc_timeout = atoi(pos);
++ } else if (os_strcmp(buf, "pbss") == 0) {
++ bss->pbss = atoi(pos);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unknown configuration item '%s'",
+@@ -3429,7 +4369,7 @@
+ {
+ struct hostapd_config *conf;
+ FILE *f;
+- char buf[512], *pos;
++ char buf[4096], *pos;
+ int line = 0;
+ int errors = 0;
+ size_t i;
+--- contrib/wpa/hostapd/config_file.h.orig
++++ contrib/wpa/hostapd/config_file.h
+@@ -13,5 +13,10 @@
+ int hostapd_set_iface(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss, const char *field,
+ char *value);
++int hostapd_acl_comp(const void *a, const void *b);
++int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
++ int vlan_id, const u8 *addr);
++void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
++ const u8 *addr);
+
+ #endif /* CONFIG_FILE_H */
+--- contrib/wpa/hostapd/ctrl_iface.c.orig
++++ contrib/wpa/hostapd/ctrl_iface.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / UNIX domain socket -based control interface
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -19,10 +19,20 @@
+ #include
+ #include
+
++#ifdef CONFIG_CTRL_IFACE_UDP
++#include
++#endif /* CONFIG_CTRL_IFACE_UDP */
++
+ #include "utils/common.h"
+ #include "utils/eloop.h"
++#include "utils/module_tests.h"
+ #include "common/version.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/ctrl_iface_common.h"
++#ifdef CONFIG_DPP
++#include "common/dpp.h"
++#endif /* CONFIG_DPP */
++#include "common/wpa_ctrl.h"
+ #include "crypto/tls.h"
+ #include "drivers/driver.h"
+ #include "eapol_auth/eapol_auth_sm.h"
+@@ -42,6 +52,9 @@
+ #include "ap/wnm_ap.h"
+ #include "ap/wpa_auth.h"
+ #include "ap/beacon.h"
++#include "ap/neighbor_db.h"
++#include "ap/rrm.h"
++#include "ap/dpp_hostapd.h"
+ #include "wps/wps_defs.h"
+ #include "wps/wps.h"
+ #include "fst/fst_ctrl_iface.h"
+@@ -51,15 +64,16 @@
+
+ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
+-struct wpa_ctrl_dst {
+- struct wpa_ctrl_dst *next;
+- struct sockaddr_un addr;
+- socklen_t addrlen;
+- int debug_level;
+- int errors;
+-};
++#ifdef CONFIG_CTRL_IFACE_UDP
++#define COOKIE_LEN 8
++static unsigned char cookie[COOKIE_LEN];
++static unsigned char gcookie[COOKIE_LEN];
++#define HOSTAPD_CTRL_IFACE_PORT 8877
++#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50
++#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878
++#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50
++#endif /* CONFIG_CTRL_IFACE_UDP */
+
+-
+ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+ enum wpa_msg_type type,
+ const char *buf, size_t len);
+@@ -66,81 +80,27 @@
+
+
+ static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
+- struct sockaddr_un *from,
+- socklen_t fromlen)
++ struct sockaddr_storage *from,
++ socklen_t fromlen, const char *input)
+ {
+- struct wpa_ctrl_dst *dst;
+-
+- dst = os_zalloc(sizeof(*dst));
+- if (dst == NULL)
+- return -1;
+- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+- dst->addrlen = fromlen;
+- dst->debug_level = MSG_INFO;
+- dst->next = hapd->ctrl_dst;
+- hapd->ctrl_dst = dst;
+- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
+- (u8 *) from->sun_path,
+- fromlen - offsetof(struct sockaddr_un, sun_path));
+- return 0;
++ return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input);
+ }
+
+
+ static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
+- struct sockaddr_un *from,
++ struct sockaddr_storage *from,
+ socklen_t fromlen)
+ {
+- struct wpa_ctrl_dst *dst, *prev = NULL;
+-
+- dst = hapd->ctrl_dst;
+- while (dst) {
+- if (fromlen == dst->addrlen &&
+- os_memcmp(from->sun_path, dst->addr.sun_path,
+- fromlen - offsetof(struct sockaddr_un, sun_path))
+- == 0) {
+- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
+- (u8 *) from->sun_path,
+- fromlen -
+- offsetof(struct sockaddr_un, sun_path));
+- if (prev == NULL)
+- hapd->ctrl_dst = dst->next;
+- else
+- prev->next = dst->next;
+- os_free(dst);
+- return 0;
+- }
+- prev = dst;
+- dst = dst->next;
+- }
+- return -1;
++ return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen);
+ }
+
+
+ static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
+- struct sockaddr_un *from,
++ struct sockaddr_storage *from,
+ socklen_t fromlen,
+ char *level)
+ {
+- struct wpa_ctrl_dst *dst;
+-
+- wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+-
+- dst = hapd->ctrl_dst;
+- while (dst) {
+- if (fromlen == dst->addrlen &&
+- os_memcmp(from->sun_path, dst->addr.sun_path,
+- fromlen - offsetof(struct sockaddr_un, sun_path))
+- == 0) {
+- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
+- "level", (u8 *) from->sun_path, fromlen -
+- offsetof(struct sockaddr_un, sun_path));
+- dst->debug_level = atoi(level);
+- return 0;
+- }
+- dst = dst->next;
+- }
+-
+- return -1;
++ return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level);
+ }
+
+
+@@ -808,7 +768,7 @@
+ #endif /* CONFIG_INTERWORKING */
+
+
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+
+ static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd)
+@@ -883,7 +843,9 @@
+ char *url = NULL;
+ int ret;
+ u8 nei_rep[1000];
+- u8 *nei_pos = nei_rep;
++ int nei_len;
++ u8 mbo[10];
++ size_t mbo_len = 0;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+@@ -921,7 +883,7 @@
+ /* TODO: TSF configurable/learnable */
+ bss_term_dur[0] = 4; /* Subelement ID */
+ bss_term_dur[1] = 10; /* Length */
+- os_memset(bss_term_dur, 2, 8);
++ os_memset(&bss_term_dur[2], 0, 8);
+ end = os_strchr(pos, ',');
+ if (end == NULL) {
+ wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+@@ -931,100 +893,11 @@
+ WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+ }
+
++ nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
++ sizeof(nei_rep));
++ if (nei_len < 0)
++ return -1;
+
+- /*
+- * BSS Transition Candidate List Entries - Neighbor Report elements
+- * neighbor=,,,
+- * ,[,]
+- */
+- pos = cmd;
+- while (pos) {
+- u8 *nei_start;
+- long int val;
+- char *endptr, *tmp;
+-
+- pos = os_strstr(pos, " neighbor=");
+- if (!pos)
+- break;
+- if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
+- wpa_printf(MSG_DEBUG,
+- "Not enough room for additional neighbor");
+- return -1;
+- }
+- pos += 10;
+-
+- nei_start = nei_pos;
+- *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+- nei_pos++; /* length to be filled in */
+-
+- if (hwaddr_aton(pos, nei_pos)) {
+- wpa_printf(MSG_DEBUG, "Invalid BSSID");
+- return -1;
+- }
+- nei_pos += ETH_ALEN;
+- pos += 17;
+- if (*pos != ',') {
+- wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+- return -1;
+- }
+- pos++;
+-
+- val = strtol(pos, &endptr, 0);
+- WPA_PUT_LE32(nei_pos, val);
+- nei_pos += 4;
+- if (*endptr != ',') {
+- wpa_printf(MSG_DEBUG, "Missing Operating Class");
+- return -1;
+- }
+- pos = endptr + 1;
+-
+- *nei_pos++ = atoi(pos); /* Operating Class */
+- pos = os_strchr(pos, ',');
+- if (pos == NULL) {
+- wpa_printf(MSG_DEBUG, "Missing Channel Number");
+- return -1;
+- }
+- pos++;
+-
+- *nei_pos++ = atoi(pos); /* Channel Number */
+- pos = os_strchr(pos, ',');
+- if (pos == NULL) {
+- wpa_printf(MSG_DEBUG, "Missing PHY Type");
+- return -1;
+- }
+- pos++;
+-
+- *nei_pos++ = atoi(pos); /* PHY Type */
+- end = os_strchr(pos, ' ');
+- tmp = os_strchr(pos, ',');
+- if (tmp && (!end || tmp < end)) {
+- /* Optional Subelements (hexdump) */
+- size_t len;
+-
+- pos = tmp + 1;
+- end = os_strchr(pos, ' ');
+- if (end)
+- len = end - pos;
+- else
+- len = os_strlen(pos);
+- if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
+- wpa_printf(MSG_DEBUG,
+- "Not enough room for neighbor subelements");
+- return -1;
+- }
+- if (len & 0x01 ||
+- hexstr2bin(pos, nei_pos, len / 2) < 0) {
+- wpa_printf(MSG_DEBUG,
+- "Invalid neighbor subelement info");
+- return -1;
+- }
+- nei_pos += len / 2;
+- pos = end;
+- }
+-
+- nei_start[1] = nei_pos - nei_start - 2;
+- }
+-
+ pos = os_strstr(cmd, " url=");
+ if (pos) {
+ size_t len;
+@@ -1049,17 +922,115 @@
+ if (os_strstr(cmd, " disassoc_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
++#ifdef CONFIG_MBO
++ pos = os_strstr(cmd, "mbo=");
++ if (pos) {
++ unsigned int mbo_reason, cell_pref, reassoc_delay;
++ u8 *mbo_pos = mbo;
++
++ ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
++ &reassoc_delay, &cell_pref);
++ if (ret != 3) {
++ wpa_printf(MSG_DEBUG,
++ "MBO requires three arguments: mbo=::");
++ ret = -1;
++ goto fail;
++ }
++
++ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
++ wpa_printf(MSG_DEBUG,
++ "Invalid MBO transition reason code %u",
++ mbo_reason);
++ ret = -1;
++ goto fail;
++ }
++
++ /* Valid values for Cellular preference are: 0, 1, 255 */
++ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
++ wpa_printf(MSG_DEBUG,
++ "Invalid MBO cellular capability %u",
++ cell_pref);
++ ret = -1;
++ goto fail;
++ }
++
++ if (reassoc_delay > 65535 ||
++ (reassoc_delay &&
++ !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
++ wpa_printf(MSG_DEBUG,
++ "MBO: Assoc retry delay is only valid in disassoc imminent mode");
++ ret = -1;
++ goto fail;
++ }
++
++ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = mbo_reason;
++ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = cell_pref;
++
++ if (reassoc_delay) {
++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
++ *mbo_pos++ = 2;
++ WPA_PUT_LE16(mbo_pos, reassoc_delay);
++ mbo_pos += 2;
++ }
++
++ mbo_len = mbo_pos - mbo;
++ }
++#endif /* CONFIG_MBO */
++
+ ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+ valid_int, bss_term_dur, url,
+- nei_pos > nei_rep ? nei_rep : NULL,
+- nei_pos - nei_rep);
++ nei_len ? nei_rep : NULL, nei_len,
++ mbo_len ? mbo : NULL, mbo_len);
++#ifdef CONFIG_MBO
++fail:
++#endif /* CONFIG_MBO */
+ os_free(url);
+ return ret;
+ }
+
+-#endif /* CONFIG_WNM */
+
++static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
++ const char *cmd)
++{
++ u8 addr[ETH_ALEN];
++ struct sta_info *sta;
++ const char *pos;
++ unsigned int auto_report, timeout;
+
++ if (hwaddr_aton(cmd, addr)) {
++ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
++ return -1;
++ }
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR
++ " not found for Collocated Interference Request",
++ MAC2STR(addr));
++ return -1;
++ }
++
++ pos = cmd + 17;
++ if (*pos != ' ')
++ return -1;
++ pos++;
++ auto_report = atoi(pos);
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ timeout = atoi(pos);
++
++ return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
++}
++
++#endif /* CONFIG_WNM_AP */
++
++
+ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+ {
+@@ -1083,7 +1054,7 @@
+ return pos - buf;
+ pos += ret;
+ }
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ ret = os_snprintf(pos, end - pos, "FT-PSK ");
+ if (os_snprintf_error(end - pos, ret))
+@@ -1096,6 +1067,14 @@
+ return pos - buf;
+ pos += ret;
+ }
++#ifdef CONFIG_SHA384
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
++ ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++#endif /* CONFIG_SHA384 */
+ #ifdef CONFIG_SAE
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ ret = os_snprintf(pos, end - pos, "FT-SAE ");
+@@ -1104,7 +1083,21 @@
+ pos += ret;
+ }
+ #endif /* CONFIG_SAE */
+-#endif /* CONFIG_IEEE80211R */
++#ifdef CONFIG_FILS
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
++ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
++ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++#endif /* CONFIG_FILS */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+@@ -1141,7 +1134,39 @@
+ return pos - buf;
+ pos += ret;
+ }
++#ifdef CONFIG_FILS
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
++ ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
++ ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++#endif /* CONFIG_FILS */
+
++#ifdef CONFIG_OWE
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
++ ret = os_snprintf(pos, end - pos, "OWE ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++#endif /* CONFIG_OWE */
++
++#ifdef CONFIG_DPP
++ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
++ ret = os_snprintf(pos, end - pos, "DPP ");
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++ }
++#endif /* CONFIG_DPP */
++
+ if (pos > buf && *(pos - 1) == ' ') {
+ *(pos - 1) = '\0';
+ pos--;
+@@ -1269,6 +1294,42 @@
+ }
+
+
++static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
++{
++ struct sta_info *sta;
++ struct vlan_description vlan_id;
++
++ if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
++ return;
++
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ if (!hostapd_maclist_found(hapd->conf->accept_mac,
++ hapd->conf->num_accept_mac,
++ sta->addr, &vlan_id) ||
++ (vlan_id.notempty &&
++ vlan_compare(&vlan_id, sta->vlan_desc)))
++ ap_sta_disconnect(hapd, sta, sta->addr,
++ WLAN_REASON_UNSPECIFIED);
++ }
++}
++
++
++static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
++{
++ struct sta_info *sta;
++ struct vlan_description vlan_id;
++
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ if (hostapd_maclist_found(hapd->conf->deny_mac,
++ hapd->conf->num_deny_mac, sta->addr,
++ &vlan_id) &&
++ (!vlan_id.notempty ||
++ !vlan_compare(&vlan_id, sta->vlan_desc)))
++ ap_sta_disconnect(hapd, sta, sta->addr,
++ WLAN_REASON_UNSPECIFIED);
++ }
++}
++
+ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+ {
+ char *value;
+@@ -1306,51 +1367,67 @@
+ wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+ wps_corrupt_pkhash);
+ #endif /* CONFIG_WPS_TESTING */
+-#ifdef CONFIG_INTERWORKING
+- } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
+- int val = atoi(value);
+- if (val <= 0)
+- ret = -1;
+- else
+- hapd->gas_frag_limit = val;
+-#endif /* CONFIG_INTERWORKING */
+ #ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+ hapd->ext_mgmt_frame_handling = atoi(value);
+ } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+ hapd->ext_eapol_frame_io = atoi(value);
++#ifdef CONFIG_DPP
++ } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
++ os_free(hapd->dpp_config_obj_override);
++ hapd->dpp_config_obj_override = os_strdup(value);
++ } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
++ os_free(hapd->dpp_discovery_override);
++ hapd->dpp_discovery_override = os_strdup(value);
++ } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
++ os_free(hapd->dpp_groups_override);
++ hapd->dpp_groups_override = os_strdup(value);
++ } else if (os_strcasecmp(cmd,
++ "dpp_ignore_netaccesskey_mismatch") == 0) {
++ hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
++ } else if (os_strcasecmp(cmd, "dpp_test") == 0) {
++ dpp_test = atoi(value);
++#endif /* CONFIG_DPP */
+ #endif /* CONFIG_TESTING_OPTIONS */
++#ifdef CONFIG_MBO
++ } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
++ int val;
++
++ if (!hapd->conf->mbo_enabled)
++ return -1;
++
++ val = atoi(value);
++ if (val < 0 || val > 1)
++ return -1;
++
++ hapd->mbo_assoc_disallow = val;
++ ieee802_11_update_beacons(hapd->iface);
++
++ /*
++ * TODO: Need to configure drivers that do AP MLME offload with
++ * disallowing station logic.
++ */
++#endif /* CONFIG_MBO */
++#ifdef CONFIG_DPP
++ } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
++ os_free(hapd->dpp_configurator_params);
++ hapd->dpp_configurator_params = os_strdup(value);
++#endif /* CONFIG_DPP */
+ } else {
+- struct sta_info *sta;
+- int vlan_id;
+-
+ ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
+ if (ret)
+ return ret;
+
+ if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
+- for (sta = hapd->sta_list; sta; sta = sta->next) {
+- if (hostapd_maclist_found(
+- hapd->conf->deny_mac,
+- hapd->conf->num_deny_mac, sta->addr,
+- &vlan_id) &&
+- (!vlan_id || vlan_id == sta->vlan_id))
+- ap_sta_disconnect(
+- hapd, sta, sta->addr,
+- WLAN_REASON_UNSPECIFIED);
+- }
+- } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
+- os_strcasecmp(cmd, "accept_mac_file") == 0) {
+- for (sta = hapd->sta_list; sta; sta = sta->next) {
+- if (!hostapd_maclist_found(
+- hapd->conf->accept_mac,
+- hapd->conf->num_accept_mac,
+- sta->addr, &vlan_id) ||
+- (vlan_id && vlan_id != sta->vlan_id))
+- ap_sta_disconnect(
+- hapd, sta, sta->addr,
+- WLAN_REASON_UNSPECIFIED);
+- }
++ hostapd_disassoc_deny_mac(hapd);
++ } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
++ hostapd_disassoc_accept_mac(hapd);
++ } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
++ os_strncmp(cmd, "wmm_ac_", 7) == 0) {
++ hapd->parameter_set_count++;
++ if (ieee802_11_update_beacons(hapd->iface))
++ wpa_printf(MSG_DEBUG,
++ "Failed to update beacons with WMM parameters");
+ }
+ }
+
+@@ -1411,6 +1488,63 @@
+ }
+
+
++static int
++hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd,
++ struct sta_info *sta, void *ctx)
++{
++ struct hostapd_wpa_psk *psk;
++ const u8 *pmk;
++ int pmk_len;
++ int pmk_match;
++ int sta_match;
++ int bss_match;
++ int reason;
++
++ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
++
++ for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) {
++ pmk_match = PMK_LEN == pmk_len &&
++ os_memcmp(psk->psk, pmk, pmk_len) == 0;
++ sta_match = psk->group == 0 &&
++ os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0;
++ bss_match = psk->group == 1;
++
++ if (pmk_match && (sta_match || bss_match))
++ return 0;
++ }
++
++ wpa_printf(MSG_INFO, "STA " MACSTR
++ " PSK/passphrase no longer valid - disconnect",
++ MAC2STR(sta->addr));
++ reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
++ hostapd_drv_sta_deauth(hapd, sta->addr, reason);
++ ap_sta_deauthenticate(hapd, sta, reason);
++
++ return 0;
++}
++
++
++static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd)
++{
++ struct hostapd_bss_config *conf = hapd->conf;
++ int err;
++
++ hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
++
++ err = hostapd_setup_wpa_psk(conf);
++ if (err < 0) {
++ wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d",
++ err);
++ return -1;
++ }
++
++ ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter,
++ NULL);
++
++ return 0;
++}
++
++
+ #ifdef CONFIG_TESTING_OPTIONS
+
+ static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
+@@ -1500,6 +1634,137 @@
+ }
+
+
++static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
++ char *cmd)
++{
++ char *pos, *param;
++ size_t len;
++ u8 *buf;
++ int stype = 0, ok = 0;
++ union wpa_event_data event;
++
++ if (!hapd->ext_mgmt_frame_handling)
++ return -1;
++
++ /* stype= ok=<0/1> buf= */
++
++ wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
++
++ pos = cmd;
++ param = os_strstr(pos, "stype=");
++ if (param) {
++ param += 6;
++ stype = atoi(param);
++ }
++
++ param = os_strstr(pos, " ok=");
++ if (param) {
++ param += 4;
++ ok = atoi(param);
++ }
++
++ param = os_strstr(pos, " buf=");
++ if (!param)
++ return -1;
++ param += 5;
++
++ len = os_strlen(param);
++ if (len & 1)
++ return -1;
++ len /= 2;
++
++ buf = os_malloc(len);
++ if (!buf || hexstr2bin(param, buf, len) < 0) {
++ os_free(buf);
++ return -1;
++ }
++
++ os_memset(&event, 0, sizeof(event));
++ event.tx_status.type = WLAN_FC_TYPE_MGMT;
++ event.tx_status.data = buf;
++ event.tx_status.data_len = len;
++ event.tx_status.stype = stype;
++ event.tx_status.ack = ok;
++ hapd->ext_mgmt_frame_handling = 0;
++ wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
++ hapd->ext_mgmt_frame_handling = 1;
++
++ os_free(buf);
++
++ return 0;
++}
++
++
++static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
++ char *cmd)
++{
++ char *pos, *param;
++ size_t len;
++ u8 *buf;
++ int freq = 0, datarate = 0, ssi_signal = 0;
++ union wpa_event_data event;
++
++ if (!hapd->ext_mgmt_frame_handling)
++ return -1;
++
++ /* freq= datarate= ssi_signal= frame= */
++
++ wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
++
++ pos = cmd;
++ param = os_strstr(pos, "freq=");
++ if (param) {
++ param += 5;
++ freq = atoi(param);
++ }
++
++ param = os_strstr(pos, " datarate=");
++ if (param) {
++ param += 10;
++ datarate = atoi(param);
++ }
++
++ param = os_strstr(pos, " ssi_signal=");
++ if (param) {
++ param += 12;
++ ssi_signal = atoi(param);
++ }
++
++ param = os_strstr(pos, " frame=");
++ if (param == NULL)
++ return -1;
++ param += 7;
++
++ len = os_strlen(param);
++ if (len & 1)
++ return -1;
++ len /= 2;
++
++ buf = os_malloc(len);
++ if (buf == NULL)
++ return -1;
++
++ if (hexstr2bin(param, buf, len) < 0) {
++ os_free(buf);
++ return -1;
++ }
++
++ os_memset(&event, 0, sizeof(event));
++ event.rx_mgmt.freq = freq;
++ event.rx_mgmt.frame = buf;
++ event.rx_mgmt.frame_len = len;
++ event.rx_mgmt.ssi_signal = ssi_signal;
++ event.rx_mgmt.datarate = datarate;
++ hapd->ext_mgmt_frame_handling = 0;
++ wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
++ hapd->ext_mgmt_frame_handling = 1;
++
++ os_free(buf);
++
++ return 0;
++}
++
++
+ static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
+ {
+ char *pos;
+@@ -1557,8 +1822,8 @@
+ #define HWSIM_PACKETLEN 1500
+ #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+-void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+- size_t len)
++static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
++ size_t len)
+ {
+ struct hostapd_data *hapd = ctx;
+ const struct ether_header *eth;
+@@ -1745,8 +2010,6 @@
+ static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+ {
+ #ifdef WPA_TRACE_BFD
+- extern char wpa_trace_fail_func[256];
+- extern unsigned int wpa_trace_fail_after;
+ char *pos;
+
+ wpa_trace_fail_after = atoi(cmd);
+@@ -1770,9 +2033,6 @@
+ char *buf, size_t buflen)
+ {
+ #ifdef WPA_TRACE_BFD
+- extern char wpa_trace_fail_func[256];
+- extern unsigned int wpa_trace_fail_after;
+-
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+ wpa_trace_fail_func);
+ #else /* WPA_TRACE_BFD */
+@@ -1784,8 +2044,6 @@
+ static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
+ {
+ #ifdef WPA_TRACE_BFD
+- extern char wpa_trace_test_fail_func[256];
+- extern unsigned int wpa_trace_test_fail_after;
+ char *pos;
+
+ wpa_trace_test_fail_after = atoi(cmd);
+@@ -1809,9 +2067,6 @@
+ char *buf, size_t buflen)
+ {
+ #ifdef WPA_TRACE_BFD
+- extern char wpa_trace_test_fail_func[256];
+- extern unsigned int wpa_trace_test_fail_after;
+-
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+ wpa_trace_test_fail_func);
+ #else /* WPA_TRACE_BFD */
+@@ -1819,6 +2074,245 @@
+ #endif /* WPA_TRACE_BFD */
+ }
+
++
++static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
++{
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++ u8 zero[WPA_TK_MAX_LEN];
++
++ os_memset(zero, 0, sizeof(zero));
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++
++#ifdef CONFIG_IEEE80211W
++ if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
++ if (hapd->last_igtk_alg == WPA_ALG_NONE)
++ return -1;
++
++ wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
++
++ /* First, use a zero key to avoid any possible duplicate key
++ * avoidance in the driver. */
++ if (hostapd_drv_set_key(hapd->conf->iface, hapd,
++ hapd->last_igtk_alg,
++ broadcast_ether_addr,
++ hapd->last_igtk_key_idx, 1, NULL, 0,
++ zero, hapd->last_igtk_len) < 0)
++ return -1;
++
++ /* Set the previously configured key to reset its TSC */
++ return hostapd_drv_set_key(hapd->conf->iface, hapd,
++ hapd->last_igtk_alg,
++ broadcast_ether_addr,
++ hapd->last_igtk_key_idx, 1, NULL, 0,
++ hapd->last_igtk,
++ hapd->last_igtk_len);
++ }
++#endif /* CONFIG_IEEE80211W */
++
++ if (is_broadcast_ether_addr(addr)) {
++ if (hapd->last_gtk_alg == WPA_ALG_NONE)
++ return -1;
++
++ wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
++
++ /* First, use a zero key to avoid any possible duplicate key
++ * avoidance in the driver. */
++ if (hostapd_drv_set_key(hapd->conf->iface, hapd,
++ hapd->last_gtk_alg,
++ broadcast_ether_addr,
++ hapd->last_gtk_key_idx, 1, NULL, 0,
++ zero, hapd->last_gtk_len) < 0)
++ return -1;
++
++ /* Set the previously configured key to reset its TSC */
++ return hostapd_drv_set_key(hapd->conf->iface, hapd,
++ hapd->last_gtk_alg,
++ broadcast_ether_addr,
++ hapd->last_gtk_key_idx, 1, NULL, 0,
++ hapd->last_gtk, hapd->last_gtk_len);
++ }
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return -1;
++
++ if (sta->last_tk_alg == WPA_ALG_NONE)
++ return -1;
++
++ wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
++ MAC2STR(sta->addr));
++
++ /* First, use a zero key to avoid any possible duplicate key avoidance
++ * in the driver. */
++ if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
++ zero, sta->last_tk_len) < 0)
++ return -1;
++
++ /* Set the previously configured key to reset its TSC/RSC */
++ return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
++ sta->last_tk, sta->last_tk_len);
++}
++
++
++static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
++{
++ u8 addr[ETH_ALEN];
++ const char *pos = cmd;
++ enum wpa_alg alg;
++ int idx, set_tx;
++ u8 seq[6], key[WPA_TK_MAX_LEN];
++ size_t key_len;
++
++ /* parameters: alg addr idx set_tx seq key */
++
++ alg = atoi(pos);
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ if (hwaddr_aton(pos, addr))
++ return -1;
++ pos += 17;
++ if (*pos != ' ')
++ return -1;
++ pos++;
++ idx = atoi(pos);
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ set_tx = atoi(pos);
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ if (hexstr2bin(pos, seq, sizeof(seq)) < 0)
++ return -1;
++ pos += 2 * 6;
++ if (*pos != ' ')
++ return -1;
++ pos++;
++ key_len = os_strlen(pos) / 2;
++ if (hexstr2bin(pos, key, key_len) < 0)
++ return -1;
++
++ wpa_printf(MSG_INFO, "TESTING: Set key");
++ return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
++ set_tx, seq, 6, key, key_len);
++}
++
++
++static void restore_tk(void *ctx1, void *ctx2)
++{
++ struct hostapd_data *hapd = ctx1;
++ struct sta_info *sta = ctx2;
++
++ wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
++ MAC2STR(sta->addr));
++ /* This does not really restore the TSC properly, so this will result
++ * in replay protection issues for now since there is no clean way of
++ * preventing encryption of a single EAPOL frame. */
++ hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
++ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
++ sta->last_tk, sta->last_tk_len);
++}
++
++
++static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
++{
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++ int plain = os_strstr(cmd, "plaintext") != NULL;
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || !sta->wpa_sm)
++ return -1;
++
++ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
++ plain = 0; /* no need for special processing */
++ if (plain) {
++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
++ MAC2STR(sta->addr));
++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
++ NULL, 0);
++ }
++
++ wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
++ return wpa_auth_resend_m1(sta->wpa_sm,
++ os_strstr(cmd, "change-anonce") != NULL,
++ plain ? restore_tk : NULL, hapd, sta);
++}
++
++
++static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
++{
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++ int plain = os_strstr(cmd, "plaintext") != NULL;
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || !sta->wpa_sm)
++ return -1;
++
++ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
++ plain = 0; /* no need for special processing */
++ if (plain) {
++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
++ MAC2STR(sta->addr));
++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
++ NULL, 0);
++ }
++
++ wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
++ return wpa_auth_resend_m3(sta->wpa_sm,
++ plain ? restore_tk : NULL, hapd, sta);
++}
++
++
++static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
++ const char *cmd)
++{
++ struct sta_info *sta;
++ u8 addr[ETH_ALEN];
++ int plain = os_strstr(cmd, "plaintext") != NULL;
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || !sta->wpa_sm)
++ return -1;
++
++ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
++ plain = 0; /* no need for special processing */
++ if (plain) {
++ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
++ MAC2STR(sta->addr));
++ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
++ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
++ NULL, 0);
++ }
++
++ wpa_printf(MSG_INFO,
++ "TESTING: Send group M1 for the same GTK and zero RSC to "
++ MACSTR, MAC2STR(sta->addr));
++ return wpa_auth_resend_group_m1(sta->wpa_sm,
++ plain ? restore_tk : NULL, hapd, sta);
++}
++
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+
+@@ -1835,6 +2329,11 @@
+ return ret;
+
+ for (i = 0; i < iface->num_bss; i++) {
++
++ /* Save CHAN_SWITCH VHT config */
++ hostapd_chan_switch_vht_config(
++ iface->bss[i], settings.freq_params.vht_enabled);
++
+ ret = hostapd_switch_channel(iface->bss[i], &settings);
+ if (ret) {
+ /* FIX: What do we do if CSA fails in the middle of
+@@ -1875,13 +2374,13 @@
+
+ /* cmd: [] */
+ vendor_id = strtoul(cmd, &pos, 16);
+- if (!isblank(*pos))
++ if (!isblank((unsigned char) *pos))
+ return -EINVAL;
+
+ subcmd = strtoul(pos, &pos, 10);
+
+ if (*pos != '\0') {
+- if (!isblank(*pos++))
++ if (!isblank((unsigned char) *pos++))
+ return -EINVAL;
+ data_len = os_strlen(pos);
+ }
+@@ -2016,6 +2515,9 @@
+ struct hostapd_sta_info *info;
+ struct os_reltime now;
+
++ if (!iface->num_sta_seen)
++ return 0;
++
+ sta_track_expire(iface, 0);
+
+ pos = buf;
+@@ -2028,8 +2530,9 @@
+ int ret;
+
+ os_reltime_sub(&now, &info->last_seen, &age);
+- ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+- MAC2STR(info->addr), (unsigned int) age.sec);
++ ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
++ MAC2STR(info->addr), (unsigned int) age.sec,
++ info->ssi_signal);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+@@ -2040,10 +2543,378 @@
+ #endif /* NEED_AP_MLME */
+
+
++static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
++ const char *cmd)
++{
++ u8 addr[ETH_ALEN];
++
++ if (hwaddr_aton(cmd, addr)) {
++ wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
++ return -1;
++ }
++
++ return hostapd_send_lci_req(hapd, addr);
++}
++
++
++static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
++{
++ u8 addr[ETH_ALEN];
++ char *token, *context = NULL;
++ int random_interval, min_ap;
++ u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
++ unsigned int n_responders;
++
++ token = str_token(cmd, " ", &context);
++ if (!token || hwaddr_aton(token, addr)) {
++ wpa_printf(MSG_INFO,
++ "CTRL: REQ_RANGE - Bad destination address");
++ return -1;
++ }
++
++ token = str_token(cmd, " ", &context);
++ if (!token)
++ return -1;
++
++ random_interval = atoi(token);
++ if (random_interval < 0 || random_interval > 0xffff)
++ return -1;
++
++ token = str_token(cmd, " ", &context);
++ if (!token)
++ return -1;
++
++ min_ap = atoi(token);
++ if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
++ return -1;
++
++ n_responders = 0;
++ while ((token = str_token(cmd, " ", &context))) {
++ if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
++ wpa_printf(MSG_INFO,
++ "CTRL: REQ_RANGE: Too many responders");
++ return -1;
++ }
++
++ if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
++ wpa_printf(MSG_INFO,
++ "CTRL: REQ_RANGE: Bad responder address");
++ return -1;
++ }
++
++ n_responders++;
++ }
++
++ if (!n_responders) {
++ wpa_printf(MSG_INFO,
++ "CTRL: REQ_RANGE - No FTM responder address");
++ return -1;
++ }
++
++ return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
++ responders, n_responders);
++}
++
++
++static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
++ const char *cmd, char *reply,
++ size_t reply_size)
++{
++ u8 addr[ETH_ALEN];
++ const char *pos;
++ struct wpabuf *req;
++ int ret;
++ u8 req_mode = 0;
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++ pos = os_strchr(cmd, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ if (os_strncmp(pos, "req_mode=", 9) == 0) {
++ int val = hex2byte(pos + 9);
++
++ if (val < 0)
++ return -1;
++ req_mode = val;
++ pos += 11;
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++ }
++ req = wpabuf_parse_bin(pos);
++ if (!req)
++ return -1;
++
++ ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
++ wpabuf_free(req);
++ if (ret >= 0)
++ ret = os_snprintf(reply, reply_size, "%d", ret);
++ return ret;
++}
++
++
++static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
++{
++ struct wpa_ssid_value ssid;
++ u8 bssid[ETH_ALEN];
++ struct wpabuf *nr, *lci = NULL, *civic = NULL;
++ int stationary = 0;
++ char *tmp;
++ int ret;
++
++ if (!(hapd->conf->radio_measurements[0] &
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
++ return -1;
++ }
++
++ if (hwaddr_aton(buf, bssid)) {
++ wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
++ return -1;
++ }
++
++ tmp = os_strstr(buf, "ssid=");
++ if (!tmp || ssid_parse(tmp + 5, &ssid)) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Bad or missing SSID");
++ return -1;
++ }
++ buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
++ if (!buf)
++ return -1;
++
++ tmp = os_strstr(buf, "nr=");
++ if (!tmp) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
++ return -1;
++ }
++
++ buf = os_strchr(tmp, ' ');
++ if (buf)
++ *buf++ = '\0';
++
++ nr = wpabuf_parse_bin(tmp + 3);
++ if (!nr) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
++ return -1;
++ }
++
++ if (!buf)
++ goto set;
++
++ tmp = os_strstr(buf, "lci=");
++ if (tmp) {
++ buf = os_strchr(tmp, ' ');
++ if (buf)
++ *buf++ = '\0';
++ lci = wpabuf_parse_bin(tmp + 4);
++ if (!lci) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Bad LCI subelement");
++ wpabuf_free(nr);
++ return -1;
++ }
++ }
++
++ if (!buf)
++ goto set;
++
++ tmp = os_strstr(buf, "civic=");
++ if (tmp) {
++ buf = os_strchr(tmp, ' ');
++ if (buf)
++ *buf++ = '\0';
++ civic = wpabuf_parse_bin(tmp + 6);
++ if (!civic) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: SET_NEIGHBOR: Bad civic subelement");
++ wpabuf_free(nr);
++ wpabuf_free(lci);
++ return -1;
++ }
++ }
++
++ if (!buf)
++ goto set;
++
++ if (os_strstr(buf, "stat"))
++ stationary = 1;
++
++set:
++ ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
++ stationary);
++
++ wpabuf_free(nr);
++ wpabuf_free(lci);
++ wpabuf_free(civic);
++
++ return ret;
++}
++
++
++static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
++ char *buf)
++{
++ struct wpa_ssid_value ssid;
++ u8 bssid[ETH_ALEN];
++ char *tmp;
++
++ if (hwaddr_aton(buf, bssid)) {
++ wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
++ return -1;
++ }
++
++ tmp = os_strstr(buf, "ssid=");
++ if (!tmp || ssid_parse(tmp + 5, &ssid)) {
++ wpa_printf(MSG_ERROR,
++ "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
++ return -1;
++ }
++
++ return hostapd_neighbor_remove(hapd, bssid, &ssid);
++}
++
++
++static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
++ size_t buflen)
++{
++ int ret, i;
++ char *pos, *end;
++
++ ret = os_snprintf(buf, buflen, "%016llX:\n",
++ (long long unsigned) iface->drv_flags);
++ if (os_snprintf_error(buflen, ret))
++ return -1;
++
++ pos = buf + ret;
++ end = buf + buflen;
++
++ for (i = 0; i < 64; i++) {
++ if (iface->drv_flags & (1LLU << i)) {
++ ret = os_snprintf(pos, end - pos, "%s\n",
++ driver_flag_to_string(1LLU << i));
++ if (os_snprintf_error(end - pos, ret))
++ return -1;
++ pos += ret;
++ }
++ }
++
++ return pos - buf;
++}
++
++
++static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
++ const char *txtaddr)
++{
++ u8 addr[ETH_ALEN];
++ struct vlan_description vlan_id;
++
++ if (!(*num))
++ return 0;
++
++ if (hwaddr_aton(txtaddr, addr))
++ return -1;
++
++ if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
++ hostapd_remove_acl_mac(acl, num, addr);
++
++ return 0;
++}
++
++
++static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
++ int *num)
++{
++ while (*num)
++ hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
++}
++
++
++static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
++ char *buf, size_t buflen)
++{
++ int i = 0, len = 0, ret = 0;
++
++ if (!acl)
++ return 0;
++
++ while (i < num) {
++ ret = os_snprintf(buf + len, buflen - len,
++ MACSTR " VLAN_ID=%d\n",
++ MAC2STR(acl[i].addr),
++ acl[i].vlan_id.untagged);
++ if (ret < 0 || (size_t) ret >= buflen - len)
++ return len;
++ i++;
++ len += ret;
++ }
++ return len;
++}
++
++
++static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
++ const char *cmd)
++{
++ u8 addr[ETH_ALEN];
++ struct vlan_description vlan_id;
++ int ret = 0, vlanid = 0;
++ const char *pos;
++
++ if (hwaddr_aton(cmd, addr))
++ return -1;
++
++ pos = os_strstr(cmd, "VLAN_ID=");
++ if (pos)
++ vlanid = atoi(pos + 8);
++
++ if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
++ ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
++ if (ret != -1 && *acl)
++ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
++ }
++
++ return ret < 0 ? -1 : 0;
++}
++
++
++static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
++ const char *field, char *buf,
++ size_t buflen)
++{
++ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field);
++
++#ifdef CONFIG_DPP
++ if (os_strcmp(field, "dpp") == 0) {
++ int res;
++
++#ifdef CONFIG_DPP2
++ res = os_snprintf(buf, buflen, "DPP=2");
++#else /* CONFIG_DPP2 */
++ res = os_snprintf(buf, buflen, "DPP=1");
++#endif /* CONFIG_DPP2 */
++ if (os_snprintf_error(buflen, res))
++ return -1;
++ return res;
++ }
++#endif /* CONFIG_DPP */
++
++ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
++ field);
++
++ return -1;
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+- struct sockaddr_un *from,
++ struct sockaddr_storage *from,
+ socklen_t fromlen)
+ {
+ int reply_len, res;
+@@ -2057,6 +2928,8 @@
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
++ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
++ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
+ } else if (os_strcmp(buf, "STATUS") == 0) {
+ reply_len = hostapd_ctrl_iface_status(hapd, reply,
+ reply_size);
+@@ -2104,8 +2977,11 @@
+ reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ reply_size);
+ } else if (os_strcmp(buf, "ATTACH") == 0) {
+- if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
++ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ reply_len = -1;
++ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
++ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7))
++ reply_len = -1;
+ } else if (os_strcmp(buf, "DETACH") == 0) {
+ if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
+ reply_len = -1;
+@@ -2122,6 +2998,14 @@
+ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
+ reply_len = -1;
++#ifdef CONFIG_TAXONOMY
++ } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
++ reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
++ reply, reply_size);
++#endif /* CONFIG_TAXONOMY */
++ } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
++ if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
++ reply_len = -1;
+ } else if (os_strcmp(buf, "STOP_AP") == 0) {
+ if (hostapd_ctrl_iface_stop_ap(hapd))
+ reply_len = -1;
+@@ -2188,7 +3072,7 @@
+ if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
+ reply_len = -1;
+ #endif /* CONFIG_HS20 */
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
+ reply_len = -1;
+@@ -2198,7 +3082,10 @@
+ } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+ if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
+ reply_len = -1;
+-#endif /* CONFIG_WNM */
++ } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
++ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
++ reply_len = -1;
++#endif /* CONFIG_WNM_AP */
+ } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ reply_size);
+@@ -2211,6 +3098,9 @@
+ } else if (os_strncmp(buf, "ENABLE", 6) == 0) {
+ if (hostapd_ctrl_iface_enable(hapd->iface))
+ reply_len = -1;
++ } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
++ if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
++ reply_len = -1;
+ } else if (os_strncmp(buf, "RELOAD", 6) == 0) {
+ if (hostapd_ctrl_iface_reload(hapd->iface))
+ reply_len = -1;
+@@ -2227,6 +3117,13 @@
+ } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+ if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
+ reply_len = -1;
++ } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
++ if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
++ buf + 23) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
++ if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
++ reply_len = -1;
+ } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+ if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
+ reply_len = -1;
+@@ -2250,6 +3147,24 @@
+ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_FAIL") == 0) {
+ reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
++ } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
++ if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
++ if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
++ if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
++ if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
++ if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
++ reply_len = -1;
++ } else if (os_strcmp(buf, "REKEY_GTK") == 0) {
++ if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
++ reply_len = -1;
+ #endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
+@@ -2276,6 +3191,165 @@
+ reply_len = hostapd_ctrl_iface_track_sta_list(
+ hapd, reply, reply_size);
+ #endif /* NEED_AP_MLME */
++ } else if (os_strcmp(buf, "PMKSA") == 0) {
++ reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
++ reply_size);
++ } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
++ hostapd_ctrl_iface_pmksa_flush(hapd);
++ } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
++ if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
++ if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
++ reply_len = -1;
++ } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
++ if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
++ reply_len = -1;
++ } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
++ if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
++ reply_len = -1;
++ } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
++ if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
++ reply_len = -1;
++ } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
++ reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
++ reply, reply_size);
++ } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
++ reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
++ reply_size);
++ } else if (os_strcmp(buf, "TERMINATE") == 0) {
++ eloop_terminate();
++ } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
++ if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
++ if (!hostapd_ctrl_iface_acl_add_mac(
++ &hapd->conf->accept_mac,
++ &hapd->conf->num_accept_mac, buf + 19))
++ hostapd_disassoc_accept_mac(hapd);
++ else
++ reply_len = -1;
++ } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
++ hostapd_ctrl_iface_acl_del_mac(
++ &hapd->conf->accept_mac,
++ &hapd->conf->num_accept_mac, buf + 19);
++ } else if (os_strcmp(buf + 11, "SHOW") == 0) {
++ reply_len = hostapd_ctrl_iface_acl_show_mac(
++ hapd->conf->accept_mac,
++ hapd->conf->num_accept_mac, reply, reply_size);
++ } else if (os_strcmp(buf + 11, "CLEAR") == 0) {
++ hostapd_ctrl_iface_acl_clear_list(
++ &hapd->conf->accept_mac,
++ &hapd->conf->num_accept_mac);
++ }
++ } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
++ if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
++ if (!hostapd_ctrl_iface_acl_add_mac(
++ &hapd->conf->deny_mac,
++ &hapd->conf->num_deny_mac, buf + 17))
++ hostapd_disassoc_deny_mac(hapd);
++ } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
++ hostapd_ctrl_iface_acl_del_mac(
++ &hapd->conf->deny_mac,
++ &hapd->conf->num_deny_mac, buf + 17);
++ } else if (os_strcmp(buf + 9, "SHOW") == 0) {
++ reply_len = hostapd_ctrl_iface_acl_show_mac(
++ hapd->conf->deny_mac,
++ hapd->conf->num_deny_mac, reply, reply_size);
++ } else if (os_strcmp(buf + 9, "CLEAR") == 0) {
++ hostapd_ctrl_iface_acl_clear_list(
++ &hapd->conf->deny_mac,
++ &hapd->conf->num_deny_mac);
++ }
++#ifdef CONFIG_DPP
++ } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
++ res = hostapd_dpp_qr_code(hapd, buf + 12);
++ if (res < 0) {
++ reply_len = -1;
++ } else {
++ reply_len = os_snprintf(reply, reply_size, "%d", res);
++ if (os_snprintf_error(reply_size, reply_len))
++ reply_len = -1;
++ }
++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
++ res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18);
++ if (res < 0) {
++ reply_len = -1;
++ } else {
++ reply_len = os_snprintf(reply, reply_size, "%d", res);
++ if (os_snprintf_error(reply_size, reply_len))
++ reply_len = -1;
++ }
++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
++ if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp,
++ buf + 21) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
++ const char *uri;
++
++ uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp,
++ atoi(buf + 22));
++ if (!uri) {
++ reply_len = -1;
++ } else {
++ reply_len = os_snprintf(reply, reply_size, "%s", uri);
++ if (os_snprintf_error(reply_size, reply_len))
++ reply_len = -1;
++ }
++ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
++ reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp,
++ atoi(buf + 19),
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
++ if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
++ if (hostapd_dpp_listen(hapd, buf + 11) < 0)
++ reply_len = -1;
++ } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
++ hostapd_dpp_stop(hapd);
++ hostapd_dpp_listen_stop(hapd);
++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
++ res = dpp_configurator_add(hapd->iface->interfaces->dpp,
++ buf + 20);
++ if (res < 0) {
++ reply_len = -1;
++ } else {
++ reply_len = os_snprintf(reply, reply_size, "%d", res);
++ if (os_snprintf_error(reply_size, reply_len))
++ reply_len = -1;
++ }
++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
++ if (dpp_configurator_remove(hapd->iface->interfaces->dpp,
++ buf + 24) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
++ if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0)
++ reply_len = -1;
++ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
++ reply_len = dpp_configurator_get_key_id(
++ hapd->iface->interfaces->dpp,
++ atoi(buf + 25),
++ reply, reply_size);
++ } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
++ res = hostapd_dpp_pkex_add(hapd, buf + 12);
++ if (res < 0) {
++ reply_len = -1;
++ } else {
++ reply_len = os_snprintf(reply, reply_size, "%d", res);
++ if (os_snprintf_error(reply_size, reply_len))
++ reply_len = -1;
++ }
++ } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
++ if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
++ reply_len = -1;
++#endif /* CONFIG_DPP */
++#ifdef RADIUS_SERVER
++ } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
++ if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
++ reply_len = -1;
++#endif /* RADIUS_SERVER */
++ } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
++ reply_len = hostapd_ctrl_iface_get_capability(
++ hapd, buf + 15, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+@@ -2296,12 +3370,15 @@
+ struct hostapd_data *hapd = eloop_ctx;
+ char buf[4096];
+ int res;
+- struct sockaddr_un from;
++ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+- char *reply;
++ char *reply, *pos = buf;
+ const int reply_size = 4096;
+ int reply_len;
+ int level = MSG_DEBUG;
++#ifdef CONFIG_CTRL_IFACE_UDP
++ unsigned char lcookie[COOKIE_LEN];
++#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+@@ -2311,9 +3388,6 @@
+ return;
+ }
+ buf[res] = '\0';
+- if (os_strcmp(buf, "PING") == 0)
+- level = MSG_EXCESSIVE;
+- wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
+
+ reply = os_malloc(reply_size);
+ if (reply == NULL) {
+@@ -2325,10 +3399,46 @@
+ return;
+ }
+
+- reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
++#ifdef CONFIG_CTRL_IFACE_UDP
++ if (os_strcmp(buf, "GET_COOKIE") == 0) {
++ os_memcpy(reply, "COOKIE=", 7);
++ wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
++ cookie, COOKIE_LEN);
++ reply_len = 7 + 2 * COOKIE_LEN;
++ goto done;
++ }
++
++ if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
++ hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "CTRL: No cookie in the request - drop request");
++ os_free(reply);
++ return;
++ }
++
++ if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "CTRL: Invalid cookie in the request - drop request");
++ os_free(reply);
++ return;
++ }
++
++ pos = buf + 7 + 2 * COOKIE_LEN;
++ while (*pos == ' ')
++ pos++;
++#endif /* CONFIG_CTRL_IFACE_UDP */
++
++ if (os_strcmp(pos, "PING") == 0)
++ level = MSG_EXCESSIVE;
++ wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res);
++
++ reply_len = hostapd_ctrl_iface_receive_process(hapd, pos,
+ reply, reply_size,
+ &from, fromlen);
+
++#ifdef CONFIG_CTRL_IFACE_UDP
++done:
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+@@ -2338,6 +3448,7 @@
+ }
+
+
++#ifndef CONFIG_CTRL_IFACE_UDP
+ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+ {
+ char *buf;
+@@ -2357,6 +3468,7 @@
+ buf[len - 1] = '\0';
+ return buf;
+ }
++#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
+@@ -2372,6 +3484,99 @@
+
+ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+ {
++#ifdef CONFIG_CTRL_IFACE_UDP
++ int port = HOSTAPD_CTRL_IFACE_PORT;
++ char p[32] = { 0 };
++ char port_str[40], *tmp;
++ char *pos;
++ struct addrinfo hints = { 0 }, *res, *saveres;
++ int n;
++
++ if (hapd->ctrl_sock > -1) {
++ wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
++ return 0;
++ }
++
++ if (hapd->conf->ctrl_interface == NULL)
++ return 0;
++
++ pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
++ if (pos) {
++ pos += 4;
++ port = atoi(pos);
++ if (port <= 0) {
++ wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
++ goto fail;
++ }
++ }
++
++ dl_list_init(&hapd->ctrl_dst);
++ hapd->ctrl_sock = -1;
++ os_get_random(cookie, COOKIE_LEN);
++
++#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
++ hints.ai_flags = AI_PASSIVE;
++#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
++
++#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
++ hints.ai_family = AF_INET6;
++#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
++ hints.ai_family = AF_INET;
++#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
++ hints.ai_socktype = SOCK_DGRAM;
++
++try_again:
++ os_snprintf(p, sizeof(p), "%d", port);
++ n = getaddrinfo(NULL, p, &hints, &res);
++ if (n) {
++ wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
++ goto fail;
++ }
++
++ saveres = res;
++ hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype,
++ res->ai_protocol);
++ if (hapd->ctrl_sock < 0) {
++ wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
++ goto fail;
++ }
++
++ if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
++ port--;
++ if ((HOSTAPD_CTRL_IFACE_PORT - port) <
++ HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
++ goto try_again;
++ wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
++ goto fail;
++ }
++
++ freeaddrinfo(saveres);
++
++ os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
++ tmp = os_strdup(port_str);
++ if (tmp) {
++ os_free(hapd->conf->ctrl_interface);
++ hapd->conf->ctrl_interface = tmp;
++ }
++ wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
++
++ if (eloop_register_read_sock(hapd->ctrl_sock,
++ hostapd_ctrl_iface_receive, hapd, NULL) <
++ 0) {
++ hostapd_ctrl_iface_deinit(hapd);
++ return -1;
++ }
++
++ hapd->msg_ctx = hapd;
++ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
++
++ return 0;
++
++fail:
++ if (hapd->ctrl_sock >= 0)
++ close(hapd->ctrl_sock);
++ return -1;
++#else /* CONFIG_CTRL_IFACE_UDP */
+ struct sockaddr_un addr;
+ int s = -1;
+ char *fname = NULL;
+@@ -2381,6 +3586,8 @@
+ return 0;
+ }
+
++ dl_list_init(&hapd->ctrl_dst);
++
+ if (hapd->conf->ctrl_interface == NULL)
+ return 0;
+
+@@ -2396,9 +3603,9 @@
+ }
+
+ if (hapd->conf->ctrl_interface_gid_set &&
+- chown(hapd->conf->ctrl_interface, -1,
+- hapd->conf->ctrl_interface_gid) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
++ lchown(hapd->conf->ctrl_interface, -1,
++ hapd->conf->ctrl_interface_gid) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
+ strerror(errno));
+ return -1;
+ }
+@@ -2405,9 +3612,9 @@
+
+ if (!hapd->conf->ctrl_interface_gid_set &&
+ hapd->iface->interfaces->ctrl_iface_group &&
+- chown(hapd->conf->ctrl_interface, -1,
+- hapd->iface->interfaces->ctrl_iface_group) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
++ lchown(hapd->conf->ctrl_interface, -1,
++ hapd->iface->interfaces->ctrl_iface_group) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
+ strerror(errno));
+ return -1;
+ }
+@@ -2480,8 +3687,8 @@
+ }
+
+ if (hapd->conf->ctrl_interface_gid_set &&
+- chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
++ lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
+ strerror(errno));
+ goto fail;
+ }
+@@ -2488,8 +3695,8 @@
+
+ if (!hapd->conf->ctrl_interface_gid_set &&
+ hapd->iface->interfaces->ctrl_iface_group &&
+- chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
++ lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
+ strerror(errno));
+ goto fail;
+ }
+@@ -2520,6 +3727,7 @@
+ os_free(fname);
+ }
+ return -1;
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ }
+
+
+@@ -2528,10 +3736,14 @@
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (hapd->ctrl_sock > -1) {
++#ifndef CONFIG_CTRL_IFACE_UDP
+ char *fname;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
+ eloop_unregister_read_sock(hapd->ctrl_sock);
+ close(hapd->ctrl_sock);
+ hapd->ctrl_sock = -1;
++#ifndef CONFIG_CTRL_IFACE_UDP
+ fname = hostapd_ctrl_iface_path(hapd);
+ if (fname)
+ unlink(fname);
+@@ -2550,15 +3762,12 @@
+ strerror(errno));
+ }
+ }
++#endif /* !CONFIG_CTRL_IFACE_UDP */
+ }
+
+- dst = hapd->ctrl_dst;
+- hapd->ctrl_dst = NULL;
+- while (dst) {
+- prev = dst;
+- dst = dst->next;
+- os_free(prev);
+- }
++ dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst,
++ list)
++ os_free(dst);
+
+ #ifdef CONFIG_TESTING_OPTIONS
+ l2_packet_deinit(hapd->l2_test);
+@@ -2590,54 +3799,19 @@
+
+
+ static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
+- struct sockaddr_un *from,
+- socklen_t fromlen)
++ struct sockaddr_storage *from,
++ socklen_t fromlen, char *input)
+ {
+- struct wpa_ctrl_dst *dst;
+-
+- dst = os_zalloc(sizeof(*dst));
+- if (dst == NULL)
+- return -1;
+- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+- dst->addrlen = fromlen;
+- dst->debug_level = MSG_INFO;
+- dst->next = interfaces->global_ctrl_dst;
+- interfaces->global_ctrl_dst = dst;
+- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)",
+- from->sun_path,
+- fromlen - offsetof(struct sockaddr_un, sun_path));
+- return 0;
++ return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen,
++ input);
+ }
+
+
+ static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
+- struct sockaddr_un *from,
++ struct sockaddr_storage *from,
+ socklen_t fromlen)
+ {
+- struct wpa_ctrl_dst *dst, *prev = NULL;
+-
+- dst = interfaces->global_ctrl_dst;
+- while (dst) {
+- if (fromlen == dst->addrlen &&
+- os_memcmp(from->sun_path, dst->addr.sun_path,
+- fromlen - offsetof(struct sockaddr_un, sun_path))
+- == 0) {
+- wpa_hexdump(MSG_DEBUG,
+- "CTRL_IFACE monitor detached (global)",
+- from->sun_path,
+- fromlen -
+- offsetof(struct sockaddr_un, sun_path));
+- if (prev == NULL)
+- interfaces->global_ctrl_dst = dst->next;
+- else
+- prev->next = dst->next;
+- os_free(dst);
+- return 0;
+- }
+- prev = dst;
+- dst = dst->next;
+- }
+- return -1;
++ return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen);
+ }
+
+
+@@ -2648,6 +3822,16 @@
+ wps_testing_dummy_cred = 0;
+ wps_corrupt_pkhash = 0;
+ #endif /* CONFIG_WPS_TESTING */
++
++#ifdef CONFIG_TESTING_OPTIONS
++#ifdef CONFIG_DPP
++ dpp_test = DPP_TEST_DISABLED;
++#endif /* CONFIG_DPP */
++#endif /* CONFIG_TESTING_OPTIONS */
++
++#ifdef CONFIG_DPP
++ dpp_global_clear(interfaces->dpp);
++#endif /* CONFIG_DPP */
+ }
+
+
+@@ -2791,6 +3975,51 @@
+
+
+ static int
++hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces,
++ const char *input,
++ char *reply, int reply_size)
++{
++ size_t i, j;
++ int res;
++ char *pos, *end;
++ struct hostapd_iface *iface;
++ int show_ctrl = 0;
++
++ if (input)
++ show_ctrl = !!os_strstr(input, "ctrl");
++
++ pos = reply;
++ end = reply + reply_size;
++
++ for (i = 0; i < interfaces->count; i++) {
++ iface = interfaces->iface[i];
++
++ for (j = 0; j < iface->num_bss; j++) {
++ struct hostapd_bss_config *conf;
++
++ conf = iface->conf->bss[j];
++ if (show_ctrl)
++ res = os_snprintf(pos, end - pos,
++ "%s ctrl_iface=%s\n",
++ conf->iface,
++ conf->ctrl_interface ?
++ conf->ctrl_interface : "N/A");
++ else
++ res = os_snprintf(pos, end - pos, "%s\n",
++ conf->iface);
++ if (os_snprintf_error(end - pos, res)) {
++ *pos = '\0';
++ return pos - reply;
++ }
++ pos += res;
++ }
++ }
++
++ return pos - reply;
++}
++
++
++static int
+ hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
+ char *cmd)
+ {
+@@ -2839,7 +4068,7 @@
+ const char *ifname,
+ char *buf, char *reply,
+ int reply_size,
+- struct sockaddr_un *from,
++ struct sockaddr_storage *from,
+ socklen_t fromlen)
+ {
+ struct hostapd_data *hapd;
+@@ -2863,15 +4092,18 @@
+ void *sock_ctx)
+ {
+ void *interfaces = eloop_ctx;
+- char buf[256];
++ char buffer[256], *buf = buffer;
+ int res;
+- struct sockaddr_un from;
++ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+ char *reply;
+ int reply_len;
+ const int reply_size = 4096;
++#ifdef CONFIG_CTRL_IFACE_UDP
++ unsigned char lcookie[COOKIE_LEN];
++#endif /* CONFIG_CTRL_IFACE_UDP */
+
+- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
++ res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+@@ -2894,6 +4126,35 @@
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
++#ifdef CONFIG_CTRL_IFACE_UDP
++ if (os_strcmp(buf, "GET_COOKIE") == 0) {
++ os_memcpy(reply, "COOKIE=", 7);
++ wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
++ gcookie, COOKIE_LEN);
++ reply_len = 7 + 2 * COOKIE_LEN;
++ goto send_reply;
++ }
++
++ if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
++ hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "CTRL: No cookie in the request - drop request");
++ os_free(reply);
++ return;
++ }
++
++ if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "CTRL: Invalid cookie in the request - drop request");
++ os_free(reply);
++ return;
++ }
++
++ buf += 7 + 2 * COOKIE_LEN;
++ while (*buf == ' ')
++ buf++;
++#endif /* CONFIG_CTRL_IFACE_UDP */
++
+ if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+ char *pos = os_strchr(buf + 7, ' ');
+
+@@ -2922,8 +4183,12 @@
+ reply_len = -1;
+ } else if (os_strcmp(buf, "ATTACH") == 0) {
+ if (hostapd_global_ctrl_iface_attach(interfaces, &from,
+- fromlen))
++ fromlen, NULL))
+ reply_len = -1;
++ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
++ if (hostapd_global_ctrl_iface_attach(interfaces, &from,
++ fromlen, buf + 7))
++ reply_len = -1;
+ } else if (os_strcmp(buf, "DETACH") == 0) {
+ if (hostapd_global_ctrl_iface_detach(interfaces, &from,
+ fromlen))
+@@ -2930,7 +4195,6 @@
+ reply_len = -1;
+ #ifdef CONFIG_MODULE_TESTS
+ } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+- int hapd_module_tests(void);
+ if (hapd_module_tests() < 0)
+ reply_len = -1;
+ #endif /* CONFIG_MODULE_TESTS */
+@@ -2954,6 +4218,11 @@
+ reply_len = os_snprintf(reply, reply_size, "OK\n");
+ else
+ reply_len = -1;
++ } else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
++ reply_len = hostapd_global_ctrl_iface_interfaces(
++ interfaces, buf + 10, reply, sizeof(buffer));
++ } else if (os_strcmp(buf, "TERMINATE") == 0) {
++ eloop_terminate();
+ } else {
+ wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
+ "ignored");
+@@ -2975,6 +4244,7 @@
+ }
+
+
++#ifndef CONFIG_CTRL_IFACE_UDP
+ static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
+ {
+ char *buf;
+@@ -2994,10 +4264,93 @@
+ buf[len - 1] = '\0';
+ return buf;
+ }
++#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+ {
++#ifdef CONFIG_CTRL_IFACE_UDP
++ int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
++ char p[32] = { 0 };
++ char *pos;
++ struct addrinfo hints = { 0 }, *res, *saveres;
++ int n;
++
++ if (interface->global_ctrl_sock > -1) {
++ wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
++ return 0;
++ }
++
++ if (interface->global_iface_path == NULL)
++ return 0;
++
++ pos = os_strstr(interface->global_iface_path, "udp:");
++ if (pos) {
++ pos += 4;
++ port = atoi(pos);
++ if (port <= 0) {
++ wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
++ goto fail;
++ }
++ }
++
++ os_get_random(gcookie, COOKIE_LEN);
++
++#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
++ hints.ai_flags = AI_PASSIVE;
++#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
++
++#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
++ hints.ai_family = AF_INET6;
++#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
++ hints.ai_family = AF_INET;
++#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
++ hints.ai_socktype = SOCK_DGRAM;
++
++try_again:
++ os_snprintf(p, sizeof(p), "%d", port);
++ n = getaddrinfo(NULL, p, &hints, &res);
++ if (n) {
++ wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
++ goto fail;
++ }
++
++ saveres = res;
++ interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype,
++ res->ai_protocol);
++ if (interface->global_ctrl_sock < 0) {
++ wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
++ goto fail;
++ }
++
++ if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) <
++ 0) {
++ port++;
++ if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
++ HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
++ goto try_again;
++ wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
++ goto fail;
++ }
++
++ freeaddrinfo(saveres);
++
++ wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port);
++
++ if (eloop_register_read_sock(interface->global_ctrl_sock,
++ hostapd_global_ctrl_iface_receive,
++ interface, NULL) < 0) {
++ hostapd_global_ctrl_iface_deinit(interface);
++ return -1;
++ }
++
++ return 0;
++
++fail:
++ if (interface->global_ctrl_sock >= 0)
++ close(interface->global_ctrl_sock);
++ return -1;
++#else /* CONFIG_CTRL_IFACE_UDP */
+ struct sockaddr_un addr;
+ int s = -1;
+ char *fname = NULL;
+@@ -3017,9 +4370,9 @@
+ goto fail;
+ }
+ } else if (interface->ctrl_iface_group &&
+- chown(interface->global_iface_path, -1,
+- interface->ctrl_iface_group) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
++ lchown(interface->global_iface_path, -1,
++ interface->ctrl_iface_group) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
+ strerror(errno));
+ goto fail;
+ }
+@@ -3076,8 +4429,8 @@
+ }
+
+ if (interface->ctrl_iface_group &&
+- chown(fname, -1, interface->ctrl_iface_group) < 0) {
+- wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
++ lchown(fname, -1, interface->ctrl_iface_group) < 0) {
++ wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
+ strerror(errno));
+ goto fail;
+ }
+@@ -3103,12 +4456,15 @@
+ os_free(fname);
+ }
+ return -1;
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ }
+
+
+ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
+ {
++#ifndef CONFIG_CTRL_IFACE_UDP
+ char *fname = NULL;
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (interfaces->global_ctrl_sock > -1) {
+@@ -3115,6 +4471,7 @@
+ eloop_unregister_read_sock(interfaces->global_ctrl_sock);
+ close(interfaces->global_ctrl_sock);
+ interfaces->global_ctrl_sock = -1;
++#ifndef CONFIG_CTRL_IFACE_UDP
+ fname = hostapd_global_ctrl_iface_path(interfaces);
+ if (fname) {
+ unlink(fname);
+@@ -3134,26 +4491,36 @@
+ strerror(errno));
+ }
+ }
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ }
+
+ os_free(interfaces->global_iface_path);
+ interfaces->global_iface_path = NULL;
+
+- dst = interfaces->global_ctrl_dst;
+- interfaces->global_ctrl_dst = NULL;
+- while (dst) {
+- prev = dst;
+- dst = dst->next;
+- os_free(prev);
+- }
++ dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst,
++ struct wpa_ctrl_dst, list)
++ os_free(dst);
+ }
+
+
++static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
++ const char *buf)
++{
++ /* Enable Probe Request events based on explicit request.
++ * Other events are enabled by default.
++ */
++ if (str_starts(buf, RX_PROBE_REQUEST))
++ return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST);
++ return 1;
++}
++
++
+ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+ enum wpa_msg_type type,
+ const char *buf, size_t len)
+ {
+ struct wpa_ctrl_dst *dst, *next;
++ struct dl_list *ctrl_dst;
+ struct msghdr msg;
+ int idx;
+ struct iovec io[2];
+@@ -3162,13 +4529,13 @@
+
+ if (type != WPA_MSG_ONLY_GLOBAL) {
+ s = hapd->ctrl_sock;
+- dst = hapd->ctrl_dst;
++ ctrl_dst = &hapd->ctrl_dst;
+ } else {
+ s = hapd->iface->interfaces->global_ctrl_sock;
+- dst = hapd->iface->interfaces->global_ctrl_dst;
++ ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst;
+ }
+
+- if (s < 0 || dst == NULL)
++ if (s < 0 || dl_list_empty(ctrl_dst))
+ return;
+
+ os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+@@ -3181,12 +4548,11 @@
+ msg.msg_iovlen = 2;
+
+ idx = 0;
+- while (dst) {
+- next = dst->next;
+- if (level >= dst->debug_level) {
+- wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
+- (u8 *) dst->addr.sun_path, dst->addrlen -
+- offsetof(struct sockaddr_un, sun_path));
++ dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
++ if ((level >= dst->debug_level) &&
++ hostapd_ctrl_check_event_enabled(dst, buf)) {
++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
++ &dst->addr, dst->addrlen);
+ msg.msg_name = &dst->addr;
+ msg.msg_namelen = dst->addrlen;
+ if (sendmsg(s, &msg, 0) < 0) {
+@@ -3210,7 +4576,6 @@
+ dst->errors = 0;
+ }
+ idx++;
+- dst = next;
+ }
+ }
+
+--- contrib/wpa/hostapd/defconfig.orig
++++ contrib/wpa/hostapd/defconfig
+@@ -18,6 +18,9 @@
+ # Driver interface for drivers using the nl80211 kernel interface
+ CONFIG_DRIVER_NL80211=y
+
++# QCA vendor extensions to nl80211
++#CONFIG_DRIVER_NL80211_QCA=y
++
+ # driver_nl80211.c requires libnl. If you are compiling it yourself
+ # you may need to point hostapd to your version of libnl.
+ #
+@@ -28,7 +31,7 @@
+ #CONFIG_LIBNL20=y
+
+ # Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+-#CONFIG_LIBNL32=y
++CONFIG_LIBNL32=y
+
+
+ # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
+@@ -47,12 +50,12 @@
+ # WPA2/IEEE 802.11i RSN pre-authentication
+ CONFIG_RSN_PREAUTH=y
+
+-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+-CONFIG_PEERKEY=y
+-
+ # IEEE 802.11w (management frame protection)
+ CONFIG_IEEE80211W=y
+
++# Support Operating Channel Validation
++#CONFIG_OCV=y
++
+ # Integrated EAP server
+ CONFIG_EAP=y
+
+@@ -154,6 +157,12 @@
+ # IEEE 802.11ac (Very High Throughput) support
+ #CONFIG_IEEE80211AC=y
+
++# IEEE 802.11ax HE support
++# Note: This is experimental and work in progress. The definitions are still
++# subject to change and this should not be expected to interoperate with the
++# final IEEE 802.11ax version.
++#CONFIG_IEEE80211AX=y
++
+ # Remove debugging code that is printing out debug messages to stdout.
+ # This can be used to reduce the size of the hostapd considerably if debugging
+ # code is not needed.
+@@ -163,6 +172,9 @@
+ # Disabled by default.
+ #CONFIG_DEBUG_FILE=y
+
++# Send debug messages to syslog instead of stdout
++#CONFIG_DEBUG_SYSLOG=y
++
+ # Add support for sending all debug messages (regardless of debug verbosity)
+ # to the Linux kernel tracing facility. This helps debug the entire stack by
+ # making it easy to record everything happening from the driver up into the
+@@ -240,6 +252,11 @@
+ # requirements described above.
+ #CONFIG_NO_RANDOM_POOL=y
+
++# Should we attempt to use the getrandom(2) call that provides more reliable
++# yet secure randomness source than /dev/random on Linux 3.17 and newer.
++# Requires glibc 2.25 to build, falls back to /dev/random if unavailable.
++#CONFIG_GETRANDOM=y
++
+ # Should we use poll instead of select? Select is used by default.
+ #CONFIG_ELOOP_POLL=y
+
+@@ -246,10 +263,14 @@
+ # Should we use epoll instead of select? Select is used by default.
+ #CONFIG_ELOOP_EPOLL=y
+
++# Should we use kqueue instead of select? Select is used by default.
++#CONFIG_ELOOP_KQUEUE=y
++
+ # Select TLS implementation
+ # openssl = OpenSSL (default)
+ # gnutls = GnuTLS
+ # internal = Internal TLSv1 implementation (experimental)
++# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
+ # none = Empty template
+ #CONFIG_TLS=openssl
+
+@@ -262,6 +283,10 @@
+ # can be enabled to enable use of stronger crypto algorithms.
+ #CONFIG_TLSV12=y
+
++# Select which ciphers to use by default with OpenSSL if the user does not
++# specify them.
++#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
++
+ # If CONFIG_TLS=internal is used, additional library and include paths are
+ # needed for LibTomMath. Alternatively, an integrated, minimal version of
+ # LibTomMath can be used. See beginning of libtommath.c for details on benefits
+@@ -326,3 +351,31 @@
+ # http://wireless.kernel.org/en/users/Documentation/acs
+ #
+ #CONFIG_ACS=y
++
++# Multiband Operation support
++# These extentions facilitate efficient use of multiple frequency bands
++# available to the AP and the devices that may associate with it.
++#CONFIG_MBO=y
++
++# Client Taxonomy
++# Has the AP retain the Probe Request and (Re)Association Request frames from
++# a client, from which a signature can be produced which can identify the model
++# of client device like "Nexus 6P" or "iPhone 5s".
++#CONFIG_TAXONOMY=y
++
++# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
++#CONFIG_FILS=y
++# FILS shared key authentication with PFS
++#CONFIG_FILS_SK_PFS=y
++
++# Include internal line edit mode in hostapd_cli. This can be used to provide
++# limited command line editing and history support.
++#CONFIG_WPA_CLI_EDIT=y
++
++# Opportunistic Wireless Encryption (OWE)
++# Experimental implementation of draft-harkins-owe-07.txt
++#CONFIG_OWE=y
++
++# Override default value for the wpa_disable_eapol_key_retries configuration
++# parameter. See that parameter in hostapd.conf for more details.
++#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
+--- contrib/wpa/hostapd/hapd_module_tests.c.orig
++++ contrib/wpa/hostapd/hapd_module_tests.c
+@@ -9,6 +9,7 @@
+ #include "utils/includes.h"
+
+ #include "utils/common.h"
++#include "utils/module_tests.h"
+
+ int hapd_module_tests(void)
+ {
+--- contrib/wpa/hostapd/hlr_auc_gw.c.orig
++++ contrib/wpa/hostapd/hlr_auc_gw.c
+@@ -1,6 +1,6 @@
+ /*
+ * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+- * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen
++ * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -284,7 +284,7 @@
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+- printf("Could not open GSM tripler data file '%s'\n", fname);
++ printf("Could not open GSM triplet data file '%s'\n", fname);
+ return -1;
+ }
+
+@@ -312,66 +312,40 @@
+ }
+
+ /* IMSI */
+- pos2 = strchr(pos, ':');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid IMSI (%s)\n",
+- fname, line, pos);
++ pos2 = NULL;
++ pos = str_token(buf, ":", &pos2);
++ if (!pos || os_strlen(pos) >= sizeof(g->imsi)) {
++ printf("%s:%d - Invalid IMSI\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) >= sizeof(g->imsi)) {
+- printf("%s:%d - Too long IMSI (%s)\n",
+- fname, line, pos);
+- ret = -1;
+- break;
+- }
+ os_strlcpy(g->imsi, pos, sizeof(g->imsi));
+- pos = pos2 + 1;
+
+ /* Kc */
+- pos2 = strchr(pos, ':');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
++ pos = str_token(buf, ":", &pos2);
++ if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
++ printf("%s:%d - Invalid Kc\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
+- printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
+- ret = -1;
+- break;
+- }
+- pos = pos2 + 1;
+
+ /* SRES */
+- pos2 = strchr(pos, ':');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid SRES (%s)\n", fname, line,
+- pos);
++ pos = str_token(buf, ":", &pos2);
++ if (!pos || os_strlen(pos) != 8 ||
++ hexstr2bin(pos, g->sres, 4)) {
++ printf("%s:%d - Invalid SRES\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
+- printf("%s:%d - Invalid SRES (%s)\n", fname, line,
+- pos);
+- ret = -1;
+- break;
+- }
+- pos = pos2 + 1;
+
+ /* RAND */
+- pos2 = strchr(pos, ':');
+- if (pos2)
+- *pos2 = '\0';
+- if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
+- printf("%s:%d - Invalid RAND (%s)\n", fname, line,
+- pos);
++ pos = str_token(buf, ":", &pos2);
++ if (!pos || os_strlen(pos) != 32 ||
++ hexstr2bin(pos, g->_rand, 16)) {
++ printf("%s:%d - Invalid RAND\n", fname, line);
+ ret = -1;
+ break;
+ }
+- pos = pos2 + 1;
+
+ g->next = gsm_db;
+ gsm_db = g;
+@@ -450,86 +424,58 @@
+ }
+
+ /* IMSI */
+- pos2 = strchr(pos, ' ');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid IMSI (%s)\n",
+- fname, line, pos);
++ pos2 = NULL;
++ pos = str_token(buf, " ", &pos2);
++ if (!pos || os_strlen(pos) >= sizeof(m->imsi)) {
++ printf("%s:%d - Invalid IMSI\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) >= sizeof(m->imsi)) {
+- printf("%s:%d - Too long IMSI (%s)\n",
+- fname, line, pos);
+- ret = -1;
+- break;
+- }
+ os_strlcpy(m->imsi, pos, sizeof(m->imsi));
+- pos = pos2 + 1;
+
+ /* Ki */
+- pos2 = strchr(pos, ' ');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
++ pos = str_token(buf, " ", &pos2);
++ if (!pos || os_strlen(pos) != 32 ||
++ hexstr2bin(pos, m->ki, 16)) {
++ printf("%s:%d - Invalid Ki\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
+- printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
+- ret = -1;
+- break;
+- }
+- pos = pos2 + 1;
+
+ /* OPc */
+- pos2 = strchr(pos, ' ');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
++ pos = str_token(buf, " ", &pos2);
++ if (!pos || os_strlen(pos) != 32 ||
++ hexstr2bin(pos, m->opc, 16)) {
++ printf("%s:%d - Invalid OPc\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
+- printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
+- ret = -1;
+- break;
+- }
+- pos = pos2 + 1;
+
+ /* AMF */
+- pos2 = strchr(pos, ' ');
+- if (pos2 == NULL) {
+- printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
++ pos = str_token(buf, " ", &pos2);
++ if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
++ printf("%s:%d - Invalid AMF\n", fname, line);
+ ret = -1;
+ break;
+ }
+- *pos2 = '\0';
+- if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
+- printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
+- ret = -1;
+- break;
+- }
+- pos = pos2 + 1;
+
+ /* SQN */
+- pos2 = strchr(pos, ' ');
+- if (pos2)
+- *pos2 = '\0';
+- if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
+- printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
++ pos = str_token(buf, " ", &pos2);
++ if (!pos || os_strlen(pos) != 12 ||
++ hexstr2bin(pos, m->sqn, 6)) {
++ printf("%s:%d - Invalid SEQ\n", fname, line);
+ ret = -1;
+ break;
+ }
+
+- if (pos2) {
+- pos = pos2 + 1;
++ pos = str_token(buf, " ", &pos2);
++ if (pos) {
+ m->res_len = atoi(pos);
+ if (m->res_len &&
+ (m->res_len < EAP_AKA_RES_MIN_LEN ||
+ m->res_len > EAP_AKA_RES_MAX_LEN)) {
+- printf("%s:%d - Invalid RES_len (%s)\n",
+- fname, line, pos);
++ printf("%s:%d - Invalid RES_len\n",
++ fname, line);
+ ret = -1;
+ break;
+ }
+@@ -1027,7 +973,7 @@
+ {
+ printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
+ "database/authenticator\n"
+- "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen \n"
++ "Copyright (c) 2005-2017, Jouni Malinen \n"
+ "\n"
+ "usage:\n"
+ "hlr_auc_gw [-hu] [-s] [-g] "
+--- contrib/wpa/hostapd/hostapd.conf.orig
++++ contrib/wpa/hostapd/hostapd.conf
+@@ -3,6 +3,8 @@
+
+ # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
+ # management frames with the Host AP driver); wlan0 with many nl80211 drivers
++# Note: This attribute can be overridden by the values supplied with the '-i'
++# command line parameter.
+ interface=wlan0
+
+ # In case of atheros and nl80211 driver interfaces, an additional
+@@ -96,8 +98,25 @@
+ # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+ # Set as needed to indicate country in which device is operating.
+ # This can limit available channels and transmit power.
++# These two octets are used as the first two octets of the Country String
++# (dot11CountryString)
+ #country_code=US
+
++# The third octet of the Country String (dot11CountryString)
++# This parameter is used to set the third octet of the country string.
++#
++# All environments of the current frequency band and country (default)
++#country3=0x20
++# Outdoor environment only
++#country3=0x4f
++# Indoor environment only
++#country3=0x49
++# Noncountry entity (country_code=XX)
++#country3=0x58
++# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f
++# Annex E, Table E-4 (Global operating classes)
++#country3=0x04
++
+ # Enable IEEE 802.11d. This advertises the country_code and the set of allowed
+ # channels and transmit power levels based on the regulatory limits. The
+ # country_code setting must be configured with the correct country for
+@@ -125,11 +144,13 @@
+ # ieee80211d=1 and local_pwr_constraint configured.
+ #spectrum_mgmt_required=1
+
+-# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
+-# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
+-# specify band). When using ACS (see channel parameter), a special value "any"
+-# can be used to indicate that any support band can be used. This special case
+-# is currently supported only with drivers with which offloaded ACS is used.
++# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
++# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
++# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
++# needs to be set to hw_mode=a. When using ACS (see channel parameter), a
++# special value "any" can be used to indicate that any support band can be used.
++# This special case is currently supported only with drivers with which
++# offloaded ACS is used.
+ # Default: IEEE 802.11b
+ hw_mode=g
+
+@@ -173,11 +194,16 @@
+ # Channel list restriction. This option allows hostapd to select one of the
+ # provided channels when a channel should be automatically selected.
+ # Channel list can be provided as range using hyphen ('-') or individual
+-# channels can be specified by space (' ') seperated values
++# channels can be specified by space (' ') separated values
+ # Default: all channels allowed in selected hw_mode
+ #chanlist=100 104 108 112 116
+ #chanlist=1 6 11-13
+
++# Exclude DFS channels from ACS
++# This option can be used to exclude all DFS channels from the ACS channel list
++# in cases where the driver supports DFS channels.
++#acs_exclude_dfs=1
++
+ # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
+ beacon_int=100
+
+@@ -192,16 +218,16 @@
+ # (default: 2007)
+ max_num_sta=255
+
+-# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
++# RTS/CTS threshold; -1 = disabled (default); range -1..65535
+ # If this field is not included in hostapd.conf, hostapd will not control
+ # RTS threshold and 'iwconfig wlan# rts ' can be used to set it.
+-rts_threshold=2347
++rts_threshold=-1
+
+-# Fragmentation threshold; 2346 = disabled (default); range 256..2346
++# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346
+ # If this field is not included in hostapd.conf, hostapd will not control
+ # fragmentation threshold and 'iwconfig wlan# frag ' can be used to set
+ # it.
+-fragm_threshold=2346
++fragm_threshold=-1
+
+ # Rate configuration
+ # Default is to enable all rates supported by the hardware. This configuration
+@@ -223,6 +249,19 @@
+ #basic_rates=10 20 55 110
+ #basic_rates=60 120 240
+
++# Beacon frame TX rate configuration
++# This sets the TX rate that is used to transmit Beacon frames. If this item is
++# not included, the driver default rate (likely lowest rate) is used.
++# Legacy (CCK/OFDM rates):
++# beacon_rate=
++# HT:
++# beacon_rate=ht:
++# VHT:
++# beacon_rate=vht:
++#
++# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM).
++#beacon_rate=10
++
+ # Short Preamble
+ # This parameter can be used to enable optional use of short preamble for
+ # frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
+@@ -267,7 +306,14 @@
+ # requests for broadcast SSID
+ ignore_broadcast_ssid=0
+
+-# Additional vendor specfic elements for Beacon and Probe Response frames
++# Do not reply to broadcast Probe Request frames from unassociated STA if there
++# is no room for additional stations (max_num_sta). This can be used to
++# discourage a STA from trying to associate with this AP if the association
++# would be rejected due to maximum STA limit.
++# Default: 0 (disabled)
++#no_probe_resp_if_max_sta=0
++
++# Additional vendor specific elements for Beacon and Probe Response frames
+ # This parameter can be used to add additional vendor specific element(s) into
+ # the end of the Beacon and Probe Response frames. The format for these
+ # element(s) is a hexdump of the raw information elements (id+len+payload for
+@@ -274,9 +320,16 @@
+ # one or more elements)
+ #vendor_elements=dd0411223301
+
++# Additional vendor specific elements for (Re)Association Response frames
++# This parameter can be used to add additional vendor specific element(s) into
++# the end of the (Re)Association Response frames. The format for these
++# element(s) is a hexdump of the raw information elements (id+len+payload for
++# one or more elements)
++#assocresp_elements=dd0411223301
++
+ # TX queue parameters (EDCF / bursting)
+ # tx_queue__
+-# queues: data0, data1, data2, data3, after_beacon, beacon
++# queues: data0, data1, data2, data3
+ # (data0 is the highest priority queue)
+ # parameters:
+ # aifs: AIFS (default 2)
+@@ -385,6 +438,13 @@
+ wmm_ac_vo_acm=0
+ # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
+
++# Enable Multi-AP functionality
++# 0 = disabled (default)
++# 1 = AP support backhaul BSS
++# 2 = AP support fronthaul BSS
++# 3 = AP supports both backhaul BSS and fronthaul BSS
++#multi_ap=0
++
+ # Static WEP key configuration
+ #
+ # The key number to use when transmitting.
+@@ -458,6 +518,12 @@
+ # Beacon and Probe Response frames.
+ #bss_load_update_period=50
+
++# Channel utilization averaging period (in BUs)
++# This field is used to enable and configure channel utilization average
++# calculation with bss_load_update_period. This should be in multiples of
++# bss_load_update_period for more accurate calculation.
++#chan_util_avg_period=600
++
+ # Fixed BSS Load value for testing purposes
+ # This field can be used to configure hostapd to add a fixed BSS Load element
+ # into Beacon and Probe Response frames for testing purposes. The format is
+@@ -464,6 +530,26 @@
+ # ::
+ #bss_load_test=12:80:20000
+
++# Multicast to unicast conversion
++# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and
++# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent
++# to each station separately, with the DA replaced by their own MAC address
++# rather than the group address.
++#
++# Note that this may break certain expectations of the receiver, such as the
++# ability to drop unicast IP packets received within multicast L2 frames, or the
++# ability to not send ICMP destination unreachable messages for packets received
++# in L2 multicast (which is required, but the receiver can't tell the difference
++# if this new option is enabled).
++#
++# This also doesn't implement the 802.11 DMS (directed multicast service).
++#
++#multicast_to_unicast=0
++
++# Send broadcast Deauthentication frame on AP start/stop
++# Default: 1 (enabled)
++#broadcast_deauth=1
++
+ ##### IEEE 802.11n related configuration ######################################
+
+ # ieee80211n: Whether IEEE 802.11n (HT) is enabled
+@@ -470,6 +556,7 @@
+ # 0 = disabled (default)
+ # 1 = enabled
+ # Note: You will also need to enable WMM for full HT functionality.
++# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
+ #ieee80211n=1
+
+ # ht_capab: HT capabilities (list of flags)
+@@ -523,6 +610,7 @@
+ # 0 = disabled (default)
+ # 1 = enabled
+ # Note: You will also need to enable WMM for full VHT functionality.
++# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT.
+ #ieee80211ac=1
+
+ # vht_capab: VHT capabilities (list of flags)
+@@ -605,9 +693,9 @@
+ # VHT TXOP PS: [VHT-TXOP-PS]
+ # Indicates whether or not the AP supports VHT TXOP Power Save Mode
+ # or whether or not the STA is in VHT TXOP Power Save mode
+-# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
++# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
+ # mode
+-# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
++# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
+ # mode
+ #
+ # +HTC-VHT Capable: [HTC-VHT]
+@@ -665,6 +753,78 @@
+ #
+ #vht_oper_centr_freq_seg1_idx=159
+
++# Workaround to use station's nsts capability in (Re)Association Response frame
++# This may be needed with some deployed devices as an interoperability
++# workaround for beamforming if the AP's capability is greater than the
++# station's capability. This is disabled by default and can be enabled by
++# setting use_sta_nsts=1.
++#use_sta_nsts=0
++
++##### IEEE 802.11ax related configuration #####################################
++
++#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled
++# 0 = disabled (default)
++# 1 = enabled
++#ieee80211ax=1
++
++#he_su_beamformer: HE single user beamformer support
++# 0 = not supported (default)
++# 1 = supported
++#he_su_beamformer=1
++
++#he_su_beamformee: HE single user beamformee support
++# 0 = not supported (default)
++# 1 = supported
++#he_su_beamformee=1
++
++#he_mu_beamformer: HE multiple user beamformer support
++# 0 = not supported (default)
++# 1 = supported
++#he_mu_beamformer=1
++
++# he_bss_color: BSS color
++# 0 = no BSS color (default)
++# unsigned integer = BSS color
++#he_bss_color=0
++
++#he_default_pe_duration: The duration of PE field in an HE PPDU in us
++# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us
++#he_default_pe_duration=0
++
++#he_twt_required: Whether TWT is required
++# 0 = not required (default)
++# 1 = required
++#he_twt_required=0
++
++#he_rts_threshold: Duration of STA transmission
++# 0 = not set (default)
++# unsigned integer = duration in units of 16 us
++#he_rts_threshold=0
++
++#he_mu_edca_qos_info_param_count
++#he_mu_edca_qos_info_q_ack
++#he_mu_edca_qos_info_queue_request=1
++#he_mu_edca_qos_info_txop_request
++#he_mu_edca_ac_be_aifsn=0
++#he_mu_edca_ac_be_ecwmin=15
++#he_mu_edca_ac_be_ecwmax=15
++#he_mu_edca_ac_be_timer=255
++#he_mu_edca_ac_bk_aifsn=0
++#he_mu_edca_ac_bk_aci=1
++#he_mu_edca_ac_bk_ecwmin=15
++#he_mu_edca_ac_bk_ecwmax=15
++#he_mu_edca_ac_bk_timer=255
++#he_mu_edca_ac_vi_ecwmin=15
++#he_mu_edca_ac_vi_ecwmax=15
++#he_mu_edca_ac_vi_aifsn=0
++#he_mu_edca_ac_vi_aci=2
++#he_mu_edca_ac_vi_timer=255
++#he_mu_edca_ac_vo_aifsn=0
++#he_mu_edca_ac_vo_aci=3
++#he_mu_edca_ac_vo_ecwmin=15
++#he_mu_edca_ac_vo_ecwmax=15
++#he_mu_edca_ac_vo_timer=255
++
+ ##### IEEE 802.1X-2004 related configuration ##################################
+
+ # Require IEEE 802.1X authorization
+@@ -762,12 +922,56 @@
+ # valid CRL signed by the CA is required to be included in the ca_cert file.
+ # This can be done by using PEM format for CA certificate and CRL and
+ # concatenating these into one file. Whenever CRL changes, hostapd needs to be
+-# restarted to take the new CRL into use.
++# restarted to take the new CRL into use. Alternatively, crl_reload_interval can
++# be used to configure periodic updating of the loaded CRL information.
+ # 0 = do not verify CRLs (default)
+ # 1 = check the CRL of the user certificate
+ # 2 = check all CRLs in the certificate path
+ #check_crl=1
+
++# Specify whether to ignore certificate CRL validity time mismatches with
++# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID.
++#
++# 0 = ignore errors
++# 1 = do not ignore errors (default)
++#check_crl_strict=1
++
++# CRL reload interval in seconds
++# This can be used to reload ca_cert file and the included CRL on every new TLS
++# session if difference between last reload and the current reload time in
++# seconds is greater than crl_reload_interval.
++# Note: If interval time is very short, CPU overhead may be negatively affected
++# and it is advised to not go below 300 seconds.
++# This is applicable only with check_crl values 1 and 2.
++# 0 = do not reload CRLs (default)
++# crl_reload_interval = 300
++
++# If check_cert_subject is set, the value of every field will be checked
++# against the DN of the subject in the client certificate. If the values do
++# not match, the certificate verification will fail, rejecting the user.
++# This option allows hostapd to match every individual field in the right order
++# against the DN of the subject in the client certificate.
++#
++# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check
++# every individual DN field of the subject in the client certificate. If OU=XYZ
++# comes first in terms of the order in the client certificate (DN field of
++# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the
++# client because the order of 'OU' is not matching the specified string in
++# check_cert_subject.
++#
++# This option also allows '*' as a wildcard. This option has some limitation.
++# It can only be used as per the following example.
++#
++# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two
++# clients and DN of the subject in the first client certificate is
++# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is
++# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both
++# clients because the value of 'OU' field in both client certificates matches
++# 'OU' value in 'check_cert_subject' up to 'wildcard'.
++#
++# * (Allow all clients, e.g., check_cert_subject=*)
++#check_cert_subject=string
++
+ # TLS Session Lifetime in seconds
+ # This can be used to allow TLS sessions to be cached and resumed with an
+ # abbreviated handshake when using EAP-TLS/TTLS/PEAP.
+@@ -774,6 +978,27 @@
+ # (default: 0 = session caching and resumption disabled)
+ #tls_session_lifetime=3600
+
++# TLS flags
++# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on
++# the TLS library, these may be disabled by default to enforce stronger
++# security)
++# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests
++# the TLS library to accept certificates even if they are not currently
++# valid, i.e., have expired or have not yet become valid; this should be
++# used only for testing purposes)
++# [DISABLE-TLSv1.0] = disable use of TLSv1.0
++# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows
++# systemwide TLS policies to be overridden)
++# [DISABLE-TLSv1.1] = disable use of TLSv1.1
++# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows
++# systemwide TLS policies to be overridden)
++# [DISABLE-TLSv1.2] = disable use of TLSv1.2
++# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows
++# systemwide TLS policies to be overridden)
++# [DISABLE-TLSv1.3] = disable use of TLSv1.3
++# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default)
++#tls_flags=[flag1][flag2]...
++
+ # Cached OCSP stapling response (DER encoded)
+ # If set, this file is sent as a certificate status response by the EAP server
+ # if the EAP peer requests certificate status in the ClientHello message.
+@@ -788,6 +1013,11 @@
+ # -respout /tmp/ocsp-cache.der
+ #ocsp_stapling_response=/tmp/ocsp-cache.der
+
++# Cached OCSP stapling response list (DER encoded OCSPResponseList)
++# This is similar to ocsp_stapling_response, but the extended version defined in
++# RFC 6961 to allow multiple OCSP responses to be provided.
++#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der
++
+ # dh_file: File path to DH/DSA parameters file (in PEM format)
+ # This is an optional configuration file for setting parameters for an
+ # ephemeral DH key exchange. In most cases, the default RSA authentication does
+@@ -803,12 +1033,26 @@
+ # OpenSSL cipher string
+ #
+ # This is an OpenSSL specific configuration option for configuring the default
+-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
++# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
++# by default) is used.
+ # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+ # on cipher suite configuration. This is applicable only if hostapd is built to
+ # use OpenSSL.
+ #openssl_ciphers=DEFAULT:!EXP:!LOW
+
++# OpenSSL ECDH curves
++#
++# This is an OpenSSL specific configuration option for configuring the ECDH
++# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve
++# selection is enabled. If set to an empty string, ECDH curve configuration is
++# not done (the exact library behavior depends on the library version).
++# Otherwise, this is a colon separated list of the supported curves (e.g.,
++# P-521:P-384:P-256). This is applicable only if hostapd is built to use
++# OpenSSL. This must not be used for Suite B cases since the same OpenSSL
++# parameter is set differently in those cases and this might conflict with that
++# design.
++#openssl_ecdh_curves=P-521:P-384:P-256
++
+ # Fragment size for EAP methods
+ #fragment_size=1400
+
+@@ -825,6 +1069,11 @@
+ #eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+ #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
+
++# EAP-SIM DB request timeout
++# This parameter sets the maximum time to wait for a database request response.
++# The parameter value is in seconds.
++#eap_sim_db_timeout=1
++
+ # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
+ # random value. It is configured as a 16-octet value in hex format. It can be
+ # generated, e.g., with the following command:
+@@ -888,11 +1137,23 @@
+ # The own IP address of the access point (used as NAS-IP-Address)
+ own_ip_addr=127.0.0.1
+
+-# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+-# a unique to the NAS within the scope of the RADIUS server. For example, a
+-# fully qualified domain name can be used here.
++# NAS-Identifier string for RADIUS messages. When used, this should be unique
++# to the NAS within the scope of the RADIUS server. Please note that hostapd
++# uses a separate RADIUS client for each BSS and as such, a unique
++# nas_identifier value should be configured separately for each BSS. This is
++# particularly important for cases where RADIUS accounting is used
++# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions
++# and that may get interpreted as applying to all BSSes if the same
++# NAS-Identifier value is used.) For example, a fully qualified domain name
++# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here.
++#
+ # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and
+ # 48 octets long.
++#
++# It is mandatory to configure either own_ip_addr or nas_identifier to be
++# compliant with the RADIUS protocol. When using RADIUS accounting, it is
++# strongly recommended that nas_identifier is set to a unique value for each
++# BSS.
+ #nas_identifier=ap.example.com
+
+ # RADIUS client forced local IP address for the access point
+@@ -952,11 +1213,24 @@
+ # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
+ # VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can
+ # be used to set static client MAC address to VLAN ID mapping.
+-# 0 = disabled (default)
+-# 1 = option; use default interface if RADIUS server does not include VLAN ID
++# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2
++# passphrase from wpa_psk_file or vlan_id parameter from sae_password.
++# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used
++# 1 = optional; use default interface if RADIUS server does not include VLAN ID
+ # 2 = required; reject authentication if RADIUS server does not include VLAN ID
+ #dynamic_vlan=0
+
++# Per-Station AP_VLAN interface mode
++# If enabled, each station is assigned its own AP_VLAN interface.
++# This implies per-station group keying and ebtables filtering of inter-STA
++# traffic (when passed through the AP).
++# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be
++# added to the bridge given by the "bridge" configuration option (see above).
++# Otherwise, it will be added to the per-VLAN bridge.
++# 0 = disabled (default)
++# 1 = enabled
++#per_sta_vif=0
++
+ # VLAN interface list for dynamic VLAN mode is read from a separate text file.
+ # This list is used to map VLAN ID from the RADIUS server to a network
+ # interface. Each station is bound to one interface in the same way as with
+@@ -965,6 +1239,7 @@
+ # white space (space or tab).
+ # If no entries are provided by this file, the station is statically mapped
+ # to . interfaces.
++# Each line can optionally also contain the name of a bridge to add the VLAN to
+ #vlan_file=/etc/hostapd.vlan
+
+ # Interface where 802.1q tagged packets should appear when a RADIUS server is
+@@ -1028,6 +1303,8 @@
+ #radius_das_port=3799
+ #
+ # DAS client (the host that can send Disconnect/CoA requests) and shared secret
++# Format:
++# IP address 0.0.0.0 can be used to allow requests from any address.
+ #radius_das_client=192.168.1.123 shared secret here
+ #
+ # DAS Event-Timestamp time window in seconds
+@@ -1035,6 +1312,9 @@
+ #
+ # DAS require Event-Timestamp
+ #radius_das_require_event_timestamp=1
++#
++# DAS require Message-Authenticator
++#radius_das_require_message_authenticator=1
+
+ ##### RADIUS authentication server configuration ##############################
+
+@@ -1071,7 +1351,10 @@
+ # and/or WPA2 (full IEEE 802.11i/RSN):
+ # bit0 = WPA
+ # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+-#wpa=1
++# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2.
++# In other words, for WPA3, wpa=2 is used the configuration (and
++# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
++#wpa=2
+
+ # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+ # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+@@ -1100,17 +1383,39 @@
+ # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+ # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+ # added to enable SHA256-based stronger algorithms.
++# WPA-PSK = WPA-Personal / WPA2-Personal
++# WPA-PSK-SHA256 = WPA2-Personal using SHA256
++# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
++# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
++# SAE = SAE (WPA3-Personal)
++# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
++# FT-PSK = FT with passphrase/PSK
++# FT-EAP = FT with EAP
++# FT-EAP-SHA384 = FT with EAP using SHA384
++# FT-SAE = FT with SAE
++# FILS-SHA256 = Fast Initial Link Setup with SHA256
++# FILS-SHA384 = Fast Initial Link Setup with SHA384
++# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
++# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
++# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
++# DPP = Device Provisioning Protocol
++# OSEN = Hotspot 2.0 online signup with encryption
+ # (dot11RSNAConfigAuthenticationSuitesTable)
+ #wpa_key_mgmt=WPA-PSK WPA-EAP
+
+ # Set of accepted cipher suites (encryption algorithms) for pairwise keys
+ # (unicast packets). This is a space separated list of algorithms:
+-# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+-# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
++# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
++# TKIP = Temporal Key Integrity Protocol
++# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
++# GCMP = Galois/counter mode protocol (GCMP-128)
++# GCMP-256 = Galois/counter mode protocol with 256-bit key
+ # Group cipher suite (encryption algorithm for broadcast and multicast frames)
+ # is automatically selected based on this configuration. If only CCMP is
+ # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+-# TKIP will be used as the group cipher.
++# TKIP will be used as the group cipher. The optional group_cipher parameter can
++# be used to override this automatic selection.
++#
+ # (dot11RSNAConfigPairwiseCiphersTable)
+ # Pairwise cipher for WPA (v1) (default: TKIP)
+ #wpa_pairwise=TKIP CCMP
+@@ -1117,14 +1422,34 @@
+ # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
+ #rsn_pairwise=CCMP
+
++# Optional override for automatic group cipher selection
++# This can be used to select a specific group cipher regardless of which
++# pairwise ciphers were enabled for WPA and RSN. It should be noted that
++# overriding the group cipher with an unexpected value can result in
++# interoperability issues and in general, this parameter is mainly used for
++# testing purposes.
++#group_cipher=CCMP
++
+ # Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+ # seconds. (dot11RSNAConfigGroupRekeyTime)
+-#wpa_group_rekey=600
++# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the
++# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the
++# group cipher.
++#wpa_group_rekey=86400
+
+ # Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
+ # (dot11RSNAConfigGroupRekeyStrict)
+ #wpa_strict_rekey=1
+
++# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is
++#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount)
++# This value should only be increased when stations are constantly
++# deauthenticated during GTK rekeying with the log message
++# "group key handshake failed...".
++# You should consider to also increase wpa_pairwise_update_count then.
++# Range 1..4294967295; default: 4
++#wpa_group_update_count=4
++
+ # Time interval for rekeying GMK (master key used internally to generate GTKs
+ # (in seconds).
+ #wpa_gmk_rekey=86400
+@@ -1133,6 +1458,36 @@
+ # PTK to mitigate some attacks against TKIP deficiencies.
+ #wpa_ptk_rekey=600
+
++# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way
++# Handshake are retried per 4-Way Handshake attempt.
++# (dot11RSNAConfigPairwiseUpdateCount)
++# Range 1..4294967295; default: 4
++#wpa_pairwise_update_count=4
++
++# Workaround for key reinstallation attacks
++#
++# This parameter can be used to disable retransmission of EAPOL-Key frames that
++# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
++# is similar to setting wpa_group_update_count=1 and
++# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
++# extended timeout on the response to avoid causing issues with stations that
++# may use aggressive power saving have very long time in replying to the
++# EAPOL-Key messages.
++#
++# This option can be used to work around key reinstallation attacks on the
++# station (supplicant) side in cases those station devices cannot be updated
++# for some reason. By removing the retransmissions the attacker cannot cause
++# key reinstallation with a delayed frame transmission. This is related to the
++# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
++# CVE-2017-13080, and CVE-2017-13081.
++#
++# This workaround might cause interoperability issues and reduced robustness of
++# key negotiation especially in environments with heavy traffic load due to the
++# number of attempts to perform the key exchange is reduced significantly. As
++# such, this workaround is disabled by default (unless overridden in build
++# configuration). To enable this, set the parameter to 1.
++#wpa_disable_eapol_key_retries=1
++
+ # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+ # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+ # authentication and key handshake before actually associating with a new AP.
+@@ -1148,12 +1503,6 @@
+ # one.
+ #rsn_preauth_interfaces=eth0
+
+-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
+-# allowed. This is only used with RSN/WPA2.
+-# 0 = disabled (default)
+-# 1 = enabled
+-#peerkey=1
+-
+ # ieee80211w: Whether management frame protection (MFP) is enabled
+ # 0 = disabled (default)
+ # 1 = optional
+@@ -1181,6 +1530,13 @@
+ # dot11AssociationSAQueryRetryTimeout, 1...4294967295
+ #assoc_sa_query_retry_timeout=201
+
++# ocv: Operating Channel Validation
++# This is a countermeasure against multi-channel man-in-the-middle attacks.
++# Enabling this automatically also enables ieee80211w, if not yet enabled.
++# 0 = disabled (default)
++# 1 = enabled
++#ocv=1
++
+ # disable_pmksa_caching: Disable PMKSA caching
+ # This parameter can be used to disable caching of PMKSA created through EAP
+ # authentication. RSN preauthentication may still end up using PMKSA caching if
+@@ -1196,20 +1552,134 @@
+ # 1 = enabled
+ #okc=1
+
++# SAE password
++# This parameter can be used to set passwords for SAE. By default, the
++# wpa_passphrase value is used if this separate parameter is not used, but
++# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
++# SAE passwords do not have such constraints. If the BSS enabled both SAE and
++# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK
++# uses the wpa_passphrase value.
++#
++# Each sae_password entry is added to a list of available passwords. This
++# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value
++# starts with the password (dot11RSNAConfigPasswordCredential). That value can
++# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
++# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In
++# addition, an optional VLAN ID specification can be used to bind the station
++# to the specified VLAN whenver the specific SAE password entry is used.
++#
++# If the peer MAC address is not included or is set to the wildcard address
++# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a
++# specific peer MAC address is included, only a station with that MAC address
++# is allowed to use the entry.
++#
++# If the password identifier (with non-zero length) is included, the entry is
++# limited to be used only with that specified identifier.
++
++# The last matching (based on peer MAC address and identifier) entry is used to
++# select which password to use. Setting sae_password to an empty string has a
++# special meaning of removing all previously added entries.
++#
++# sae_password uses the following encoding:
++#[|mac=][|vlanid=][|id=]
++# Examples:
++#sae_password=secret
++#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
++#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
++#sae_password=example secret|vlanid=3|id=pw identifier
++
+ # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
+ # This parameter defines how many open SAE instances can be in progress at the
+ # same time before the anti-clogging mechanism is taken into use.
+ #sae_anti_clogging_threshold=5
+
++# Maximum number of SAE synchronization errors (dot11RSNASAESync)
++# The offending SAe peer will be disconnected if more than this many
++# synchronization errors happen.
++#sae_sync=5
++
+ # Enabled SAE finite cyclic groups
+ # SAE implementation are required to support group 19 (ECC group defined over a
+-# 256-bit prime order field). All groups that are supported by the
+-# implementation are enabled by default. This configuration parameter can be
++# 256-bit prime order field). This configuration parameter can be used to
++# specify a set of allowed groups. If not included, only the mandatory group 19
++# is enabled.
++# The group values are listed in the IANA registry:
++# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
++# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production
++# purposes due limited security (see RFC 8247). Groups that are not as strong as
++# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases
++# since all implementations are required to support group 19.
++#sae_groups=19 20 21
++
++# Require MFP for all associations using SAE
++# This parameter can be used to enforce negotiation of MFP for all associations
++# that negotiate use of SAE. This is used in cases where SAE-capable devices are
++# known to be MFP-capable and the BSS is configured with optional MFP
++# (ieee80211w=1) for legacy support. The non-SAE stations can connect without
++# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1.
++#sae_require_mfp=0
++
++# FILS Cache Identifier (16-bit value in hexdump format)
++#fils_cache_id=0011
++
++# FILS Realm Information
++# One or more FILS realms need to be configured when FILS is enabled. This list
++# of realms is used to define which realms (used in keyName-NAI by the client)
++# can be used with FILS shared key authentication for ERP.
++#fils_realm=example.com
++#fils_realm=example.org
++
++# FILS DH Group for PFS
++# 0 = PFS disabled with FILS shared key authentication (default)
++# 1-65535 DH Group to use for FILS PFS
++#fils_dh_group=0
++
++# OWE DH groups
++# OWE implementations are required to support group 19 (NIST P-256). All groups
++# that are supported by the implementation (e.g., groups 19, 20, and 21 when
++# using OpenSSL) are enabled by default. This configuration parameter can be
+ # used to specify a limited set of allowed groups. The group values are listed
+ # in the IANA registry:
+-# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
+-#sae_groups=19 20 21 25 26
++# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
++#owe_groups=19 20 21
+
++# OWE transition mode configuration
++# Pointer to the matching open/OWE BSS
++#owe_transition_bssid=
++# SSID in same format as ssid2 described above.
++#owe_transition_ssid=
++# Alternatively, OWE transition mode BSSID/SSID can be configured with a
++# reference to a BSS operated by this hostapd process.
++#owe_transition_ifname=
++
++# DHCP server for FILS HLP
++# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
++# that include a DHCPDISCOVER message and send them to the specific DHCP
++# server for processing. hostapd will then wait for a response from that server
++# before replying with (Re)Association Response frame that encapsulates this
++# DHCP response. own_ip_addr is used as the local address for the communication
++# with the DHCP server.
++#dhcp_server=127.0.0.1
++
++# DHCP server UDP port
++# Default: 67
++#dhcp_server_port=67
++
++# DHCP relay UDP port on the local device
++# Default: 67; 0 means not to bind any specific port
++#dhcp_relay_port=67
++
++# DHCP rapid commit proxy
++# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to
++# allow the rapid commit options (two message DHCP exchange) to be used with a
++# server that supports only the four message DHCP exchange. This is disabled by
++# default (= 0) and can be enabled by setting this to 1.
++#dhcp_rapid_commit_proxy=0
++
++# Wait time for FILS HLP (dot11HLPWaitTime) in TUs
++# default: 30 TUs (= 30.72 milliseconds)
++#fils_hlp_wait_time=30
++
+ ##### IEEE 802.11r configuration ##############################################
+
+ # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
+@@ -1222,12 +1692,20 @@
+ # 1 to 48 octet identifier.
+ # This is configured with nas_identifier (see RADIUS client section above).
+
+-# Default lifetime of the PMK-RO in minutes; range 1..65535
++# Default lifetime of the PMK-R0 in seconds; range 60..4294967295
++# (default: 14 days / 1209600 seconds; 0 = disable timeout)
+ # (dot11FTR0KeyLifetime)
+-#r0_key_lifetime=10000
++#ft_r0_key_lifetime=1209600
+
++# Maximum lifetime for PMK-R1; applied only if not zero
++# PMK-R1 is removed at latest after this limit.
++# Removing any PMK-R1 for expiry can be disabled by setting this to -1.
++# (default: 0)
++#r1_max_key_lifetime=0
++
+ # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
+ # 6-octet identifier as a hex string.
++# Defaults to BSSID.
+ #r1_key_holder=000102030405
+
+ # Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535)
+@@ -1235,23 +1713,53 @@
+ #reassociation_deadline=1000
+
+ # List of R0KHs in the same Mobility Domain
+-# format: <128-bit key as hex string>
++# format: <256-bit key as hex string>
+ # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
+ # address when requesting PMK-R1 key from the R0KH that the STA used during the
+ # Initial Mobility Domain Association.
+-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
+-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
++#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
++#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
+ # And so on.. One line per R0KH.
++# Wildcard entry:
++# Upon receiving a response from R0KH, it will be added to this list, so
++# subsequent requests won't be broadcast. If R0KH does not reply, it will be
++# blacklisted.
++#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
+
+ # List of R1KHs in the same Mobility Domain
+-# format: <128-bit key as hex string>
++# format: <256-bit key as hex string>
+ # This list is used to map R1KH-ID to a destination MAC address when sending
+ # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
+ # that can request PMK-R1 keys.
+-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
+-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
++#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
++#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
+ # And so on.. One line per R1KH.
++# Wildcard entry:
++# Upon receiving a request from an R1KH not yet known, it will be added to this
++# list and thus will receive push notifications.
++#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
+
++# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
++# Special values: 0 -> do not expire
++# Warning: do not cache implies no sequence number validation with wildcards
++#rkh_pos_timeout=86400 (default = 1 day)
++
++# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
++# and number of retries.
++#rkh_pull_timeout=1000 (default = 1 second)
++#rkh_pull_retries=4 (default)
++
++# Timeout (seconds) for non replying R0KH (see wildcard entries above)
++# Special values: 0 -> do not cache
++# default: 60 seconds
++#rkh_neg_timeout=60
++
++# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
++# format was changed. That shorter key length is still supported for backwards
++# compatibility of the configuration files. If such a shorter key is used, a
++# 256-bit key is derived from it. For new deployments, configuring the 256-bit
++# key is recommended.
++
+ # Whether PMK-R1 push is enabled at R0KH
+ # 0 = do not push PMK-R1 to all configured R1KHs (default)
+ # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
+@@ -1262,6 +1770,14 @@
+ # 1 = FT-over-DS enabled (default)
+ #ft_over_ds=1
+
++# Whether to generate FT response locally for PSK networks
++# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as
++# the required information (PSK and other session data) is already locally
++# available.
++# 0 = disabled (default)
++# 1 = enabled
++#ft_psk_generate_local=0
++
+ ##### Neighbor table ##########################################################
+ # Maximum number of entries kept in AP table (either for neigbor table or for
+ # detecting Overlapping Legacy BSS Condition). The oldest entry will be
+@@ -1452,6 +1968,14 @@
+ # the configuration appropriately in this case.
+ #wps_cred_processing=0
+
++# Whether to enable SAE (WPA3-Personal transition mode) automatically for
++# WPA2-PSK credentials received using WPS.
++# 0 = only add the explicitly listed WPA2-PSK configuration (default)
++# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the
++# AP gets configured in WPA3-Personal transition mode (supports both
++# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients).
++#wps_cred_add_sae=0
++
+ # AP Settings Attributes for M7
+ # By default, hostapd generates the AP Settings Attributes for M7 based on the
+ # current configuration. It is possible to override this by providing a file
+@@ -1460,6 +1984,15 @@
+ # attribute.
+ #ap_settings=hostapd.ap_settings
+
++# Multi-AP backhaul BSS config
++# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials.
++# These are passed in WPS M8 instead of the normal (fronthaul) credentials
++# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted
++# like ssid2. The key is set like wpa_psk or wpa_passphrase.
++#multi_ap_backhaul_ssid="backhaul"
++#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
++#multi_ap_backhaul_wpa_passphrase=secret passphrase
++
+ # WPS UPnP interface
+ # If set, support for external Registrars is enabled.
+ #upnp_iface=br0
+@@ -1532,6 +2065,18 @@
+ # 1 = enabled (allow stations to use WNM-Sleep Mode)
+ #wnm_sleep_mode=1
+
++# WNM-Sleep Mode GTK/IGTK workaround
++# Normally, WNM-Sleep Mode exit with management frame protection negotiated
++# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode
++# Response frame. Some station implementations may have a vulnerability that
++# results in GTK/IGTK reinstallation based on this frame being replayed. This
++# configuration parameter can be used to disable that behavior and use EAPOL-Key
++# frames for GTK/IGTK update instead. This would likely be only used with
++# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues
++# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087
++# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1.
++#wnm_sleep_mode_no_keys=0
++
+ # BSS Transition Management
+ # 0 = disabled (default)
+ # 1 = enabled
+@@ -1619,6 +2164,15 @@
+ # (double quoted string, printf-escaped string)
+ #venue_name=P"eng:Example\nvenue"
+
++# Venue URL information
++# This parameter can be used to configure one or more Venue URL Duples to
++# provide additional information corresponding to Venue Name information.
++# Each entry has a Venue Number value separated by colon from the Venue URL
++# string. Venue Number indicates the corresponding venue_name entry (1 = 1st
++# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name)
++#venue_url=1:http://www.example.com/info-eng
++#venue_url=2:http://www.example.com/info-fin
++
+ # Network Authentication Type
+ # This parameter indicates what type of network authentication is used in the
+ # network.
+@@ -1684,6 +2238,24 @@
+ # username/password
+ #nai_realm=0,example.org,13[5:6],21[2:4][5:7]
+
++# Arbitrary ANQP-element configuration
++# Additional ANQP-elements with arbitrary values can be defined by specifying
++# their contents in raw format as a hexdump of the payload. Note that these
++# values will override ANQP-element contents that may have been specified in the
++# more higher layer configuration parameters listed above.
++# format: anqp_elem=:
++# For example, AP Geospatial Location ANQP-element with unknown location:
++#anqp_elem=265:0000
++# For example, AP Civic Location ANQP-element with unknown location:
++#anqp_elem=266:000000
++
++# GAS Address 3 behavior
++# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default
++# based on GAS request Address3
++# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3
++# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases)
++#gas_address3=0
++
+ # QoS Map Set configuration
+ #
+ # Comma delimited QoS Map Set in decimal values
+@@ -1771,7 +2343,27 @@
+ # channels 36-48):
+ #hs20_operating_class=5173
+
+-# OSU icons
++# Terms and Conditions information
++#
++# hs20_t_c_filename contains the Terms and Conditions filename that the AP
++# indicates in RADIUS Access-Request messages.
++#hs20_t_c_filename=terms-and-conditions
++#
++# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP
++# indicates in RADIUS Access-Request messages. Usually, this contains the number
++# of seconds since January 1, 1970 00:00 UTC showing the time when the file was
++# last modified.
++#hs20_t_c_timestamp=1234567
++#
++# hs20_t_c_server_url contains a template for the Terms and Conditions server
++# URL. This template is used to generate the URL for a STA that needs to
++# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this
++# parameter is used on the authentication server, not the AP.
++# Macros:
++# @1@ = MAC address of the STA (colon separated hex octets)
++#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
++
++# OSU and Operator icons
+ # :::::
+ #hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
+ #hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
+@@ -1783,12 +2375,15 @@
+ # OSU Providers
+ # One or more sets of following parameter. Each OSU provider is started by the
+ # mandatory osu_server_uri item. The other parameters add information for the
+-# last added OSU provider.
++# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
++# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
++# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
+ #
+ #osu_server_uri=https://example.com/osu/
+ #osu_friendly_name=eng:Example operator
+ #osu_friendly_name=fin:Esimerkkipalveluntarjoaja
+ #osu_nai=anonymous@example.com
++#osu_nai2=anonymous@example.com
+ #osu_method_list=1 0
+ #osu_icon=icon32
+ #osu_icon=icon64
+@@ -1797,6 +2392,50 @@
+ #
+ #osu_server_uri=...
+
++# Operator Icons
++# Operator icons are specified using references to the hs20_icon entries
++# (Name subfield). This information, if present, is advertsised in the
++# Operator Icon Metadata ANQO-element.
++#operator_icon=icon32
++#operator_icon=icon64
++
++##### Multiband Operation (MBO) ###############################################
++#
++# MBO enabled
++# 0 = disabled (default)
++# 1 = enabled
++#mbo=1
++#
++# Cellular data connection preference
++# 0 = Excluded - AP does not want STA to use the cellular data connection
++# 1 = AP prefers the STA not to use cellular data connection
++# 255 = AP prefers the STA to use cellular data connection
++#mbo_cell_data_conn_pref=1
++
++##### Optimized Connectivity Experience (OCE) #################################
++#
++# Enable OCE specific features (bitmap)
++# BIT(0) - Reserved
++# Set BIT(1) (= 2) to enable OCE in STA-CFON mode
++# Set BIT(2) (= 4) to enable OCE in AP mode
++# Default is 0 = OCE disabled
++#oce=0
++
++# RSSI-based assocition rejection
++#
++# Reject STA association if RSSI is below given threshold (in dBm)
++# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled)
++# Note: This rejection happens based on a signal strength detected while
++# receiving a single frame and as such, there is significant risk of the value
++# not being accurate and this resulting in valid stations being rejected. As
++# such, this functionality is not recommended to be used for purposes other than
++# testing.
++#rssi_reject_assoc_rssi=-75
++#
++# Association retry delay in seconds allowed by the STA if RSSI has not met the
++# threshold (range: 0..255, default=30).
++#rssi_reject_assoc_timeout=30
++
+ ##### Fast Session Transfer (FST) support #####################################
+ #
+ # The options in this section are only available when the build configuration
+@@ -1823,6 +2462,36 @@
+ # Transitioning between states).
+ #fst_llt=100
+
++##### Radio measurements / location ###########################################
++
++# The content of a LCI measurement subelement
++#lci=
++
++# The content of a location civic measurement subelement
++#civic=
++
++# Enable neighbor report via radio measurements
++#rrm_neighbor_report=1
++
++# Enable beacon report via radio measurements
++#rrm_beacon_report=1
++
++# Publish fine timing measurement (FTM) responder functionality
++# This parameter only controls publishing via Extended Capabilities element.
++# Actual functionality is managed outside hostapd.
++#ftm_responder=0
++
++# Publish fine timing measurement (FTM) initiator functionality
++# This parameter only controls publishing via Extended Capabilities element.
++# Actual functionality is managed outside hostapd.
++#ftm_initiator=0
++#
++# Stationary AP config indicates that the AP doesn't move hence location data
++# can be considered as always up to date. If configured, LCI data will be sent
++# as a radio measurement even if the request doesn't contain a max age element
++# that allows sending of such data. Default: 0.
++#stationary_ap=0
++
+ ##### TESTING OPTIONS #########################################################
+ #
+ # The options in this section are only available when the build configuration
+@@ -1844,6 +2513,10 @@
+ #
+ # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
+ #corrupt_gtk_rekey_mic_probability=0.0
++#
++# Include only ECSA IE without CSA IE where possible
++# (channel switch operating class is needed)
++#ecsa_ie_only=0
+
+ ##### Multiple BSSID support ##################################################
+ #
+@@ -1866,6 +2539,10 @@
+ # - is not the same as the MAC address of the radio
+ # - is not the same as any other explicitly specified BSSID
+ #
++# Alternatively, the 'use_driver_iface_addr' parameter can be used to request
++# hostapd to use the driver auto-generated interface address (e.g., to use the
++# exact MAC addresses allocated to the device).
++#
+ # Not all drivers support multiple BSSes. The exact mechanism for determining
+ # the driver capabilities is driver specific. With the current (i.e., a recent
+ # kernel) drivers using nl80211, this information can be checked with "iw list"
+--- contrib/wpa/hostapd/hostapd.eap_user_sqlite.orig
++++ contrib/wpa/hostapd/hostapd.eap_user_sqlite
+@@ -3,7 +3,8 @@
+ methods TEXT,
+ password TEXT,
+ remediation TEXT,
+- phase2 INTEGER
++ phase2 INTEGER,
++ t_c_timestamp INTEGER
+ );
+
+ CREATE TABLE wildcards(
+@@ -24,3 +25,18 @@
+ username TEXT,
+ note TEXT
+ );
++
++CREATE TABLE pending_tc(
++ mac_addr TEXT PRIMARY KEY,
++ identity TEXT
++);
++
++CREATE TABLE current_sessions(
++ mac_addr TEXT PRIMARY KEY,
++ identity TEXT,
++ start_time TEXT,
++ nas TEXT,
++ hs20_t_c_filtering BOOLEAN,
++ waiting_coa_ack BOOLEAN,
++ coa_ack_received BOOLEAN
++);
+--- contrib/wpa/hostapd/hostapd.wpa_psk.orig
++++ contrib/wpa/hostapd/hostapd.wpa_psk
+@@ -3,7 +3,13 @@
+ # Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that
+ # anyone can use. PSK can be configured as an ASCII passphrase of 8..63
+ # characters or as a 256-bit hex PSK (64 hex digits).
++# An optional key identifier can be added by prefixing the line with
++# keyid=
++# An optional VLAN ID can be specified by prefixing the line with
++# vlanid=.
+ 00:00:00:00:00:00 secret passphrase
+ 00:11:22:33:44:55 another passphrase
+ 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
++keyid=example_id 00:11:22:33:44:77 passphrase with keyid
++vlanid=3 00:00:00:00:00:00 passphrase with vlanid
+ 00:00:00:00:00:00 another passphrase for all STAs
+--- contrib/wpa/hostapd/hostapd_cli.c.orig
++++ contrib/wpa/hostapd/hostapd_cli.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd - command line interface for hostapd daemon
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -15,80 +15,14 @@
+ #include "utils/eloop.h"
+ #include "utils/edit.h"
+ #include "common/version.h"
++#include "common/cli.h"
+
++#ifndef CONFIG_NO_CTRL_IFACE
+
+ static const char *const hostapd_cli_version =
+ "hostapd_cli v" VERSION_STR "\n"
+-"Copyright (c) 2004-2015, Jouni Malinen and contributors";
++"Copyright (c) 2004-2019, Jouni Malinen and contributors";
+
+-
+-static const char *const hostapd_cli_license =
+-"This software may be distributed under the terms of the BSD license.\n"
+-"See README for more details.\n";
+-
+-static const char *const hostapd_cli_full_license =
+-"This software may be distributed under the terms of the BSD license.\n"
+-"\n"
+-"Redistribution and use in source and binary forms, with or without\n"
+-"modification, are permitted provided that the following conditions are\n"
+-"met:\n"
+-"\n"
+-"1. Redistributions of source code must retain the above copyright\n"
+-" notice, this list of conditions and the following disclaimer.\n"
+-"\n"
+-"2. Redistributions in binary form must reproduce the above copyright\n"
+-" notice, this list of conditions and the following disclaimer in the\n"
+-" documentation and/or other materials provided with the distribution.\n"
+-"\n"
+-"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+-" names of its contributors may be used to endorse or promote products\n"
+-" derived from this software without specific prior written permission.\n"
+-"\n"
+-"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+-"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+-"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+-"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+-"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+-"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+-"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+-"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+-"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+-"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+-"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+-"\n";
+-
+-static const char *const commands_help =
+-"Commands:\n"
+-" mib get MIB variables (dot1x, dot11, radius)\n"
+-" sta get MIB variables for one station\n"
+-" all_sta get MIB variables for all stations\n"
+-" new_sta add a new station\n"
+-" deauthenticate deauthenticate a station\n"
+-" disassociate disassociate a station\n"
+-#ifdef CONFIG_IEEE80211W
+-" sa_query send SA Query to a station\n"
+-#endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_WPS
+-" wps_pin [timeout] [addr] add WPS Enrollee PIN\n"
+-" wps_check_pin verify PIN checksum\n"
+-" wps_pbc indicate button pushed to initiate PBC\n"
+-" wps_cancel cancel the pending WPS operation\n"
+-#ifdef CONFIG_WPS_NFC
+-" wps_nfc_tag_read report read NFC tag with WPS data\n"
+-" wps_nfc_config_token build NFC configuration token\n"
+-" wps_nfc_token manager NFC password token\n"
+-#endif /* CONFIG_WPS_NFC */
+-" wps_ap_pin [params..] enable/disable AP PIN\n"
+-" wps_config configure AP\n"
+-" wps_get_status show current WPS status\n"
+-#endif /* CONFIG_WPS */
+-" get_config show current configuration\n"
+-" help show this usage help\n"
+-" interface [ifname] show interfaces/select interface\n"
+-" level change debug level\n"
+-" license show full hostapd_cli license\n"
+-" quit exit hostapd_cli\n";
+-
+ static struct wpa_ctrl *ctrl_conn;
+ static int hostapd_cli_quit = 0;
+ static int hostapd_cli_attached = 0;
+@@ -104,8 +38,17 @@
+ static const char *action_file = NULL;
+ static int ping_interval = 5;
+ static int interactive = 0;
++static int event_handler_registered = 0;
+
++static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
+
++static void print_help(FILE *stream, const char *cmd);
++static char ** list_cmd_list(void);
++static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
++static void update_stations(struct wpa_ctrl *ctrl);
++static void cli_event(const char *str);
++
++
+ static void usage(void)
+ {
+ fprintf(stderr, "%s\n", hostapd_cli_version);
+@@ -128,20 +71,49 @@
+ " -B run a daemon in the background\n"
+ " -i Interface to listen on (default: first "
+ "interface found in the\n"
+- " socket path)\n\n"
+- "%s",
+- commands_help);
++ " socket path)\n\n");
++ print_help(stderr, NULL);
+ }
+
+
++static void register_event_handler(struct wpa_ctrl *ctrl)
++{
++ if (!ctrl_conn)
++ return;
++ if (interactive) {
++ event_handler_registered =
++ !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl),
++ hostapd_cli_receive,
++ NULL, NULL);
++ }
++}
++
++
++static void unregister_event_handler(struct wpa_ctrl *ctrl)
++{
++ if (!ctrl_conn)
++ return;
++ if (interactive && event_handler_registered) {
++ eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl));
++ event_handler_registered = 0;
++ }
++}
++
++
+ static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
+ {
++#ifndef CONFIG_CTRL_IFACE_UDP
+ char *cfile;
+ int flen;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
+
+ if (ifname == NULL)
+ return NULL;
+
++#ifdef CONFIG_CTRL_IFACE_UDP
++ ctrl_conn = wpa_ctrl_open(ifname);
++ return ctrl_conn;
++#else /* CONFIG_CTRL_IFACE_UDP */
+ flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
+ cfile = malloc(flen);
+ if (cfile == NULL)
+@@ -158,6 +130,7 @@
+ ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
+ free(cfile);
+ return ctrl_conn;
++#endif /* CONFIG_CTRL_IFACE_UDP */
+ }
+
+
+@@ -166,6 +139,7 @@
+ if (ctrl_conn == NULL)
+ return;
+
++ unregister_event_handler(ctrl_conn);
+ if (hostapd_cli_attached) {
+ wpa_ctrl_detach(ctrl_conn);
+ hostapd_cli_attached = 0;
+@@ -175,13 +149,45 @@
+ }
+
+
++static int hostapd_cli_reconnect(const char *ifname)
++{
++ char *next_ctrl_ifname;
++
++ hostapd_cli_close_connection();
++
++ if (!ifname)
++ return -1;
++
++ next_ctrl_ifname = os_strdup(ifname);
++ os_free(ctrl_ifname);
++ ctrl_ifname = next_ctrl_ifname;
++ if (!ctrl_ifname)
++ return -1;
++
++ ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
++ if (!ctrl_conn)
++ return -1;
++ if (!interactive && !action_file)
++ return 0;
++ if (wpa_ctrl_attach(ctrl_conn) == 0) {
++ hostapd_cli_attached = 1;
++ register_event_handler(ctrl_conn);
++ update_stations(ctrl_conn);
++ } else {
++ printf("Warning: Failed to attach to hostapd.\n");
++ }
++ return 0;
++}
++
++
+ static void hostapd_cli_msg_cb(char *msg, size_t len)
+ {
++ cli_event(msg);
+ printf("%s\n", msg);
+ }
+
+
+-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
++static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
+ {
+ char buf[4096];
+ size_t len;
+@@ -209,12 +215,28 @@
+ }
+
+
+-static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
++static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
+ {
+ return _wpa_ctrl_command(ctrl, cmd, 1);
+ }
+
+
++static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd,
++ int min_args, int argc, char *argv[])
++{
++ char buf[4096];
++
++ if (argc < min_args) {
++ printf("Invalid %s command - at least %d argument%s required.\n",
++ cmd, min_args, min_args > 1 ? "s are" : " is");
++ return -1;
++ }
++ if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
++ return -1;
++ return wpa_ctrl_command(ctrl, buf);
++}
++
++
+ static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ return wpa_ctrl_command(ctrl, "PING");
+@@ -298,6 +320,21 @@
+ }
+
+
++static char ** hostapd_complete_stations(const char *str, int pos)
++{
++ int arg = get_cmd_arg_num(str, pos);
++ char **res = NULL;
++
++ switch (arg) {
++ case 1:
++ res = cli_txt_list_array(&stations);
++ break;
++ }
++
++ return res;
++}
++
++
+ static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+@@ -348,6 +385,22 @@
+ }
+
+
++#ifdef CONFIG_TAXONOMY
++static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char buf[64];
++
++ if (argc != 1) {
++ printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n");
++ return -1;
++ }
++ os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
++ return wpa_ctrl_command(ctrl, buf);
++}
++#endif /* CONFIG_TAXONOMY */
++
++
+ #ifdef CONFIG_IEEE80211W
+ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+@@ -667,8 +720,8 @@
+ }
+
+
+-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
+- char *addr, size_t addr_len)
++static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
++ char *addr, size_t addr_len, int print)
+ {
+ char buf[4096], *pos;
+ size_t len;
+@@ -692,7 +745,8 @@
+ buf[len] = '\0';
+ if (memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+- printf("%s", buf);
++ if (print)
++ printf("%s", buf);
+
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+@@ -708,27 +762,59 @@
+ {
+ char addr[32], cmd[64];
+
+- if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
+ return 0;
+ do {
+ snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+- } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
+
+ return -1;
+ }
+
+
++static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char addr[32], cmd[64];
++
++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
++ return 0;
++ do {
++ if (os_strcmp(addr, "") != 0)
++ printf("%s\n", addr);
++ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
++
++ return 0;
++}
++
++
+ static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+- printf("%s", commands_help);
++ print_help(stdout, argc > 0 ? argv[0] : NULL);
+ return 0;
+ }
+
+
++static char ** hostapd_cli_complete_help(const char *str, int pos)
++{
++ int arg = get_cmd_arg_num(str, pos);
++ char **res = NULL;
++
++ switch (arg) {
++ case 1:
++ res = list_cmd_list();
++ break;
++ }
++
++ return res;
++}
++
++
+ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+ {
+- printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
++ printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license);
+ return 0;
+ }
+
+@@ -839,6 +925,47 @@
+ }
+
+
++static void update_stations(struct wpa_ctrl *ctrl)
++{
++ char addr[32], cmd[64];
++
++ if (!ctrl || !interactive)
++ return;
++
++ cli_txt_list_flush(&stations);
++
++ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
++ return;
++ do {
++ if (os_strcmp(addr, "") != 0)
++ cli_txt_list_add(&stations, addr);
++ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
++ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
++}
++
++
++static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
++ struct dl_list *interfaces)
++{
++ struct dirent *dent;
++ DIR *dir;
++
++ if (!ctrl || !interfaces)
++ return;
++ dir = opendir(ctrl_iface_dir);
++ if (dir == NULL)
++ return;
++
++ while ((dent = readdir(dir))) {
++ if (strcmp(dent->d_name, ".") == 0 ||
++ strcmp(dent->d_name, "..") == 0)
++ continue;
++ cli_txt_list_add(interfaces, dent->d_name);
++ }
++ closedir(dir);
++}
++
++
+ static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
+ {
+ struct dirent *dent;
+@@ -869,22 +996,7 @@
+ hostapd_cli_list_interfaces(ctrl);
+ return 0;
+ }
+-
+- hostapd_cli_close_connection();
+- os_free(ctrl_ifname);
+- ctrl_ifname = os_strdup(argv[0]);
+- if (ctrl_ifname == NULL)
+- return -1;
+-
+- if (hostapd_cli_open_connection(ctrl_ifname)) {
+- printf("Connected to interface '%s.\n", ctrl_ifname);
+- if (wpa_ctrl_attach(ctrl_conn) == 0) {
+- hostapd_cli_attached = 1;
+- } else {
+- printf("Warning: Failed to attach to "
+- "hostapd.\n");
+- }
+- } else {
++ if (hostapd_cli_reconnect(argv[0]) != 0) {
+ printf("Could not connect to interface '%s' - re-trying\n",
+ ctrl_ifname);
+ }
+@@ -892,9 +1004,27 @@
+ }
+
+
++static char ** hostapd_complete_interface(const char *str, int pos)
++{
++ int arg = get_cmd_arg_num(str, pos);
++ char **res = NULL;
++ DEFINE_DL_LIST(interfaces);
++
++ switch (arg) {
++ case 1:
++ hostapd_cli_get_interfaces(ctrl_conn, &interfaces);
++ res = cli_txt_list_array(&interfaces);
++ cli_txt_list_flush(&interfaces);
++ break;
++ }
++
++ return res;
++}
++
++
+ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+- char cmd[256];
++ char cmd[2048];
+ int res;
+
+ if (argc != 2) {
+@@ -912,6 +1042,44 @@
+ }
+
+
++static char ** hostapd_complete_set(const char *str, int pos)
++{
++ int arg = get_cmd_arg_num(str, pos);
++ const char *fields[] = {
++#ifdef CONFIG_WPS_TESTING
++ "wps_version_number", "wps_testing_dummy_cred",
++ "wps_corrupt_pkhash",
++#endif /* CONFIG_WPS_TESTING */
++#ifdef CONFIG_INTERWORKING
++ "gas_frag_limit",
++#endif /* CONFIG_INTERWORKING */
++#ifdef CONFIG_TESTING_OPTIONS
++ "ext_mgmt_frame_handling", "ext_eapol_frame_io",
++#endif /* CONFIG_TESTING_OPTIONS */
++#ifdef CONFIG_MBO
++ "mbo_assoc_disallow",
++#endif /* CONFIG_MBO */
++ "deny_mac_file", "accept_mac_file",
++ };
++ int i, num_fields = ARRAY_SIZE(fields);
++
++ if (arg == 1) {
++ char **res;
++
++ res = os_calloc(num_fields + 1, sizeof(char *));
++ if (!res)
++ return NULL;
++ for (i = 0; i < num_fields; i++) {
++ res[i] = os_strdup(fields[i]);
++ if (!res[i])
++ return res;
++ }
++ return res;
++ }
++ return NULL;
++}
++
++
+ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ char cmd[256];
+@@ -932,6 +1100,31 @@
+ }
+
+
++static char ** hostapd_complete_get(const char *str, int pos)
++{
++ int arg = get_cmd_arg_num(str, pos);
++ const char *fields[] = {
++ "version", "tls_library",
++ };
++ int i, num_fields = ARRAY_SIZE(fields);
++
++ if (arg == 1) {
++ char **res;
++
++ res = os_calloc(num_fields + 1, sizeof(char *));
++ if (!res)
++ return NULL;
++ for (i = 0; i < num_fields; i++) {
++ res[i] = os_strdup(fields[i]);
++ if (!res[i])
++ return res;
++ }
++ return res;
++ }
++ return NULL;
++}
++
++
+ #ifdef CONFIG_FST
+ static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+@@ -1068,68 +1261,460 @@
+ }
+
+
++static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
++{
++ if (argc == 0)
++ return -1;
++ return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
++}
++
++
++static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "PMKSA");
++}
++
++
++static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
++}
++
++
++static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char cmd[2048];
++ int res;
++
++ if (argc < 3 || argc > 6) {
++ printf("Invalid set_neighbor command: needs 3-6 arguments\n");
++ return -1;
++ }
++
++ res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
++ argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
++ argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
++ if (os_snprintf_error(sizeof(cmd), res)) {
++ printf("Too long SET_NEIGHBOR command.\n");
++ return -1;
++ }
++ return wpa_ctrl_command(ctrl, cmd);
++}
++
++
++static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char cmd[400];
++ int res;
++
++ if (argc != 2) {
++ printf("Invalid remove_neighbor command: needs 2 arguments\n");
++ return -1;
++ }
++
++ res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s",
++ argv[0], argv[1]);
++ if (os_snprintf_error(sizeof(cmd), res)) {
++ printf("Too long REMOVE_NEIGHBOR command.\n");
++ return -1;
++ }
++ return wpa_ctrl_command(ctrl, cmd);
++}
++
++
++static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ char cmd[256];
++ int res;
++
++ if (argc != 1) {
++ printf("Invalid req_lci command - requires destination address\n");
++ return -1;
++ }
++
++ res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]);
++ if (os_snprintf_error(sizeof(cmd), res)) {
++ printf("Too long REQ_LCI command.\n");
++ return -1;
++ }
++ return wpa_ctrl_command(ctrl, cmd);
++}
++
++
++static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ if (argc < 4) {
++ printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n");
++ return -1;
++ }
++
++ return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
++}
++
++
++#ifdef CONFIG_DPP
++
++static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
++ int argc, char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
++}
++
++
++static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
++ int argc, char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl,
++ int argc, char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl,
++ int argc, char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
++}
++
++#endif /* CONFIG_DPP */
++
++
++static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv);
++}
++
++
++static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
++ char *argv[])
++{
++ return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK");
++}
++
++
+ struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
++ char ** (*completion)(const char *str, int pos);
++ const char *usage;
+ };
+
+ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+- { "ping", hostapd_cli_cmd_ping },
+- { "mib", hostapd_cli_cmd_mib },
+- { "relog", hostapd_cli_cmd_relog },
+- { "status", hostapd_cli_cmd_status },
+- { "sta", hostapd_cli_cmd_sta },
+- { "all_sta", hostapd_cli_cmd_all_sta },
+- { "new_sta", hostapd_cli_cmd_new_sta },
+- { "deauthenticate", hostapd_cli_cmd_deauthenticate },
+- { "disassociate", hostapd_cli_cmd_disassociate },
++ { "ping", hostapd_cli_cmd_ping, NULL,
++ "= pings hostapd" },
++ { "mib", hostapd_cli_cmd_mib, NULL,
++ "= get MIB variables (dot1x, dot11, radius)" },
++ { "relog", hostapd_cli_cmd_relog, NULL,
++ "= reload/truncate debug log output file" },
++ { "status", hostapd_cli_cmd_status, NULL,
++ "= show interface status info" },
++ { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
++ " = get MIB variables for one station" },
++ { "all_sta", hostapd_cli_cmd_all_sta, NULL,
++ "= get MIB variables for all stations" },
++ { "list_sta", hostapd_cli_cmd_list_sta, NULL,
++ "= list all stations" },
++ { "new_sta", hostapd_cli_cmd_new_sta, NULL,
++ " = add a new station" },
++ { "deauthenticate", hostapd_cli_cmd_deauthenticate,
++ hostapd_complete_stations,
++ " = deauthenticate a station" },
++ { "disassociate", hostapd_cli_cmd_disassociate,
++ hostapd_complete_stations,
++ " = disassociate a station" },
++#ifdef CONFIG_TAXONOMY
++ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
++ " = get taxonomy signature for a station" },
++#endif /* CONFIG_TAXONOMY */
+ #ifdef CONFIG_IEEE80211W
+- { "sa_query", hostapd_cli_cmd_sa_query },
++ { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
++ " = send SA Query to a station" },
+ #endif /* CONFIG_IEEE80211W */
+ #ifdef CONFIG_WPS
+- { "wps_pin", hostapd_cli_cmd_wps_pin },
+- { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
+- { "wps_pbc", hostapd_cli_cmd_wps_pbc },
+- { "wps_cancel", hostapd_cli_cmd_wps_cancel },
++ { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
++ " [timeout] [addr] = add WPS Enrollee PIN" },
++ { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
++ " = verify PIN checksum" },
++ { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL,
++ "= indicate button pushed to initiate PBC" },
++ { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL,
++ "= cancel the pending WPS operation" },
+ #ifdef CONFIG_WPS_NFC
+- { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
+- { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
+- { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+- { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
++ { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL,
++ " = report read NFC tag with WPS data" },
++ { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL,
++ " = build NFC configuration token" },
++ { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL,
++ " = manager NFC password token" },
++ { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL,
++ NULL },
+ #endif /* CONFIG_WPS_NFC */
+- { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
+- { "wps_config", hostapd_cli_cmd_wps_config },
+- { "wps_get_status", hostapd_cli_cmd_wps_get_status },
++ { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL,
++ " [params..] = enable/disable AP PIN" },
++ { "wps_config", hostapd_cli_cmd_wps_config, NULL,
++ " = configure AP" },
++ { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
++ "= show current WPS status" },
+ #endif /* CONFIG_WPS */
+- { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
+- { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+- { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
+- { "get_config", hostapd_cli_cmd_get_config },
+- { "help", hostapd_cli_cmd_help },
+- { "interface", hostapd_cli_cmd_interface },
++ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
++ "= send Disassociation Imminent notification" },
++ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
++ "= send ESS Dissassociation Imminent notification" },
++ { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL,
++ "= send BSS Transition Management Request" },
++ { "get_config", hostapd_cli_cmd_get_config, NULL,
++ "= show current configuration" },
++ { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
++ "= show this usage help" },
++ { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
++ "[ifname] = show interfaces/select interface" },
+ #ifdef CONFIG_FST
+- { "fst", hostapd_cli_cmd_fst },
++ { "fst", hostapd_cli_cmd_fst, NULL,
++ " = send FST-MANAGER control interface command" },
+ #endif /* CONFIG_FST */
+- { "level", hostapd_cli_cmd_level },
+- { "license", hostapd_cli_cmd_license },
+- { "quit", hostapd_cli_cmd_quit },
+- { "set", hostapd_cli_cmd_set },
+- { "get", hostapd_cli_cmd_get },
+- { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
+- { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
+- { "chan_switch", hostapd_cli_cmd_chan_switch },
+- { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+- { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
+- { "vendor", hostapd_cli_cmd_vendor },
+- { "enable", hostapd_cli_cmd_enable },
+- { "reload", hostapd_cli_cmd_reload },
+- { "disable", hostapd_cli_cmd_disable },
+- { "erp_flush", hostapd_cli_cmd_erp_flush },
+- { "log_level", hostapd_cli_cmd_log_level },
+- { NULL, NULL }
++ { "raw", hostapd_cli_cmd_raw, NULL,
++ " = send unprocessed command" },
++ { "level", hostapd_cli_cmd_level, NULL,
++ " = change debug level" },
++ { "license", hostapd_cli_cmd_license, NULL,
++ "= show full hostapd_cli license" },
++ { "quit", hostapd_cli_cmd_quit, NULL,
++ "= exit hostapd_cli" },
++ { "set", hostapd_cli_cmd_set, hostapd_complete_set,
++ " = set runtime variables" },
++ { "get", hostapd_cli_cmd_get, hostapd_complete_get,
++ " = get runtime info" },
++ { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
++ " = set QoS Map set element" },
++ { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
++ hostapd_complete_stations,
++ " = send QoS Map Configure frame" },
++ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
++ " [sec_channel_offset=] [center_freq1=]\n"
++ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
++ " = initiate channel switch announcement" },
++ { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
++ " \n"
++ " = send WNM-Notification Subscription Remediation Request" },
++ { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
++ " [url]\n"
++ " = send WNM-Notification imminent deauthentication indication" },
++ { "vendor", hostapd_cli_cmd_vendor, NULL,
++ " []\n"
++ " = send vendor driver command" },
++ { "enable", hostapd_cli_cmd_enable, NULL,
++ "= enable hostapd on current interface" },
++ { "reload", hostapd_cli_cmd_reload, NULL,
++ "= reload configuration for current interface" },
++ { "disable", hostapd_cli_cmd_disable, NULL,
++ "= disable hostapd on current interface" },
++ { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
++ "= drop all ERP keys"},
++ { "log_level", hostapd_cli_cmd_log_level, NULL,
++ "[level] = show/change log verbosity level" },
++ { "pmksa", hostapd_cli_cmd_pmksa, NULL,
++ " = show PMKSA cache entries" },
++ { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL,
++ " = flush PMKSA cache" },
++ { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
++ " [lci=] [civic=] [stat]\n"
++ " = add AP to neighbor database" },
++ { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
++ " = remove AP from neighbor database" },
++ { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
++ " = send LCI request to a station"},
++ { "req_range", hostapd_cli_cmd_req_range, NULL,
++ " = send FTM range request"},
++ { "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
++ " = show supported driver flags"},
++#ifdef CONFIG_DPP
++ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
++ "report a scanned DPP URI from a QR Code" },
++ { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
++ "type= [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
++ { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
++ "*| = remove DPP bootstrap information" },
++ { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
++ " = get DPP bootstrap URI" },
++ { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
++ " = show DPP bootstrap information" },
++ { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
++ "peer= [own=] = initiate DPP bootstrapping" },
++ { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
++ " = start DPP listen" },
++ { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL,
++ "= stop DPP listen" },
++ { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
++ "[curve=..] [key=..] = add DPP configurator" },
++ { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
++ NULL,
++ "*| = remove DPP configurator" },
++ { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key,
++ NULL,
++ " = Get DPP configurator's private key" },
++ { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL,
++ "conf= configurator= = generate self DPP configuration" },
++ { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
++ "add PKEX code" },
++ { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
++ "*| = remove DPP pkex information" },
++#endif /* CONFIG_DPP */
++ { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
++ "=Add/Delete/Show/Clear accept MAC ACL" },
++ { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL,
++ "=Add/Delete/Show/Clear deny MAC ACL" },
++ { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations,
++ " = poll a STA to check connectivity with a QoS null frame" },
++ { "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
++ " [req_mode=] = send a Beacon report request to a station" },
++ { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
++ "= reload wpa_psk_file only" },
++ { NULL, NULL, NULL, NULL }
+ };
+
+
++/*
++ * Prints command usage, lines are padded with the specified string.
++ */
++static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd,
++ const char *pad)
++{
++ char c;
++ size_t n;
++
++ if (cmd->usage == NULL)
++ return;
++ fprintf(stream, "%s%s ", pad, cmd->cmd);
++ for (n = 0; (c = cmd->usage[n]); n++) {
++ fprintf(stream, "%c", c);
++ if (c == '\n')
++ fprintf(stream, "%s", pad);
++ }
++ fprintf(stream, "\n");
++}
++
++
++static void print_help(FILE *stream, const char *cmd)
++{
++ int n;
++
++ fprintf(stream, "commands:\n");
++ for (n = 0; hostapd_cli_commands[n].cmd; n++) {
++ if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd))
++ print_cmd_help(stream, &hostapd_cli_commands[n], " ");
++ }
++}
++
++
+ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ const struct hostapd_cli_cmd *cmd, *match = NULL;
+@@ -1169,6 +1754,34 @@
+ }
+
+
++static void cli_event(const char *str)
++{
++ const char *start, *s;
++
++ start = os_strchr(str, '>');
++ if (start == NULL)
++ return;
++
++ start++;
++
++ if (str_starts(start, AP_STA_CONNECTED)) {
++ s = os_strchr(start, ' ');
++ if (s == NULL)
++ return;
++ cli_txt_list_add(&stations, s + 1);
++ return;
++ }
++
++ if (str_starts(start, AP_STA_DISCONNECTED)) {
++ s = os_strchr(start, ' ');
++ if (s == NULL)
++ return;
++ cli_txt_list_del_addr(&stations, s + 1);
++ return;
++ }
++}
++
++
+ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
+ int action_monitor)
+ {
+@@ -1176,7 +1789,7 @@
+ if (ctrl_conn == NULL)
+ return;
+ while (wpa_ctrl_pending(ctrl)) {
+- char buf[256];
++ char buf[4096];
+ size_t len = sizeof(buf) - 1;
+ if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
+ buf[len] = '\0';
+@@ -1183,6 +1796,7 @@
+ if (action_monitor)
+ hostapd_cli_action_process(buf, len);
+ else {
++ cli_event(buf);
+ if (in_read && first)
+ printf("\n");
+ first = 0;
+@@ -1196,35 +1810,9 @@
+ }
+
+
+-#define max_args 10
+-
+-static int tokenize_cmd(char *cmd, char *argv[])
++static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx)
+ {
+- char *pos;
+- int argc = 0;
+-
+- pos = cmd;
+- for (;;) {
+- while (*pos == ' ')
+- pos++;
+- if (*pos == '\0')
+- break;
+- argv[argc] = pos;
+- argc++;
+- if (argc == max_args)
+- break;
+- if (*pos == '"') {
+- char *pos2 = os_strrchr(pos, '"');
+- if (pos2)
+- pos = pos2 + 1;
+- }
+- while (*pos != '\0' && *pos != ' ')
+- pos++;
+- if (*pos == ' ')
+- *pos++ = '\0';
+- }
+-
+- return argc;
++ hostapd_cli_recv_pending(ctrl_conn, 0, 0);
+ }
+
+
+@@ -1234,18 +1822,8 @@
+ printf("Connection to hostapd lost - trying to reconnect\n");
+ hostapd_cli_close_connection();
+ }
+- if (!ctrl_conn) {
+- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+- if (ctrl_conn) {
+- printf("Connection to hostapd re-established\n");
+- if (wpa_ctrl_attach(ctrl_conn) == 0) {
+- hostapd_cli_attached = 1;
+- } else {
+- printf("Warning: Failed to attach to "
+- "hostapd.\n");
+- }
+- }
+- }
++ if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
++ printf("Connection to hostapd re-established\n");
+ if (ctrl_conn)
+ hostapd_cli_recv_pending(ctrl_conn, 1, 0);
+ eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+@@ -1274,18 +1852,100 @@
+ }
+
+
++static char ** list_cmd_list(void)
++{
++ char **res;
++ int i, count;
++
++ count = ARRAY_SIZE(hostapd_cli_commands);
++ res = os_calloc(count + 1, sizeof(char *));
++ if (res == NULL)
++ return NULL;
++
++ for (i = 0; hostapd_cli_commands[i].cmd; i++) {
++ res[i] = os_strdup(hostapd_cli_commands[i].cmd);
++ if (res[i] == NULL)
++ break;
++ }
++
++ return res;
++}
++
++
++static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str,
++ int pos)
++{
++ int i;
++
++ for (i = 0; hostapd_cli_commands[i].cmd; i++) {
++ if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0)
++ continue;
++ if (hostapd_cli_commands[i].completion)
++ return hostapd_cli_commands[i].completion(str, pos);
++ if (!hostapd_cli_commands[i].usage)
++ return NULL;
++ edit_clear_line();
++ printf("\r%s\n", hostapd_cli_commands[i].usage);
++ edit_redraw();
++ break;
++ }
++
++ return NULL;
++}
++
++
++static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str,
++ int pos)
++{
++ char **res;
++ const char *end;
++ char *cmd;
++
++ end = os_strchr(str, ' ');
++ if (end == NULL || str + pos < end)
++ return list_cmd_list();
++
++ cmd = os_malloc(pos + 1);
++ if (cmd == NULL)
++ return NULL;
++ os_memcpy(cmd, str, pos);
++ cmd[end - str] = '\0';
++ res = hostapd_cli_cmd_completion(cmd, str, pos);
++ os_free(cmd);
++ return res;
++}
++
++
+ static void hostapd_cli_interactive(void)
+ {
++ char *hfile = NULL;
++ char *home;
++
+ printf("\nInteractive mode\n\n");
+
++#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
++ home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
++#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
++ home = getenv("HOME");
++#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
++ if (home) {
++ const char *fname = ".hostapd_cli_history";
++ int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
++ hfile = os_malloc(hfile_len);
++ if (hfile)
++ os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
++ }
++
+ eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
+ edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
+- NULL, NULL, NULL, NULL);
++ hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
+ eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+
+ eloop_run();
+
+- edit_deinit(NULL, NULL);
++ cli_txt_list_flush(&stations);
++ edit_deinit(hfile, NULL);
++ os_free(hfile);
+ eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
+ }
+
+@@ -1388,8 +2048,7 @@
+ interactive = (argc == optind) && (action_file == NULL);
+
+ if (interactive) {
+- printf("%s\n\n%s\n\n", hostapd_cli_version,
+- hostapd_cli_license);
++ printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license);
+ }
+
+ if (eloop_init())
+@@ -1413,7 +2072,7 @@
+ closedir(dir);
+ }
+ }
+- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
++ hostapd_cli_reconnect(ctrl_ifname);
+ if (ctrl_conn) {
+ if (warning_displayed)
+ printf("Connection established.\n");
+@@ -1434,18 +2093,10 @@
+ continue;
+ }
+
+- if (interactive || action_file) {
+- if (wpa_ctrl_attach(ctrl_conn) == 0) {
+- hostapd_cli_attached = 1;
+- } else {
+- printf("Warning: Failed to attach to hostapd.\n");
+- if (action_file)
+- return -1;
+- }
+- }
+-
+- if (daemonize && os_daemonize(pid_file))
++ if (action_file && !hostapd_cli_attached)
+ return -1;
++ if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
++ return -1;
+
+ if (interactive)
+ hostapd_cli_interactive();
+@@ -1454,8 +2105,18 @@
+ else
+ wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+
++ unregister_event_handler(ctrl_conn);
+ os_free(ctrl_ifname);
+ eloop_destroy();
+ hostapd_cli_cleanup();
+ return 0;
+ }
++
++#else /* CONFIG_NO_CTRL_IFACE */
++
++int main(int argc, char *argv[])
++{
++ return -1;
++}
++
++#endif /* CONFIG_NO_CTRL_IFACE */
+--- contrib/wpa/hostapd/main.c.orig
++++ contrib/wpa/hostapd/main.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / main()
+- * Copyright (c) 2002-2015, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -18,6 +18,7 @@
+ #include "crypto/random.h"
+ #include "crypto/tls.h"
+ #include "common/version.h"
++#include "common/dpp.h"
+ #include "drivers/driver.h"
+ #include "eap_server/eap.h"
+ #include "eap_server/tncs.h"
+@@ -24,6 +25,7 @@
+ #include "ap/hostapd.h"
+ #include "ap/ap_config.h"
+ #include "ap/ap_drv_ops.h"
++#include "ap/dpp_hostapd.h"
+ #include "fst/fst.h"
+ #include "config_file.h"
+ #include "eap_register.h"
+@@ -108,6 +110,10 @@
+ module_str ? module_str : "",
+ module_str ? ": " : "", txt);
+
++#ifdef CONFIG_DEBUG_SYSLOG
++ if (wpa_debug_syslog)
++ conf_stdout = 0;
++#endif /* CONFIG_DEBUG_SYSLOG */
+ if ((conf_stdout & module) && level >= conf_stdout_level) {
+ wpa_debug_print_timestamp();
+ wpa_printf(MSG_INFO, "%s", format);
+@@ -171,7 +177,8 @@
+
+ if (global.drv_priv[i] == NULL &&
+ wpa_drivers[i]->global_init) {
+- global.drv_priv[i] = wpa_drivers[i]->global_init();
++ global.drv_priv[i] =
++ wpa_drivers[i]->global_init(iface->interfaces);
+ if (global.drv_priv[i] == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize "
+ "driver '%s'",
+@@ -216,11 +223,20 @@
+ iface->drv_flags = capa.flags;
+ iface->smps_modes = capa.smps_modes;
+ iface->probe_resp_offloads = capa.probe_resp_offloads;
++ /*
++ * Use default extended capa values from per-radio information
++ */
+ iface->extended_capa = capa.extended_capa;
+ iface->extended_capa_mask = capa.extended_capa_mask;
+ iface->extended_capa_len = capa.extended_capa_len;
+ iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
+
++ /*
++ * Override extended capa with per-interface type (AP), if
++ * available from the driver.
++ */
++ hostapd_get_ext_capa(iface);
++
+ triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
+ if (triggs && hapd->driver->set_wowlan) {
+ if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
+@@ -238,10 +254,10 @@
+ *
+ * This function is used to parse configuration file for a full interface (one
+ * or more BSSes sharing the same radio) and allocate memory for the BSS
+- * interfaces. No actiual driver operations are started.
++ * interfaces. No actual driver operations are started.
+ */
+ static struct hostapd_iface *
+-hostapd_interface_init(struct hapd_interfaces *interfaces,
++hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
+ const char *config_fname, int debug)
+ {
+ struct hostapd_iface *iface;
+@@ -251,6 +267,12 @@
+ iface = hostapd_init(interfaces, config_fname);
+ if (!iface)
+ return NULL;
++
++ if (if_name) {
++ os_strlcpy(iface->conf->bss[0]->iface, if_name,
++ sizeof(iface->conf->bss[0]->iface));
++ }
++
+ iface->interfaces = interfaces;
+
+ for (k = 0; k < debug; k++) {
+@@ -260,7 +282,8 @@
+
+ if (iface->conf->bss[0]->iface[0] == '\0' &&
+ !hostapd_drv_none(iface->bss[0])) {
+- wpa_printf(MSG_ERROR, "Interface name not specified in %s",
++ wpa_printf(MSG_ERROR,
++ "Interface name not specified in %s, nor by '-i' parameter",
+ config_fname);
+ hostapd_interface_deinit_free(iface);
+ return NULL;
+@@ -329,6 +352,7 @@
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return -1;
+ }
++ interfaces->eloop_initialized = 1;
+
+ random_init(entropy_file);
+
+@@ -356,7 +380,7 @@
+ }
+
+
+-static void hostapd_global_deinit(const char *pid_file)
++static void hostapd_global_deinit(const char *pid_file, int eloop_initialized)
+ {
+ int i;
+
+@@ -374,7 +398,8 @@
+
+ random_deinit();
+
+- eloop_destroy();
++ if (eloop_initialized)
++ eloop_destroy();
+
+ #ifndef CONFIG_NATIVE_WINDOWS
+ closelog();
+@@ -408,9 +433,16 @@
+ }
+ #endif /* EAP_SERVER_TNC */
+
+- if (daemonize && os_daemonize(pid_file)) {
+- wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+- return -1;
++ if (daemonize) {
++ if (os_daemonize(pid_file)) {
++ wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
++ return -1;
++ }
++ if (eloop_sock_requeue()) {
++ wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
++ strerror(errno));
++ return -1;
++ }
+ }
+
+ eloop_run();
+@@ -425,7 +457,7 @@
+ "hostapd v" VERSION_STR "\n"
+ "User space daemon for IEEE 802.11 AP management,\n"
+ "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
+- "Copyright (c) 2002-2015, Jouni Malinen "
++ "Copyright (c) 2002-2019, Jouni Malinen "
+ "and contributors\n");
+ }
+
+@@ -437,7 +469,8 @@
+ "\n"
+ "usage: hostapd [-hdBKtv] [-P ] [-e ] "
+ "\\\n"
+- " [-g ] [-G ] \\\n"
++ " [-g ] [-G ]\\\n"
++ " [-i ]\\\n"
+ " \n"
+ "\n"
+ "options:\n"
+@@ -453,9 +486,14 @@
+ " -f log output to debug file instead of stdout\n"
+ #endif /* CONFIG_DEBUG_FILE */
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+- " -T = record to Linux tracing in addition to logging\n"
++ " -T record to Linux tracing in addition to logging\n"
+ " (records all messages regardless of debug verbosity)\n"
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
++ " -i list of interface names to use\n"
++#ifdef CONFIG_DEBUG_SYSLOG
++ " -s log output to syslog instead of stdout\n"
++#endif /* CONFIG_DEBUG_SYSLOG */
++ " -S start all the interfaces synchronously\n"
+ " -t include timestamps in some debug messages\n"
+ " -v show hostapd version\n");
+
+@@ -466,9 +504,8 @@
+ static const char * hostapd_msg_ifname_cb(void *ctx)
+ {
+ struct hostapd_data *hapd = ctx;
+- if (hapd && hapd->iconf && hapd->iconf->bss &&
+- hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
+- return hapd->iconf->bss[0]->iface;
++ if (hapd && hapd->conf)
++ return hapd->conf->iface;
+ return NULL;
+ }
+
+@@ -476,11 +513,16 @@
+ static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
+ const char *path)
+ {
++#ifndef CONFIG_CTRL_IFACE_UDP
+ char *pos;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
++
+ os_free(interfaces->global_iface_path);
+ interfaces->global_iface_path = os_strdup(path);
+ if (interfaces->global_iface_path == NULL)
+ return -1;
++
++#ifndef CONFIG_CTRL_IFACE_UDP
+ pos = os_strrchr(interfaces->global_iface_path, '/');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "No '/' in the global control interface "
+@@ -492,6 +534,7 @@
+
+ *pos = '\0';
+ interfaces->global_iface_name = pos + 1;
++#endif /* !CONFIG_CTRL_IFACE_UDP */
+
+ return 0;
+ }
+@@ -513,6 +556,43 @@
+ }
+
+
++static int hostapd_get_interface_names(char ***if_names,
++ size_t *if_names_size,
++ char *arg)
++{
++ char *if_name, *tmp, **nnames;
++ size_t i;
++
++ if (!arg)
++ return -1;
++ if_name = strtok_r(arg, ",", &tmp);
++
++ while (if_name) {
++ nnames = os_realloc_array(*if_names, 1 + *if_names_size,
++ sizeof(char *));
++ if (!nnames)
++ goto fail;
++ *if_names = nnames;
++
++ (*if_names)[*if_names_size] = os_strdup(if_name);
++ if (!(*if_names)[*if_names_size])
++ goto fail;
++ (*if_names_size)++;
++ if_name = strtok_r(NULL, ",", &tmp);
++ }
++
++ return 0;
++
++fail:
++ for (i = 0; i < *if_names_size; i++)
++ os_free((*if_names)[i]);
++ os_free(*if_names);
++ *if_names = NULL;
++ *if_names_size = 0;
++ return -1;
++}
++
++
+ #ifdef CONFIG_WPS
+ static int gen_uuid(const char *txt_addr)
+ {
+@@ -570,6 +650,9 @@
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ int enable_trace_dbg = 0;
+ #endif /* CONFIG_DEBUG_LINUX_TRACING */
++ int start_ifaces_in_sync = 0;
++ char **if_names = NULL;
++ size_t if_names_size = 0;
+
+ if (os_program_init())
+ return -1;
+@@ -584,10 +667,18 @@
+ interfaces.global_iface_path = NULL;
+ interfaces.global_iface_name = NULL;
+ interfaces.global_ctrl_sock = -1;
+- interfaces.global_ctrl_dst = NULL;
++ dl_list_init(&interfaces.global_ctrl_dst);
++#ifdef CONFIG_ETH_P_OUI
++ dl_list_init(&interfaces.eth_p_oui);
++#endif /* CONFIG_ETH_P_OUI */
++#ifdef CONFIG_DPP
++ interfaces.dpp = dpp_global_init();
++ if (!interfaces.dpp)
++ return -1;
++#endif /* CONFIG_DPP */
+
+ for (;;) {
+- c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
++ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -644,10 +735,23 @@
+ bss_config = tmp_bss;
+ bss_config[num_bss_configs++] = optarg;
+ break;
++#ifdef CONFIG_DEBUG_SYSLOG
++ case 's':
++ wpa_debug_syslog = 1;
++ break;
++#endif /* CONFIG_DEBUG_SYSLOG */
++ case 'S':
++ start_ifaces_in_sync = 1;
++ break;
+ #ifdef CONFIG_WPS
+ case 'u':
+ return gen_uuid(optarg);
+ #endif /* CONFIG_WPS */
++ case 'i':
++ if (hostapd_get_interface_names(&if_names,
++ &if_names_size, optarg))
++ goto out;
++ break;
+ default:
+ usage();
+ break;
+@@ -664,6 +768,10 @@
+ wpa_debug_open_file(log_file);
+ else
+ wpa_debug_setup_stdout();
++#ifdef CONFIG_DEBUG_SYSLOG
++ if (wpa_debug_syslog)
++ wpa_debug_open_syslog();
++#endif /* CONFIG_DEBUG_SYSLOG */
+ #ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (enable_trace_dbg) {
+ int tret = wpa_debug_open_linux_tracing();
+@@ -705,7 +813,13 @@
+
+ /* Allocate and parse configuration for full interface files */
+ for (i = 0; i < interfaces.count; i++) {
++ char *if_name = NULL;
++
++ if (i < if_names_size)
++ if_name = if_names[i];
++
+ interfaces.iface[i] = hostapd_interface_init(&interfaces,
++ if_name,
+ argv[optind + i],
+ debug);
+ if (!interfaces.iface[i]) {
+@@ -712,6 +826,8 @@
+ wpa_printf(MSG_ERROR, "Failed to initialize interface");
+ goto out;
+ }
++ if (start_ifaces_in_sync)
++ interfaces.iface[i]->need_to_start_in_sync = 1;
+ }
+
+ /* Allocate and parse configuration for per-BSS files */
+@@ -787,10 +903,16 @@
+ }
+ os_free(interfaces.iface);
+
+- eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
+- hostapd_global_deinit(pid_file);
++#ifdef CONFIG_DPP
++ dpp_global_deinit(interfaces.dpp);
++#endif /* CONFIG_DPP */
++
++ if (interfaces.eloop_initialized)
++ eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
++ hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
+ os_free(pid_file);
+
++ wpa_debug_close_syslog();
+ if (log_file)
+ wpa_debug_close_file();
+ wpa_debug_close_linux_tracing();
+@@ -797,6 +919,10 @@
+
+ os_free(bss_config);
+
++ for (i = 0; i < if_names_size; i++)
++ os_free(if_names[i]);
++ os_free(if_names);
++
+ fst_global_deinit();
+
+ os_program_deinit();
+--- contrib/wpa/hostapd/wps-ap-nfc.py.orig
++++ contrib/wpa/hostapd/wps-ap-nfc.py
+@@ -26,7 +26,7 @@
+ success_file = None
+
+ def summary(txt):
+- print txt
++ print(txt)
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
+@@ -42,12 +42,12 @@
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+- except OSError, error:
+- print "Could not find hostapd: ", error
++ except OSError as error:
++ print("Could not find hostapd: ", error)
+ return None
+
+ if len(ifaces) < 1:
+- print "No hostapd control interface found"
++ print("No hostapd control interface found")
+ return None
+
+ for ctrl in ifaces:
+@@ -54,7 +54,7 @@
+ try:
+ wpas = wpaspy.Ctrl(ctrl)
+ return wpas
+- except Exception, e:
++ except Exception as e:
+ pass
+ return None
+
+@@ -133,15 +133,15 @@
+ def process_request(self, request):
+ summary("HandoverServer - request received")
+ try:
+- print "Parsed handover request: " + request.pretty()
+- except Exception, e:
+- print e
+- print str(request).encode("hex")
++ print("Parsed handover request: " + request.pretty())
++ except Exception as e:
++ print(e)
++ print(str(request).encode("hex"))
+
+ sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+ for carrier in request.carriers:
+- print "Remote carrier type: " + carrier.type
++ print("Remote carrier type: " + carrier.type)
+ if carrier.type == "application/vnd.wfa.wsc":
+ summary("WPS carrier type match - add WPS carrier record")
+ data = wpas_get_handover_sel()
+@@ -148,8 +148,8 @@
+ if data is None:
+ summary("Could not get handover select carrier record from hostapd")
+ continue
+- print "Handover select carrier record from hostapd:"
+- print data.encode("hex")
++ print("Handover select carrier record from hostapd:")
++ print(data.encode("hex"))
+ if "OK" in wpas_report_handover(carrier.record, data):
+ success_report("Handover reported successfully")
+ else:
+@@ -158,12 +158,12 @@
+ message = nfc.ndef.Message(data);
+ sel.add_carrier(message[0], "active", message[1:])
+
+- print "Handover select:"
++ print("Handover select:")
+ try:
+- print sel.pretty()
+- except Exception, e:
+- print e
+- print str(sel).encode("hex")
++ print(sel.pretty())
++ except Exception as e:
++ print(e)
++ print(str(sel).encode("hex"))
+
+ summary("Sending handover select")
+ self.success = True
+@@ -174,7 +174,7 @@
+ success = False
+ if len(tag.ndef.message):
+ for record in tag.ndef.message:
+- print "record type " + record.type
++ print("record type " + record.type)
+ if record.type == "application/vnd.wfa.wsc":
+ summary("WPS tag - send to hostapd")
+ success = wpas_tag_read(tag.ndef.message)
+@@ -193,7 +193,7 @@
+ global write_data
+ tag.ndef.message = str(write_data)
+ success_report("Tag write succeeded")
+- print "Done - remove tag"
++ print("Done - remove tag")
+ global only_one
+ if only_one:
+ global continue_loop
+@@ -211,7 +211,7 @@
+ summary("Could not get WPS config token from hostapd")
+ return
+
+- print "Touch an NFC tag"
++ print("Touch an NFC tag")
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+@@ -224,7 +224,7 @@
+ summary("Could not get WPS password token from hostapd")
+ return
+
+- print "Touch an NFC tag"
++ print("Touch an NFC tag")
+ clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+@@ -233,11 +233,11 @@
+ summary("Tag connected: " + str(tag))
+
+ if tag.ndef:
+- print "NDEF tag: " + tag.type
++ print("NDEF tag: " + tag.type)
+ try:
+- print tag.ndef.message.pretty()
+- except Exception, e:
+- print e
++ print(tag.ndef.message.pretty())
++ except Exception as e:
++ print(e)
+ success = wps_tag_read(tag)
+ if only_one and success:
+ global continue_loop
+@@ -250,13 +250,13 @@
+
+
+ def llcp_startup(clf, llc):
+- print "Start LLCP server"
++ print("Start LLCP server")
+ global srv
+ srv = HandoverServer(llc)
+ return llc
+
+ def llcp_connected(llc):
+- print "P2P LLCP connected"
++ print("P2P LLCP connected")
+ global wait_connection
+ wait_connection = False
+ global srv
+@@ -304,7 +304,7 @@
+
+ try:
+ if not clf.open("usb"):
+- print "Could not open connection with an NFC device"
++ print("Could not open connection with an NFC device")
+ raise SystemExit
+
+ if args.command == "write-config":
+@@ -317,7 +317,7 @@
+
+ global continue_loop
+ while continue_loop:
+- print "Waiting for a tag or peer to be touched"
++ print("Waiting for a tag or peer to be touched")
+ wait_connection = True
+ try:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected},
+@@ -324,8 +324,8 @@
+ llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected}):
+ break
+- except Exception, e:
+- print "clf.connect failed"
++ except Exception as e:
++ print("clf.connect failed")
+
+ global srv
+ if only_one and srv and srv.success:
+--- contrib/wpa/hs20/client/Android.mk.orig
++++ contrib/wpa/hs20/client/Android.mk
+@@ -4,7 +4,6 @@
+ INCLUDES += $(LOCAL_PATH)/../../src/utils
+ INCLUDES += $(LOCAL_PATH)/../../src/common
+ INCLUDES += $(LOCAL_PATH)/../../src
+-INCLUDES += external/openssl/include
+ INCLUDES += external/libxml2/include
+ INCLUDES += external/curl/include
+ INCLUDES += external/webkit/Source/WebKit/gtk
+@@ -55,6 +54,7 @@
+ OBJS += ../../src/crypto/md5-internal.c
+ OBJS += ../../src/crypto/sha1-internal.c
+ OBJS += ../../src/crypto/sha256-internal.c
++OBJS += ../../src/crypto/tls_openssl_ocsp.c
+
+ L_CFLAGS += -DEAP_TLS_OPENSSL
+
+--- contrib/wpa/hs20/client/Makefile.orig
++++ contrib/wpa/hs20/client/Makefile
+@@ -8,12 +8,17 @@
+ LDO=$(CC)
+ endif
+
++ifeq ($(QUIET), 1)
+ Q=@
++E=true
++else
++Q=@
+ E=echo
+ ifeq ($(V), 1)
+ Q=
+ E=true
+ endif
++endif
+
+ ifndef CFLAGS
+ CFLAGS = -MMD -O2 -Wall -g
+@@ -76,6 +81,7 @@
+ endif
+
+ CFLAGS += -DEAP_TLS_OPENSSL
++OBJS += ../../src/crypto/tls_openssl_ocsp.o
+ LIBS += -lssl -lcrypto
+
+ hs20-osu-client: $(OBJS)
+--- contrib/wpa/hs20/client/est.c.orig
++++ contrib/wpa/hs20/client/est.c
+@@ -16,6 +16,10 @@
+ #include
+ #include
+ #include
++#include
++#ifdef OPENSSL_IS_BORINGSSL
++#include
++#endif /* OPENSSL_IS_BORINGSSL */
+
+ #include "common.h"
+ #include "utils/base64.h"
+@@ -27,12 +31,28 @@
+ static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
+ size_t len, char *pem_file, char *der_file)
+ {
++#ifdef OPENSSL_IS_BORINGSSL
++ CBS pkcs7_cbs;
++#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = pkcs7;
++#endif /* OPENSSL_IS_BORINGSSL */
+ STACK_OF(X509) *certs;
+ int i, num, ret = -1;
+ BIO *out = NULL;
+
++#ifdef OPENSSL_IS_BORINGSSL
++ certs = sk_X509_new_null();
++ if (!certs)
++ goto fail;
++ CBS_init(&pkcs7_cbs, pkcs7, len);
++ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
++ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ write_result(ctx, "Could not parse PKCS#7 object from EST");
++ goto fail;
++ }
++#else /* OPENSSL_IS_BORINGSSL */
+ p7 = d2i_PKCS7(NULL, &p, len);
+ if (p7 == NULL) {
+ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+@@ -52,6 +72,7 @@
+ certs = NULL;
+ break;
+ }
++#endif /* OPENSSL_IS_BORINGSSL */
+
+ if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+ wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
+@@ -84,7 +105,12 @@
+ ret = 0;
+
+ fail:
++#ifdef OPENSSL_IS_BORINGSSL
++ if (certs)
++ sk_X509_pop_free(certs, X509_free);
++#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7_free(p7);
++#endif /* OPENSSL_IS_BORINGSSL */
+ if (out)
+ BIO_free_all(out);
+
+@@ -194,6 +220,10 @@
+ } d;
+ } AttrOrOID;
+
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
++DEFINE_STACK_OF(AttrOrOID)
++#endif
++
+ typedef struct {
+ int type;
+ STACK_OF(AttrOrOID) *attrs;
+@@ -310,9 +340,34 @@
+ if (!csrattrs || ! csrattrs->attrs)
+ return;
+
++#ifdef OPENSSL_IS_BORINGSSL
++ num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
++ csrattrs->attrs));
++ for (i = 0; i < num; i++) {
++ AttrOrOID *ao = sk_value(
++ CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *,
++ csrattrs->attrs), i);
++ switch (ao->type) {
++ case 0:
++ add_csrattrs_oid(ctx, ao->d.oid, exts);
++ break;
++ case 1:
++ add_csrattrs_attr(ctx, ao->d.attribute, exts);
++ break;
++ }
++ }
++#else /* OPENSSL_IS_BORINGSSL */
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
++ num = sk_AttrOrOID_num(csrattrs->attrs);
++#else
+ num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
++#endif
+ for (i = 0; i < num; i++) {
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
++ AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
++#else
+ AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
++#endif
+ switch (ao->type) {
+ case 0:
+ add_csrattrs_oid(ctx, ao->d.oid, exts);
+@@ -322,6 +377,7 @@
+ break;
+ }
+ }
++#endif /* OPENSSL_IS_BORINGSSL */
+ }
+
+
+@@ -340,6 +396,7 @@
+ STACK_OF(X509_EXTENSION) *exts = NULL;
+ X509_EXTENSION *ex;
+ BIO *out;
++ CONF *ctmp = NULL;
+
+ wpa_printf(MSG_INFO, "Generate RSA private key");
+ write_summary(ctx, "Generate RSA private key");
+@@ -421,20 +478,20 @@
+ if (!exts)
+ goto fail;
+
+- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
+- "CA:FALSE");
++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
++ "CA:FALSE");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
+- "nonRepudiation,digitalSignature,keyEncipherment");
++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
++ "nonRepudiation,digitalSignature,keyEncipherment");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+
+- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
+- "1.3.6.1.4.1.40808.1.1.2");
++ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
++ "1.3.6.1.4.1.40808.1.1.2");
+ if (ex == NULL ||
+ !sk_X509_EXTENSION_push(exts, ex))
+ goto fail;
+@@ -454,7 +511,9 @@
+ char *txt;
+ size_t rlen;
+
++#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
+ X509_REQ_print(out, req);
++#endif
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+@@ -473,7 +532,9 @@
+ FILE *f = fopen(csr_pem, "w");
+ if (f == NULL)
+ goto fail;
++#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
+ X509_REQ_print_fp(f, req);
++#endif
+ if (!PEM_write_X509_REQ(f, req)) {
+ fclose(f);
+ goto fail;
+@@ -618,7 +679,6 @@
+ char *buf, *resp, *req, *req2;
+ size_t buflen, resp_len, len, pkcs7_len;
+ unsigned char *pkcs7;
+- FILE *f;
+ char client_cert_buf[200];
+ char client_key_buf[200];
+ const char *client_cert = NULL, *client_key = NULL;
+@@ -673,11 +733,6 @@
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
+- f = fopen("Cert/est-resp.raw", "w");
+- if (f) {
+- fwrite(resp, resp_len, 1, f);
+- fclose(f);
+- }
+
+ pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ if (pkcs7 == NULL) {
+--- contrib/wpa/hs20/client/oma_dm_client.c.orig
++++ contrib/wpa/hs20/client/oma_dm_client.c
+@@ -111,6 +111,12 @@
+ xml_node_t *syncml, *synchdr;
+ xml_namespace_t *ns;
+
++ if (!ctx->devid) {
++ wpa_printf(MSG_ERROR,
++ "DevId from devinfo.xml is not available - cannot use OMA DM");
++ return NULL;
++ }
++
+ syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
+ "SyncML");
+
+--- contrib/wpa/hs20/client/osu_client.c.orig
++++ contrib/wpa/hs20/client/osu_client.c
+@@ -105,6 +105,35 @@
+ }
+
+
++static int android_update_permission(const char *path, mode_t mode)
++{
++#ifdef ANDROID
++ /* we need to change file/folder permission for Android */
++
++ if (!path) {
++ wpa_printf(MSG_ERROR, "file path null");
++ return -1;
++ }
++
++ /* Allow processes running with Group ID as AID_WIFI,
++ * to read files from SP, SP/, Cert and osu-info directories */
++ if (lchown(path, -1, AID_WIFI)) {
++ wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s",
++ strerror(errno));
++ return -1;
++ }
++
++ if (chmod(path, mode) < 0) {
++ wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
++ strerror(errno));
++ return -1;
++ }
++#endif /* ANDROID */
++
++ return 0;
++}
++
++
+ int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
+ {
+ xml_node_t *node;
+@@ -169,6 +198,8 @@
+ }
+
+ mkdir("Cert", S_IRWXU);
++ android_update_permission("Cert", S_IRWXU | S_IRWXG);
++
+ if (est_load_cacerts(ctx, url) < 0 ||
+ est_build_csr(ctx, url) < 0 ||
+ est_simple_enroll(ctx, url, user, pw) < 0)
+@@ -262,7 +293,6 @@
+
+ unlink("Cert/est-req.b64");
+ unlink("Cert/est-req.pem");
+- unlink("Cert/est-resp.raw");
+ rmdir("Cert");
+
+ return 0;
+@@ -406,7 +436,7 @@
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+- return -1;
++ return -2;
+ }
+
+ ret = download_cert(ctx, node, ca_fname);
+@@ -433,7 +463,7 @@
+ if (node == NULL) {
+ wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+ xml_node_free(ctx->xml, pps);
+- return -1;
++ return -2;
+ }
+
+ aaa = xml_node_first_child(ctx->xml, node);
+@@ -455,7 +485,7 @@
+ {
+ char *dir, *pos;
+ char fname[300];
+- int ret;
++ int ret, ret1;
+
+ dir = os_strdup(pps_fname);
+ if (dir == NULL)
+@@ -470,9 +500,13 @@
+ snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
+ ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
+ snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
+- cmd_dl_polupd_ca(ctx, pps_fname, fname);
++ ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
++ if (ret == 0 && ret1 == -1)
++ ret = -1;
+ snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
+- cmd_dl_aaa_ca(ctx, pps_fname, fname);
++ ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
++ if (ret == 0 && ret1 == -1)
++ ret = -1;
+
+ os_free(dir);
+
+@@ -578,20 +612,8 @@
+ }
+ }
+
+-#ifdef ANDROID
+- /* Allow processes running with Group ID as AID_WIFI,
+- * to read files from SP/ directory */
+- if (chown(fname, -1, AID_WIFI)) {
+- wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
+- strerror(errno));
+- /* Try to continue anyway */
+- }
+- if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
+- wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
+- strerror(errno));
+- /* Try to continue anyway */
+- }
+-#endif /* ANDROID */
++ android_update_permission("SP", S_IRWXU | S_IRWXG);
++ android_update_permission(fname, S_IRWXU | S_IRWXG);
+
+ snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+@@ -1213,8 +1235,7 @@
+ homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+ } else {
+- if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
+- homeoi) < 0)
++ if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+ }
+
+@@ -1289,7 +1310,9 @@
+ if (str == NULL)
+ return;
+ wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
+- /* TODO: Set to wpa_supplicant */
++ if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums",
++ str) < 0)
++ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums");
+ xml_node_get_text_free(ctx->xml, str);
+ }
+
+@@ -1442,10 +1465,92 @@
+ }
+
+
++static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx,
++ int id, xml_node_t *node)
++{
++ char *str = xml_node_get_text(ctx->xml, node);
++ int type;
++ const char *eap_method = NULL;
++
++ if (!str)
++ return;
++ wpa_printf(MSG_INFO,
++ "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str);
++ type = atoi(str);
++ switch (type) {
++ case EAP_TYPE_TLS:
++ eap_method = "TLS";
++ break;
++ case EAP_TYPE_TTLS:
++ eap_method = "TTLS";
++ break;
++ case EAP_TYPE_PEAP:
++ eap_method = "PEAP";
++ break;
++ case EAP_TYPE_PWD:
++ eap_method = "PWD";
++ break;
++ }
++ xml_node_get_text_free(ctx->xml, str);
++ if (!eap_method) {
++ wpa_printf(MSG_INFO, "Unknown EAPType value");
++ return;
++ }
++
++ if (set_cred(ctx->ifname, id, "eap", eap_method) < 0)
++ wpa_printf(MSG_INFO, "Failed to set cred eap");
++}
++
++
++static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx,
++ int id, xml_node_t *node)
++{
++ char *str = xml_node_get_text(ctx->xml, node);
++ const char *phase2 = NULL;
++
++ if (!str)
++ return;
++ wpa_printf(MSG_INFO,
++ "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s",
++ str);
++ if (os_strcmp(str, "PAP") == 0)
++ phase2 = "auth=PAP";
++ else if (os_strcmp(str, "CHAP") == 0)
++ phase2 = "auth=CHAP";
++ else if (os_strcmp(str, "MS-CHAP") == 0)
++ phase2 = "auth=MSCHAP";
++ else if (os_strcmp(str, "MS-CHAP-V2") == 0)
++ phase2 = "auth=MSCHAPV2";
++ xml_node_get_text_free(ctx->xml, str);
++ if (!phase2) {
++ wpa_printf(MSG_INFO, "Unknown InnerMethod value");
++ return;
++ }
++
++ if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0)
++ wpa_printf(MSG_INFO, "Failed to set cred phase2");
++}
++
++
+ static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
+ xml_node_t *node)
+ {
+- wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
++ xml_node_t *child;
++ const char *name;
++
++ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod");
++
++ xml_node_for_each_child(ctx->xml, child, node) {
++ xml_node_for_each_check(ctx->xml, child);
++ name = xml_node_get_localname(ctx->xml, child);
++ if (os_strcasecmp(name, "EAPType") == 0)
++ set_pps_cred_eap_method_eap_type(ctx, id, child);
++ else if (os_strcasecmp(name, "InnerMethod") == 0)
++ set_pps_cred_eap_method_inner_method(ctx, id, child);
++ else
++ wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'",
++ name);
++ }
+ }
+
+
+@@ -1884,7 +1989,9 @@
+ char url[256];
+ unsigned int methods;
+ char osu_ssid[33];
++ char osu_ssid2[33];
+ char osu_nai[256];
++ char osu_nai2[256];
+ struct osu_lang_text friendly_name[MAX_OSU_VALS];
+ size_t friendly_name_count;
+ struct osu_lang_text serv_desc[MAX_OSU_VALS];
+@@ -1943,6 +2050,12 @@
+ continue;
+ }
+
++ if (strncmp(buf, "osu_ssid2=", 10) == 0) {
++ snprintf(last->osu_ssid2, sizeof(last->osu_ssid2),
++ "%s", buf + 10);
++ continue;
++ }
++
+ if (os_strncmp(buf, "osu_nai=", 8) == 0) {
+ os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+ "%s", buf + 8);
+@@ -1949,6 +2062,12 @@
+ continue;
+ }
+
++ if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
++ os_snprintf(last->osu_nai2, sizeof(last->osu_nai2),
++ "%s", buf + 9);
++ continue;
++ }
++
+ if (strncmp(buf, "friendly_name=", 14) == 0) {
+ struct osu_lang_text *txt;
+ if (last->friendly_name_count == MAX_OSU_VALS)
+@@ -2024,9 +2143,9 @@
+
+
+ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
+- const char *ssid, const char *url,
++ const char *ssid, const char *ssid2, const char *url,
+ unsigned int methods, int no_prod_assoc,
+- const char *osu_nai)
++ const char *osu_nai, const char *osu_nai2)
+ {
+ int id;
+ const char *ifname = ctx->ifname;
+@@ -2034,11 +2153,32 @@
+ struct wpa_ctrl *mon;
+ int res;
+
++ if (ssid2 && ssid2[0] == '\0')
++ ssid2 = NULL;
++
++ if (ctx->osu_ssid) {
++ if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "Enforced OSU SSID matches ANQP info");
++ ssid2 = NULL;
++ } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "Enforced OSU SSID matches RSN[OSEN] info");
++ ssid = ssid2;
++ } else {
++ wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
++ write_summary(ctx, "Enforced OSU SSID did not match");
++ return -1;
++ }
++ }
++
+ id = add_network(ifname);
+ if (id < 0)
+ return -1;
+ if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
+ return -1;
++ if (ssid2)
++ osu_nai = osu_nai2;
+ if (osu_nai && os_strlen(osu_nai) > 0) {
+ char dir[255], fname[300];
+ if (getcwd(dir, sizeof(dir)) == NULL)
+@@ -2045,15 +2185,22 @@
+ return -1;
+ os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+
++ if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
++ return -1;
++
+ if (set_network(ifname, id, "proto", "OSEN") < 0 ||
+ set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
+ set_network(ifname, id, "pairwise", "CCMP") < 0 ||
+- set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
++ set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 ||
+ set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
+ set_network(ifname, id, "ocsp", "2") < 0 ||
+ set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
+ set_network_quoted(ifname, id, "ca_cert", fname) < 0)
+ return -1;
++ } else if (ssid2) {
++ wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
++ write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
++ return -1;
+ } else {
+ if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
+ return -1;
+@@ -2134,7 +2281,7 @@
+ char fname[255];
+ FILE *f;
+ struct osu_data *osu = NULL, *last = NULL;
+- size_t osu_count, i, j;
++ size_t osu_count = 0, i, j;
+ int ret;
+
+ write_summary(ctx, "OSU provider selection");
+@@ -2229,8 +2376,12 @@
+ fprintf(f, "
BSSID: %s \n"
+ "SSID: %s \n",
+ last->bssid, last->osu_ssid);
+- if (last->osu_nai)
++ if (last->osu_ssid2[0])
++ fprintf(f, "SSID2: %s \n", last->osu_ssid2);
++ if (last->osu_nai[0])
+ fprintf(f, "NAI: %s \n", last->osu_nai);
++ if (last->osu_nai2[0])
++ fprintf(f, "NAI2: %s \n", last->osu_nai2);
+ fprintf(f, "URL: %s \n"
+ "methods:%s%s \n"
+ " \n",
+@@ -2257,6 +2408,8 @@
+ ret = 0;
+ wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
+ wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
++ if (last->osu_ssid2[0])
++ wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
+ wpa_printf(MSG_INFO, "URL: %s", last->url);
+ write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
+ ret, last->bssid, last->osu_ssid, last->url);
+@@ -2311,10 +2464,13 @@
+ "No supported OSU provisioning method");
+ ret = -1;
+ }
+- } else if (connect)
++ } else if (connect) {
+ ret = osu_connect(ctx, last->bssid, last->osu_ssid,
++ last->osu_ssid2,
+ last->url, last->methods,
+- no_prod_assoc, last->osu_nai);
++ no_prod_assoc, last->osu_nai,
++ last->osu_nai2);
++ }
+ } else
+ ret = -1;
+
+@@ -2339,12 +2495,15 @@
+ return -1;
+
+ snprintf(fname, sizeof(fname), "%s/osu-info", dir);
+- if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
++ if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
++ errno != EEXIST) {
+ wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+ fname, strerror(errno));
+ return -1;
+ }
+
++ android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
++
+ snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
+ if (wpa_command(ifname, buf) < 0) {
+ wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
+@@ -2559,7 +2718,7 @@
+ if (!pps_fname) {
+ char buf[256];
+ wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+- if (os_strncmp(address, "fqdn=", 5) == 0) {
++ if (address && os_strncmp(address, "fqdn=", 5) == 0) {
+ wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+ os_snprintf(buf, sizeof(buf), "%s", address + 5);
+ address = NULL;
+@@ -2909,26 +3068,19 @@
+ return -1;
+
+ devinfo = node_from_file(ctx->xml, "devinfo.xml");
+- if (!devinfo) {
+- wpa_printf(MSG_ERROR, "devinfo.xml not found");
+- return -1;
+- }
++ if (devinfo) {
++ devid = get_node(ctx->xml, devinfo, "DevId");
++ if (devid) {
++ char *tmp = xml_node_get_text(ctx->xml, devid);
+
+- devid = get_node(ctx->xml, devinfo, "DevId");
+- if (devid) {
+- char *tmp = xml_node_get_text(ctx->xml, devid);
+- if (tmp) {
+- ctx->devid = os_strdup(tmp);
+- xml_node_get_text_free(ctx->xml, tmp);
++ if (tmp) {
++ ctx->devid = os_strdup(tmp);
++ xml_node_get_text_free(ctx->xml, tmp);
++ }
+ }
++ xml_node_free(ctx->xml, devinfo);
+ }
+- xml_node_free(ctx->xml, devinfo);
+
+- if (ctx->devid == NULL) {
+- wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
+- return -1;
+- }
+-
+ ctx->http = http_init_ctx(ctx, ctx->xml);
+ if (ctx->http == NULL) {
+ xml_node_deinit_ctx(ctx->xml);
+@@ -3029,7 +3181,7 @@
+ return -1;
+
+ for (;;) {
+- c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:");
++ c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
+ if (c < 0)
+ break;
+ switch (c) {
+@@ -3046,6 +3198,9 @@
+ case 'N':
+ no_prod_assoc = 1;
+ break;
++ case 'o':
++ ctx.osu_ssid = optarg;
++ break;
+ case 'O':
+ friendly_name = optarg;
+ break;
+@@ -3122,20 +3277,12 @@
+ usage();
+ exit(0);
+ }
+- if (argc - optind < 2)
+- wpa_printf(MSG_ERROR, "Server URL missing from command line");
+- else
+- ret = cmd_sub_rem(&ctx, argv[optind + 1],
+- argc > optind + 2 ?
+- argv[optind + 2] : NULL,
+- argc > optind + 3 ?
+- argv[optind + 3] : NULL);
++ ret = cmd_sub_rem(&ctx, argv[optind + 1],
++ argc > optind + 2 ? argv[optind + 2] : NULL,
++ argc > optind + 3 ? argv[optind + 3] : NULL);
+ } else if (strcmp(argv[optind], "pol_upd") == 0) {
+- if (argc - optind < 2) {
+- usage();
+- exit(0);
+- }
+- ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
++ ret = cmd_pol_upd(&ctx,
++ argc > optind + 1 ? argv[optind + 1] : NULL,
+ argc > optind + 2 ? argv[optind + 2] : NULL,
+ argc > optind + 3 ? argv[optind + 3] : NULL);
+ } else if (strcmp(argv[optind], "prov") == 0) {
+--- contrib/wpa/hs20/client/osu_client.h.orig
++++ contrib/wpa/hs20/client/osu_client.h
+@@ -47,6 +47,7 @@
+ int client_cert_present;
+ char **server_dnsname;
+ size_t server_dnsname_count;
++ const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
+ #define WORKAROUND_OCSP_OPTIONAL 0x00000001
+ unsigned long int workarounds;
+ };
+--- contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch.orig
++++ contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch
+@@ -1,397 +0,0 @@
+-This patch adds support for TLS SessionTicket extension (RFC 5077) for
+-the parts used by EAP-FAST (RFC 4851).
+-
+-This is based on the patch from Alexey Kobozev
+-(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+-
+-OpenSSL 0.9.8za does not enable TLS extension support by default, so it
+-will need to be enabled by adding enable-tlsext to config script
+-command line.
+-
+-
+-diff -upr openssl-0.9.8za.orig/ssl/s3_clnt.c openssl-0.9.8za/ssl/s3_clnt.c
+---- openssl-0.9.8za.orig/ssl/s3_clnt.c 2014-06-05 11:09:26.000000000 +0300
+-+++ openssl-0.9.8za/ssl/s3_clnt.c 2014-06-05 20:37:09.221387312 +0300
+-@@ -767,6 +767,22 @@ int ssl3_get_server_hello(SSL *s)
+- goto f_err;
+- }
+-
+-+#ifndef OPENSSL_NO_TLSEXT
+-+ /* check if we want to resume the session based on external pre-shared secret */
+-+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
+-+ {
+-+ SSL_CIPHER *pref_cipher=NULL;
+-+ s->session->master_key_length=sizeof(s->session->master_key);
+-+ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
+-+ NULL, &pref_cipher, s->tls_session_secret_cb_arg))
+-+ {
+-+ s->session->cipher=pref_cipher ?
+-+ pref_cipher : ssl_get_cipher_by_char(s,p+j);
+-+ s->s3->flags |= SSL3_FLAGS_CCS_OK;
+-+ }
+-+ }
+-+#endif /* OPENSSL_NO_TLSEXT */
+-+
+- if (j != 0 && j == s->session->session_id_length
+- && memcmp(p,s->session->session_id,j) == 0)
+- {
+-@@ -2745,11 +2760,8 @@ int ssl3_check_finished(SSL *s)
+- {
+- int ok;
+- long n;
+-- /* If we have no ticket or session ID is non-zero length (a match of
+-- * a non-zero session length would never reach here) it cannot be a
+-- * resumed session.
+-- */
+-- if (!s->session->tlsext_tick || s->session->session_id_length)
+-+ /* If we have no ticket it cannot be a resumed session. */
+-+ if (!s->session->tlsext_tick)
+- return 1;
+- /* this function is called when we really expect a Certificate
+- * message, so permit appropriate message length */
+-diff -upr openssl-0.9.8za.orig/ssl/s3_srvr.c openssl-0.9.8za/ssl/s3_srvr.c
+---- openssl-0.9.8za.orig/ssl/s3_srvr.c 2014-06-05 11:09:26.000000000 +0300
+-+++ openssl-0.9.8za/ssl/s3_srvr.c 2014-06-05 20:37:09.225387312 +0300
+-@@ -1011,6 +1011,59 @@ int ssl3_get_client_hello(SSL *s)
+- SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+- goto err;
+- }
+-+
+-+ /* Check if we want to use external pre-shared secret for this
+-+ * handshake for not reused session only. We need to generate
+-+ * server_random before calling tls_session_secret_cb in order to allow
+-+ * SessionTicket processing to use it in key derivation. */
+-+ {
+-+ unsigned long Time;
+-+ unsigned char *pos;
+-+ Time=(unsigned long)time(NULL); /* Time */
+-+ pos=s->s3->server_random;
+-+ l2n(Time,pos);
+-+ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
+-+ {
+-+ al=SSL_AD_INTERNAL_ERROR;
+-+ goto f_err;
+-+ }
+-+ }
+-+
+-+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
+-+ {
+-+ SSL_CIPHER *pref_cipher=NULL;
+-+
+-+ s->session->master_key_length=sizeof(s->session->master_key);
+-+ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
+-+ ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
+-+ {
+-+ s->hit=1;
+-+ s->session->ciphers=ciphers;
+-+ s->session->verify_result=X509_V_OK;
+-+
+-+ ciphers=NULL;
+-+
+-+ /* check if some cipher was preferred by call back */
+-+ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
+-+ if (pref_cipher == NULL)
+-+ {
+-+ al=SSL_AD_HANDSHAKE_FAILURE;
+-+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
+-+ goto f_err;
+-+ }
+-+
+-+ s->session->cipher=pref_cipher;
+-+
+-+ if (s->cipher_list)
+-+ sk_SSL_CIPHER_free(s->cipher_list);
+-+
+-+ if (s->cipher_list_by_id)
+-+ sk_SSL_CIPHER_free(s->cipher_list_by_id);
+-+
+-+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
+-+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
+-+ }
+-+ }
+- #endif
+- /* Worst case, we will use the NULL compression, but if we have other
+- * options, we will now look for them. We have i-1 compression
+-@@ -1161,16 +1214,22 @@ int ssl3_send_server_hello(SSL *s)
+- unsigned char *buf;
+- unsigned char *p,*d;
+- int i,sl;
+-- unsigned long l,Time;
+-+ unsigned long l;
+-+#ifdef OPENSSL_NO_TLSEXT
+-+ unsigned long Time;
+-+#endif
+-
+- if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+- {
+- buf=(unsigned char *)s->init_buf->data;
+-+#ifdef OPENSSL_NO_TLSEXT
+- p=s->s3->server_random;
+-+ /* Generate server_random if it was not needed previously */
+- Time=(unsigned long)time(NULL); /* Time */
+- l2n(Time,p);
+- if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+- return -1;
+-+#endif
+- /* Do the message type and length last */
+- d=p= &(buf[4]);
+-
+-diff -upr openssl-0.9.8za.orig/ssl/ssl_err.c openssl-0.9.8za/ssl/ssl_err.c
+---- openssl-0.9.8za.orig/ssl/ssl_err.c 2014-06-05 11:09:08.000000000 +0300
+-+++ openssl-0.9.8za/ssl/ssl_err.c 2014-06-05 20:37:09.225387312 +0300
+-@@ -265,6 +265,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+- {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
+- {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"},
+- {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"},
+-+{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+- {0,NULL}
+- };
+-
+-diff -upr openssl-0.9.8za.orig/ssl/ssl.h openssl-0.9.8za/ssl/ssl.h
+---- openssl-0.9.8za.orig/ssl/ssl.h 2014-06-05 11:09:08.000000000 +0300
+-+++ openssl-0.9.8za/ssl/ssl.h 2014-06-05 20:37:09.229387312 +0300
+-@@ -344,6 +344,7 @@ extern "C" {
+- * 'struct ssl_st *' function parameters used to prototype callbacks
+- * in SSL_CTX. */
+- typedef struct ssl_st *ssl_crock_st;
+-+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+-
+- /* used to hold info on the particular ciphers used */
+- typedef struct ssl_cipher_st
+-@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st
+-
+- DECLARE_STACK_OF(SSL_CIPHER)
+-
+-+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
+-+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
+-+
+- /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+- typedef struct ssl_method_st
+- {
+-@@ -1053,6 +1057,18 @@ struct ssl_st
+-
+- /* RFC4507 session ticket expected to be received or sent */
+- int tlsext_ticket_expected;
+-+
+-+ /* TLS Session Ticket extension override */
+-+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
+-+
+-+ /* TLS Session Ticket extension callback */
+-+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
+-+ void *tls_session_ticket_ext_cb_arg;
+-+
+-+ /* TLS pre-shared secret session resumption */
+-+ tls_session_secret_cb_fn tls_session_secret_cb;
+-+ void *tls_session_secret_cb_arg;
+-+
+- SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+- #define session_ctx initial_ctx
+- #else
+-@@ -1668,6 +1684,15 @@ void *SSL_COMP_get_compression_methods(v
+- int SSL_COMP_add_compression_method(int id,void *cm);
+- #endif
+-
+-+/* TLS extensions functions */
+-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
+-+
+-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+-+ void *arg);
+-+
+-+/* Pre-shared secret session resumption functions */
+-+int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
+-+
+- /* BEGIN ERROR CODES */
+- /* The following lines are auto generated by the script mkerr.pl. Any changes
+- * made after this point may be overwritten when the script is next run.
+-@@ -1872,6 +1897,7 @@ void ERR_load_SSL_strings(void);
+- #define SSL_F_TLS1_ENC 210
+- #define SSL_F_TLS1_SETUP_KEY_BLOCK 211
+- #define SSL_F_WRITE_PENDING 212
+-+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213
+-
+- /* Reason codes. */
+- #define SSL_R_APP_DATA_IN_HANDSHAKE 100
+-diff -upr openssl-0.9.8za.orig/ssl/ssl_sess.c openssl-0.9.8za/ssl/ssl_sess.c
+---- openssl-0.9.8za.orig/ssl/ssl_sess.c 2014-06-05 11:09:08.000000000 +0300
+-+++ openssl-0.9.8za/ssl/ssl_sess.c 2014-06-05 20:37:09.229387312 +0300
+-@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+- return(s->session_timeout);
+- }
+-
+-+#ifndef OPENSSL_NO_TLSEXT
+-+int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
+-+ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
+-+ {
+-+ if (s == NULL) return(0);
+-+ s->tls_session_secret_cb = tls_session_secret_cb;
+-+ s->tls_session_secret_cb_arg = arg;
+-+ return(1);
+-+ }
+-+
+-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+-+ void *arg)
+-+ {
+-+ if (s == NULL) return(0);
+-+ s->tls_session_ticket_ext_cb = cb;
+-+ s->tls_session_ticket_ext_cb_arg = arg;
+-+ return(1);
+-+ }
+-+
+-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
+-+ {
+-+ if (s->version >= TLS1_VERSION)
+-+ {
+-+ if (s->tlsext_session_ticket)
+-+ {
+-+ OPENSSL_free(s->tlsext_session_ticket);
+-+ s->tlsext_session_ticket = NULL;
+-+ }
+-+
+-+ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
+-+ if (!s->tlsext_session_ticket)
+-+ {
+-+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
+-+ return 0;
+-+ }
+-+
+-+ if (ext_data)
+-+ {
+-+ s->tlsext_session_ticket->length = ext_len;
+-+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
+-+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
+-+ }
+-+ else
+-+ {
+-+ s->tlsext_session_ticket->length = 0;
+-+ s->tlsext_session_ticket->data = NULL;
+-+ }
+-+
+-+ return 1;
+-+ }
+-+
+-+ return 0;
+-+ }
+-+#endif /* OPENSSL_NO_TLSEXT */
+-+
+- typedef struct timeout_param_st
+- {
+- SSL_CTX *ctx;
+-diff -upr openssl-0.9.8za.orig/ssl/t1_lib.c openssl-0.9.8za/ssl/t1_lib.c
+---- openssl-0.9.8za.orig/ssl/t1_lib.c 2014-06-05 11:09:08.000000000 +0300
+-+++ openssl-0.9.8za/ssl/t1_lib.c 2014-06-05 20:37:09.229387312 +0300
+-@@ -106,6 +106,12 @@ int tls1_new(SSL *s)
+-
+- void tls1_free(SSL *s)
+- {
+-+#ifndef OPENSSL_NO_TLSEXT
+-+ if (s->tlsext_session_ticket)
+-+ {
+-+ OPENSSL_free(s->tlsext_session_ticket);
+-+ }
+-+#endif
+- ssl3_free(s);
+- }
+-
+-@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex
+- int ticklen;
+- if (!s->new_session && s->session && s->session->tlsext_tick)
+- ticklen = s->session->tlsext_ticklen;
+-+ else if (s->session && s->tlsext_session_ticket &&
+-+ s->tlsext_session_ticket->data)
+-+ {
+-+ ticklen = s->tlsext_session_ticket->length;
+-+ s->session->tlsext_tick = OPENSSL_malloc(ticklen);
+-+ if (!s->session->tlsext_tick)
+-+ return NULL;
+-+ memcpy(s->session->tlsext_tick,
+-+ s->tlsext_session_ticket->data,
+-+ ticklen);
+-+ s->session->tlsext_ticklen = ticklen;
+-+ }
+- else
+- ticklen = 0;
+-+ if (ticklen == 0 && s->tlsext_session_ticket &&
+-+ s->tlsext_session_ticket->data == NULL)
+-+ goto skip_ext;
+- /* Check for enough room 2 for extension type, 2 for len
+- * rest for ticket
+- */
+-@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex
+- ret += ticklen;
+- }
+- }
+-+ skip_ext:
+-
+- if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
+- s->version != DTLS1_VERSION)
+-@@ -574,6 +596,15 @@ int ssl_parse_clienthello_tlsext(SSL *s,
+- return 0;
+- renegotiate_seen = 1;
+- }
+-+ else if (type == TLSEXT_TYPE_session_ticket)
+-+ {
+-+ if (s->tls_session_ticket_ext_cb &&
+-+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
+-+ {
+-+ *al = TLS1_AD_INTERNAL_ERROR;
+-+ return 0;
+-+ }
+-+ }
+- else if (type == TLSEXT_TYPE_status_request &&
+- s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb)
+- {
+-@@ -751,6 +782,12 @@ int ssl_parse_serverhello_tlsext(SSL *s,
+- }
+- else if (type == TLSEXT_TYPE_session_ticket)
+- {
+-+ if (s->tls_session_ticket_ext_cb &&
+-+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
+-+ {
+-+ *al = TLS1_AD_INTERNAL_ERROR;
+-+ return 0;
+-+ }
+- if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+- || (size > 0))
+- {
+-@@ -1043,6 +1080,15 @@ int tls1_process_ticket(SSL *s, unsigned
+- s->tlsext_ticket_expected = 1;
+- return 0; /* Cache miss */
+- }
+-+ if (s->tls_session_secret_cb)
+-+ {
+-+ /* Indicate cache miss here and instead of
+-+ * generating the session from ticket now,
+-+ * trigger abbreviated handshake based on
+-+ * external mechanism to calculate the master
+-+ * secret later. */
+-+ return 0;
+-+ }
+- return tls_decrypt_ticket(s, p, size, session_id, len,
+- ret);
+- }
+-diff -upr openssl-0.9.8za.orig/ssl/tls1.h openssl-0.9.8za/ssl/tls1.h
+---- openssl-0.9.8za.orig/ssl/tls1.h 2014-06-05 11:09:08.000000000 +0300
+-+++ openssl-0.9.8za/ssl/tls1.h 2014-06-05 20:37:09.229387312 +0300
+-@@ -415,6 +415,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+- #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/
+- #endif
+-
+-+/* TLS extension struct */
+-+struct tls_session_ticket_ext_st
+-+ {
+-+ unsigned short length;
+-+ void *data;
+-+ };
+-+
+- #ifdef __cplusplus
+- }
+- #endif
+-diff -upr openssl-0.9.8za.orig/util/ssleay.num openssl-0.9.8za/util/ssleay.num
+---- openssl-0.9.8za.orig/util/ssleay.num 2014-06-05 12:38:45.000000000 +0300
+-+++ openssl-0.9.8za/util/ssleay.num 2014-06-05 20:37:09.229387312 +0300
+-@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
+- SSL_get_servername 291 EXIST::FUNCTION:TLSEXT
+- SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT
+- SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE
+-+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT
+-+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT
+--- contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch.orig
++++ contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch
+@@ -1,398 +0,0 @@
+-This patch adds support for TLS SessionTicket extension (RFC 5077) for
+-the parts used by EAP-FAST (RFC 4851).
+-
+-This is based on the patch from Alexey Kobozev
+-(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+-
+-OpenSSL 0.9.8zf does not enable TLS extension support by default, so it
+-will need to be enabled by adding enable-tlsext to config script
+-command line.
+-
+-
+-diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c
+---- openssl-0.9.8zf.orig/ssl/s3_clnt.c 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/s3_clnt.c 2015-03-24 16:19:14.043911769 +0200
+-@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s)
+- goto f_err;
+- }
+-
+-+#ifndef OPENSSL_NO_TLSEXT
+-+ /* check if we want to resume the session based on external pre-shared secret */
+-+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
+-+ SSL_CIPHER *pref_cipher = NULL;
+-+
+-+ s->session->master_key_length = sizeof(s->session->master_key);
+-+ if (s->tls_session_secret_cb(s, s->session->master_key,
+-+ &s->session->master_key_length,
+-+ NULL, &pref_cipher,
+-+ s->tls_session_secret_cb_arg)) {
+-+ s->session->cipher = pref_cipher ?
+-+ pref_cipher : ssl_get_cipher_by_char(s, p + j);
+-+ s->s3->flags |= SSL3_FLAGS_CCS_OK;
+-+ }
+-+ }
+-+#endif /* OPENSSL_NO_TLSEXT */
+-+
+- if (j != 0 && j == s->session->session_id_length
+- && memcmp(p, s->session->session_id, j) == 0) {
+- if (s->sid_ctx_length != s->session->sid_ctx_length
+-@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s)
+- {
+- int ok;
+- long n;
+-- /*
+-- * If we have no ticket or session ID is non-zero length (a match of a
+-- * non-zero session length would never reach here) it cannot be a resumed
+-- * session.
+-- */
+-- if (!s->session->tlsext_tick || s->session->session_id_length)
+-+ /* If we have no ticket it cannot be a resumed session. */
+-+ if (!s->session->tlsext_tick)
+- return 1;
+- /*
+- * this function is called when we really expect a Certificate message,
+-diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c
+---- openssl-0.9.8zf.orig/ssl/s3_srvr.c 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/s3_srvr.c 2015-03-24 16:23:34.567909681 +0200
+-@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s)
+- SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
+- goto err;
+- }
+-+
+-+ /* Check if we want to use external pre-shared secret for this
+-+ * handshake for not reused session only. We need to generate
+-+ * server_random before calling tls_session_secret_cb in order to allow
+-+ * SessionTicket processing to use it in key derivation. */
+-+ {
+-+ unsigned long Time;
+-+ unsigned char *pos;
+-+ Time = (unsigned long)time(NULL); /* Time */
+-+ pos = s->s3->server_random;
+-+ l2n(Time, pos);
+-+ if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) {
+-+ al = SSL_AD_INTERNAL_ERROR;
+-+ goto f_err;
+-+ }
+-+ }
+-+
+-+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
+-+ SSL_CIPHER *pref_cipher = NULL;
+-+
+-+ s->session->master_key_length = sizeof(s->session->master_key);
+-+ if (s->tls_session_secret_cb(s, s->session->master_key,
+-+ &s->session->master_key_length,
+-+ ciphers, &pref_cipher,
+-+ s->tls_session_secret_cb_arg)) {
+-+ s->hit = 1;
+-+ s->session->ciphers = ciphers;
+-+ s->session->verify_result = X509_V_OK;
+-+
+-+ ciphers = NULL;
+-+
+-+ /* check if some cipher was preferred by call back */
+-+ pref_cipher = pref_cipher ? pref_cipher :
+-+ ssl3_choose_cipher(s, s->session->ciphers,
+-+ SSL_get_ciphers(s));
+-+ if (pref_cipher == NULL) {
+-+ al = SSL_AD_HANDSHAKE_FAILURE;
+-+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER);
+-+ goto f_err;
+-+ }
+-+
+-+ s->session->cipher = pref_cipher;
+-+
+-+ if (s->cipher_list)
+-+ sk_SSL_CIPHER_free(s->cipher_list);
+-+
+-+ if (s->cipher_list_by_id)
+-+ sk_SSL_CIPHER_free(s->cipher_list_by_id);
+-+
+-+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
+-+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
+-+ }
+-+ }
+- #endif
+- /*
+- * Worst case, we will use the NULL compression, but if we have other
+-@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s)
+- unsigned char *buf;
+- unsigned char *p, *d;
+- int i, sl;
+-- unsigned long l, Time;
+-+ unsigned long l;
+-+#ifdef OPENSSL_NO_TLSEXT
+-+ unsigned long Time;
+-+#endif
+-
+- if (s->state == SSL3_ST_SW_SRVR_HELLO_A) {
+- buf = (unsigned char *)s->init_buf->data;
+-+#ifdef OPENSSL_NO_TLSEXT
+- p = s->s3->server_random;
+-+ /* Generate server_random if it was not needed previously */
+- Time = (unsigned long)time(NULL); /* Time */
+- l2n(Time, p);
+- if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0)
+- return -1;
+-+#endif
+- /* Do the message type and length last */
+- d = p = &(buf[4]);
+-
+-diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c
+---- openssl-0.9.8zf.orig/ssl/ssl_err.c 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/ssl_err.c 2015-03-24 16:35:58.627903717 +0200
+-@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[]
+- {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
+- {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"},
+- {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"},
+-+ {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+- {0, NULL}
+- };
+-
+-diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h
+---- openssl-0.9.8zf.orig/ssl/ssl.h 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200
+-@@ -349,6 +349,7 @@ extern "C" {
+- * function parameters used to prototype callbacks in SSL_CTX.
+- */
+- typedef struct ssl_st *ssl_crock_st;
+-+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+-
+- /* used to hold info on the particular ciphers used */
+- typedef struct ssl_cipher_st {
+-@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st {
+-
+- DECLARE_STACK_OF(SSL_CIPHER)
+-
+-+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data,
+-+ int len, void *arg);
+-+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len,
+-+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+-+ SSL_CIPHER **cipher, void *arg);
+-+
+- /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+- typedef struct ssl_method_st {
+- int version;
+-@@ -1116,6 +1123,18 @@ struct ssl_st {
+- int tlsext_ocsp_resplen;
+- /* RFC4507 session ticket expected to be received or sent */
+- int tlsext_ticket_expected;
+-+
+-+ /* TLS Session Ticket extension override */
+-+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
+-+
+-+ /* TLS Session Ticket extension callback */
+-+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
+-+ void *tls_session_ticket_ext_cb_arg;
+-+
+-+ /* TLS pre-shared secret session resumption */
+-+ tls_session_secret_cb_fn tls_session_secret_cb;
+-+ void *tls_session_secret_cb_arg;
+-+
+- SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */
+- # define session_ctx initial_ctx
+- # else
+-@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v
+- int SSL_COMP_add_compression_method(int id, void *cm);
+- # endif
+-
+-+/* TLS extensions functions */
+-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
+-+
+-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+-+ void *arg);
+-+
+-+/* Pre-shared secret session resumption functions */
+-+int SSL_set_session_secret_cb(SSL *s,
+-+ tls_session_secret_cb_fn tls_session_secret_cb,
+-+ void *arg);
+-+
+- /* BEGIN ERROR CODES */
+- /*
+- * The following lines are auto generated by the script mkerr.pl. Any changes
+-@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void);
+- # define SSL_F_TLS1_ENC 210
+- # define SSL_F_TLS1_SETUP_KEY_BLOCK 211
+- # define SSL_F_WRITE_PENDING 212
+-+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213
+-
+- /* Reason codes. */
+- # define SSL_R_APP_DATA_IN_HANDSHAKE 100
+-diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c
+---- openssl-0.9.8zf.orig/ssl/ssl_sess.c 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/ssl_sess.c 2015-03-24 16:28:04.819907515 +0200
+-@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+- return (s->session_timeout);
+- }
+-
+-+#ifndef OPENSSL_NO_TLSEXT
+-+int SSL_set_session_secret_cb(
+-+ SSL *s,
+-+ int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
+-+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+-+ SSL_CIPHER **cipher, void *arg), void *arg)
+-+{
+-+ if (s == NULL)
+-+ return 0;
+-+ s->tls_session_secret_cb = tls_session_secret_cb;
+-+ s->tls_session_secret_cb_arg = arg;
+-+ return 1;
+-+}
+-+
+-+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+-+ void *arg)
+-+{
+-+ if (s == NULL)
+-+ return 0;
+-+ s->tls_session_ticket_ext_cb = cb;
+-+ s->tls_session_ticket_ext_cb_arg = arg;
+-+ return 1;
+-+}
+-+
+-+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
+-+{
+-+ if (s->version >= TLS1_VERSION) {
+-+ if (s->tlsext_session_ticket) {
+-+ OPENSSL_free(s->tlsext_session_ticket);
+-+ s->tlsext_session_ticket = NULL;
+-+ }
+-+
+-+ s->tlsext_session_ticket = OPENSSL_malloc(
+-+ sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
+-+ if (!s->tlsext_session_ticket) {
+-+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
+-+ return 0;
+-+ }
+-+
+-+ if (ext_data) {
+-+ s->tlsext_session_ticket->length = ext_len;
+-+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
+-+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
+-+ } else {
+-+ s->tlsext_session_ticket->length = 0;
+-+ s->tlsext_session_ticket->data = NULL;
+-+ }
+-+
+-+ return 1;
+-+ }
+-+
+-+ return 0;
+-+}
+-+#endif /* OPENSSL_NO_TLSEXT */
+-+
+- typedef struct timeout_param_st {
+- SSL_CTX *ctx;
+- long time;
+-diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c
+---- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/t1_lib.c 2015-03-24 16:32:46.923905254 +0200
+-@@ -108,6 +108,11 @@ int tls1_new(SSL *s)
+-
+- void tls1_free(SSL *s)
+- {
+-+#ifndef OPENSSL_NO_TLSEXT
+-+ if (s->tlsext_session_ticket) {
+-+ OPENSSL_free(s->tlsext_session_ticket);
+-+ }
+-+#endif
+- ssl3_free(s);
+- }
+-
+-@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex
+- int ticklen;
+- if (!s->new_session && s->session && s->session->tlsext_tick)
+- ticklen = s->session->tlsext_ticklen;
+-- else
+-+ else if (s->session && s->tlsext_session_ticket &&
+-+ s->tlsext_session_ticket->data) {
+-+ ticklen = s->tlsext_session_ticket->length;
+-+ s->session->tlsext_tick = OPENSSL_malloc(ticklen);
+-+ if (!s->session->tlsext_tick)
+-+ return NULL;
+-+ memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data,
+-+ ticklen);
+-+ s->session->tlsext_ticklen = ticklen;
+-+ } else
+- ticklen = 0;
+-+ if (ticklen == 0 && s->tlsext_session_ticket &&
+-+ s->tlsext_session_ticket->data == NULL)
+-+ goto skip_ext;
+- /*
+- * Check for enough room 2 for extension type, 2 for len rest for
+- * ticket
+-@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex
+- ret += ticklen;
+- }
+- }
+-+skip_ext:
+-
+- if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
+- s->version != DTLS1_VERSION) {
+-@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s,
+- if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al))
+- return 0;
+- renegotiate_seen = 1;
+-+ } else if (type == TLSEXT_TYPE_session_ticket) {
+-+ if (s->tls_session_ticket_ext_cb &&
+-+ !s->tls_session_ticket_ext_cb(s, data, size,
+-+ s->tls_session_ticket_ext_cb_arg))
+-+ {
+-+ *al = TLS1_AD_INTERNAL_ERROR;
+-+ return 0;
+-+ }
+- } else if (type == TLSEXT_TYPE_status_request &&
+- s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) {
+-
+-@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s,
+- }
+- tlsext_servername = 1;
+- } else if (type == TLSEXT_TYPE_session_ticket) {
+-+ if (s->tls_session_ticket_ext_cb &&
+-+ !s->tls_session_ticket_ext_cb(
+-+ s, data, size,
+-+ s->tls_session_ticket_ext_cb_arg)) {
+-+ *al = TLS1_AD_INTERNAL_ERROR;
+-+ return 0;
+-+ }
+- if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+- || (size > 0)) {
+- *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+-@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned
+- s->tlsext_ticket_expected = 1;
+- return 0; /* Cache miss */
+- }
+-+ if (s->tls_session_secret_cb) {
+-+ /* Indicate cache miss here and instead of
+-+ * generating the session from ticket now,
+-+ * trigger abbreviated handshake based on
+-+ * external mechanism to calculate the master
+-+ * secret later. */
+-+ return 0;
+-+ }
+- return tls_decrypt_ticket(s, p, size, session_id, len, ret);
+- }
+- p += size;
+-diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h
+---- openssl-0.9.8zf.orig/ssl/tls1.h 2015-03-19 15:46:46.000000000 +0200
+-+++ openssl-0.9.8zf/ssl/tls1.h 2015-03-24 16:33:31.855904894 +0200
+-@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+- # define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"
+- # endif
+-
+-+/* TLS extension struct */
+-+struct tls_session_ticket_ext_st {
+-+ unsigned short length;
+-+ void *data;
+-+};
+-+
+- #ifdef __cplusplus
+- }
+- #endif
+-diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num
+---- openssl-0.9.8zf.orig/util/ssleay.num 2015-03-19 15:47:15.000000000 +0200
+-+++ openssl-0.9.8zf/util/ssleay.num 2015-03-24 16:33:51.127904739 +0200
+-@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
+- SSL_get_servername 291 EXIST::FUNCTION:TLSEXT
+- SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT
+- SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE
+-+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT
+-+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT
+--- contrib/wpa/src/ap/accounting.c.orig
++++ contrib/wpa/src/ap/accounting.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / RADIUS Accounting
+- * Copyright (c) 2002-2009, 2012, Jouni Malinen
++ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -41,6 +41,7 @@
+ size_t len;
+ int i;
+ struct wpabuf *b;
++ struct os_time now;
+
+ msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+ radius_client_get_id(hapd->radius));
+@@ -49,27 +50,6 @@
+ return NULL;
+ }
+
+- if (sta) {
+- radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+-
+- if ((hapd->conf->wpa & 2) &&
+- !hapd->conf->disable_pmksa_caching &&
+- sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) {
+- os_snprintf(buf, sizeof(buf), "%08X+%08X",
+- sta->eapol_sm->acct_multi_session_id_hi,
+- sta->eapol_sm->acct_multi_session_id_lo);
+- if (!radius_msg_add_attr(
+- msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+- (u8 *) buf, os_strlen(buf))) {
+- wpa_printf(MSG_INFO,
+- "Could not add Acct-Multi-Session-Id");
+- goto fail;
+- }
+- }
+- } else {
+- radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
+- }
+-
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+ status_type)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
+@@ -76,17 +56,18 @@
+ goto fail;
+ }
+
+- if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+- RADIUS_ATTR_ACCT_AUTHENTIC) &&
+- !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+- hapd->conf->ieee802_1x ?
+- RADIUS_ACCT_AUTHENTIC_RADIUS :
+- RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+- wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
+- goto fail;
+- }
++ if (sta) {
++ if (!hostapd_config_get_radius_attr(
++ hapd->conf->radius_acct_req_attr,
++ RADIUS_ATTR_ACCT_AUTHENTIC) &&
++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
++ hapd->conf->ieee802_1x ?
++ RADIUS_ACCT_AUTHENTIC_RADIUS :
++ RADIUS_ACCT_AUTHENTIC_LOCAL)) {
++ wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
++ goto fail;
++ }
+
+- if (sta) {
+ /* Use 802.1X identity if available */
+ val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+@@ -147,8 +128,34 @@
+ wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+ goto fail;
+ }
++
++ if (sta->ipaddr &&
++ !radius_msg_add_attr_int32(msg,
++ RADIUS_ATTR_FRAMED_IP_ADDRESS,
++ be_to_host32(sta->ipaddr))) {
++ wpa_printf(MSG_ERROR,
++ "Could not add Framed-IP-Address");
++ goto fail;
++ }
+ }
+
++ os_get_time(&now);
++ if (now.sec > 1000000000 &&
++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
++ now.sec)) {
++ wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
++ goto fail;
++ }
++
++ /*
++ * Add Acct-Delay-Time with zero value for the first transmission. This
++ * will be updated within radius_client.c when retransmitting the frame.
++ */
++ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
++ wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
++ goto fail;
++ }
++
+ return msg;
+
+ fail:
+@@ -164,19 +171,25 @@
+ if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+ return -1;
+
+- if (sta->last_rx_bytes > data->rx_bytes)
+- sta->acct_input_gigawords++;
+- if (sta->last_tx_bytes > data->tx_bytes)
+- sta->acct_output_gigawords++;
+- sta->last_rx_bytes = data->rx_bytes;
+- sta->last_tx_bytes = data->tx_bytes;
++ if (!data->bytes_64bit) {
++ /* Extend 32-bit counters from the driver to 64-bit counters */
++ if (sta->last_rx_bytes_lo > data->rx_bytes)
++ sta->last_rx_bytes_hi++;
++ sta->last_rx_bytes_lo = data->rx_bytes;
+
++ if (sta->last_tx_bytes_lo > data->tx_bytes)
++ sta->last_tx_bytes_hi++;
++ sta->last_tx_bytes_lo = data->tx_bytes;
++ }
++
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+- HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
+- "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
+- "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
+- sta->last_rx_bytes, sta->acct_input_gigawords,
+- sta->last_tx_bytes, sta->acct_output_gigawords);
++ HOSTAPD_LEVEL_DEBUG,
++ "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
++ data->rx_bytes, sta->last_rx_bytes_hi,
++ sta->last_rx_bytes_lo,
++ data->tx_bytes, sta->last_tx_bytes_hi,
++ sta->last_tx_bytes_lo,
++ data->bytes_64bit);
+
+ return 0;
+ }
+@@ -217,12 +230,14 @@
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+- "starting accounting session %08X-%08X",
+- sta->acct_session_id_hi, sta->acct_session_id_lo);
++ "starting accounting session %016llX",
++ (unsigned long long) sta->acct_session_id);
+
+ os_get_reltime(&sta->acct_session_start);
+- sta->last_rx_bytes = sta->last_tx_bytes = 0;
+- sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
++ sta->last_rx_bytes_hi = 0;
++ sta->last_rx_bytes_lo = 0;
++ sta->last_tx_bytes_hi = 0;
++ sta->last_tx_bytes_lo = 0;
+ hostapd_drv_sta_clear_stats(hapd, sta->addr);
+
+ if (!hapd->conf->radius->acct_server)
+@@ -251,8 +266,7 @@
+ int cause = sta->acct_terminate_cause;
+ struct hostap_sta_driver_data data;
+ struct os_reltime now_r, diff;
+- struct os_time now;
+- u32 gigawords;
++ u64 bytes;
+
+ if (!hapd->conf->radius->acct_server)
+ return;
+@@ -266,7 +280,6 @@
+ }
+
+ os_get_reltime(&now_r);
+- os_get_time(&now);
+ os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+ diff.sec)) {
+@@ -287,48 +300,42 @@
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
+ goto fail;
+ }
++ if (data.bytes_64bit)
++ bytes = data.rx_bytes;
++ else
++ bytes = ((u64) sta->last_rx_bytes_hi << 32) |
++ sta->last_rx_bytes_lo;
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS,
+- data.rx_bytes)) {
++ (u32) bytes)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
+ goto fail;
+ }
+- gigawords = sta->acct_input_gigawords;
+-#if __WORDSIZE == 64
+- gigawords += data.rx_bytes >> 32;
+-#endif
+- if (gigawords &&
+- !radius_msg_add_attr_int32(
+- msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+- gigawords)) {
++ if (!radius_msg_add_attr_int32(msg,
++ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
++ (u32) (bytes >> 32))) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
+ goto fail;
+ }
++ if (data.bytes_64bit)
++ bytes = data.tx_bytes;
++ else
++ bytes = ((u64) sta->last_tx_bytes_hi << 32) |
++ sta->last_tx_bytes_lo;
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+- data.tx_bytes)) {
++ (u32) bytes)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
+ goto fail;
+ }
+- gigawords = sta->acct_output_gigawords;
+-#if __WORDSIZE == 64
+- gigawords += data.tx_bytes >> 32;
+-#endif
+- if (gigawords &&
+- !radius_msg_add_attr_int32(
+- msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+- gigawords)) {
++ if (!radius_msg_add_attr_int32(msg,
++ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
++ (u32) (bytes >> 32))) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
+ goto fail;
+ }
+ }
+
+- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+- now.sec)) {
+- wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
+- goto fail;
+- }
+-
+ if (eloop_terminated())
+ cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+@@ -375,22 +382,17 @@
+ eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+- "stopped accounting session %08X-%08X",
+- sta->acct_session_id_hi,
+- sta->acct_session_id_lo);
++ "stopped accounting session %016llX",
++ (unsigned long long) sta->acct_session_id);
+ sta->acct_session_started = 0;
+ }
+ }
+
+
+-void accounting_sta_get_id(struct hostapd_data *hapd,
+- struct sta_info *sta)
++int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+- sta->acct_session_id_lo = hapd->acct_session_id_lo++;
+- if (hapd->acct_session_id_lo == 0) {
+- hapd->acct_session_id_hi++;
+- }
+- sta->acct_session_id_hi = hapd->acct_session_id_hi;
++ return radius_gen_session_id((u8 *) &sta->acct_session_id,
++ sizeof(sta->acct_session_id));
+ }
+
+
+@@ -437,12 +439,14 @@
+ if (!msg)
+ return;
+
+- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+- RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
+- {
+- wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
+- radius_msg_free(msg);
+- return;
++ if (hapd->acct_session_id) {
++ char buf[20];
++
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long) hapd->acct_session_id);
++ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
++ (u8 *) buf, os_strlen(buf)))
++ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+ }
+
+ if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+@@ -450,6 +454,63 @@
+ }
+
+
++static void accounting_interim_error_cb(const u8 *addr, void *ctx)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ unsigned int i, wait_time;
++ int res;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return;
++ sta->acct_interim_errors++;
++ if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
++ wpa_printf(MSG_DEBUG,
++ "Interim RADIUS accounting update failed for " MACSTR
++ " - too many errors, abandon this interim accounting update",
++ MAC2STR(addr));
++ sta->acct_interim_errors = 0;
++ /* Next update will be tried after normal update interval */
++ return;
++ }
++
++ /*
++ * Use a shorter update interval as an improved retransmission mechanism
++ * for failed interim accounting updates. This allows the statistics to
++ * be updated for each retransmission.
++ *
++ * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
++ * Schedule the first retry attempt immediately and every following one
++ * with exponential backoff.
++ */
++ if (sta->acct_interim_errors == 1) {
++ wait_time = 0;
++ } else {
++ wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
++ for (i = 1; i < sta->acct_interim_errors; i++)
++ wait_time *= 2;
++ }
++ res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
++ hapd, sta);
++ if (res == 1)
++ wpa_printf(MSG_DEBUG,
++ "Interim RADIUS accounting update failed for " MACSTR
++ " (error count: %u) - schedule next update in %u seconds",
++ MAC2STR(addr), sta->acct_interim_errors, wait_time);
++ else if (res == 0)
++ wpa_printf(MSG_DEBUG,
++ "Interim RADIUS accounting update failed for " MACSTR
++ " (error count: %u)", MAC2STR(addr),
++ sta->acct_interim_errors);
++ else
++ wpa_printf(MSG_DEBUG,
++ "Interim RADIUS accounting update failed for " MACSTR
++ " (error count: %u) - no timer found", MAC2STR(addr),
++ sta->acct_interim_errors);
++}
++
++
+ /**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+@@ -457,20 +518,15 @@
+ */
+ int accounting_init(struct hostapd_data *hapd)
+ {
+- struct os_time now;
++ if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
++ sizeof(hapd->acct_session_id)) < 0)
++ return -1;
+
+- /* Acct-Session-Id should be unique over reboots. Using a random number
+- * is preferred. If that is not available, take the current time. Mix
+- * in microseconds to make this more likely to be unique. */
+- os_get_time(&now);
+- if (os_get_random((u8 *) &hapd->acct_session_id_hi,
+- sizeof(hapd->acct_session_id_hi)) < 0)
+- hapd->acct_session_id_hi = now.sec;
+- hapd->acct_session_id_hi ^= now.usec;
+-
+ if (radius_client_register(hapd->radius, RADIUS_ACCT,
+ accounting_receive, hapd))
+ return -1;
++ radius_client_set_interim_error_cb(hapd->radius,
++ accounting_interim_error_cb, hapd);
+
+ accounting_report_state(hapd, 1);
+
+--- contrib/wpa/src/ap/accounting.h.orig
++++ contrib/wpa/src/ap/accounting.h
+@@ -10,9 +10,10 @@
+ #define ACCOUNTING_H
+
+ #ifdef CONFIG_NO_ACCOUNTING
+-static inline void accounting_sta_get_id(struct hostapd_data *hapd,
+- struct sta_info *sta)
++static inline int accounting_sta_get_id(struct hostapd_data *hapd,
++ struct sta_info *sta)
+ {
++ return 0;
+ }
+
+ static inline void accounting_sta_start(struct hostapd_data *hapd,
+@@ -34,7 +35,7 @@
+ {
+ }
+ #else /* CONFIG_NO_ACCOUNTING */
+-void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
++int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+ int accounting_init(struct hostapd_data *hapd);
+--- contrib/wpa/src/ap/acs.c.orig
++++ contrib/wpa/src/ap/acs.c
+@@ -13,6 +13,7 @@
+ #include "utils/common.h"
+ #include "utils/list.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/hw_features_common.h"
+ #include "common/wpa_ctrl.h"
+ #include "drivers/driver.h"
+ #include "hostapd.h"
+@@ -260,7 +261,7 @@
+ }
+
+
+-static void acs_cleanup(struct hostapd_iface *iface)
++void acs_cleanup(struct hostapd_iface *iface)
+ {
+ int i;
+ struct hostapd_channel_data *chan;
+@@ -314,7 +315,7 @@
+
+ /* TODO: figure out the best multiplier for noise floor base */
+ factor = pow(10, survey->nf / 5.0L) +
+- (busy / total) *
++ (total ? (busy / total) : 0) *
+ pow(2, pow(10, (long double) survey->nf / 10.0L) -
+ pow(10, (long double) min_nf / 10.0L));
+
+@@ -331,12 +332,10 @@
+ long double int_factor = 0;
+ unsigned count = 0;
+
+- if (dl_list_empty(&chan->survey_list))
++ if (dl_list_empty(&chan->survey_list) ||
++ (chan->flag & HOSTAPD_CHAN_DISABLED))
+ return;
+
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+- return;
+-
+ chan->interference_factor = 0;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+@@ -359,13 +358,12 @@
+ (unsigned long) survey->channel_time_rx);
+ }
+
+- if (!count)
+- return;
+- chan->interference_factor /= count;
++ if (count)
++ chan->interference_factor /= count;
+ }
+
+
+-static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
++static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
+ {
+ const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
+ 157, 184, 192 };
+@@ -379,7 +377,7 @@
+ }
+
+
+-static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
++static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
+ {
+ const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+ unsigned int i;
+@@ -392,6 +390,19 @@
+ }
+
+
++static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
++{
++ const int allowed[] = { 36, 100 };
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(allowed); i++)
++ if (chan->chan == allowed[i])
++ return 1;
++
++ return 0;
++}
++
++
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ if (!(survey->filled & SURVEY_HAS_NF)) {
+@@ -450,13 +461,9 @@
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+- continue;
+-
+- if (!acs_survey_list_is_sufficient(chan))
+- continue;
+-
+- valid++;
++ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
++ acs_survey_list_is_sufficient(chan))
++ valid++;
+ }
+
+ /* We need at least survey data for one channel */
+@@ -466,13 +473,9 @@
+
+ static int acs_usable_chan(struct hostapd_channel_data *chan)
+ {
+- if (dl_list_empty(&chan->survey_list))
+- return 0;
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+- return 0;
+- if (!acs_survey_list_is_sufficient(chan))
+- return 0;
+- return 1;
++ return !dl_list_empty(&chan->survey_list) &&
++ !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
++ acs_survey_list_is_sufficient(chan);
+ }
+
+
+@@ -576,6 +579,7 @@
+ long double factor, ideal_factor = 0;
+ int i, j;
+ int n_chans = 1;
++ u32 bw;
+ unsigned int k;
+
+ /* TODO: HT40- support */
+@@ -590,18 +594,24 @@
+ iface->conf->secondary_channel)
+ n_chans = 2;
+
+- if (iface->conf->ieee80211ac &&
+- iface->conf->vht_oper_chwidth == 1)
+- n_chans = 4;
++ if (iface->conf->ieee80211ac) {
++ switch (iface->conf->vht_oper_chwidth) {
++ case VHT_CHANWIDTH_80MHZ:
++ n_chans = 4;
++ break;
++ case VHT_CHANWIDTH_160MHZ:
++ n_chans = 8;
++ break;
++ }
++ }
+
+- /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
++ bw = num_chan_to_bw(n_chans);
+
+- wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
+- n_chans == 1 ? 20 :
+- n_chans == 2 ? 40 :
+- n_chans == 4 ? 80 :
+- -1);
++ /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
+
++ wpa_printf(MSG_DEBUG,
++ "ACS: Survey analysis for selected bandwidth %d MHz", bw);
++
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ double total_weight;
+ struct acs_bias *bias, tmp_bias;
+@@ -608,12 +618,23 @@
+
+ chan = &iface->current_mode->channels[i];
+
+- if (chan->flag & HOSTAPD_CHAN_DISABLED)
++ /* Since in the current ACS implementation the first channel is
++ * always a primary channel, skip channels not available as
++ * primary until more sophisticated channel selection is
++ * implemented. */
++ if (!chan_pri_allowed(chan))
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
++ if (!chan_bw_allowed(chan, bw, 1, 1)) {
++ wpa_printf(MSG_DEBUG,
++ "ACS: Channel %d: BW %u is not supported",
++ chan->chan, bw);
++ continue;
++ }
++
+ /* HT40 on 5 GHz has a limited set of primary channels as per
+ * 11n Annex J */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+@@ -626,12 +647,24 @@
+ }
+
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+- iface->conf->ieee80211ac &&
+- iface->conf->vht_oper_chwidth == 1 &&
+- !acs_usable_vht80_chan(chan)) {
+- wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+- chan->chan);
+- continue;
++ iface->conf->ieee80211ac) {
++ if (iface->conf->vht_oper_chwidth ==
++ VHT_CHANWIDTH_80MHZ &&
++ !acs_usable_vht80_chan(chan)) {
++ wpa_printf(MSG_DEBUG,
++ "ACS: Channel %d: not allowed as primary channel for VHT80",
++ chan->chan);
++ continue;
++ }
++
++ if (iface->conf->vht_oper_chwidth ==
++ VHT_CHANWIDTH_160MHZ &&
++ !acs_usable_vht160_chan(chan)) {
++ wpa_printf(MSG_DEBUG,
++ "ACS: Channel %d: not allowed as primary channel for VHT160",
++ chan->chan);
++ continue;
++ }
+ }
+
+ factor = 0;
+@@ -644,6 +677,13 @@
+ if (!adj_chan)
+ break;
+
++ if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
++ wpa_printf(MSG_DEBUG,
++ "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
++ chan->chan, adj_chan->chan, bw);
++ break;
++ }
++
+ if (acs_usable_chan(adj_chan)) {
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
+@@ -756,10 +796,14 @@
+ case VHT_CHANWIDTH_80MHZ:
+ offset = 6;
+ break;
++ case VHT_CHANWIDTH_160MHZ:
++ offset = 14;
++ break;
+ default:
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+- wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
++ wpa_printf(MSG_INFO,
++ "ACS: Only VHT20/40/80/160 is supported now");
+ return;
+ }
+
+@@ -789,10 +833,7 @@
+
+ static int acs_study_options(struct hostapd_iface *iface)
+ {
+- int err;
+-
+- err = acs_study_survey_based(iface);
+- if (err == 0)
++ if (acs_study_survey_based(iface) == 0)
+ return 0;
+
+ /* TODO: If no surveys are available/sufficient this is a good
+@@ -921,22 +962,21 @@
+
+ enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+ {
+- int err;
+-
+ wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+ wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+- err = hostapd_drv_do_acs(iface->bss[0]);
+- if (err)
++ if (hostapd_drv_do_acs(iface->bss[0]))
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_ACS;
+ }
+
++ if (!iface->current_mode)
++ return HOSTAPD_CHAN_INVALID;
++
+ acs_cleanup(iface);
+
+- err = acs_request_scan(iface);
+- if (err < 0)
++ if (acs_request_scan(iface) < 0)
+ return HOSTAPD_CHAN_INVALID;
+
+ hostapd_set_state(iface, HAPD_IFACE_ACS);
+--- contrib/wpa/src/ap/acs.h.orig
++++ contrib/wpa/src/ap/acs.h
+@@ -13,6 +13,7 @@
+ #ifdef CONFIG_ACS
+
+ enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
++void acs_cleanup(struct hostapd_iface *iface);
+
+ #else /* CONFIG_ACS */
+
+@@ -22,6 +23,10 @@
+ return HOSTAPD_CHAN_INVALID;
+ }
+
++static inline void acs_cleanup(struct hostapd_iface *iface)
++{
++}
++
+ #endif /* CONFIG_ACS */
+
+ #endif /* ACS_H */
+--- contrib/wpa/src/ap/ap_config.c.orig
++++ contrib/wpa/src/ap/ap_config.c
+@@ -10,9 +10,11 @@
+
+ #include "utils/common.h"
+ #include "crypto/sha1.h"
++#include "crypto/tls.h"
+ #include "radius/radius_client.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/eapol_common.h"
++#include "common/dhcp.h"
+ #include "eap_common/eap_wsc_common.h"
+ #include "eap_server/eap.h"
+ #include "wpa_auth.h"
+@@ -36,8 +38,14 @@
+ }
+
+
++#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
++#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
++#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
++
+ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+ {
++ dl_list_init(&bss->anqp_elem);
++
+ bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_syslog = (unsigned int) -1;
+@@ -53,6 +61,10 @@
+
+ bss->wpa_group_rekey = 600;
+ bss->wpa_gmk_rekey = 86400;
++ bss->wpa_group_update_count = 4;
++ bss->wpa_pairwise_update_count = 4;
++ bss->wpa_disable_eapol_key_retries =
++ DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+ bss->wpa_pairwise = WPA_CIPHER_TKIP;
+ bss->wpa_group = WPA_CIPHER_TKIP;
+@@ -63,6 +75,7 @@
+ bss->dtim_period = 2;
+
+ bss->radius_server_auth_port = 1812;
++ bss->eap_sim_db_timeout = 1;
+ bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+ bss->eapol_version = EAPOL_VERSION;
+
+@@ -85,13 +98,48 @@
+ /* Set to -1 as defaults depends on HT in setup */
+ bss->wmm_enabled = -1;
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ bss->ft_over_ds = 1;
+-#endif /* CONFIG_IEEE80211R */
++ bss->rkh_pos_timeout = 86400;
++ bss->rkh_neg_timeout = 60;
++ bss->rkh_pull_timeout = 1000;
++ bss->rkh_pull_retries = 4;
++ bss->r0_key_lifetime = 1209600;
++#endif /* CONFIG_IEEE80211R_AP */
+
+ bss->radius_das_time_window = 300;
+
+ bss->sae_anti_clogging_threshold = 5;
++ bss->sae_sync = 5;
++
++ bss->gas_frag_limit = 1400;
++
++#ifdef CONFIG_FILS
++ dl_list_init(&bss->fils_realms);
++ bss->fils_hlp_wait_time = 30;
++ bss->dhcp_server_port = DHCP_SERVER_PORT;
++ bss->dhcp_relay_port = DHCP_SERVER_PORT;
++#endif /* CONFIG_FILS */
++
++ bss->broadcast_deauth = 1;
++
++#ifdef CONFIG_MBO
++ bss->mbo_cell_data_conn_pref = -1;
++#endif /* CONFIG_MBO */
++
++ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
++ * This can be enabled by default once the implementation has been fully
++ * completed and tested with other implementations. */
++ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
++
++ bss->send_probe_response = 1;
++
++#ifdef CONFIG_HS20
++ bss->hs20_release = (HS20_VERSION >> 4) + 1;
++#endif /* CONFIG_HS20 */
++
++ /* Default to strict CRL checking. */
++ bss->check_crl_strict = 1;
+ }
+
+
+@@ -152,9 +200,8 @@
+ conf->num_bss = 1;
+
+ conf->beacon_int = 100;
+- conf->rts_threshold = -1; /* use driver default: 2347 */
+- conf->fragm_threshold = -1; /* user driver default: 2346 */
+- conf->send_probe_response = 1;
++ conf->rts_threshold = -2; /* use driver default: 2347 */
++ conf->fragm_threshold = -2; /* user driver default: 2346 */
+ /* Set to invalid value means do not add Power Constraint IE */
+ conf->local_pwr_constraint = -1;
+
+@@ -180,6 +227,7 @@
+ conf->ignore_assoc_probability = 0.0;
+ conf->ignore_reassoc_probability = 0.0;
+ conf->corrupt_gtk_rekey_mic_probability = 0.0;
++ conf->ecsa_ie_only = 0;
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+ conf->acs = 0;
+@@ -188,6 +236,14 @@
+ conf->acs_num_scans = 5;
+ #endif /* CONFIG_ACS */
+
++ /* The third octet of the country string uses an ASCII space character
++ * by default to indicate that the regulations encompass all
++ * environments for the current frequency band in the country. */
++ conf->country[2] = ' ';
++
++ conf->rssi_reject_assoc_rssi = 0;
++ conf->rssi_reject_assoc_timeout = 30;
++
+ return conf;
+ }
+
+@@ -198,18 +254,17 @@
+ }
+
+
+-int hostapd_mac_comp_empty(const void *a)
+-{
+- macaddr empty = { 0 };
+- return os_memcmp(a, empty, sizeof(macaddr));
+-}
+-
+-
+ static int hostapd_config_read_wpa_psk(const char *fname,
+ struct hostapd_ssid *ssid)
+ {
+ FILE *f;
+ char buf[128], *pos;
++ const char *keyid;
++ char *context;
++ char *context2;
++ char *token;
++ char *name;
++ char *value;
+ int line = 0, ret = 0, len, ok;
+ u8 addr[ETH_ALEN];
+ struct hostapd_wpa_psk *psk;
+@@ -224,6 +279,8 @@
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
++ int vlan_id = 0;
++
+ line++;
+
+ if (buf[0] == '#')
+@@ -239,9 +296,39 @@
+ if (buf[0] == '\0')
+ continue;
+
+- if (hwaddr_aton(buf, addr)) {
++ context = NULL;
++ keyid = NULL;
++ while ((token = str_token(buf, " ", &context))) {
++ if (!os_strchr(token, '='))
++ break;
++ context2 = NULL;
++ name = str_token(token, "=", &context2);
++ if (!name)
++ break;
++ value = str_token(token, "", &context2);
++ if (!value)
++ value = "";
++ if (!os_strcmp(name, "keyid")) {
++ keyid = value;
++ } else if (!os_strcmp(name, "vlanid")) {
++ vlan_id = atoi(value);
++ } else {
++ wpa_printf(MSG_ERROR,
++ "Unrecognized '%s=%s' on line %d in '%s'",
++ name, value, line, fname);
++ ret = -1;
++ break;
++ }
++ }
++
++ if (ret == -1)
++ break;
++
++ if (!token)
++ token = "";
++ if (hwaddr_aton(token, addr)) {
+ wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
+- "line %d in '%s'", buf, line, fname);
++ "line %d in '%s'", token, line, fname);
+ ret = -1;
+ break;
+ }
+@@ -252,13 +339,14 @@
+ ret = -1;
+ break;
+ }
++ psk->vlan_id = vlan_id;
+ if (is_zero_ether_addr(addr))
+ psk->group = 1;
+ else
+ os_memcpy(psk->addr, addr, ETH_ALEN);
+
+- pos = buf + 17;
+- if (*pos == '\0') {
++ pos = str_token(buf, "", &context);
++ if (!pos) {
+ wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+@@ -265,7 +353,6 @@
+ ret = -1;
+ break;
+ }
+- pos++;
+
+ ok = 0;
+ len = os_strlen(pos);
+@@ -284,6 +371,18 @@
+ break;
+ }
+
++ if (keyid) {
++ len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
++ if ((size_t) len >= sizeof(psk->keyid)) {
++ wpa_printf(MSG_ERROR,
++ "PSK keyid too long on line %d in '%s'",
++ line, fname);
++ os_free(psk);
++ ret = -1;
++ break;
++ }
++ }
++
+ psk->next = ssid->wpa_psk;
+ ssid->wpa_psk = psk;
+ }
+@@ -332,13 +431,7 @@
+ ssid->wpa_psk->group = 1;
+ }
+
+- if (ssid->wpa_psk_file) {
+- if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
+- &conf->ssid))
+- return -1;
+- }
+-
+- return 0;
++ return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
+ }
+
+
+@@ -383,10 +476,23 @@
+ hostapd_config_free_radius_attr(user->accept_attr);
+ os_free(user->identity);
+ bin_clear_free(user->password, user->password_len);
++ bin_clear_free(user->salt, user->salt_len);
+ os_free(user);
+ }
+
+
++void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
++{
++ struct hostapd_eap_user *prev_user;
++
++ while (user) {
++ prev_user = user;
++ user = user->next;
++ hostapd_config_free_eap_user(prev_user);
++ }
++}
++
++
+ static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+ {
+ int i;
+@@ -410,10 +516,51 @@
+ }
+
+
++static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
++{
++ struct anqp_element *elem;
++
++ while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
++ list))) {
++ dl_list_del(&elem->list);
++ wpabuf_free(elem->payload);
++ os_free(elem);
++ }
++}
++
++
++static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
++{
++#ifdef CONFIG_FILS
++ struct fils_realm *realm;
++
++ while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
++ list))) {
++ dl_list_del(&realm->list);
++ os_free(realm);
++ }
++#endif /* CONFIG_FILS */
++}
++
++
++static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
++{
++ struct sae_password_entry *pw, *tmp;
++
++ pw = conf->sae_passwords;
++ conf->sae_passwords = NULL;
++ while (pw) {
++ tmp = pw;
++ pw = pw->next;
++ str_clear_free(tmp->password);
++ os_free(tmp->identifier);
++ os_free(tmp);
++ }
++}
++
++
+ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+ {
+- struct hostapd_eap_user *user, *prev_user;
+-
+ if (conf == NULL)
+ return;
+
+@@ -426,12 +573,7 @@
+ os_free(conf->ssid.vlan_tagged_interface);
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+- user = conf->eap_user;
+- while (user) {
+- prev_user = user;
+- user = user->next;
+- hostapd_config_free_eap_user(prev_user);
+- }
++ hostapd_config_free_eap_users(conf->eap_user);
+ os_free(conf->eap_user_sqlite);
+
+ os_free(conf->eap_req_id_text);
+@@ -453,9 +595,12 @@
+ os_free(conf->server_cert);
+ os_free(conf->private_key);
+ os_free(conf->private_key_passwd);
++ os_free(conf->check_cert_subject);
+ os_free(conf->ocsp_stapling_response);
++ os_free(conf->ocsp_stapling_response_multi);
+ os_free(conf->dh_file);
+ os_free(conf->openssl_ciphers);
++ os_free(conf->openssl_ecdh_curves);
+ os_free(conf->pac_opaque_encr_key);
+ os_free(conf->eap_fast_a_id);
+ os_free(conf->eap_fast_a_id_info);
+@@ -466,7 +611,7 @@
+ hostapd_config_free_vlan(conf);
+ os_free(conf->time_zone);
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ {
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+@@ -487,7 +632,7 @@
+ os_free(r1kh_prev);
+ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #ifdef CONFIG_WPS
+ os_free(conf->wps_pin_requests);
+@@ -500,6 +645,8 @@
+ os_free(conf->ap_pin);
+ os_free(conf->extra_cred);
+ os_free(conf->ap_settings);
++ hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
++ str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ os_free(conf->upnp_iface);
+ os_free(conf->friendly_name);
+ os_free(conf->manufacturer_url);
+@@ -519,10 +666,12 @@
+
+ os_free(conf->roaming_consortium);
+ os_free(conf->venue_name);
++ os_free(conf->venue_url);
+ os_free(conf->nai_realm_data);
+ os_free(conf->network_auth_type);
+ os_free(conf->anqp_3gpp_cell_net);
+ os_free(conf->domain_name);
++ hostapd_config_free_anqp_elem(conf);
+
+ #ifdef CONFIG_RADIUS_TEST
+ os_free(conf->dump_msk_file);
+@@ -547,16 +696,31 @@
+ os_free(p->icons[j]);
+ os_free(p->icons);
+ os_free(p->osu_nai);
++ os_free(p->osu_nai2);
+ os_free(p->service_desc);
+ }
+ os_free(conf->hs20_osu_providers);
+ }
++ if (conf->hs20_operator_icon) {
++ size_t i;
++
++ for (i = 0; i < conf->hs20_operator_icon_count; i++)
++ os_free(conf->hs20_operator_icon[i]);
++ os_free(conf->hs20_operator_icon);
++ }
+ os_free(conf->subscr_remediation_url);
++ os_free(conf->hs20_sim_provisioning_url);
++ os_free(conf->t_c_filename);
++ os_free(conf->t_c_server_url);
+ #endif /* CONFIG_HS20 */
+
+ wpabuf_free(conf->vendor_elements);
++ wpabuf_free(conf->assocresp_elements);
+
+ os_free(conf->sae_groups);
++#ifdef CONFIG_OWE
++ os_free(conf->owe_groups);
++#endif /* CONFIG_OWE */
+
+ os_free(conf->wowlan_triggers);
+
+@@ -564,11 +728,22 @@
+
+ #ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->own_ie_override);
++ wpabuf_free(conf->sae_commit_override);
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+ os_free(conf->no_probe_resp_if_seen_on);
+ os_free(conf->no_auth_if_seen_on);
+
++ hostapd_config_free_fils_realms(conf);
++
++#ifdef CONFIG_DPP
++ os_free(conf->dpp_connector);
++ wpabuf_free(conf->dpp_netaccesskey);
++ wpabuf_free(conf->dpp_csign);
++#endif /* CONFIG_DPP */
++
++ hostapd_config_free_sae_passwords(conf);
++
+ os_free(conf);
+ }
+
+@@ -594,6 +769,8 @@
+ #ifdef CONFIG_ACS
+ os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++ wpabuf_free(conf->lci);
++ wpabuf_free(conf->civic);
+
+ os_free(conf);
+ }
+@@ -610,7 +787,7 @@
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+- const u8 *addr, int *vlan_id)
++ const u8 *addr, struct vlan_description *vlan_id)
+ {
+ int start, end, middle, res;
+
+@@ -650,11 +827,26 @@
+ }
+
+
+-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
++int hostapd_vlan_valid(struct hostapd_vlan *vlan,
++ struct vlan_description *vlan_desc)
+ {
+ struct hostapd_vlan *v = vlan;
++ int i;
++
++ if (!vlan_desc->notempty || vlan_desc->untagged < 0 ||
++ vlan_desc->untagged > MAX_VLAN_ID)
++ return 0;
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
++ if (vlan_desc->tagged[i] < 0 ||
++ vlan_desc->tagged[i] > MAX_VLAN_ID)
++ return 0;
++ }
++ if (!vlan_desc->untagged && !vlan_desc->tagged[0])
++ return 0;
++
+ while (v) {
+- if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
++ if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
++ v->vlan_id == VLAN_ID_WILDCARD)
+ return 1;
+ v = v->next;
+ }
+@@ -676,11 +868,14 @@
+
+ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *p2p_dev_addr,
+- const u8 *prev_psk)
++ const u8 *prev_psk, int *vlan_id)
+ {
+ struct hostapd_wpa_psk *psk;
+ int next_ok = prev_psk == NULL;
+
++ if (vlan_id)
++ *vlan_id = 0;
++
+ if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " p2p_dev_addr=" MACSTR " prev_psk=%p",
+@@ -698,8 +893,11 @@
+ (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (!addr && p2p_dev_addr &&
+ os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+- 0)))
++ 0))) {
++ if (vlan_id)
++ *vlan_id = psk->vlan_id;
+ return psk->psk;
++ }
+
+ if (psk->psk == prev_psk)
+ next_ok = 1;
+@@ -756,7 +954,7 @@
+ return -1;
+ }
+
+- if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) {
++ if (full_config && !is_zero_ether_addr(bss->bssid)) {
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+@@ -772,7 +970,7 @@
+ }
+ }
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+ (bss->nas_identifier == NULL ||
+ os_strlen(bss->nas_identifier) < 1 ||
+@@ -782,7 +980,7 @@
+ "string");
+ return -1;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #ifdef CONFIG_IEEE80211N
+ if (full_config && conf->ieee80211n &&
+@@ -811,6 +1009,25 @@
+ }
+ #endif /* CONFIG_IEEE80211N */
+
++#ifdef CONFIG_IEEE80211AC
++ if (full_config && conf->ieee80211ac &&
++ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
++ bss->disable_11ac = 1;
++ wpa_printf(MSG_ERROR,
++ "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
++ }
++
++ if (full_config && conf->ieee80211ac && bss->wpa &&
++ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
++ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
++ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
++ {
++ bss->disable_11ac = 1;
++ wpa_printf(MSG_ERROR,
++ "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
++ }
++#endif /* CONFIG_IEEE80211AC */
++
+ #ifdef CONFIG_WPS
+ if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+ wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+@@ -827,7 +1044,9 @@
+
+ if (full_config && bss->wps_state && bss->wpa &&
+ (!(bss->wpa & 2) ||
+- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
++ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
++ WPA_CIPHER_CCMP_256 |
++ WPA_CIPHER_GCMP_256)))) {
+ wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+ "WPA2/CCMP/GCMP forced WPS to be disabled");
+ bss->wps_state = 0;
+@@ -847,6 +1066,24 @@
+ }
+ #endif /* CONFIG_HS20 */
+
++#ifdef CONFIG_MBO
++ if (full_config && bss->mbo_enabled && (bss->wpa & 2) &&
++ bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
++ wpa_printf(MSG_ERROR,
++ "MBO: PMF needs to be enabled whenever using WPA2 with MBO");
++ return -1;
++ }
++#endif /* CONFIG_MBO */
++
++#ifdef CONFIG_OCV
++ if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
++ bss->ocv) {
++ wpa_printf(MSG_ERROR,
++ "OCV: PMF needs to be enabled whenever using OCV");
++ return -1;
++ }
++#endif /* CONFIG_OCV */
++
+ return 0;
+ }
+
+@@ -928,8 +1165,15 @@
+
+ if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+ bss->rsn_pairwise = bss->wpa_pairwise;
+- bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+- bss->rsn_pairwise);
++ if (bss->group_cipher)
++ bss->wpa_group = bss->group_cipher;
++ else
++ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
++ bss->wpa_pairwise,
++ bss->rsn_pairwise);
++ if (!bss->wpa_group_rekey_set)
++ bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
++ 600 : 86400;
+
+ if (full_config) {
+ bss->radius->auth_server = bss->radius->auth_servers;
+@@ -983,3 +1227,26 @@
+ }
+ }
+ }
++
++
++int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
++{
++ int with_id = 0, without_id = 0;
++ struct sae_password_entry *pw;
++
++ if (conf->ssid.wpa_passphrase)
++ without_id = 1;
++
++ for (pw = conf->sae_passwords; pw; pw = pw->next) {
++ if (pw->identifier)
++ with_id = 1;
++ else
++ without_id = 1;
++ if (with_id && without_id)
++ break;
++ }
++
++ if (with_id && !without_id)
++ return 2;
++ return with_id;
++}
+--- contrib/wpa/src/ap/ap_config.h.orig
++++ contrib/wpa/src/ap/ap_config.h
+@@ -10,6 +10,7 @@
+ #define HOSTAPD_CONFIG_H
+
+ #include "common/defs.h"
++#include "utils/list.h"
+ #include "ip_addr.h"
+ #include "common/wpa_common.h"
+ #include "common/ieee802_11_defs.h"
+@@ -16,6 +17,7 @@
+ #include "common/ieee802_11_common.h"
+ #include "wps/wps.h"
+ #include "fst/fst.h"
++#include "vlan.h"
+
+ /**
+ * mesh_conf - local MBSS state and settings
+@@ -39,6 +41,11 @@
+ #define MESH_CONF_SEC_AUTH BIT(1)
+ #define MESH_CONF_SEC_AMPE BIT(2)
+ unsigned int security;
++ enum mfp_options ieee80211w;
++ int ocv;
++ unsigned int pairwise_cipher;
++ unsigned int group_cipher;
++ unsigned int mgmt_group_cipher;
+ int dot11MeshMaxRetries;
+ int dot11MeshRetryTimeout; /* msec */
+ int dot11MeshConfirmTimeout; /* msec */
+@@ -52,7 +59,7 @@
+
+ struct mac_acl_entry {
+ macaddr addr;
+- int vlan_id;
++ struct vlan_description vlan_id;
+ };
+
+ struct hostapd_radius_servers;
+@@ -102,6 +109,7 @@
+ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+ #define DYNAMIC_VLAN_NAMING_END 2
+ int vlan_naming;
++ int per_sta_vif;
+ #ifdef CONFIG_FULL_DYNAMIC_VLAN
+ char *vlan_tagged_interface;
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+@@ -113,7 +121,9 @@
+ struct hostapd_vlan {
+ struct hostapd_vlan *next;
+ int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
++ struct vlan_description vlan_desc;
+ char ifname[IFNAMSIZ + 1];
++ char bridge[IFNAMSIZ + 1];
+ int configured;
+ int dynamic_vlan;
+ #ifdef CONFIG_FULL_DYNAMIC_VLAN
+@@ -124,17 +134,25 @@
+ };
+
+ #define PMK_LEN 32
++#define KEYID_LEN 32
++#define MIN_PASSPHRASE_LEN 8
++#define MAX_PASSPHRASE_LEN 63
+ struct hostapd_sta_wpa_psk_short {
+ struct hostapd_sta_wpa_psk_short *next;
++ unsigned int is_passphrase:1;
+ u8 psk[PMK_LEN];
++ char passphrase[MAX_PASSPHRASE_LEN + 1];
++ int ref; /* (number of references held) - 1 */
+ };
+
+ struct hostapd_wpa_psk {
+ struct hostapd_wpa_psk *next;
+ int group;
++ char keyid[KEYID_LEN];
+ u8 psk[PMK_LEN];
+ u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
++ int vlan_id;
+ };
+
+ struct hostapd_eap_user {
+@@ -147,6 +165,8 @@
+ } methods[EAP_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
++ u8 *salt;
++ size_t salt_len; /* non-zero when password is salted */
+ int phase2;
+ int force_version;
+ unsigned int wildcard_prefix:1;
+@@ -156,6 +176,7 @@
+ unsigned int macacl:1;
+ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+ struct hostapd_radius_attr *accept_attr;
++ u32 t_c_timestamp;
+ };
+
+ struct hostapd_radius_attr {
+@@ -188,6 +209,12 @@
+ u8 name[252];
+ };
+
++struct hostapd_venue_url {
++ u8 venue_number;
++ u8 url_len;
++ u8 url[254];
++};
++
+ #define MAX_NAI_REALMS 10
+ #define MAX_NAI_REALMLEN 255
+ #define MAX_NAI_EAP_METHODS 5
+@@ -205,6 +232,26 @@
+ } eap_method[MAX_NAI_EAP_METHODS];
+ };
+
++struct anqp_element {
++ struct dl_list list;
++ u16 infoid;
++ struct wpabuf *payload;
++};
++
++struct fils_realm {
++ struct dl_list list;
++ u8 hash[2];
++ char realm[];
++};
++
++struct sae_password_entry {
++ struct sae_password_entry *next;
++ char *password;
++ char *identifier;
++ u8 peer_addr[ETH_ALEN];
++ int vlan_id;
++};
++
+ /**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+@@ -222,7 +269,8 @@
+ int max_num_sta; /* maximum number of STAs in station table */
+
+ int dtim_period;
+- int bss_load_update_period;
++ unsigned int bss_load_update_period;
++ unsigned int chan_util_avg_period;
+
+ int ieee802_1x; /* use IEEE 802.1X */
+ int eapol_version;
+@@ -231,6 +279,7 @@
+ struct hostapd_eap_user *eap_user;
+ char *eap_user_sqlite;
+ char *eap_sim_db;
++ unsigned int eap_sim_db_timeout;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ struct hostapd_ip_addr own_ip_addr;
+ char *nas_identifier;
+@@ -242,6 +291,7 @@
+ int radius_das_port;
+ unsigned int radius_das_time_window;
+ int radius_das_require_event_timestamp;
++ int radius_das_require_message_authenticator;
+ struct hostapd_ip_addr radius_das_client_addr;
+ u8 *radius_das_shared_secret;
+ size_t radius_das_shared_secret_len;
+@@ -265,7 +315,7 @@
+ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+ * frames */
+
+- enum {
++ enum macaddr_acl {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+@@ -291,6 +341,9 @@
+ /* dot11AssociationSAQueryRetryTimeout (in TUs) */
+ int assoc_sa_query_retry_timeout;
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ int ocv; /* Operating Channel Validation */
++#endif /* CONFIG_OCV */
+ enum {
+ PSK_RADIUS_IGNORED = 0,
+ PSK_RADIUS_ACCEPTED = 1,
+@@ -297,27 +350,37 @@
+ PSK_RADIUS_REQUIRED = 2
+ } wpa_psk_radius;
+ int wpa_pairwise;
++ int group_cipher; /* wpa_group value override from configuation */
+ int wpa_group;
+ int wpa_group_rekey;
++ int wpa_group_rekey_set;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
++ u32 wpa_group_update_count;
++ u32 wpa_pairwise_update_count;
++ int wpa_disable_eapol_key_retries;
+ int rsn_pairwise;
+ int rsn_preauth;
+ char *rsn_preauth_interfaces;
+- int peerkey;
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ /* IEEE 802.11r - Fast BSS Transition */
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+- u32 r0_key_lifetime;
++ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
++ int rkh_pos_timeout;
++ int rkh_neg_timeout;
++ int rkh_pull_timeout; /* ms */
++ int rkh_pull_retries;
+ u32 reassociation_deadline;
+ struct ft_remote_r0kh *r0kh_list;
+ struct ft_remote_r1kh *r1kh_list;
+ int pmk_r1_push;
+ int ft_over_ds;
+-#endif /* CONFIG_IEEE80211R */
++ int ft_psk_generate_local;
++ int r1_max_key_lifetime;
++#endif /* CONFIG_IEEE80211R_AP */
+
+ char *ctrl_interface; /* directory for UNIX domain sockets */
+ #ifndef CONFIG_NATIVE_WINDOWS
+@@ -329,11 +392,17 @@
+ char *server_cert;
+ char *private_key;
+ char *private_key_passwd;
++ char *check_cert_subject;
+ int check_crl;
++ int check_crl_strict;
++ unsigned int crl_reload_interval;
+ unsigned int tls_session_lifetime;
++ unsigned int tls_flags;
+ char *ocsp_stapling_response;
++ char *ocsp_stapling_response_multi;
+ char *dh_file;
+ char *openssl_ciphers;
++ char *openssl_ecdh_curves;
+ u8 *pac_opaque_encr_key;
+ u8 *eap_fast_a_id;
+ size_t eap_fast_a_id_len;
+@@ -358,6 +427,7 @@
+
+ int ap_max_inactivity;
+ int ignore_broadcast_ssid;
++ int no_probe_resp_if_max_sta;
+
+ int wmm_enabled;
+ int wmm_uapsd;
+@@ -395,9 +465,11 @@
+ u8 *extra_cred;
+ size_t extra_cred_len;
+ int wps_cred_processing;
++ int wps_cred_add_sae;
+ int force_per_enrollee_psk;
+ u8 *ap_settings;
+ size_t ap_settings_len;
++ struct hostapd_ssid multi_ap_backhaul_ssid;
+ char *upnp_iface;
+ char *friendly_name;
+ char *manufacturer_url;
+@@ -440,6 +512,7 @@
+ int time_advertisement;
+ char *time_zone;
+ int wnm_sleep_mode;
++ int wnm_sleep_mode_no_keys;
+ int bss_transition;
+
+ /* IEEE 802.11u - Interworking */
+@@ -462,6 +535,10 @@
+ unsigned int venue_name_count;
+ struct hostapd_lang_string *venue_name;
+
++ /* Venue URL duples */
++ unsigned int venue_url_count;
++ struct hostapd_venue_url *venue_url;
++
+ /* IEEE 802.11u - Network Authentication Type */
+ u8 *network_auth_type;
+ size_t network_auth_type_len;
+@@ -481,8 +558,11 @@
+ unsigned int nai_realm_count;
+ struct hostapd_nai_realm_data *nai_realm_data;
+
++ struct dl_list anqp_elem; /* list of struct anqp_element */
++
+ u16 gas_comeback_delay;
+- int gas_frag_limit;
++ size_t gas_frag_limit;
++ int gas_address3;
+
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+@@ -492,6 +572,7 @@
+ int na_mcast_to_ucast;
+ #ifdef CONFIG_HS20
+ int hs20;
++ int hs20_release;
+ int disable_dgaf;
+ u16 anqp_domain_id;
+ unsigned int hs20_oper_friendly_name_count;
+@@ -520,13 +601,21 @@
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
++ char *osu_nai2;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
++ size_t hs20_osu_providers_nai_count;
++ char **hs20_operator_icon;
++ size_t hs20_operator_icon_count;
+ unsigned int hs20_deauth_req_timeout;
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
++ char *hs20_sim_provisioning_url;
++ char *t_c_filename;
++ u32 t_c_timestamp;
++ char *t_c_server_url;
+ #endif /* CONFIG_HS20 */
+
+ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
+@@ -536,9 +625,13 @@
+ #endif /* CONFIG_RADIUS_TEST */
+
+ struct wpabuf *vendor_elements;
++ struct wpabuf *assocresp_elements;
+
+ unsigned int sae_anti_clogging_threshold;
++ unsigned int sae_sync;
++ int sae_require_mfp;
+ int *sae_groups;
++ struct sae_password_entry *sae_passwords;
+
+ char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+@@ -546,21 +639,98 @@
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+ struct wpabuf *own_ie_override;
++ int sae_reflection_attack;
++ struct wpabuf *sae_commit_override;
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+ #define MESH_ENABLED BIT(0)
+ int mesh;
+
+- int radio_measurements;
++ u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
+
+ int vendor_vht;
++ int use_sta_nsts;
+
+ char *no_probe_resp_if_seen_on;
+ char *no_auth_if_seen_on;
++
++ int pbss;
++
++#ifdef CONFIG_MBO
++ int mbo_enabled;
++ /**
++ * oce - Enable OCE in AP and/or STA-CFON mode
++ * - BIT(0) is Reserved
++ * - Set BIT(1) to enable OCE in STA-CFON mode
++ * - Set BIT(2) to enable OCE in AP mode
++ */
++ unsigned int oce;
++ int mbo_cell_data_conn_pref;
++#endif /* CONFIG_MBO */
++
++ int ftm_responder;
++ int ftm_initiator;
++
++#ifdef CONFIG_FILS
++ u8 fils_cache_id[FILS_CACHE_ID_LEN];
++ int fils_cache_id_set;
++ struct dl_list fils_realms; /* list of struct fils_realm */
++ int fils_dh_group;
++ struct hostapd_ip_addr dhcp_server;
++ int dhcp_rapid_commit_proxy;
++ unsigned int fils_hlp_wait_time;
++ u16 dhcp_server_port;
++ u16 dhcp_relay_port;
++#endif /* CONFIG_FILS */
++
++ int multicast_to_unicast;
++
++ int broadcast_deauth;
++
++#ifdef CONFIG_DPP
++ char *dpp_connector;
++ struct wpabuf *dpp_netaccesskey;
++ unsigned int dpp_netaccesskey_expiry;
++ struct wpabuf *dpp_csign;
++#endif /* CONFIG_DPP */
++
++#ifdef CONFIG_OWE
++ macaddr owe_transition_bssid;
++ u8 owe_transition_ssid[SSID_MAX_LEN];
++ size_t owe_transition_ssid_len;
++ char owe_transition_ifname[IFNAMSIZ + 1];
++ int *owe_groups;
++#endif /* CONFIG_OWE */
++
++ int coloc_intf_reporting;
++
++ u8 send_probe_response;
++
++#define BACKHAUL_BSS 1
++#define FRONTHAUL_BSS 2
++ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+ };
+
++/**
++ * struct he_phy_capabilities_info - HE PHY capabilities
++ */
++struct he_phy_capabilities_info {
++ Boolean he_su_beamformer;
++ Boolean he_su_beamformee;
++ Boolean he_mu_beamformer;
++};
+
+ /**
++ * struct he_operation - HE operation
++ */
++struct he_operation {
++ u8 he_bss_color;
++ u8 he_default_pe_duration;
++ u8 he_twt_required;
++ u8 he_rts_threshold;
++};
++
++/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+ struct hostapd_config {
+@@ -570,10 +740,10 @@
+ u16 beacon_int;
+ int rts_threshold;
+ int fragm_threshold;
+- u8 send_probe_response;
+ u8 channel;
+ u8 acs;
+ struct wpa_freq_range_list acs_ch_list;
++ int acs_exclude_dfs;
+ enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+ enum {
+ LONG_PREAMBLE = 0,
+@@ -582,6 +752,8 @@
+
+ int *supported_rates;
+ int *basic_rates;
++ unsigned int beacon_rate;
++ enum beacon_rate_type rate_type;
+
+ const struct wpa_driver_ops *driver;
+ char *driver_params;
+@@ -597,6 +769,9 @@
+ * ' ' (ascii 32): all environments
+ * 'O': Outdoor environemnt only
+ * 'I': Indoor environment only
++ * 'X': Used with noncountry entity ("XXX")
++ * 0x00..0x31: identifying IEEE 802.11 standard
++ * Annex E table (0x04 = global table)
+ */
+
+ int ieee80211d;
+@@ -637,7 +812,11 @@
+ u8 vht_oper_chwidth;
+ u8 vht_oper_centr_freq_seg0_idx;
+ u8 vht_oper_centr_freq_seg1_idx;
++ u8 ht40_plus_minus_allowed;
+
++ /* Use driver-generated interface addresses when adding multiple BSSs */
++ u8 use_driver_iface_addr;
++
+ #ifdef CONFIG_FST
+ struct fst_iface_cfg fst_cfg;
+ #endif /* CONFIG_FST */
+@@ -652,6 +831,7 @@
+ double ignore_assoc_probability;
+ double ignore_reassoc_probability;
+ double corrupt_gtk_rekey_mic_probability;
++ int ecsa_ie_only;
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+ #ifdef CONFIG_ACS
+@@ -662,25 +842,45 @@
+ } *acs_chan_bias;
+ unsigned int num_acs_chan_bias;
+ #endif /* CONFIG_ACS */
++
++ struct wpabuf *lci;
++ struct wpabuf *civic;
++ int stationary_ap;
++
++ int ieee80211ax;
++#ifdef CONFIG_IEEE80211AX
++ struct he_phy_capabilities_info he_phy_capab;
++ struct he_operation he_op;
++ struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
++#endif /* CONFIG_IEEE80211AX */
++
++ /* VHT enable/disable config from CHAN_SWITCH */
++#define CH_SWITCH_VHT_ENABLED BIT(0)
++#define CH_SWITCH_VHT_DISABLED BIT(1)
++ unsigned int ch_switch_vht_config;
++
++ int rssi_reject_assoc_rssi;
++ int rssi_reject_assoc_timeout;
+ };
+
+
+ int hostapd_mac_comp(const void *a, const void *b);
+-int hostapd_mac_comp_empty(const void *a);
+ struct hostapd_config * hostapd_config_defaults(void);
+ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+ void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
++void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
+ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+ void hostapd_config_free_bss(struct hostapd_bss_config *conf);
+ void hostapd_config_free(struct hostapd_config *conf);
+ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+- const u8 *addr, int *vlan_id);
++ const u8 *addr, struct vlan_description *vlan_id);
+ int hostapd_rate_found(int *list, int rate);
+ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *p2p_dev_addr,
+- const u8 *prev_psk);
++ const u8 *prev_psk, int *vlan_id);
+ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
++int hostapd_vlan_valid(struct hostapd_vlan *vlan,
++ struct vlan_description *vlan_desc);
+ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+ int vlan_id);
+ struct hostapd_radius_attr *
+@@ -688,5 +888,6 @@
+ int hostapd_config_check(struct hostapd_config *conf, int full_config);
+ void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config);
++int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+
+ #endif /* HOSTAPD_CONFIG_H */
+--- contrib/wpa/src/ap/ap_drv_ops.c.orig
++++ contrib/wpa/src/ap/ap_drv_ops.c
+@@ -19,6 +19,7 @@
+ #include "ap_config.h"
+ #include "p2p_hostapd.h"
+ #include "hs20.h"
++#include "wpa_auth.h"
+ #include "ap_drv_ops.h"
+
+
+@@ -33,10 +34,36 @@
+ res |= WPA_STA_SHORT_PREAMBLE;
+ if (flags & WLAN_STA_MFP)
+ res |= WPA_STA_MFP;
++ if (flags & WLAN_STA_AUTH)
++ res |= WPA_STA_AUTHENTICATED;
++ if (flags & WLAN_STA_ASSOC)
++ res |= WPA_STA_ASSOCIATED;
+ return res;
+ }
+
+
++static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
++{
++ if (!src)
++ return 0;
++ if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
++ return -1;
++ wpabuf_put_buf(*dst, src);
++ return 0;
++}
++
++
++static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
++{
++ if (!data || !len)
++ return 0;
++ if (wpabuf_resize(dst, len) != 0)
++ return -1;
++ wpabuf_put_data(*dst, data, len);
++ return 0;
++}
++
++
+ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon_ret,
+ struct wpabuf **proberesp_ret,
+@@ -49,82 +76,45 @@
+
+ pos = buf;
+ pos = hostapd_eid_time_adv(hapd, pos);
+- if (pos != buf) {
+- if (wpabuf_resize(&beacon, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(beacon, buf, pos - buf);
+- }
++ if (add_buf_data(&beacon, buf, pos - buf) < 0)
++ goto fail;
+ pos = hostapd_eid_time_zone(hapd, pos);
+- if (pos != buf) {
+- if (wpabuf_resize(&proberesp, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(proberesp, buf, pos - buf);
+- }
++ if (add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
+
+ pos = buf;
+ pos = hostapd_eid_ext_capab(hapd, pos);
+- if (pos != buf) {
+- if (wpabuf_resize(&assocresp, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(assocresp, buf, pos - buf);
+- }
++ if (add_buf_data(&assocresp, buf, pos - buf) < 0)
++ goto fail;
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+- if (pos != buf) {
+- if (wpabuf_resize(&beacon, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(beacon, buf, pos - buf);
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
+
+- if (wpabuf_resize(&proberesp, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(proberesp, buf, pos - buf);
+- }
+-
+ #ifdef CONFIG_FST
+- if (hapd->iface->fst_ies) {
+- size_t add = wpabuf_len(hapd->iface->fst_ies);
+-
+- if (wpabuf_resize(&beacon, add) < 0)
+- goto fail;
+- wpabuf_put_buf(beacon, hapd->iface->fst_ies);
+- if (wpabuf_resize(&proberesp, add) < 0)
+- goto fail;
+- wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
+- if (wpabuf_resize(&assocresp, add) < 0)
+- goto fail;
+- wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
+- }
++ if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
++ add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
++ add_buf(&assocresp, hapd->iface->fst_ies) < 0)
++ goto fail;
+ #endif /* CONFIG_FST */
+
+- if (hapd->wps_beacon_ie) {
+- if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
+- 0)
+- goto fail;
+- wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+- }
++#ifdef CONFIG_FILS
++ pos = hostapd_eid_fils_indic(hapd, buf, 0);
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
++#endif /* CONFIG_FILS */
+
+- if (hapd->wps_probe_resp_ie) {
+- if (wpabuf_resize(&proberesp,
+- wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
+- goto fail;
+- wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+- }
++ if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
++ add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
++ goto fail;
+
+ #ifdef CONFIG_P2P
+- if (hapd->p2p_beacon_ie) {
+- if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
+- 0)
+- goto fail;
+- wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+- }
+-
+- if (hapd->p2p_probe_resp_ie) {
+- if (wpabuf_resize(&proberesp,
+- wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
+- goto fail;
+- wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+- }
++ if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
++ add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
++ goto fail;
+ #endif /* CONFIG_P2P */
+
+ #ifdef CONFIG_P2P_MANAGER
+@@ -148,8 +138,7 @@
+ #ifdef CONFIG_WPS
+ if (hapd->conf->wps_state) {
+ struct wpabuf *a = wps_build_assoc_resp_ie();
+- if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+- wpabuf_put_buf(assocresp, a);
++ add_buf(&assocresp, a);
+ wpabuf_free(a);
+ }
+ #endif /* CONFIG_WPS */
+@@ -169,45 +158,45 @@
+ if (hapd->p2p_group) {
+ struct wpabuf *a;
+ a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+- if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+- wpabuf_put_buf(assocresp, a);
++ add_buf(&assocresp, a);
+ wpabuf_free(a);
+ }
+ #endif /* CONFIG_WIFI_DISPLAY */
+
+ #ifdef CONFIG_HS20
+- pos = buf;
+- pos = hostapd_eid_hs20_indication(hapd, pos);
+- if (pos != buf) {
+- if (wpabuf_resize(&beacon, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(beacon, buf, pos - buf);
++ pos = hostapd_eid_hs20_indication(hapd, buf);
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
+
+- if (wpabuf_resize(&proberesp, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(proberesp, buf, pos - buf);
+- }
+-
+ pos = hostapd_eid_osen(hapd, buf);
+- if (pos != buf) {
+- if (wpabuf_resize(&beacon, pos - buf) != 0)
+- goto fail;
+- wpabuf_put_data(beacon, buf, pos - buf);
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
++#endif /* CONFIG_HS20 */
+
+- if (wpabuf_resize(&proberesp, pos - buf) != 0)
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled ||
++ OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
++ pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0 ||
++ add_buf_data(&assocresp, buf, pos - buf) < 0)
+ goto fail;
+- wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+-#endif /* CONFIG_HS20 */
++#endif /* CONFIG_MBO */
+
+- if (hapd->conf->vendor_elements) {
+- size_t add = wpabuf_len(hapd->conf->vendor_elements);
+- if (wpabuf_resize(&beacon, add) == 0)
+- wpabuf_put_buf(beacon, hapd->conf->vendor_elements);
+- if (wpabuf_resize(&proberesp, add) == 0)
+- wpabuf_put_buf(proberesp, hapd->conf->vendor_elements);
+- }
++#ifdef CONFIG_OWE
++ pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
++ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
++ add_buf_data(&proberesp, buf, pos - buf) < 0)
++ goto fail;
++#endif /* CONFIG_OWE */
+
++ add_buf(&beacon, hapd->conf->vendor_elements);
++ add_buf(&proberesp, hapd->conf->vendor_elements);
++ add_buf(&assocresp, hapd->conf->assocresp_elements);
++
+ *beacon_ret = beacon;
+ *proberesp_ret = proberesp;
+ *assocresp_ret = assocresp;
+@@ -367,10 +356,44 @@
+ int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len)
+ {
++ struct wpa_driver_sta_auth_params params;
++#ifdef CONFIG_FILS
++ struct sta_info *sta;
++#endif /* CONFIG_FILS */
++
+ if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
+ return 0;
+- return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+- seq, status, ie, len);
++
++ os_memset(¶ms, 0, sizeof(params));
++
++#ifdef CONFIG_FILS
++ sta = ap_get_sta(hapd, addr);
++ if (!sta) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR
++ " not found for sta_auth processing",
++ MAC2STR(addr));
++ return 0;
++ }
++
++ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) {
++ params.fils_auth = 1;
++ wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
++ params.fils_snonce,
++ params.fils_kek,
++ ¶ms.fils_kek_len);
++ }
++#endif /* CONFIG_FILS */
++
++ params.own_addr = hapd->own_addr;
++ params.addr = addr;
++ params.seq = seq;
++ params.status = status;
++ params.ie = ie;
++ params.len = len;
++
++ return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
+ }
+
+
+@@ -390,7 +413,8 @@
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+- u32 flags, u8 qosinfo, u8 vht_opmode)
++ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
++ int set)
+ {
+ struct hostapd_sta_add_params params;
+
+@@ -412,6 +436,8 @@
+ params.vht_opmode = vht_opmode;
+ params.flags = hostapd_sta_flags_to_drv(flags);
+ params.qosinfo = qosinfo;
++ params.support_p2p_ps = supp_p2p_ps;
++ params.set = set;
+ return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
+ }
+
+@@ -468,7 +494,7 @@
+ return -1;
+ return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ bss_ctx, drv_priv, force_ifname, if_addr,
+- bridge, use_existing);
++ bridge, use_existing, 1);
+ }
+
+
+@@ -578,13 +604,13 @@
+
+ struct hostapd_hw_modes *
+ hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+- u16 *flags)
++ u16 *flags, u8 *dfs_domain)
+ {
+ if (hapd->driver == NULL ||
+ hapd->driver->get_hw_feature_data == NULL)
+ return NULL;
+ return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+- flags);
++ flags, dfs_domain);
+ }
+
+
+@@ -647,9 +673,21 @@
+ int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack)
+ {
++ if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
++ return 0;
++ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
++ NULL, 0);
++}
++
++
++int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
++ const void *msg, size_t len, int noack,
++ const u16 *csa_offs, size_t csa_offs_len)
++{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+- return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
++ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
++ csa_offs, csa_offs_len);
+ }
+
+
+@@ -656,7 +694,7 @@
+ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+ {
+- if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
++ if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+@@ -666,7 +704,7 @@
+ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+ {
+- if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
++ if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+@@ -687,6 +725,45 @@
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+ {
++ const u8 *bssid;
++ const u8 wildcard_bssid[ETH_ALEN] = {
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++ };
++
++ if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
++ return 0;
++ bssid = hapd->own_addr;
++ if (!is_multicast_ether_addr(dst) &&
++ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
++ struct sta_info *sta;
++
++ /*
++ * Public Action frames to a STA that is not a member of the BSS
++ * shall use wildcard BSSID value.
++ */
++ sta = ap_get_sta(hapd, dst);
++ if (!sta || !(sta->flags & WLAN_STA_ASSOC))
++ bssid = wildcard_bssid;
++ } else if (is_broadcast_ether_addr(dst) &&
++ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
++ /*
++ * The only current use case of Public Action frames with
++ * broadcast destination address is DPP PKEX. That case is
++ * directing all devices and not just the STAs within the BSS,
++ * so have to use the wildcard BSSID value.
++ */
++ bssid = wildcard_bssid;
++ }
++ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
++ hapd->own_addr, bssid, data, len, 0);
++}
++
++
++int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
++ unsigned int freq,
++ unsigned int wait, const u8 *dst,
++ const u8 *data, size_t len)
++{
+ if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+ return 0;
+ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+@@ -736,7 +813,7 @@
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+ {
+- if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
++ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+@@ -756,12 +833,28 @@
+ if ((acs_ch_list_all ||
+ freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+ chan->chan)) &&
+- !(chan->flag & HOSTAPD_CHAN_DISABLED))
++ !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
++ !(hapd->iface->conf->acs_exclude_dfs &&
++ (chan->flag & HOSTAPD_CHAN_RADAR)))
+ int_array_add_unique(freq_list, chan->freq);
+ }
+ }
+
+
++void hostapd_get_ext_capa(struct hostapd_iface *iface)
++{
++ struct hostapd_data *hapd = iface->bss[0];
++
++ if (!hapd->driver || !hapd->driver->get_ext_capab)
++ return;
++
++ hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
++ &iface->extended_capa,
++ &iface->extended_capa_mask,
++ &iface->extended_capa_len);
++}
++
++
+ int hostapd_drv_do_acs(struct hostapd_data *hapd)
+ {
+ struct drv_acs_params params;
+@@ -797,6 +890,9 @@
+ &hapd->iface->conf->acs_ch_list,
+ chan->chan))
+ continue;
++ if (hapd->iface->conf->acs_exclude_dfs &&
++ (chan->flag & HOSTAPD_CHAN_RADAR))
++ continue;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+ channels[num_channels++] = chan->chan;
+ int_array_add_unique(&freq_list, chan->freq);
+--- contrib/wpa/src/ap/ap_drv_ops.h.orig
++++ contrib/wpa/src/ap/ap_drv_ops.h
+@@ -41,7 +41,8 @@
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+- u32 flags, u8 qosinfo, u8 vht_opmode);
++ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
++ int set);
+ int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+ int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len);
+@@ -71,7 +72,7 @@
+ int cw_min, int cw_max, int burst_time);
+ struct hostapd_hw_modes *
+ hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+- u16 *flags);
++ u16 *flags, u8 *dfs_domain);
+ int hostapd_driver_commit(struct hostapd_data *hapd);
+ int hostapd_drv_none(struct hostapd_data *hapd);
+ int hostapd_driver_scan(struct hostapd_data *hapd,
+@@ -88,6 +89,9 @@
+ const u8 *key, size_t key_len);
+ int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack);
++int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
++ const void *msg, size_t len, int noack,
++ const u16 *csa_offs, size_t csa_offs_len);
+ int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+@@ -95,6 +99,18 @@
+ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len);
++int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
++ unsigned int freq,
++ unsigned int wait, const u8 *dst,
++ const u8 *data, size_t len);
++static inline void
++hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
++{
++ if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
++ !hapd->drv_priv)
++ return;
++ hapd->driver->send_action_cancel_wait(hapd->drv_priv);
++}
+ int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg);
+ int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+@@ -120,6 +136,8 @@
+ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
++void hostapd_get_ext_capa(struct hostapd_iface *iface);
++
+ static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+ int enabled)
+ {
+@@ -150,7 +168,7 @@
+ static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+ {
+- if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
++ if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_remove(hapd->drv_priv, addr);
+ }
+@@ -264,8 +282,9 @@
+ static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+ {
+- if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
+- return -ENOTSUP;
++ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
++ hapd->drv_priv == NULL)
++ return -1;
+
+ return hapd->driver->switch_channel(hapd->drv_priv, settings);
+ }
+@@ -273,7 +292,7 @@
+ static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+ {
+- if (hapd->driver == NULL || hapd->driver->status == NULL)
++ if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
+ return -1;
+ return hapd->driver->status(hapd->drv_priv, buf, buflen);
+ }
+@@ -332,9 +351,27 @@
+
+ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+ {
+- if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
++ if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->stop_ap(hapd->drv_priv);
+ }
+
++static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
++ struct wpa_channel_info *ci)
++{
++ if (!hapd->driver || !hapd->driver->channel_info)
++ return -1;
++ return hapd->driver->channel_info(hapd->drv_priv, ci);
++}
++
++static inline int
++hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
++ struct external_auth *params)
++{
++ if (!hapd->driver || !hapd->drv_priv ||
++ !hapd->driver->send_external_auth_status)
++ return -1;
++ return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
++}
++
+ #endif /* AP_DRV_OPS */
+--- contrib/wpa/src/ap/ap_mlme.c.orig
++++ contrib/wpa/src/ap/ap_mlme.c
+@@ -57,8 +57,13 @@
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+ MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+- if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
++ if (sta->auth_alg != WLAN_AUTH_FT &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
++ sta->auth_alg != WLAN_AUTH_FILS_PK &&
++ !(sta->flags & WLAN_STA_MFP))
+ mlme_deletekeys_request(hapd, sta);
++ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ }
+
+
+@@ -104,8 +109,12 @@
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-ASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+- if (sta->auth_alg != WLAN_AUTH_FT)
++ if (sta->auth_alg != WLAN_AUTH_FT &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
++ sta->auth_alg != WLAN_AUTH_FILS_PK)
+ mlme_deletekeys_request(hapd, sta);
++ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ }
+
+
+@@ -128,8 +137,12 @@
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-REASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+- if (sta->auth_alg != WLAN_AUTH_FT)
++ if (sta->auth_alg != WLAN_AUTH_FT &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
++ sta->auth_alg != WLAN_AUTH_FILS_PK)
+ mlme_deletekeys_request(hapd, sta);
++ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ }
+
+
+--- contrib/wpa/src/ap/authsrv.c.orig
++++ contrib/wpa/src/ap/authsrv.c
+@@ -71,13 +71,19 @@
+ }
+
+ if (eap_user->password) {
+- user->password = os_malloc(eap_user->password_len);
++ user->password = os_memdup(eap_user->password,
++ eap_user->password_len);
+ if (user->password == NULL)
+ goto out;
+- os_memcpy(user->password, eap_user->password,
+- eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
++ if (eap_user->salt && eap_user->salt_len) {
++ user->salt = os_memdup(eap_user->salt,
++ eap_user->salt_len);
++ if (!user->salt)
++ goto out;
++ user->salt_len = eap_user->salt_len;
++ }
+ }
+ user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
+@@ -84,6 +90,7 @@
+ user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
+ user->accept_attr = eap_user->accept_attr;
++ user->t_c_timestamp = eap_user->t_c_timestamp;
+ rv = 0;
+
+ out:
+@@ -129,10 +136,13 @@
+ #ifdef CONFIG_HS20
+ srv.subscr_remediation_url = conf->subscr_remediation_url;
+ srv.subscr_remediation_method = conf->subscr_remediation_method;
++ srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
++ srv.t_c_server_url = conf->t_c_server_url;
+ #endif /* CONFIG_HS20 */
+ srv.erp = conf->eap_server_erp;
+ srv.erp_domain = conf->erp_domain;
+ srv.tls_session_lifetime = conf->tls_session_lifetime;
++ srv.tls_flags = conf->tls_flags;
+
+ hapd->radius_srv = radius_server_init(&srv);
+ if (hapd->radius_srv == NULL) {
+@@ -146,6 +156,40 @@
+ #endif /* RADIUS_SERVER */
+
+
++#ifdef EAP_TLS_FUNCS
++static void authsrv_tls_event(void *ctx, enum tls_event ev,
++ union tls_event_data *data)
++{
++ switch (ev) {
++ case TLS_CERT_CHAIN_SUCCESS:
++ wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
++ break;
++ case TLS_CERT_CHAIN_FAILURE:
++ wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
++ data->cert_fail.reason,
++ data->cert_fail.depth,
++ data->cert_fail.subject,
++ data->cert_fail.reason_txt);
++ break;
++ case TLS_PEER_CERTIFICATE:
++ wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
++ data->peer_cert.depth,
++ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
++ data->peer_cert.subject);
++ break;
++ case TLS_ALERT:
++ if (data->alert.is_local)
++ wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
++ data->alert.description);
++ else
++ wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
++ data->alert.description);
++ break;
++ }
++}
++#endif /* EAP_TLS_FUNCS */
++
++
+ int authsrv_init(struct hostapd_data *hapd)
+ {
+ #ifdef EAP_TLS_FUNCS
+@@ -157,6 +201,19 @@
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
++ if (hapd->conf->crl_reload_interval > 0 &&
++ hapd->conf->check_crl <= 0) {
++ wpa_printf(MSG_INFO,
++ "Cannot enable CRL reload functionality - it depends on check_crl being set");
++ } else if (hapd->conf->crl_reload_interval > 0) {
++ conf.crl_reload_interval =
++ hapd->conf->crl_reload_interval;
++ wpa_printf(MSG_INFO,
++ "Enabled CRL reload functionality");
++ }
++ conf.tls_flags = hapd->conf->tls_flags;
++ conf.event_cb = authsrv_tls_event;
++ conf.cb_ctx = hapd;
+ hapd->ssl_ctx = tls_init(&conf);
+ if (hapd->ssl_ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+@@ -171,8 +228,12 @@
+ params.private_key_passwd = hapd->conf->private_key_passwd;
+ params.dh_file = hapd->conf->dh_file;
+ params.openssl_ciphers = hapd->conf->openssl_ciphers;
++ params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
+ params.ocsp_stapling_response =
+ hapd->conf->ocsp_stapling_response;
++ params.ocsp_stapling_response_multi =
++ hapd->conf->ocsp_stapling_response_multi;
++ params.check_cert_subject = hapd->conf->check_cert_subject;
+
+ if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) {
+ wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+@@ -181,7 +242,8 @@
+ }
+
+ if (tls_global_set_verify(hapd->ssl_ctx,
+- hapd->conf->check_crl)) {
++ hapd->conf->check_crl,
++ hapd->conf->check_crl_strict)) {
+ wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+ authsrv_deinit(hapd);
+ return -1;
+@@ -193,6 +255,7 @@
+ if (hapd->conf->eap_sim_db) {
+ hapd->eap_sim_db_priv =
+ eap_sim_db_init(hapd->conf->eap_sim_db,
++ hapd->conf->eap_sim_db_timeout,
+ hostapd_sim_db_cb, hapd);
+ if (hapd->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+--- contrib/wpa/src/ap/beacon.c.orig
++++ contrib/wpa/src/ap/beacon.c
+@@ -16,6 +16,7 @@
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
+ #include "common/hw_features_common.h"
++#include "common/wpa_ctrl.h"
+ #include "wps/wps_defs.h"
+ #include "p2p/p2p.h"
+ #include "hostapd.h"
+@@ -29,6 +30,8 @@
+ #include "beacon.h"
+ #include "hs20.h"
+ #include "dfs.h"
++#include "taxonomy.h"
++#include "ieee802_11_auth.h"
+
+
+ #ifdef NEED_AP_MLME
+@@ -36,18 +39,21 @@
+ static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+ {
+- if (!hapd->conf->radio_measurements || len < 2 + 4)
++ size_t i;
++
++ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
++ if (hapd->conf->radio_measurements[i])
++ break;
++ }
++
++ if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
+ return eid;
+
+ *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+- *eid++ = 5;
+- *eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
+- WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
+- *eid++ = 0x00;
+- *eid++ = 0x00;
+- *eid++ = 0x00;
+- *eid++ = 0x00;
+- return eid;
++ *eid++ = RRM_CAPABILITIES_IE_LEN;
++ os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
++
++ return eid + RRM_CAPABILITIES_IE_LEN;
+ }
+
+
+@@ -297,19 +303,18 @@
+
+ static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+ {
+- u8 chan;
+-
+- if (!hapd->cs_freq_params.freq)
++#ifdef CONFIG_TESTING_OPTIONS
++ if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
+ return eid;
++#endif /* CONFIG_TESTING_OPTIONS */
+
+- if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
+- NUM_HOSTAPD_MODES)
++ if (!hapd->cs_freq_params.channel)
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3;
+ *eid++ = hapd->cs_block_tx;
+- *eid++ = chan;
++ *eid++ = hapd->cs_freq_params.channel;
+ *eid++ = hapd->cs_count;
+
+ return eid;
+@@ -316,46 +321,47 @@
+ }
+
+
+-static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
++static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
+ {
+- u8 sec_ch;
+-
+- if (!hapd->cs_freq_params.sec_channel_offset)
++ if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
+ return eid;
+
+- if (hapd->cs_freq_params.sec_channel_offset == -1)
+- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+- else if (hapd->cs_freq_params.sec_channel_offset == 1)
+- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+- else
+- return eid;
++ *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
++ *eid++ = 4;
++ *eid++ = hapd->cs_block_tx;
++ *eid++ = hapd->iface->cs_oper_class;
++ *eid++ = hapd->cs_freq_params.channel;
++ *eid++ = hapd->cs_count;
+
+- *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+- *eid++ = 1;
+- *eid++ = sec_ch;
+-
+ return eid;
+ }
+
+
+-static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
+- u8 *start, unsigned int *csa_counter_off)
++static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+ {
+- u8 *old_pos = pos;
++ u8 op_class, channel;
+
+- if (!csa_counter_off)
+- return pos;
++ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
++ !hapd->iface->freq)
++ return eid;
+
+- *csa_counter_off = 0;
+- pos = hostapd_eid_csa(hapd, pos);
++ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
++ hapd->iconf->secondary_channel,
++ hapd->iconf->vht_oper_chwidth,
++ &op_class, &channel) ==
++ NUM_HOSTAPD_MODES)
++ return eid;
+
+- if (pos != old_pos) {
+- /* save an offset to the counter - should be last byte */
+- *csa_counter_off = pos - start - 1;
+- pos = hostapd_eid_secondary_channel(hapd, pos);
+- }
++ *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
++ *eid++ = 2;
+
+- return pos;
++ /* Current Operating Class */
++ *eid++ = op_class;
++
++ /* TODO: Advertise all the supported operating classes */
++ *eid++ = 0;
++
++ return eid;
+ }
+
+
+@@ -364,7 +370,7 @@
+ int is_p2p, size_t *resp_len)
+ {
+ struct ieee80211_mgmt *resp;
+- u8 *pos, *epos;
++ u8 *pos, *epos, *csa_pos;
+ size_t buflen;
+
+ #define MAX_PROBERESP_LEN 768
+@@ -387,6 +393,18 @@
+ buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
++
++#ifdef CONFIG_IEEE80211AX
++ if (hapd->iconf->ieee80211ax) {
++ buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
++ 3 + sizeof(struct ieee80211_he_operation) +
++ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
++ }
++#endif /* CONFIG_IEEE80211AX */
++
++ buflen += hostapd_mbo_ie_len(hapd);
++ buflen += hostapd_eid_owe_trans_len(hapd);
++
+ resp = os_zalloc(buflen);
+ if (resp == NULL)
+ return NULL;
+@@ -424,6 +442,12 @@
+ /* Power Constraint element */
+ pos = hostapd_eid_pwr_constraint(hapd, pos);
+
++ /* CSA IE */
++ csa_pos = hostapd_eid_csa(hapd, pos);
++ if (csa_pos != pos)
++ hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
++ pos = csa_pos;
++
+ /* ERP Information element */
+ pos = hostapd_eid_erp_info(hapd, pos);
+
+@@ -430,14 +454,27 @@
+ /* Extended supported rates */
+ pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+- /* RSN, MDIE, WPA */
+- pos = hostapd_eid_wpa(hapd, pos, epos - pos);
++ /* RSN, MDIE */
++ if (hapd->conf->wpa != WPA_PROTO_WPA)
++ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+
+ pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
+ pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+
++ /* eCSA IE */
++ csa_pos = hostapd_eid_ecsa(hapd, pos);
++ if (csa_pos != pos)
++ hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
++ pos = csa_pos;
++
++ pos = hostapd_eid_supported_op_classes(hapd, pos);
++
+ #ifdef CONFIG_IEEE80211N
++ /* Secondary Channel Offset element */
++ /* TODO: The standard doesn't specify a position for this element. */
++ pos = hostapd_eid_secondary_channel(hapd, pos);
++
+ pos = hostapd_eid_ht_capabilities(hapd, pos);
+ pos = hostapd_eid_ht_operation(hapd, pos);
+ #endif /* CONFIG_IEEE80211N */
+@@ -451,9 +488,6 @@
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+
+- pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
+- &hapd->cs_c_off_proberesp);
+-
+ #ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+@@ -464,13 +498,32 @@
+
+ #ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+- pos = hostapd_eid_vht_capabilities(hapd, pos);
++ pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+ pos = hostapd_eid_vht_operation(hapd, pos);
++ pos = hostapd_eid_txpower_envelope(hapd, pos);
++ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ }
++#endif /* CONFIG_IEEE80211AC */
++
++ pos = hostapd_eid_fils_indic(hapd, pos, 0);
++
++#ifdef CONFIG_IEEE80211AX
++ if (hapd->iconf->ieee80211ax) {
++ pos = hostapd_eid_he_capab(hapd, pos);
++ pos = hostapd_eid_he_operation(hapd, pos);
++ pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
++ }
++#endif /* CONFIG_IEEE80211AX */
++
++#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht)
+ pos = hostapd_eid_vendor_vht(hapd, pos);
+ #endif /* CONFIG_IEEE80211AC */
+
++ /* WPA */
++ if (hapd->conf->wpa == WPA_PROTO_WPA)
++ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
++
+ /* Wi-Fi Alliance WMM */
+ pos = hostapd_eid_wmm(hapd, pos);
+
+@@ -501,6 +554,9 @@
+ pos = hostapd_eid_osen(hapd, pos);
+ #endif /* CONFIG_HS20 */
+
++ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
++ pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
++
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+@@ -537,8 +593,8 @@
+
+ pos = ssid_list;
+ end = ssid_list + ssid_list_len;
+- while (pos + 1 <= end) {
+- if (pos + 2 + pos[1] > end)
++ while (end - pos >= 1) {
++ if (2 + pos[1] > end - pos)
+ break;
+ if (pos[1] == 0)
+ wildcard = 1;
+@@ -574,7 +630,7 @@
+ MAC2STR(info->addr));
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+- os_free(info);
++ sta_track_del(info);
+ }
+ }
+
+@@ -592,7 +648,7 @@
+ }
+
+
+-void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
++void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
+ {
+ struct hostapd_sta_info *info;
+
+@@ -602,13 +658,17 @@
+ dl_list_del(&info->list);
+ dl_list_add_tail(&iface->sta_seen, &info->list);
+ os_get_reltime(&info->last_seen);
++ info->ssi_signal = ssi_signal;
+ return;
+ }
+
+ /* Add a new entry */
+ info = os_zalloc(sizeof(*info));
++ if (info == NULL)
++ return;
+ os_memcpy(info->addr, addr, ETH_ALEN);
+ os_get_reltime(&info->last_seen);
++ info->ssi_signal = ssi_signal;
+
+ if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+ /* Expire oldest entry to make room for a new one */
+@@ -648,6 +708,23 @@
+ }
+
+
++#ifdef CONFIG_TAXONOMY
++void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
++ struct wpabuf **probe_ie_taxonomy)
++{
++ struct hostapd_sta_info *info;
++
++ info = sta_track_get(iface, addr);
++ if (!info)
++ return;
++
++ wpabuf_free(*probe_ie_taxonomy);
++ *probe_ie_taxonomy = info->probe_ie_taxonomy;
++ info->probe_ie_taxonomy = NULL;
++}
++#endif /* CONFIG_TAXONOMY */
++
++
+ void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int ssi_signal)
+@@ -659,14 +736,33 @@
+ size_t i, resp_len;
+ int noack;
+ enum ssid_match_result res;
++ int ret;
++ u16 csa_offs[2];
++ size_t csa_offs_len;
++ u32 session_timeout, acct_interim_interval;
++ struct vlan_description vlan_id;
++ struct hostapd_sta_wpa_psk_short *psk = NULL;
++ char *identity = NULL;
++ char *radius_cui = NULL;
+
+- ie = mgmt->u.probe_req.variable;
+- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
++ if (len < IEEE80211_HDRLEN)
+ return;
++ ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
+ if (hapd->iconf->track_sta_max_num)
+- sta_track_add(hapd->iface, mgmt->sa);
+- ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
++ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
++ ie_len = len - IEEE80211_HDRLEN;
+
++ ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
++ &session_timeout,
++ &acct_interim_interval, &vlan_id,
++ &psk, &identity, &radius_cui, 1);
++ if (ret == HOSTAPD_ACL_REJECT) {
++ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
++ "Ignore Probe Request frame from " MACSTR
++ " due to ACL reject ", MAC2STR(mgmt->sa));
++ return;
++ }
++
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ mgmt->sa, mgmt->da, mgmt->bssid,
+@@ -673,7 +769,7 @@
+ ie, ie_len, ssi_signal) > 0)
+ return;
+
+- if (!hapd->iconf->send_probe_response)
++ if (!hapd->conf->send_probe_response)
+ return;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+@@ -711,7 +807,7 @@
+ }
+
+ #ifdef CONFIG_P2P
+- if (hapd->p2p && elems.wps_ie) {
++ if (hapd->p2p && hapd->p2p_group && elems.wps_ie) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+@@ -724,7 +820,7 @@
+ wpabuf_free(wps);
+ }
+
+- if (hapd->p2p && elems.p2p) {
++ if (hapd->p2p && hapd->p2p_group && elems.p2p) {
+ struct wpabuf *p2p;
+ p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+ if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+@@ -754,6 +850,21 @@
+ }
+ #endif /* CONFIG_P2P */
+
++#ifdef CONFIG_TAXONOMY
++ {
++ struct sta_info *sta;
++ struct hostapd_sta_info *info;
++
++ if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
++ taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
++ } else if ((info = sta_track_get(hapd->iface,
++ mgmt->sa)) != NULL) {
++ taxonomy_hostapd_sta_info_probe_req(hapd, info,
++ ie, ie_len);
++ }
++ }
++#endif /* CONFIG_TAXONOMY */
++
+ res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+ elems.ssid_list, elems.ssid_list_len);
+ if (res == NO_SSID_MATCH) {
+@@ -825,6 +936,17 @@
+ return;
+ }
+
++ if (hapd->conf->no_probe_resp_if_max_sta &&
++ is_multicast_ether_addr(mgmt->da) &&
++ is_multicast_ether_addr(mgmt->bssid) &&
++ hapd->num_sta >= hapd->conf->max_num_sta &&
++ !ap_get_sta(hapd, mgmt->sa)) {
++ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
++ " since no room for additional STA",
++ hapd->conf->iface, MAC2STR(mgmt->sa));
++ return;
++ }
++
+ #ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->ignore_probe_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_probe_probability) {
+@@ -835,6 +957,9 @@
+ }
+ #endif /* CONFIG_TESTING_OPTIONS */
+
++ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
++ " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
++
+ resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
+ &resp_len);
+ if (resp == NULL)
+@@ -847,7 +972,22 @@
+ noack = !!(res == WILDCARD_SSID_MATCH &&
+ is_broadcast_ether_addr(mgmt->da));
+
+- if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
++ csa_offs_len = 0;
++ if (hapd->csa_in_progress) {
++ if (hapd->cs_c_off_proberesp)
++ csa_offs[csa_offs_len++] =
++ hapd->cs_c_off_proberesp;
++
++ if (hapd->cs_c_off_ecsa_proberesp)
++ csa_offs[csa_offs_len++] =
++ hapd->cs_c_off_ecsa_proberesp;
++ }
++
++ ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack,
++ csa_offs_len ? csa_offs : NULL,
++ csa_offs_len);
++
++ if (ret < 0)
+ wpa_printf(MSG_INFO, "handle_probe_req: send failed");
+
+ os_free(resp);
+@@ -896,6 +1036,16 @@
+ #endif /* NEED_AP_MLME */
+
+
++void sta_track_del(struct hostapd_sta_info *info)
++{
++#ifdef CONFIG_TAXONOMY
++ wpabuf_free(info->probe_ie_taxonomy);
++ info->probe_ie_taxonomy = NULL;
++#endif /* CONFIG_TAXONOMY */
++ os_free(info);
++}
++
++
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+ {
+@@ -906,7 +1056,7 @@
+ size_t resp_len = 0;
+ #ifdef NEED_AP_MLME
+ u16 capab_info;
+- u8 *pos, *tailpos;
++ u8 *pos, *tailpos, *csa_pos;
+
+ #define BEACON_HEAD_BUF_SIZE 256
+ #define BEACON_TAIL_BUF_SIZE 512
+@@ -934,6 +1084,17 @@
+ }
+ #endif /* CONFIG_IEEE80211AC */
+
++#ifdef CONFIG_IEEE80211AX
++ if (hapd->iconf->ieee80211ax) {
++ tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
++ 3 + sizeof(struct ieee80211_he_operation) +
++ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
++ }
++#endif /* CONFIG_IEEE80211AX */
++
++ tail_len += hostapd_mbo_ie_len(hapd);
++ tail_len += hostapd_eid_owe_trans_len(hapd);
++
+ tailpos = tail = os_malloc(tail_len);
+ if (head == NULL || tail == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to set beacon data");
+@@ -987,6 +1148,12 @@
+ /* Power Constraint element */
+ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
++ /* CSA IE */
++ csa_pos = hostapd_eid_csa(hapd, tailpos);
++ if (csa_pos != tailpos)
++ hapd->cs_c_off_beacon = csa_pos - tail - 1;
++ tailpos = csa_pos;
++
+ /* ERP Information element */
+ tailpos = hostapd_eid_erp_info(hapd, tailpos);
+
+@@ -993,9 +1160,11 @@
+ /* Extended supported rates */
+ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+- /* RSN, MDIE, WPA */
+- tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
+- tailpos);
++ /* RSN, MDIE */
++ if (hapd->conf->wpa != WPA_PROTO_WPA)
++ tailpos = hostapd_eid_wpa(hapd, tailpos,
++ tail + BEACON_TAIL_BUF_SIZE -
++ tailpos);
+
+ tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+@@ -1004,7 +1173,19 @@
+ tailpos = hostapd_eid_bss_load(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
++ /* eCSA IE */
++ csa_pos = hostapd_eid_ecsa(hapd, tailpos);
++ if (csa_pos != tailpos)
++ hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
++ tailpos = csa_pos;
++
++ tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
++
+ #ifdef CONFIG_IEEE80211N
++ /* Secondary Channel Offset element */
++ /* TODO: The standard doesn't specify a position for this element. */
++ tailpos = hostapd_eid_secondary_channel(hapd, tailpos);
++
+ tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+ #endif /* CONFIG_IEEE80211N */
+@@ -1020,8 +1201,6 @@
+ tailpos = hostapd_eid_interworking(hapd, tailpos);
+ tailpos = hostapd_eid_adv_proto(hapd, tailpos);
+ tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
+- tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
+- &hapd->cs_c_off_beacon);
+
+ #ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+@@ -1033,13 +1212,34 @@
+
+ #ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+- tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
++ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
+ tailpos = hostapd_eid_vht_operation(hapd, tailpos);
++ tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
++ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+ }
++#endif /* CONFIG_IEEE80211AC */
++
++ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
++
++#ifdef CONFIG_IEEE80211AX
++ if (hapd->iconf->ieee80211ax) {
++ tailpos = hostapd_eid_he_capab(hapd, tailpos);
++ tailpos = hostapd_eid_he_operation(hapd, tailpos);
++ tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
++ }
++#endif /* CONFIG_IEEE80211AX */
++
++#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht)
+ tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
+ #endif /* CONFIG_IEEE80211AC */
+
++ /* WPA */
++ if (hapd->conf->wpa == WPA_PROTO_WPA)
++ tailpos = hostapd_eid_wpa(hapd, tailpos,
++ tail + BEACON_TAIL_BUF_SIZE -
++ tailpos);
++
+ /* Wi-Fi Alliance WMM */
+ tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+@@ -1069,6 +1269,10 @@
+ tailpos = hostapd_eid_osen(hapd, tailpos);
+ #endif /* CONFIG_HS20 */
+
++ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
++ tailpos = hostapd_eid_owe_trans(hapd, tailpos,
++ tail + tail_len - tailpos);
++
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+@@ -1090,6 +1294,8 @@
+ params->dtim_period = hapd->conf->dtim_period;
+ params->beacon_int = hapd->iconf->beacon_int;
+ params->basic_rates = hapd->iface->basic_rates;
++ params->beacon_rate = hapd->iconf->beacon_rate;
++ params->rate_type = hapd->iconf->rate_type;
+ params->ssid = hapd->conf->ssid.ssid;
+ params->ssid_len = hapd->conf->ssid.ssid_len;
+ if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+@@ -1153,6 +1359,20 @@
+ params->osen = 1;
+ }
+ #endif /* CONFIG_HS20 */
++ params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
++ params->pbss = hapd->conf->pbss;
++
++ if (hapd->conf->ftm_responder) {
++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
++ params->ftm_responder = 1;
++ params->lci = hapd->iface->conf->lci;
++ params->civic = hapd->iface->conf->civic;
++ } else {
++ wpa_printf(MSG_WARNING,
++ "Not configuring FTM responder as the driver doesn't advertise support for it");
++ }
++ }
++
+ return 0;
+ }
+
+--- contrib/wpa/src/ap/beacon.h.orig
++++ contrib/wpa/src/ap/beacon.h
+@@ -21,10 +21,13 @@
+ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params);
+ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+-void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
++void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
++void sta_track_del(struct hostapd_sta_info *info);
+ void sta_track_expire(struct hostapd_iface *iface, int force);
+ struct hostapd_data *
+ sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+ const char *ifname);
++void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
++ struct wpabuf **probe_ie_taxonomy);
+
+ #endif /* BEACON_H */
+--- contrib/wpa/src/ap/bss_load.c.orig
++++ contrib/wpa/src/ap/bss_load.c
+@@ -16,11 +16,35 @@
+ #include "beacon.h"
+
+
++static int get_bss_load_update_timeout(struct hostapd_data *hapd,
++ unsigned int *sec, unsigned int *usec)
++{
++ unsigned int update_period = hapd->conf->bss_load_update_period;
++ unsigned int beacon_int = hapd->iconf->beacon_int;
++ unsigned int update_timeout;
++
++ if (!update_period || !beacon_int) {
++ wpa_printf(MSG_ERROR,
++ "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
++ update_period, beacon_int);
++ return -1;
++ }
++
++ update_timeout = update_period * beacon_int;
++
++ *sec = ((update_timeout / 1000) * 1024) / 1000;
++ *usec = (update_timeout % 1000) * 1024;
++
++ return 0;
++}
++
++
+ static void update_channel_utilization(void *eloop_data, void *user_data)
+ {
+ struct hostapd_data *hapd = eloop_data;
+ unsigned int sec, usec;
+ int err;
++ struct hostapd_iface *iface = hapd->iface;
+
+ if (!(hapd->beacon_set_done && hapd->started))
+ return;
+@@ -33,8 +57,24 @@
+
+ ieee802_11_set_beacon(hapd);
+
+- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
++ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
++ return;
++
++ if (hapd->conf->chan_util_avg_period) {
++ iface->chan_util_samples_sum += iface->channel_utilization;
++ iface->chan_util_num_sample_periods +=
++ hapd->conf->bss_load_update_period;
++ if (iface->chan_util_num_sample_periods >=
++ hapd->conf->chan_util_avg_period) {
++ iface->chan_util_average =
++ iface->chan_util_samples_sum /
++ (iface->chan_util_num_sample_periods /
++ hapd->conf->bss_load_update_period);
++ iface->chan_util_samples_sum = 0;
++ iface->chan_util_num_sample_periods = 0;
++ }
++ }
++
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+ }
+@@ -42,17 +82,11 @@
+
+ int bss_load_update_init(struct hostapd_data *hapd)
+ {
+- struct hostapd_bss_config *conf = hapd->conf;
+- struct hostapd_config *iconf = hapd->iconf;
+ unsigned int sec, usec;
+
+- if (!conf->bss_load_update_period || !iconf->beacon_int)
++ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+ return -1;
+
+- hapd->bss_load_update_timeout = conf->bss_load_update_period *
+- iconf->beacon_int;
+- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+ return 0;
+--- contrib/wpa/src/ap/ctrl_iface_ap.c.orig
++++ contrib/wpa/src/ap/ctrl_iface_ap.c
+@@ -1,6 +1,6 @@
+ /*
+ * Control interface for shared AP commands
+- * Copyright (c) 2004-2014, Jouni Malinen
++ * Copyright (c) 2004-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -22,8 +22,34 @@
+ #include "p2p_hostapd.h"
+ #include "ctrl_iface_ap.h"
+ #include "ap_drv_ops.h"
++#include "mbo_ap.h"
++#include "taxonomy.h"
+
+
++static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
++ size_t curr_len, const u8 *mcs_set)
++{
++ int ret;
++ size_t len = curr_len;
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "ht_mcs_bitmask=");
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ /* 77 first bits (+ 3 reserved bits) */
++ len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
++
++ ret = os_snprintf(buf + len, buflen - len, "\n");
++ if (os_snprintf_error(buflen - len, ret))
++ return curr_len;
++ len += ret;
++
++ return len;
++}
++
++
+ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+@@ -30,17 +56,111 @@
+ {
+ struct hostap_sta_driver_data data;
+ int ret;
++ int len = 0;
+
+ if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+ return 0;
+
+ ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+- "rx_bytes=%lu\ntx_bytes=%lu\n",
++ "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
++ "signal=%d\n",
+ data.rx_packets, data.tx_packets,
+- data.rx_bytes, data.tx_bytes);
++ data.rx_bytes, data.tx_bytes, data.inactive_msec,
++ data.signal);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+- return ret;
++ len += ret;
++
++ ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
++ data.current_rx_rate);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ if (data.flags & STA_DRV_DATA_RX_MCS) {
++ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
++ data.rx_mcs);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
++ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
++ data.rx_vhtmcs);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
++ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
++ data.rx_vht_nss);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
++ ret = os_snprintf(buf + len, buflen - len, " shortGI");
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ ret = os_snprintf(buf + len, buflen - len, "\n");
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++
++ ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
++ data.current_tx_rate);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ if (data.flags & STA_DRV_DATA_TX_MCS) {
++ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
++ data.tx_mcs);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
++ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
++ data.tx_vhtmcs);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
++ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
++ data.tx_vht_nss);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
++ ret = os_snprintf(buf + len, buflen - len, " shortGI");
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++ ret = os_snprintf(buf + len, buflen - len, "\n");
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++
++ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "rx_vht_mcs_map=%04x\n"
++ "tx_vht_mcs_map=%04x\n",
++ le_to_host16(sta->vht_capabilities->
++ vht_supported_mcs_set.rx_map),
++ le_to_host16(sta->vht_capabilities->
++ vht_supported_mcs_set.tx_map));
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++
++ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
++ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
++ sta->ht_capabilities->
++ supported_mcs_set);
++ }
++
++ if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "last_ack_signal=%d\n", data.last_ack_rssi);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++
++ return len;
+ }
+
+
+@@ -87,6 +207,7 @@
+ char *buf, size_t buflen)
+ {
+ int len, res, ret, i;
++ const char *keyid;
+
+ if (!sta)
+ return 0;
+@@ -161,6 +282,73 @@
+ len += res;
+ }
+
++ res = mbo_ap_get_info(sta, buf + len, buflen - len);
++ if (res >= 0)
++ len += res;
++
++ if (sta->supp_op_classes &&
++ buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
++ len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
++ len += wpa_snprintf_hex(buf + len, buflen - len,
++ sta->supp_op_classes + 1,
++ sta->supp_op_classes[0]);
++ len += os_snprintf(buf + len, buflen - len, "\n");
++ }
++
++ if (sta->power_capab) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "min_txpower=%d\n"
++ "max_txpower=%d\n",
++ sta->min_tx_power, sta->max_tx_power);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++
++#ifdef CONFIG_IEEE80211AC
++ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
++ res = os_snprintf(buf + len, buflen - len,
++ "vht_caps_info=0x%08x\n",
++ le_to_host32(sta->vht_capabilities->
++ vht_capabilities_info));
++ if (!os_snprintf_error(buflen - len, res))
++ len += res;
++ }
++#endif /* CONFIG_IEEE80211AC */
++
++#ifdef CONFIG_IEEE80211N
++ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
++ res = os_snprintf(buf + len, buflen - len,
++ "ht_caps_info=0x%04x\n",
++ le_to_host16(sta->ht_capabilities->
++ ht_capabilities_info));
++ if (!os_snprintf_error(buflen - len, res))
++ len += res;
++ }
++#endif /* CONFIG_IEEE80211N */
++
++ if (sta->ext_capability &&
++ buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
++ len += os_snprintf(buf + len, buflen - len, "ext_capab=");
++ len += wpa_snprintf_hex(buf + len, buflen - len,
++ sta->ext_capability + 1,
++ sta->ext_capability[0]);
++ len += os_snprintf(buf + len, buflen - len, "\n");
++ }
++
++ if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "wds_sta_ifname=%s\n", sta->ifname_wds);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++
++ keyid = ap_sta_wpa_get_keyid(hapd, sta);
++ if (keyid) {
++ ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
++ if (!os_snprintf_error(buflen - len, ret))
++ len += ret;
++ }
++
+ return len;
+ }
+
+@@ -244,7 +432,7 @@
+ int ret;
+ u8 *pos;
+
+- if (hapd->driver->send_frame == NULL)
++ if (!hapd->drv_priv || !hapd->driver->send_frame)
+ return -1;
+
+ mgmt = os_zalloc(sizeof(*mgmt) + 100);
+@@ -255,7 +443,7 @@
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+ " with minor reason code %u (stype=%u (%s))",
+ MAC2STR(addr), minor_reason_code, stype,
+- fc2str(mgmt->frame_control));
++ fc2str(le_to_host16(mgmt->frame_control)));
+
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+@@ -263,11 +451,11 @@
+ if (stype == WLAN_FC_STYPE_DEAUTH) {
+ mgmt->u.deauth.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+- pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
++ pos = mgmt->u.deauth.variable;
+ } else {
+ mgmt->u.disassoc.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+- pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
++ pos = mgmt->u.disassoc.variable;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+@@ -311,7 +499,7 @@
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+- if (hapd->driver->send_frame == NULL)
++ if (!hapd->drv_priv || !hapd->driver->send_frame)
+ return -1;
+ pos += 6;
+ encrypt = atoi(pos);
+@@ -338,7 +526,10 @@
+ }
+ #endif /* CONFIG_P2P_MANAGER */
+
+- hostapd_drv_sta_deauth(hapd, addr, reason);
++ if (os_strstr(txtaddr, " tx=0"))
++ hostapd_drv_sta_remove(hapd, addr);
++ else
++ hostapd_drv_sta_deauth(hapd, addr, reason);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_deauthenticate(hapd, sta, reason);
+@@ -371,7 +562,7 @@
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+- if (hapd->driver->send_frame == NULL)
++ if (!hapd->drv_priv || !hapd->driver->send_frame)
+ return -1;
+ pos += 6;
+ encrypt = atoi(pos);
+@@ -398,7 +589,10 @@
+ }
+ #endif /* CONFIG_P2P_MANAGER */
+
+- hostapd_drv_sta_disassoc(hapd, addr, reason);
++ if (os_strstr(txtaddr, " tx=0"))
++ hostapd_drv_sta_remove(hapd, addr);
++ else
++ hostapd_drv_sta_disassoc(hapd, addr, reason);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_disassociate(hapd, sta, reason);
+@@ -409,11 +603,55 @@
+ }
+
+
++#ifdef CONFIG_TAXONOMY
++int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
++ const char *txtaddr,
++ char *buf, size_t buflen)
++{
++ u8 addr[ETH_ALEN];
++ struct sta_info *sta;
++
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
++
++ if (hwaddr_aton(txtaddr, addr))
++ return -1;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return -1;
++
++ return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
++}
++#endif /* CONFIG_TAXONOMY */
++
++
++int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
++ const char *txtaddr)
++{
++ u8 addr[ETH_ALEN];
++ struct sta_info *sta;
++
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
++
++ if (hwaddr_aton(txtaddr, addr))
++ return -1;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return -1;
++
++ hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
++ sta->flags & WLAN_STA_WMM);
++ return 0;
++}
++
++
+ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+ {
+ struct hostapd_iface *iface = hapd->iface;
+- int len = 0, ret;
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ int len = 0, ret, j;
+ size_t i;
+
+ ret = os_snprintf(buf + len, buflen - len,
+@@ -474,20 +712,93 @@
+ "secondary_channel=%d\n"
+ "ieee80211n=%d\n"
+ "ieee80211ac=%d\n"
+- "vht_oper_chwidth=%d\n"
+- "vht_oper_centr_freq_seg0_idx=%d\n"
+- "vht_oper_centr_freq_seg1_idx=%d\n",
++ "beacon_int=%u\n"
++ "dtim_period=%d\n",
+ iface->conf->channel,
+- iface->conf->secondary_channel,
+- iface->conf->ieee80211n,
+- iface->conf->ieee80211ac,
+- iface->conf->vht_oper_chwidth,
+- iface->conf->vht_oper_centr_freq_seg0_idx,
+- iface->conf->vht_oper_centr_freq_seg1_idx);
++ iface->conf->ieee80211n && !hapd->conf->disable_11n ?
++ iface->conf->secondary_channel : 0,
++ iface->conf->ieee80211n && !hapd->conf->disable_11n,
++ iface->conf->ieee80211ac &&
++ !hapd->conf->disable_11ac,
++ iface->conf->beacon_int,
++ hapd->conf->dtim_period);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
++ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "vht_oper_chwidth=%d\n"
++ "vht_oper_centr_freq_seg0_idx=%d\n"
++ "vht_oper_centr_freq_seg1_idx=%d\n"
++ "vht_caps_info=%08x\n",
++ iface->conf->vht_oper_chwidth,
++ iface->conf->vht_oper_centr_freq_seg0_idx,
++ iface->conf->vht_oper_centr_freq_seg1_idx,
++ iface->conf->vht_capab);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
+
++ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
++ u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
++ u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
++
++ ret = os_snprintf(buf + len, buflen - len,
++ "rx_vht_mcs_map=%04x\n"
++ "tx_vht_mcs_map=%04x\n",
++ rxmap, txmap);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++
++ if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "ht_caps_info=%04x\n",
++ hapd->iconf->ht_capab);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++
++ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
++ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
++ mode->mcs_set);
++ }
++
++ if (iface->current_rates && iface->num_rates) {
++ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++
++ for (j = 0; j < iface->num_rates; j++) {
++ ret = os_snprintf(buf + len, buflen - len, "%s%02x",
++ j > 0 ? " " : "",
++ iface->current_rates[j].rate / 5);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++ ret = os_snprintf(buf + len, buflen - len, "\n");
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++
++ for (j = 0; mode && j < mode->num_channels; j++) {
++ if (mode->channels[j].freq == iface->freq) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "max_txpower=%u\n",
++ mode->channels[j].max_tx_power);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ break;
++ }
++ }
++
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ ret = os_snprintf(buf + len, buflen - len,
+@@ -506,6 +817,15 @@
+ len += ret;
+ }
+
++ if (hapd->conf->chan_util_avg_period) {
++ ret = os_snprintf(buf + len, buflen - len,
++ "chan_util_avg=%u\n",
++ iface->chan_util_average);
++ if (os_snprintf_error(buflen - len, ret))
++ return len;
++ len += ret;
++ }
++
+ return len;
+ }
+
+@@ -554,3 +874,121 @@
+ {
+ return hostapd_drv_stop_ap(hapd);
+ }
++
++
++int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
++ size_t len)
++{
++ return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
++}
++
++
++void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
++{
++ wpa_auth_pmksa_flush(hapd->wpa_auth);
++}
++
++
++int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
++{
++ u8 spa[ETH_ALEN];
++ u8 pmkid[PMKID_LEN];
++ u8 pmk[PMK_LEN_MAX];
++ size_t pmk_len;
++ char *pos, *pos2;
++ int akmp = 0, expiration = 0;
++
++ /*
++ * Entry format:
++ *
++ */
++
++ if (hwaddr_aton(cmd, spa))
++ return -1;
++
++ pos = os_strchr(cmd, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++
++ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
++ return -1;
++
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return -1;
++ pos++;
++
++ pos2 = os_strchr(pos, ' ');
++ if (!pos2)
++ return -1;
++ pmk_len = (pos2 - pos) / 2;
++ if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
++ hexstr2bin(pos, pmk, pmk_len) < 0)
++ return -1;
++
++ pos = pos2 + 1;
++
++ if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
++ return -1;
++
++ return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
++ pmkid, expiration, akmp);
++}
++
++
++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
++#ifdef CONFIG_MESH
++
++int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
++ const u8 *addr, char *buf, size_t len)
++{
++ return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
++}
++
++
++void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
++{
++ u8 spa[ETH_ALEN];
++ u8 pmkid[PMKID_LEN];
++ u8 pmk[PMK_LEN_MAX];
++ char *pos;
++ int expiration;
++
++ /*
++ * Entry format:
++ *
++ */
++
++ if (hwaddr_aton(cmd, spa))
++ return NULL;
++
++ pos = os_strchr(cmd, ' ');
++ if (!pos)
++ return NULL;
++ pos++;
++
++ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
++ return NULL;
++
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return NULL;
++ pos++;
++
++ if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
++ return NULL;
++
++ pos = os_strchr(pos, ' ');
++ if (!pos)
++ return NULL;
++ pos++;
++
++ if (sscanf(pos, "%d", &expiration) != 1)
++ return NULL;
++
++ return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
++}
++
++#endif /* CONFIG_MESH */
++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+--- contrib/wpa/src/ap/ctrl_iface_ap.h.orig
++++ contrib/wpa/src/ap/ctrl_iface_ap.h
+@@ -19,10 +19,22 @@
+ const char *txtaddr);
+ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+ const char *txtaddr);
++int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
++ const char *txtaddr,
++ char *buf, size_t buflen);
++int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
++ const char *txtaddr);
+ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen);
+ int hostapd_parse_csa_settings(const char *pos,
+ struct csa_settings *settings);
+ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
++int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
++ size_t len);
++void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
++int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
++int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
++ const u8 *addr, char *buf, size_t len);
++void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
+
+ #endif /* CTRL_IFACE_AP_H */
+--- contrib/wpa/src/ap/dfs.c.orig
++++ contrib/wpa/src/ap/dfs.c
+@@ -1,7 +1,7 @@
+ /*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen
+- * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
++ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -142,6 +142,7 @@
+ {
+ struct hostapd_channel_data *first_chan, *chan;
+ int i;
++ u32 bw = num_chan_to_bw(num_chans);
+
+ if (first_chan_idx + num_chans > mode->num_channels)
+ return 0;
+@@ -148,6 +149,12 @@
+
+ first_chan = &mode->channels[first_chan_idx];
+
++ /* hostapd DFS implementation assumes the first channel as primary.
++ * If it's not allowed to use the first channel as primary, decline the
++ * whole channel range. */
++ if (!chan_pri_allowed(first_chan))
++ return 0;
++
+ for (i = 0; i < num_chans; i++) {
+ chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+ first_chan_idx);
+@@ -154,6 +161,11 @@
+ if (!chan)
+ return 0;
+
++ /* HT 40 MHz secondary channel availability checked only for
++ * primary channel */
++ if (!chan_bw_allowed(chan, bw, 1, !i))
++ return 0;
++
+ if (!dfs_channel_available(chan, skip_radar))
+ return 0;
+ }
+@@ -197,7 +209,8 @@
+ /* Skip HT40/VHT incompatible channels */
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel &&
+- !dfs_is_chan_allowed(chan, n_chans))
++ (!dfs_is_chan_allowed(chan, n_chans) ||
++ !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)))
+ continue;
+
+ /* Skip incompatible chandefs */
+@@ -450,7 +463,7 @@
+ return NULL;
+
+ if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+- _rand = os_random();
++ return NULL;
+ chan_idx = _rand % num_available_chandefs;
+ dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+
+@@ -704,7 +717,8 @@
+ skip_radar);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "could not get valid channel");
+- return -1;
++ hostapd_set_state(iface, HAPD_IFACE_DFS);
++ return 0;
+ }
+
+ iface->freq = channel->freq;
+@@ -746,6 +760,23 @@
+ }
+
+
++static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
++{
++ int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
++
++ /* Get the start (first) channel for current configuration */
++ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
++ if (start_chan_idx < 0)
++ return 0;
++
++ /* Get the number of used channels, depending on width */
++ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
++
++ /* Check if all channels are DFS available */
++ return dfs_check_chans_available(iface, start_chan_idx, n_chans);
++}
++
++
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+@@ -766,8 +797,21 @@
+ set_dfs_state(iface, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_AVAILABLE);
+- iface->cac_started = 0;
+- hostapd_setup_interface_complete(iface, 0);
++ /*
++ * Just mark the channel available when CAC completion
++ * event is received in enabled state. CAC result could
++ * have been propagated from another radio having the
++ * same regulatory configuration. When CAC completion is
++ * received during non-HAPD_IFACE_ENABLED state, make
++ * sure the configured channel is available because this
++ * CAC completion event could have been propagated from
++ * another radio.
++ */
++ if (iface->state != HAPD_IFACE_ENABLED &&
++ hostapd_config_dfs_chan_available(iface)) {
++ hostapd_setup_interface_complete(iface, 0);
++ iface->cac_started = 0;
++ }
+ }
+ }
+
+@@ -775,6 +819,25 @@
+ }
+
+
++int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2)
++{
++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
++ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
++ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
++
++ /* Proceed only if DFS is not offloaded to the driver */
++ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
++ return 0;
++
++ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
++ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
++
++ return 0;
++}
++
++
+ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+ {
+ struct hostapd_channel_data *channel;
+@@ -793,7 +856,6 @@
+
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "No valid channel available");
+- hostapd_setup_interface_complete(iface, err);
+ return err;
+ }
+
+@@ -817,16 +879,6 @@
+ }
+
+
+-static int hostapd_csa_in_progress(struct hostapd_iface *iface)
+-{
+- unsigned int i;
+- for (i = 0; i < iface->num_bss; i++)
+- if (iface->bss[i]->csa_in_progress)
+- return 1;
+- return 0;
+-}
+-
+-
+ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ {
+ struct hostapd_channel_data *channel;
+@@ -850,6 +902,13 @@
+ if (iface->cac_started)
+ return hostapd_dfs_start_channel_switch_cac(iface);
+
++ /*
++ * Allow selection of DFS channel in ETSI to comply with
++ * uniform spreading.
++ */
++ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
++ skip_radar = 0;
++
+ /* Perform channel switch/CSA */
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &vht_oper_centr_freq_seg0_idx,
+@@ -868,8 +927,9 @@
+ &vht_oper_centr_freq_seg1_idx,
+ skip_radar);
+ if (!channel) {
+- /* FIXME: Wait for channel(s) to become available */
+- hostapd_disable_iface(iface);
++ wpa_printf(MSG_INFO,
++ "%s: no DFS channels left, waiting for NOP to finish",
++ __func__);
+ return err;
+ }
+
+@@ -992,6 +1052,11 @@
+ /* TODO add correct implementation here */
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
++
++ /* Handle cases where all channels were initially unavailable */
++ if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
++ hostapd_handle_dfs(iface);
++
+ return 0;
+ }
+
+@@ -1059,7 +1124,8 @@
+ return 1;
+ }
+
+- if (ieee80211_is_dfs(iface->freq)) {
++ if (ieee80211_is_dfs(iface->freq, iface->hw_features,
++ iface->num_hw_features)) {
+ wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
+ __func__, iface->freq);
+ return 0;
+--- contrib/wpa/src/ap/dfs.h.orig
++++ contrib/wpa/src/ap/dfs.h
+@@ -1,7 +1,7 @@
+ /*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen
+- * Copyright (c) 2013, Qualcomm Atheros, Inc.
++ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -14,6 +14,9 @@
+ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
++int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++ int cf1, int cf2);
+ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width,
+--- contrib/wpa/src/ap/dhcp_snoop.c.orig
++++ contrib/wpa/src/ap/dhcp_snoop.c
+@@ -7,10 +7,9 @@
+ */
+
+ #include "utils/includes.h"
+-#include
+-#include
+
+ #include "utils/common.h"
++#include "common/dhcp.h"
+ #include "l2_packet/l2_packet.h"
+ #include "hostapd.h"
+ #include "sta_info.h"
+@@ -18,30 +17,7 @@
+ #include "x_snoop.h"
+ #include "dhcp_snoop.h"
+
+-struct bootp_pkt {
+- struct iphdr iph;
+- struct udphdr udph;
+- u8 op;
+- u8 htype;
+- u8 hlen;
+- u8 hops;
+- be32 xid;
+- be16 secs;
+- be16 flags;
+- be32 client_ip;
+- be32 your_ip;
+- be32 server_ip;
+- be32 relay_ip;
+- u8 hw_addr[16];
+- u8 serv_name[64];
+- u8 boot_file[128];
+- u8 exten[312];
+-} STRUCT_PACKED;
+
+-#define DHCPACK 5
+-static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+-
+-
+ static const char * ipaddr_str(u32 addr)
+ {
+ static char buf[17];
+@@ -74,24 +50,26 @@
+ if (tot_len > (unsigned int) (len - ETH_HLEN))
+ return;
+
+- if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
++ if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
+ return;
+
+ /* Parse DHCP options */
+ end = (const u8 *) b + tot_len;
+ pos = &b->exten[4];
+- while (pos < end && *pos != 0xff) {
++ while (pos < end && *pos != DHCP_OPT_END) {
+ const u8 *opt = pos++;
+
+- if (*opt == 0) /* padding */
++ if (*opt == DHCP_OPT_PAD)
+ continue;
+
++ if (pos >= end || 1 + *pos > end - pos)
++ break;
+ pos += *pos + 1;
+ if (pos >= end)
+ break;
+
+ switch (*opt) {
+- case 1: /* subnet mask */
++ case DHCP_OPT_SUBNET_MASK:
+ if (opt[1] == 4)
+ subnet_mask = WPA_GET_BE32(&opt[2]);
+ if (subnet_mask == 0)
+@@ -101,7 +79,7 @@
+ prefixlen--;
+ }
+ break;
+- case 53: /* message type */
++ case DHCP_OPT_MSG_TYPE:
+ if (opt[1])
+ msgtype = opt[2];
+ break;
+@@ -110,6 +88,15 @@
+ }
+ }
+
++ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
++ for (sta = hapd->sta_list; sta; sta = sta->next) {
++ if (!(sta->flags & WLAN_STA_AUTHORIZED))
++ continue;
++ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
++ (u8 *) buf, len);
++ }
++ }
++
+ if (msgtype == DHCPACK) {
+ if (b->your_ip == 0)
+ return;
+@@ -121,7 +108,8 @@
+
+ wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+ " @ IPv4 address %s/%d",
+- MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
++ MAC2STR(sta->addr),
++ ipaddr_str(be_to_host32(b->your_ip)),
+ prefixlen);
+
+ if (sta->ipaddr == b->your_ip)
+@@ -145,15 +133,6 @@
+ }
+ sta->ipaddr = b->your_ip;
+ }
+-
+- if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+- for (sta = hapd->sta_list; sta; sta = sta->next) {
+- if (!(sta->flags & WLAN_STA_AUTHORIZED))
+- continue;
+- x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+- (u8 *) buf, len);
+- }
+- }
+ }
+
+
+@@ -175,4 +154,5 @@
+ void dhcp_snoop_deinit(struct hostapd_data *hapd)
+ {
+ l2_packet_deinit(hapd->sock_dhcp);
++ hapd->sock_dhcp = NULL;
+ }
+--- contrib/wpa/src/ap/dpp_hostapd.c.orig
++++ contrib/wpa/src/ap/dpp_hostapd.c
+@@ -0,0 +1,1646 @@
++/*
++ * hostapd / DPP integration
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "common/dpp.h"
++#include "common/gas.h"
++#include "common/wpa_ctrl.h"
++#include "hostapd.h"
++#include "ap_drv_ops.h"
++#include "gas_query_ap.h"
++#include "wpa_auth.h"
++#include "dpp_hostapd.h"
++
++
++static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
++static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
++static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
++static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
++
++static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++
++/**
++ * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
++ * @hapd: Pointer to hostapd_data
++ * @cmd: DPP URI read from a QR Code
++ * Returns: Identifier of the stored info or -1 on failure
++ */
++int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
++{
++ struct dpp_bootstrap_info *bi;
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
++ if (!bi)
++ return -1;
++
++ if (auth && auth->response_pending &&
++ dpp_notify_new_qr_code(auth, bi) == 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Sending out pending authentication response");
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d",
++ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
++ DPP_PA_AUTHENTICATION_RESP);
++ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
++ auth->peer_mac_addr,
++ wpabuf_head(hapd->dpp_auth->resp_msg),
++ wpabuf_len(hapd->dpp_auth->resp_msg));
++ }
++
++ return bi->id;
++}
++
++
++static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
++ void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ if (!auth || !auth->resp_msg)
++ return;
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: Retry Authentication Response after timeout");
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d",
++ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
++ DPP_PA_AUTHENTICATION_RESP);
++ hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
++ wpabuf_head(auth->resp_msg),
++ wpabuf_len(auth->resp_msg));
++}
++
++
++static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ unsigned int wait_time, max_tries;
++
++ if (!auth || !auth->resp_msg)
++ return;
++
++ if (hapd->dpp_resp_max_tries)
++ max_tries = hapd->dpp_resp_max_tries;
++ else
++ max_tries = 5;
++ auth->auth_resp_tries++;
++ if (auth->auth_resp_tries >= max_tries) {
++ wpa_printf(MSG_INFO,
++ "DPP: No confirm received from initiator - stopping exchange");
++ hostapd_drv_send_action_cancel_wait(hapd);
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ return;
++ }
++
++ if (hapd->dpp_resp_retry_time)
++ wait_time = hapd->dpp_resp_retry_time;
++ else
++ wait_time = 1000;
++ wpa_printf(MSG_DEBUG,
++ "DPP: Schedule retransmission of Authentication Response frame in %u ms",
++ wait_time);
++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
++ eloop_register_timeout(wait_time / 1000,
++ (wait_time % 1000) * 1000,
++ hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
++}
++
++
++void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
++ const u8 *data, size_t data_len, int ok)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
++ MAC2STR(dst), ok);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
++ " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
++
++ if (!hapd->dpp_auth) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Ignore TX status since there is no ongoing authentication exchange");
++ return;
++ }
++
++#ifdef CONFIG_DPP2
++ if (auth->connect_on_tx_status) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Complete exchange on configuration result");
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ return;
++ }
++#endif /* CONFIG_DPP2 */
++
++ if (hapd->dpp_auth->remove_on_tx_status) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Terminate authentication exchange due to an earlier error");
++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
++ hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
++ NULL);
++ hostapd_drv_send_action_cancel_wait(hapd);
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ return;
++ }
++
++ if (hapd->dpp_auth_ok_on_ack)
++ hostapd_dpp_auth_success(hapd, 1);
++
++ if (!is_broadcast_ether_addr(dst) && !ok) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unicast DPP Action frame was not ACKed");
++ if (auth->waiting_auth_resp) {
++ /* In case of DPP Authentication Request frame, move to
++ * the next channel immediately. */
++ hostapd_drv_send_action_cancel_wait(hapd);
++ hostapd_dpp_auth_init_next(hapd);
++ return;
++ }
++ if (auth->waiting_auth_conf) {
++ hostapd_dpp_auth_resp_retry(hapd);
++ return;
++ }
++ }
++
++ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
++ /* Allow timeout handling to stop iteration if no response is
++ * received from a peer that has ACKed a request. */
++ auth->auth_req_ack = 1;
++ }
++
++ if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
++ hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
++ hapd->dpp_auth->curr_freq,
++ hapd->dpp_auth->neg_freq);
++ hostapd_drv_send_action_cancel_wait(hapd);
++
++ if (hapd->dpp_auth->neg_freq !=
++ (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
++ /* TODO: Listen operation on non-operating channel */
++ wpa_printf(MSG_INFO,
++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
++ hapd->dpp_auth->neg_freq, hapd->iface->freq);
++ }
++ }
++
++ if (hapd->dpp_auth_ok_on_ack)
++ hapd->dpp_auth_ok_on_ack = 0;
++}
++
++
++static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ unsigned int freq;
++ struct os_reltime now, diff;
++ unsigned int wait_time, diff_ms;
++
++ if (!auth || !auth->waiting_auth_resp)
++ return;
++
++ wait_time = hapd->dpp_resp_wait_time ?
++ hapd->dpp_resp_wait_time : 2000;
++ os_get_reltime(&now);
++ os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
++ diff_ms = diff.sec * 1000 + diff.usec / 1000;
++ wpa_printf(MSG_DEBUG,
++ "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
++ wait_time, diff_ms);
++
++ if (auth->auth_req_ack && diff_ms >= wait_time) {
++ /* Peer ACK'ed Authentication Request frame, but did not reply
++ * with Authentication Response frame within two seconds. */
++ wpa_printf(MSG_INFO,
++ "DPP: No response received from responder - stopping initiation attempt");
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
++ hostapd_drv_send_action_cancel_wait(hapd);
++ hostapd_dpp_listen_stop(hapd);
++ dpp_auth_deinit(auth);
++ hapd->dpp_auth = NULL;
++ return;
++ }
++
++ if (diff_ms >= wait_time) {
++ /* Authentication Request frame was not ACK'ed and no reply
++ * was receiving within two seconds. */
++ wpa_printf(MSG_DEBUG,
++ "DPP: Continue Initiator channel iteration");
++ hostapd_drv_send_action_cancel_wait(hapd);
++ hostapd_dpp_listen_stop(hapd);
++ hostapd_dpp_auth_init_next(hapd);
++ return;
++ }
++
++ /* Driver did not support 2000 ms long wait_time with TX command, so
++ * schedule listen operation to continue waiting for the response.
++ *
++ * DPP listen operations continue until stopped, so simply schedule a
++ * new call to this function at the point when the two second reply
++ * wait has expired. */
++ wait_time -= diff_ms;
++
++ freq = auth->curr_freq;
++ if (auth->neg_freq > 0)
++ freq = auth->neg_freq;
++ wpa_printf(MSG_DEBUG,
++ "DPP: Continue reply wait on channel %u MHz for %u ms",
++ freq, wait_time);
++ hapd->dpp_in_response_listen = 1;
++
++ if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
++ /* TODO: Listen operation on non-operating channel */
++ wpa_printf(MSG_INFO,
++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
++ freq, hapd->iface->freq);
++ }
++
++ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
++ hostapd_dpp_reply_wait_timeout, hapd, NULL);
++}
++
++
++static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
++ struct dpp_authentication *auth)
++{
++#ifdef CONFIG_TESTING_OPTIONS
++ if (hapd->dpp_config_obj_override)
++ auth->config_obj_override =
++ os_strdup(hapd->dpp_config_obj_override);
++ if (hapd->dpp_discovery_override)
++ auth->discovery_override =
++ os_strdup(hapd->dpp_discovery_override);
++ if (hapd->dpp_groups_override)
++ auth->groups_override = os_strdup(hapd->dpp_groups_override);
++ auth->ignore_netaccesskey_mismatch =
++ hapd->dpp_ignore_netaccesskey_mismatch;
++#endif /* CONFIG_TESTING_OPTIONS */
++}
++
++
++static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++
++ if (!hapd->dpp_auth)
++ return;
++ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
++ hostapd_dpp_auth_init_next(hapd);
++}
++
++
++static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ const u8 *dst;
++ unsigned int wait_time, max_wait_time, freq, max_tries, used;
++ struct os_reltime now, diff;
++
++ if (!auth)
++ return -1;
++
++ if (auth->freq_idx == 0)
++ os_get_reltime(&hapd->dpp_init_iter_start);
++
++ if (auth->freq_idx >= auth->num_freq) {
++ auth->num_freq_iters++;
++ if (hapd->dpp_init_max_tries)
++ max_tries = hapd->dpp_init_max_tries;
++ else
++ max_tries = 5;
++ if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
++ wpa_printf(MSG_INFO,
++ "DPP: No response received from responder - stopping initiation attempt");
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ DPP_EVENT_AUTH_INIT_FAILED);
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
++ hapd, NULL);
++ hostapd_drv_send_action_cancel_wait(hapd);
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ return -1;
++ }
++ auth->freq_idx = 0;
++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
++ if (hapd->dpp_init_retry_time)
++ wait_time = hapd->dpp_init_retry_time;
++ else
++ wait_time = 10000;
++ os_get_reltime(&now);
++ os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
++ used = diff.sec * 1000 + diff.usec / 1000;
++ if (used > wait_time)
++ wait_time = 0;
++ else
++ wait_time -= used;
++ wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
++ wait_time);
++ eloop_register_timeout(wait_time / 1000,
++ (wait_time % 1000) * 1000,
++ hostapd_dpp_init_timeout, hapd,
++ NULL);
++ return 0;
++ }
++ freq = auth->freq[auth->freq_idx++];
++ auth->curr_freq = freq;
++
++ if (is_zero_ether_addr(auth->peer_bi->mac_addr))
++ dst = broadcast;
++ else
++ dst = auth->peer_bi->mac_addr;
++ hapd->dpp_auth_ok_on_ack = 0;
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
++ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
++ max_wait_time = hapd->dpp_resp_wait_time ?
++ hapd->dpp_resp_wait_time : 2000;
++ if (wait_time > max_wait_time)
++ wait_time = max_wait_time;
++ wait_time += 10; /* give the driver some extra time to complete */
++ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
++ hostapd_dpp_reply_wait_timeout, hapd, NULL);
++ wait_time -= 10;
++ if (auth->neg_freq > 0 && freq != auth->neg_freq) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
++ freq, auth->neg_freq);
++ }
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d",
++ MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
++ auth->auth_req_ack = 0;
++ os_get_reltime(&hapd->dpp_last_init);
++ return hostapd_drv_send_action(hapd, freq, wait_time,
++ dst,
++ wpabuf_head(hapd->dpp_auth->req_msg),
++ wpabuf_len(hapd->dpp_auth->req_msg));
++}
++
++
++int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
++{
++ const char *pos;
++ struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
++ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
++ unsigned int neg_freq = 0;
++
++ pos = os_strstr(cmd, " peer=");
++ if (!pos)
++ return -1;
++ pos += 6;
++ peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
++ if (!peer_bi) {
++ wpa_printf(MSG_INFO,
++ "DPP: Could not find bootstrapping info for the identified peer");
++ return -1;
++ }
++
++ pos = os_strstr(cmd, " own=");
++ if (pos) {
++ pos += 5;
++ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
++ atoi(pos));
++ if (!own_bi) {
++ wpa_printf(MSG_INFO,
++ "DPP: Could not find bootstrapping info for the identified local entry");
++ return -1;
++ }
++
++ if (peer_bi->curve != own_bi->curve) {
++ wpa_printf(MSG_INFO,
++ "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
++ peer_bi->curve->name, own_bi->curve->name);
++ return -1;
++ }
++ }
++
++ pos = os_strstr(cmd, " role=");
++ if (pos) {
++ pos += 6;
++ if (os_strncmp(pos, "configurator", 12) == 0)
++ allowed_roles = DPP_CAPAB_CONFIGURATOR;
++ else if (os_strncmp(pos, "enrollee", 8) == 0)
++ allowed_roles = DPP_CAPAB_ENROLLEE;
++ else if (os_strncmp(pos, "either", 6) == 0)
++ allowed_roles = DPP_CAPAB_CONFIGURATOR |
++ DPP_CAPAB_ENROLLEE;
++ else
++ goto fail;
++ }
++
++ pos = os_strstr(cmd, " neg_freq=");
++ if (pos)
++ neg_freq = atoi(pos + 10);
++
++ if (hapd->dpp_auth) {
++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
++ hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
++ NULL);
++ hostapd_drv_send_action_cancel_wait(hapd);
++ dpp_auth_deinit(hapd->dpp_auth);
++ }
++
++ hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
++ allowed_roles, neg_freq,
++ hapd->iface->hw_features,
++ hapd->iface->num_hw_features);
++ if (!hapd->dpp_auth)
++ goto fail;
++ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
++ hapd->dpp_auth, cmd) < 0) {
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ goto fail;
++ }
++
++ hapd->dpp_auth->neg_freq = neg_freq;
++
++ if (!is_zero_ether_addr(peer_bi->mac_addr))
++ os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
++ ETH_ALEN);
++
++ return hostapd_dpp_auth_init_next(hapd);
++fail:
++ return -1;
++}
++
++
++int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
++{
++ int freq;
++
++ freq = atoi(cmd);
++ if (freq <= 0)
++ return -1;
++
++ if (os_strstr(cmd, " role=configurator"))
++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
++ else if (os_strstr(cmd, " role=enrollee"))
++ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
++ else
++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
++ DPP_CAPAB_ENROLLEE;
++ hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
++
++ if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
++ /* TODO: Listen operation on non-operating channel */
++ wpa_printf(MSG_INFO,
++ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
++ freq, hapd->iface->freq);
++ return -1;
++ }
++
++ return 0;
++}
++
++
++void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
++{
++ /* TODO: Stop listen operation on non-operating channel */
++}
++
++
++static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ const u8 *r_bootstrap, *i_bootstrap;
++ u16 r_bootstrap_len, i_bootstrap_len;
++ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
++
++ if (!hapd->iface->interfaces->dpp)
++ return;
++
++ wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
++ MAC2STR(src));
++
++ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
++ &r_bootstrap_len);
++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
++ return;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
++ r_bootstrap, r_bootstrap_len);
++
++ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
++ &i_bootstrap_len);
++ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
++ return;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
++ i_bootstrap, i_bootstrap_len);
++
++ /* Try to find own and peer bootstrapping key matches based on the
++ * received hash values */
++ dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
++ r_bootstrap, &own_bi, &peer_bi);
++ if (!own_bi) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "No matching own bootstrapping key found - ignore message");
++ return;
++ }
++
++ if (hapd->dpp_auth) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Already in DPP authentication exchange - ignore new one");
++ return;
++ }
++
++ hapd->dpp_auth_ok_on_ack = 0;
++ hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
++ hapd->dpp_qr_mutual,
++ peer_bi, own_bi, freq, hdr, buf, len);
++ if (!hapd->dpp_auth) {
++ wpa_printf(MSG_DEBUG, "DPP: No response generated");
++ return;
++ }
++ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
++ hapd->dpp_auth,
++ hapd->dpp_configurator_params) < 0) {
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ return;
++ }
++ os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d",
++ MAC2STR(src), hapd->dpp_auth->curr_freq,
++ DPP_PA_AUTHENTICATION_RESP);
++ hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
++ src, wpabuf_head(hapd->dpp_auth->resp_msg),
++ wpabuf_len(hapd->dpp_auth->resp_msg));
++}
++
++
++static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
++ struct dpp_authentication *auth)
++{
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
++ dpp_akm_str(auth->akm));
++ if (auth->ssid_len)
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
++ wpa_ssid_txt(auth->ssid, auth->ssid_len));
++ if (auth->connector) {
++ /* TODO: Save the Connector and consider using a command
++ * to fetch the value instead of sending an event with
++ * it. The Connector could end up being larger than what
++ * most clients are ready to receive as an event
++ * message. */
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
++ auth->connector);
++ } else if (auth->passphrase[0]) {
++ char hex[64 * 2 + 1];
++
++ wpa_snprintf_hex(hex, sizeof(hex),
++ (const u8 *) auth->passphrase,
++ os_strlen(auth->passphrase));
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
++ hex);
++ } else if (auth->psk_set) {
++ char hex[PMK_LEN * 2 + 1];
++
++ wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
++ hex);
++ }
++ if (auth->c_sign_key) {
++ char *hex;
++ size_t hexlen;
++
++ hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
++ hex = os_malloc(hexlen);
++ if (hex) {
++ wpa_snprintf_hex(hex, hexlen,
++ wpabuf_head(auth->c_sign_key),
++ wpabuf_len(auth->c_sign_key));
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ DPP_EVENT_C_SIGN_KEY "%s", hex);
++ os_free(hex);
++ }
++ }
++ if (auth->net_access_key) {
++ char *hex;
++ size_t hexlen;
++
++ hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
++ hex = os_malloc(hexlen);
++ if (hex) {
++ wpa_snprintf_hex(hex, hexlen,
++ wpabuf_head(auth->net_access_key),
++ wpabuf_len(auth->net_access_key));
++ if (auth->net_access_key_expiry)
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
++ (unsigned long)
++ auth->net_access_key_expiry);
++ else
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ DPP_EVENT_NET_ACCESS_KEY "%s", hex);
++ os_free(hex);
++ }
++ }
++}
++
++
++static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
++ enum gas_query_ap_result result,
++ const struct wpabuf *adv_proto,
++ const struct wpabuf *resp, u16 status_code)
++{
++ struct hostapd_data *hapd = ctx;
++ const u8 *pos;
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
++
++ if (!auth || !auth->auth_success) {
++ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
++ return;
++ }
++ if (!resp || status_code != WLAN_STATUS_SUCCESS) {
++ wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
++ goto fail;
++ }
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
++ adv_proto);
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
++ resp);
++
++ if (wpabuf_len(adv_proto) != 10 ||
++ !(pos = wpabuf_head(adv_proto)) ||
++ pos[0] != WLAN_EID_ADV_PROTO ||
++ pos[1] != 8 ||
++ pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
++ pos[4] != 5 ||
++ WPA_GET_BE24(&pos[5]) != OUI_WFA ||
++ pos[8] != 0x1a ||
++ pos[9] != 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Not a DPP Advertisement Protocol ID");
++ goto fail;
++ }
++
++ if (dpp_conf_resp_rx(auth, resp) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
++ goto fail;
++ }
++
++ hostapd_dpp_handle_config_obj(hapd, auth);
++ status = DPP_STATUS_OK;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_REJECT_CONFIG) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
++ status = DPP_STATUS_CONFIG_REJECTED;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++fail:
++ if (status != DPP_STATUS_OK)
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
++#ifdef CONFIG_DPP2
++ if (auth->peer_version >= 2 &&
++ auth->conf_resp_status == DPP_STATUS_OK) {
++ struct wpabuf *msg;
++
++ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
++ msg = dpp_build_conf_result(auth, status);
++ if (!msg)
++ goto fail2;
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
++ MAC2STR(addr), auth->curr_freq,
++ DPP_PA_CONFIGURATION_RESULT);
++ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
++ addr, wpabuf_head(msg),
++ wpabuf_len(msg));
++ wpabuf_free(msg);
++
++ /* This exchange will be terminated in the TX status handler */
++ auth->connect_on_tx_status = 1;
++ return;
++ }
++fail2:
++#endif /* CONFIG_DPP2 */
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++}
++
++
++static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ struct wpabuf *buf;
++ char json[100];
++ int res;
++ int netrole_ap = 1;
++
++ os_snprintf(json, sizeof(json),
++ "{\"name\":\"Test\","
++ "\"wi-fi_tech\":\"infra\","
++ "\"netRole\":\"%s\"}",
++ netrole_ap ? "ap" : "sta");
++ wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
++
++ buf = dpp_build_conf_req(auth, json);
++ if (!buf) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No configuration request data available");
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
++ MAC2STR(auth->peer_mac_addr), auth->curr_freq);
++
++ res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
++ buf, hostapd_dpp_gas_resp_cb, hapd);
++ if (res < 0) {
++ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
++ "GAS: Failed to send Query Request");
++ wpabuf_free(buf);
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: GAS query started with dialog token %u", res);
++ }
++}
++
++
++static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
++{
++ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
++ initiator);
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at Authentication Confirm");
++ if (hapd->dpp_auth->configurator) {
++ /* Prevent GAS response */
++ hapd->dpp_auth->auth_success = 0;
++ }
++ return;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (!hapd->dpp_auth->configurator)
++ hostapd_dpp_start_gas_client(hapd);
++}
++
++
++static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ struct wpabuf *msg;
++
++ wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
++ MAC2STR(src));
++
++ if (!auth) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No DPP Authentication in progress - drop");
++ return;
++ }
++
++ if (!is_zero_ether_addr(auth->peer_mac_addr) &&
++ os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
++ return;
++ }
++
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
++
++ if (auth->curr_freq != freq && auth->neg_freq == freq) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Responder accepted request for different negotiation channel");
++ auth->curr_freq = freq;
++ }
++
++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
++ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
++ if (!msg) {
++ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
++ wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
++ return;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
++ return;
++ }
++ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d", MAC2STR(src), auth->curr_freq,
++ DPP_PA_AUTHENTICATION_CONF);
++ hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
++ wpabuf_head(msg), wpabuf_len(msg));
++ wpabuf_free(msg);
++ hapd->dpp_auth_ok_on_ack = 1;
++}
++
++
++static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
++ MAC2STR(src));
++
++ if (!auth) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No DPP Authentication in progress - drop");
++ return;
++ }
++
++ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
++ return;
++ }
++
++ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
++ return;
++ }
++
++ hostapd_dpp_auth_success(hapd, 0);
++}
++
++
++#ifdef CONFIG_DPP2
++
++static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
++ void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ if (!auth || !auth->waiting_conf_result)
++ return;
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: Timeout while waiting for Configuration Result");
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
++ dpp_auth_deinit(auth);
++ hapd->dpp_auth = NULL;
++}
++
++
++static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ enum dpp_status_error status;
++
++ wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
++ MAC2STR(src));
++
++ if (!auth || !auth->waiting_conf_result) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No DPP Configuration waiting for result - drop");
++ return;
++ }
++
++ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
++ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
++ return;
++ }
++
++ status = dpp_conf_result_rx(auth, hdr, buf, len);
++
++ hostapd_drv_send_action_cancel_wait(hapd);
++ hostapd_dpp_listen_stop(hapd);
++ if (status == DPP_STATUS_OK)
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
++ else
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
++ dpp_auth_deinit(auth);
++ hapd->dpp_auth = NULL;
++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
++ NULL);
++}
++
++#endif /* CONFIG_DPP2 */
++
++
++static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
++ const u8 *src, unsigned int freq,
++ u8 trans_id,
++ enum dpp_status_error status)
++{
++ struct wpabuf *msg;
++
++ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
++ 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
++ if (!msg)
++ return;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
++ goto skip_trans_id;
++ }
++ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
++ trans_id ^= 0x01;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Transaction ID */
++ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
++ wpabuf_put_le16(msg, 1);
++ wpabuf_put_u8(msg, trans_id);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_trans_id:
++ if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
++ goto skip_status;
++ }
++ if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
++ status = 254;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* DPP Status */
++ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
++ wpabuf_put_le16(msg, 1);
++ wpabuf_put_u8(msg, status);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_status:
++ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
++ goto skip_connector;
++ }
++ if (status == DPP_STATUS_OK &&
++ dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
++ char *connector;
++
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
++ connector = dpp_corrupt_connector_signature(
++ hapd->conf->dpp_connector);
++ if (!connector) {
++ wpabuf_free(msg);
++ return;
++ }
++ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
++ wpabuf_put_le16(msg, os_strlen(connector));
++ wpabuf_put_str(msg, connector);
++ os_free(connector);
++ goto skip_connector;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* DPP Connector */
++ if (status == DPP_STATUS_OK) {
++ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
++ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
++ wpabuf_put_str(msg, hapd->conf->dpp_connector);
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_connector:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
++ " status=%d", MAC2STR(src), status);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d status=%d", MAC2STR(src), freq,
++ DPP_PA_PEER_DISCOVERY_RESP, status);
++ hostapd_drv_send_action(hapd, freq, 0, src,
++ wpabuf_head(msg), wpabuf_len(msg));
++ wpabuf_free(msg);
++}
++
++
++static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
++ const u8 *src,
++ const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ const u8 *connector, *trans_id;
++ u16 connector_len, trans_id_len;
++ struct os_time now;
++ struct dpp_introduction intro;
++ os_time_t expire;
++ int expiration;
++ enum dpp_status_error res;
++
++ wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
++ MAC2STR(src));
++ if (!hapd->wpa_auth ||
++ !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
++ !(hapd->conf->wpa & WPA_PROTO_RSN)) {
++ wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
++ return;
++ }
++
++ if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
++ !hapd->conf->dpp_csign) {
++ wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
++ return;
++ }
++
++ os_get_time(&now);
++
++ if (hapd->conf->dpp_netaccesskey_expiry &&
++ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
++ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
++ return;
++ }
++
++ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
++ &trans_id_len);
++ if (!trans_id || trans_id_len != 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Peer did not include Transaction ID");
++ return;
++ }
++
++ connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
++ if (!connector) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Peer did not include its Connector");
++ return;
++ }
++
++ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
++ wpabuf_head(hapd->conf->dpp_netaccesskey),
++ wpabuf_len(hapd->conf->dpp_netaccesskey),
++ wpabuf_head(hapd->conf->dpp_csign),
++ wpabuf_len(hapd->conf->dpp_csign),
++ connector, connector_len, &expire);
++ if (res == 255) {
++ wpa_printf(MSG_INFO,
++ "DPP: Network Introduction protocol resulted in internal failure (peer "
++ MACSTR ")", MAC2STR(src));
++ return;
++ }
++ if (res != DPP_STATUS_OK) {
++ wpa_printf(MSG_INFO,
++ "DPP: Network Introduction protocol resulted in failure (peer "
++ MACSTR " status %d)", MAC2STR(src), res);
++ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
++ res);
++ return;
++ }
++
++ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
++ expire = hapd->conf->dpp_netaccesskey_expiry;
++ if (expire)
++ expiration = expire - now.sec;
++ else
++ expiration = 0;
++
++ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
++ intro.pmkid, expiration,
++ WPA_KEY_MGMT_DPP) < 0) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
++ return;
++ }
++
++ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
++ DPP_STATUS_OK);
++}
++
++
++static void
++hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
++ const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ struct wpabuf *msg;
++
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
++ MAC2STR(src));
++
++ /* TODO: Support multiple PKEX codes by iterating over all the enabled
++ * values here */
++
++ if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No PKEX code configured - ignore request");
++ return;
++ }
++
++ if (hapd->dpp_pkex) {
++ /* TODO: Support parallel operations */
++ wpa_printf(MSG_DEBUG,
++ "DPP: Already in PKEX session - ignore new request");
++ return;
++ }
++
++ hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
++ hapd->dpp_pkex_bi,
++ hapd->own_addr, src,
++ hapd->dpp_pkex_identifier,
++ hapd->dpp_pkex_code,
++ buf, len);
++ if (!hapd->dpp_pkex) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to process the request - ignore it");
++ return;
++ }
++
++ msg = hapd->dpp_pkex->exchange_resp;
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d", MAC2STR(src), freq,
++ DPP_PA_PKEX_EXCHANGE_RESP);
++ hostapd_drv_send_action(hapd, freq, 0, src,
++ wpabuf_head(msg), wpabuf_len(msg));
++ if (hapd->dpp_pkex->failed) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Terminate PKEX exchange due to an earlier error");
++ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
++ hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
++ dpp_pkex_free(hapd->dpp_pkex);
++ hapd->dpp_pkex = NULL;
++ }
++}
++
++
++static void
++hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
++ const u8 *buf, size_t len, unsigned int freq)
++{
++ struct wpabuf *msg;
++
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
++ MAC2STR(src));
++
++ /* TODO: Support multiple PKEX codes by iterating over all the enabled
++ * values here */
++
++ if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
++ hapd->dpp_pkex->exchange_done) {
++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
++ return;
++ }
++
++ msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
++ if (!msg) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
++ MAC2STR(src));
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d", MAC2STR(src), freq,
++ DPP_PA_PKEX_COMMIT_REVEAL_REQ);
++ hostapd_drv_send_action(hapd, freq, 0, src,
++ wpabuf_head(msg), wpabuf_len(msg));
++ wpabuf_free(msg);
++}
++
++
++static void
++hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ struct wpabuf *msg;
++ struct dpp_pkex *pkex = hapd->dpp_pkex;
++ struct dpp_bootstrap_info *bi;
++
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
++ MAC2STR(src));
++
++ if (!pkex || pkex->initiator || !pkex->exchange_done) {
++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
++ return;
++ }
++
++ msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
++ if (!msg) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
++ if (hapd->dpp_pkex->failed) {
++ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
++ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
++ hapd->dpp_pkex->own_bi->pkex_t =
++ hapd->dpp_pkex->t;
++ dpp_pkex_free(hapd->dpp_pkex);
++ hapd->dpp_pkex = NULL;
++ }
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
++ MACSTR, MAC2STR(src));
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d", MAC2STR(src), freq,
++ DPP_PA_PKEX_COMMIT_REVEAL_RESP);
++ hostapd_drv_send_action(hapd, freq, 0, src,
++ wpabuf_head(msg), wpabuf_len(msg));
++ wpabuf_free(msg);
++
++ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
++ if (!bi)
++ return;
++ hapd->dpp_pkex = NULL;
++}
++
++
++static void
++hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
++ const u8 *hdr, const u8 *buf, size_t len,
++ unsigned int freq)
++{
++ int res;
++ struct dpp_bootstrap_info *bi;
++ struct dpp_pkex *pkex = hapd->dpp_pkex;
++ char cmd[500];
++
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
++ MAC2STR(src));
++
++ if (!pkex || !pkex->initiator || !pkex->exchange_done) {
++ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
++ return;
++ }
++
++ res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
++ if (res < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
++ return;
++ }
++
++ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
++ if (!bi)
++ return;
++ hapd->dpp_pkex = NULL;
++
++ os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
++ bi->id,
++ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
++ wpa_printf(MSG_DEBUG,
++ "DPP: Start authentication after PKEX with parameters: %s",
++ cmd);
++ if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Authentication initialization failed");
++ return;
++ }
++}
++
++
++void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
++ const u8 *buf, size_t len, unsigned int freq)
++{
++ u8 crypto_suite;
++ enum dpp_public_action_frame_type type;
++ const u8 *hdr;
++ unsigned int pkex_t;
++
++ if (len < DPP_HDR_LEN)
++ return;
++ if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
++ return;
++ hdr = buf;
++ buf += 4;
++ len -= 4;
++ crypto_suite = *buf++;
++ type = *buf++;
++ len -= 2;
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: Received DPP Public Action frame crypto suite %u type %d from "
++ MACSTR " freq=%u",
++ crypto_suite, type, MAC2STR(src), freq);
++ if (crypto_suite != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
++ crypto_suite);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
++ " freq=%u type=%d ignore=unsupported-crypto-suite",
++ MAC2STR(src), freq, type);
++ return;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
++ if (dpp_check_attrs(buf, len) < 0) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
++ " freq=%u type=%d ignore=invalid-attributes",
++ MAC2STR(src), freq, type);
++ return;
++ }
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
++ " freq=%u type=%d", MAC2STR(src), freq, type);
++
++ switch (type) {
++ case DPP_PA_AUTHENTICATION_REQ:
++ hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
++ break;
++ case DPP_PA_AUTHENTICATION_RESP:
++ hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
++ break;
++ case DPP_PA_AUTHENTICATION_CONF:
++ hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
++ break;
++ case DPP_PA_PEER_DISCOVERY_REQ:
++ hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
++ break;
++ case DPP_PA_PKEX_EXCHANGE_REQ:
++ hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
++ break;
++ case DPP_PA_PKEX_EXCHANGE_RESP:
++ hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
++ break;
++ case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
++ hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
++ freq);
++ break;
++ case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
++ hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
++ freq);
++ break;
++#ifdef CONFIG_DPP2
++ case DPP_PA_CONFIGURATION_RESULT:
++ hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
++ break;
++#endif /* CONFIG_DPP2 */
++ default:
++ wpa_printf(MSG_DEBUG,
++ "DPP: Ignored unsupported frame subtype %d", type);
++ break;
++ }
++
++ if (hapd->dpp_pkex)
++ pkex_t = hapd->dpp_pkex->t;
++ else if (hapd->dpp_pkex_bi)
++ pkex_t = hapd->dpp_pkex_bi->pkex_t;
++ else
++ pkex_t = 0;
++ if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
++ hostapd_dpp_pkex_remove(hapd, "*");
++ }
++}
++
++
++struct wpabuf *
++hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
++ const u8 *query, size_t query_len)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++ struct wpabuf *resp;
++
++ wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
++ if (!auth || !auth->auth_success ||
++ os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
++ return NULL;
++ }
++ wpa_hexdump(MSG_DEBUG,
++ "DPP: Received Configuration Request (GAS Query Request)",
++ query, query_len);
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
++ MAC2STR(sa));
++ resp = dpp_conf_req_rx(auth, query, query_len);
++ if (!resp)
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
++ return resp;
++}
++
++
++void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
++{
++ struct dpp_authentication *auth = hapd->dpp_auth;
++
++ if (!auth)
++ return;
++
++ wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
++ ok);
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
++#ifdef CONFIG_DPP2
++ if (ok && auth->peer_version >= 2 &&
++ auth->conf_resp_status == DPP_STATUS_OK) {
++ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
++ auth->waiting_conf_result = 1;
++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
++ hapd, NULL);
++ eloop_register_timeout(2, 0,
++ hostapd_dpp_config_result_wait_timeout,
++ hapd, NULL);
++ return;
++ }
++#endif /* CONFIG_DPP2 */
++ hostapd_drv_send_action_cancel_wait(hapd);
++
++ if (ok)
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
++ else
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++}
++
++
++int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
++{
++ struct dpp_authentication *auth;
++ int ret = -1;
++ char *curve = NULL;
++
++ auth = os_zalloc(sizeof(*auth));
++ if (!auth)
++ return -1;
++
++ curve = get_param(cmd, " curve=");
++ hostapd_dpp_set_testing_options(hapd, auth);
++ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
++ auth, cmd) == 0 &&
++ dpp_configurator_own_config(auth, curve, 1) == 0) {
++ hostapd_dpp_handle_config_obj(hapd, auth);
++ ret = 0;
++ }
++
++ dpp_auth_deinit(auth);
++ os_free(curve);
++
++ return ret;
++}
++
++
++int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
++{
++ struct dpp_bootstrap_info *own_bi;
++ const char *pos, *end;
++
++ pos = os_strstr(cmd, " own=");
++ if (!pos)
++ return -1;
++ pos += 5;
++ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
++ if (!own_bi) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Identified bootstrap info not found");
++ return -1;
++ }
++ if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Identified bootstrap info not for PKEX");
++ return -1;
++ }
++ hapd->dpp_pkex_bi = own_bi;
++ own_bi->pkex_t = 0; /* clear pending errors on new code */
++
++ os_free(hapd->dpp_pkex_identifier);
++ hapd->dpp_pkex_identifier = NULL;
++ pos = os_strstr(cmd, " identifier=");
++ if (pos) {
++ pos += 12;
++ end = os_strchr(pos, ' ');
++ if (!end)
++ return -1;
++ hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
++ if (!hapd->dpp_pkex_identifier)
++ return -1;
++ os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
++ hapd->dpp_pkex_identifier[end - pos] = '\0';
++ }
++
++ pos = os_strstr(cmd, " code=");
++ if (!pos)
++ return -1;
++ os_free(hapd->dpp_pkex_code);
++ hapd->dpp_pkex_code = os_strdup(pos + 6);
++ if (!hapd->dpp_pkex_code)
++ return -1;
++
++ if (os_strstr(cmd, " init=1")) {
++ struct wpabuf *msg;
++
++ wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
++ dpp_pkex_free(hapd->dpp_pkex);
++ hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
++ hapd->own_addr,
++ hapd->dpp_pkex_identifier,
++ hapd->dpp_pkex_code);
++ if (!hapd->dpp_pkex)
++ return -1;
++
++ msg = hapd->dpp_pkex->exchange_req;
++ /* TODO: Which channel to use? */
++ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
++ " freq=%u type=%d", MAC2STR(broadcast), 2437,
++ DPP_PA_PKEX_EXCHANGE_REQ);
++ hostapd_drv_send_action(hapd, 2437, 0, broadcast,
++ wpabuf_head(msg), wpabuf_len(msg));
++ }
++
++ /* TODO: Support multiple PKEX info entries */
++
++ os_free(hapd->dpp_pkex_auth_cmd);
++ hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
++
++ return 1;
++}
++
++
++int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id)
++{
++ unsigned int id_val;
++
++ if (os_strcmp(id, "*") == 0) {
++ id_val = 0;
++ } else {
++ id_val = atoi(id);
++ if (id_val == 0)
++ return -1;
++ }
++
++ if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
++ return -1;
++
++ /* TODO: Support multiple PKEX entries */
++ os_free(hapd->dpp_pkex_code);
++ hapd->dpp_pkex_code = NULL;
++ os_free(hapd->dpp_pkex_identifier);
++ hapd->dpp_pkex_identifier = NULL;
++ os_free(hapd->dpp_pkex_auth_cmd);
++ hapd->dpp_pkex_auth_cmd = NULL;
++ hapd->dpp_pkex_bi = NULL;
++ /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
++ dpp_pkex_free(hapd->dpp_pkex);
++ hapd->dpp_pkex = NULL;
++ return 0;
++}
++
++
++void hostapd_dpp_stop(struct hostapd_data *hapd)
++{
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ dpp_pkex_free(hapd->dpp_pkex);
++ hapd->dpp_pkex = NULL;
++}
++
++
++int hostapd_dpp_init(struct hostapd_data *hapd)
++{
++ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
++ hapd->dpp_init_done = 1;
++ return 0;
++}
++
++
++void hostapd_dpp_deinit(struct hostapd_data *hapd)
++{
++#ifdef CONFIG_TESTING_OPTIONS
++ os_free(hapd->dpp_config_obj_override);
++ hapd->dpp_config_obj_override = NULL;
++ os_free(hapd->dpp_discovery_override);
++ hapd->dpp_discovery_override = NULL;
++ os_free(hapd->dpp_groups_override);
++ hapd->dpp_groups_override = NULL;
++ hapd->dpp_ignore_netaccesskey_mismatch = 0;
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (!hapd->dpp_init_done)
++ return;
++ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
++ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
++#ifdef CONFIG_DPP2
++ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
++ NULL);
++#endif /* CONFIG_DPP2 */
++ dpp_auth_deinit(hapd->dpp_auth);
++ hapd->dpp_auth = NULL;
++ hostapd_dpp_pkex_remove(hapd, "*");
++ hapd->dpp_pkex = NULL;
++ os_free(hapd->dpp_configurator_params);
++ hapd->dpp_configurator_params = NULL;
++}
+--- contrib/wpa/src/ap/dpp_hostapd.h.orig
++++ contrib/wpa/src/ap/dpp_hostapd.h
+@@ -0,0 +1,37 @@
++/*
++ * hostapd / DPP integration
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef DPP_HOSTAPD_H
++#define DPP_HOSTAPD_H
++
++int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
++int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
++int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
++void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
++void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
++ const u8 *buf, size_t len, unsigned int freq);
++void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
++ const u8 *data, size_t data_len, int ok);
++struct wpabuf *
++hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
++ const u8 *query, size_t query_len);
++void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
++int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
++int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
++int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
++int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
++ char *buf, size_t buflen);
++int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
++int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
++void hostapd_dpp_stop(struct hostapd_data *hapd);
++int hostapd_dpp_init(struct hostapd_data *hapd);
++void hostapd_dpp_deinit(struct hostapd_data *hapd);
++void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
++void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
++
++#endif /* DPP_HOSTAPD_H */
+--- contrib/wpa/src/ap/drv_callbacks.c.orig
++++ contrib/wpa/src/ap/drv_callbacks.c
+@@ -15,6 +15,7 @@
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
+ #include "common/wpa_ctrl.h"
++#include "common/dpp.h"
+ #include "crypto/random.h"
+ #include "p2p/p2p.h"
+ #include "wps/wps.h"
+@@ -22,6 +23,7 @@
+ #include "wnm_ap.h"
+ #include "hostapd.h"
+ #include "ieee802_11.h"
++#include "ieee802_11_auth.h"
+ #include "sta_info.h"
+ #include "accounting.h"
+ #include "tkip_countermeasures.h"
+@@ -30,11 +32,77 @@
+ #include "wps_hostapd.h"
+ #include "ap_drv_ops.h"
+ #include "ap_config.h"
++#include "ap_mlme.h"
+ #include "hw_features.h"
+ #include "dfs.h"
+ #include "beacon.h"
++#include "mbo_ap.h"
++#include "dpp_hostapd.h"
++#include "fils_hlp.h"
++#include "neighbor_db.h"
+
+
++#ifdef CONFIG_FILS
++void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ u16 reply_res = WLAN_STATUS_SUCCESS;
++ struct ieee802_11_elems elems;
++ u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
++ int new_assoc;
++
++ wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
++ __func__, MAC2STR(sta->addr));
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++ if (!sta->fils_pending_assoc_req)
++ return;
++
++ ieee802_11_parse_elems(sta->fils_pending_assoc_req,
++ sta->fils_pending_assoc_req_len, &elems, 0);
++ if (!elems.fils_session) {
++ wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
++ __func__);
++ return;
++ }
++
++ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
++ elems.fils_session,
++ sta->fils_hlp_resp);
++
++ reply_res = hostapd_sta_assoc(hapd, sta->addr,
++ sta->fils_pending_assoc_is_reassoc,
++ WLAN_STATUS_SUCCESS,
++ buf, p - buf);
++ ap_sta_set_authorized(hapd, sta, 1);
++ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
++ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
++ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
++ hostapd_set_sta_flags(hapd, sta);
++ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
++ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
++ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
++ os_free(sta->fils_pending_assoc_req);
++ sta->fils_pending_assoc_req = NULL;
++ sta->fils_pending_assoc_req_len = 0;
++ wpabuf_free(sta->fils_hlp_resp);
++ sta->fils_hlp_resp = NULL;
++ wpabuf_free(sta->hlp_dhcp_discover);
++ sta->hlp_dhcp_discover = NULL;
++ fils_hlp_deinit(hapd);
++
++ /*
++ * Remove the station in case transmission of a success response fails
++ * (the STA was added associated to the driver) or if the station was
++ * previously added unassociated.
++ */
++ if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
++}
++#endif /* CONFIG_FILS */
++
++
+ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *req_ies, size_t req_ies_len, int reassoc)
+ {
+@@ -43,10 +111,10 @@
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ielen;
+-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
++#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
+ u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+ u8 *p = buf;
+-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
++#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */
+ u16 reason = WLAN_REASON_UNSPECIFIED;
+ u16 status = WLAN_STATUS_SUCCESS;
+ const u8 *p2p_dev_addr = NULL;
+@@ -114,6 +182,21 @@
+ }
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
++ /*
++ * ACL configurations to the drivers (implementing AP SME and ACL
++ * offload) without hostapd's knowledge, can result in a disconnection
++ * though the driver accepts the connection. Skip the hostapd check for
++ * ACL if the driver supports ACL offload to avoid potentially
++ * conflicting ACL rules.
++ */
++ if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
++ hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
++ wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
++ MAC2STR(addr));
++ reason = WLAN_REASON_UNSPECIFIED;
++ goto fail;
++ }
++
+ #ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+@@ -154,6 +237,14 @@
+ elems.hs20_len - 4);
+ } else
+ sta->hs20_ie = NULL;
++
++ wpabuf_free(sta->roaming_consortium);
++ if (elems.roaming_cons_sel)
++ sta->roaming_consortium = wpabuf_alloc_copy(
++ elems.roaming_cons_sel + 4,
++ elems.roaming_cons_sel_len - 4);
++ else
++ sta->roaming_consortium = NULL;
+ #endif /* CONFIG_HS20 */
+
+ #ifdef CONFIG_FST
+@@ -164,6 +255,11 @@
+ sta->mb_ies = NULL;
+ #endif /* CONFIG_FST */
+
++ mbo_ap_check_sta_assoc(hapd, sta, &elems);
++
++ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
++ elems.supp_op_classes_len);
++
+ if (hapd->conf->wpa) {
+ if (ie == NULL || ielen == 0) {
+ #ifdef CONFIG_WPS
+@@ -176,7 +272,9 @@
+ #endif /* CONFIG_WPS */
+
+ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+- return -1;
++ reason = WLAN_REASON_INVALID_IE;
++ status = WLAN_STATUS_INVALID_IE;
++ goto fail;
+ }
+ #ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+@@ -208,8 +306,10 @@
+ return -1;
+ }
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
++ hapd->iface->freq,
+ ie, ielen,
+- elems.mdie, elems.mdie_len);
++ elems.mdie, elems.mdie_len,
++ elems.owe_dh, elems.owe_dh_len);
+ if (res != WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG,
+ "WPA/RSN information element rejected? (res %u)",
+@@ -230,8 +330,8 @@
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
+- reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+- status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
++ reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
++ status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ }
+ #endif /* CONFIG_IEEE80211W */
+ else {
+@@ -241,10 +341,14 @@
+ goto fail;
+ }
+ #ifdef CONFIG_IEEE80211W
+- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
++ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
++ !sta->sa_query_timed_out &&
+ sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
++ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
++ !sta->sa_query_timed_out &&
+ (sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA
+@@ -271,7 +375,7 @@
+ sta->flags &= ~WLAN_STA_MFP;
+ #endif /* CONFIG_IEEE80211W */
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+ req_ies_len);
+@@ -285,7 +389,7 @@
+ goto fail;
+ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ } else if (hapd->conf->wps_state) {
+ #ifdef CONFIG_WPS
+ struct wpabuf *wps;
+@@ -338,23 +442,175 @@
+ return WLAN_STATUS_INVALID_IE;
+ #endif /* CONFIG_HS20 */
+ }
++
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
++ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
++ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
++ wpa_printf(MSG_INFO,
++ "MBO: Reject WPA2 association without PMF");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++#endif /* CONFIG_MBO */
++
+ #ifdef CONFIG_WPS
+ skip_wpa_check:
+ #endif /* CONFIG_WPS */
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+ sta->auth_alg, req_ies, req_ies_len);
++ if (!p) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
+
++#ifdef CONFIG_FILS
++ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) {
++ int delay_assoc = 0;
++
++ if (!req_ies)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++
++ if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
++ req_ies_len,
++ sta->fils_session)) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Session validation failed");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
++ req_ies_len);
++ if (res < 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Key Confirm validation failed");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Delaying Assoc Response (HLP)");
++ delay_assoc = 1;
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Going ahead with Assoc Response (no HLP)");
++ }
++
++ if (sta) {
++ wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++ os_free(sta->fils_pending_assoc_req);
++ sta->fils_pending_assoc_req = NULL;
++ sta->fils_pending_assoc_req_len = 0;
++ wpabuf_free(sta->fils_hlp_resp);
++ sta->fils_hlp_resp = NULL;
++ sta->fils_drv_assoc_finish = 0;
++ }
++
++ if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
++ u8 *req_tmp;
++
++ req_tmp = os_malloc(req_ies_len);
++ if (!req_tmp) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: buffer allocation failed for assoc req");
++ goto fail;
++ }
++ os_memcpy(req_tmp, req_ies, req_ies_len);
++ sta->fils_pending_assoc_req = req_tmp;
++ sta->fils_pending_assoc_req_len = req_ies_len;
++ sta->fils_pending_assoc_is_reassoc = reassoc;
++ sta->fils_drv_assoc_finish = 1;
++ wpa_printf(MSG_DEBUG,
++ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
++ MACSTR, MAC2STR(sta->addr));
++ eloop_register_timeout(
++ 0, hapd->conf->fils_hlp_wait_time * 1024,
++ fils_hlp_timeout, hapd, sta);
++ return 0;
++ }
++ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
++ elems.fils_session,
++ sta->fils_hlp_resp);
++ wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
++ buf, p - buf);
++ }
++#endif /* CONFIG_FILS */
++
++#ifdef CONFIG_OWE
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
++ elems.owe_dh) {
++ u8 *npos;
++
++ npos = owe_assoc_req_process(hapd, sta,
++ elems.owe_dh, elems.owe_dh_len,
++ p, sizeof(buf) - (p - buf),
++ &reason);
++ if (npos)
++ p = npos;
++ if (!npos &&
++ reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
++ status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++ hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
++ p - buf);
++ return 0;
++ }
++
++ if (!npos || reason != WLAN_STATUS_SUCCESS)
++ goto fail;
++ }
++#endif /* CONFIG_OWE */
++
++#ifdef CONFIG_DPP2
++ dpp_pfs_free(sta->dpp_pfs);
++ sta->dpp_pfs = NULL;
++
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
++ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
++ elems.owe_dh) {
++ sta->dpp_pfs = dpp_pfs_init(
++ wpabuf_head(hapd->conf->dpp_netaccesskey),
++ wpabuf_len(hapd->conf->dpp_netaccesskey));
++ if (!sta->dpp_pfs) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not initialize PFS");
++ /* Try to continue without PFS */
++ goto pfs_fail;
++ }
++
++ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
++ elems.owe_dh_len) < 0) {
++ dpp_pfs_free(sta->dpp_pfs);
++ sta->dpp_pfs = NULL;
++ reason = WLAN_REASON_UNSPECIFIED;
++ goto fail;
++ }
++ }
++
++ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
++ sta->dpp_pfs->secret : NULL);
++ pfs_fail:
++#endif /* CONFIG_DPP2 */
++
++#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+- if (sta->auth_alg == WLAN_AUTH_FT)
++ if (sta->auth_alg == WLAN_AUTH_FT ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK)
+ ap_sta_set_authorized(hapd, sta, 1);
+-#else /* CONFIG_IEEE80211R */
++#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+ /* Keep compiler silent about unused variables */
+ if (status) {
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+@@ -364,6 +620,12 @@
+
+ if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
++#ifdef CONFIG_FILS
++ else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK)
++ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
++#endif /* CONFIG_FILS */
+ else
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+@@ -381,9 +643,9 @@
+ return 0;
+
+ fail:
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+ ap_free_sta(hapd, sta);
+ return -1;
+@@ -431,7 +693,7 @@
+ {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+- if (!sta || !hapd->conf->disassoc_low_ack)
++ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
+ return;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+@@ -438,22 +700,92 @@
+ HOSTAPD_LEVEL_INFO,
+ "disconnected due to excessive missing ACKs");
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+- if (sta)
+- ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
++ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+ }
+
+
++void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
++ enum smps_mode smps_mode,
++ enum chan_width chan_width, u8 rx_nss)
++{
++ struct sta_info *sta = ap_get_sta(hapd, addr);
++ const char *txt;
++
++ if (!sta)
++ return;
++
++ switch (smps_mode) {
++ case SMPS_AUTOMATIC:
++ txt = "automatic";
++ break;
++ case SMPS_OFF:
++ txt = "off";
++ break;
++ case SMPS_DYNAMIC:
++ txt = "dynamic";
++ break;
++ case SMPS_STATIC:
++ txt = "static";
++ break;
++ default:
++ txt = NULL;
++ break;
++ }
++ if (txt) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
++ MACSTR " %s", MAC2STR(addr), txt);
++ }
++
++ switch (chan_width) {
++ case CHAN_WIDTH_20_NOHT:
++ txt = "20(no-HT)";
++ break;
++ case CHAN_WIDTH_20:
++ txt = "20";
++ break;
++ case CHAN_WIDTH_40:
++ txt = "40";
++ break;
++ case CHAN_WIDTH_80:
++ txt = "80";
++ break;
++ case CHAN_WIDTH_80P80:
++ txt = "80+80";
++ break;
++ case CHAN_WIDTH_160:
++ txt = "160";
++ break;
++ default:
++ txt = NULL;
++ break;
++ }
++ if (txt) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
++ MACSTR " %s", MAC2STR(addr), txt);
++ }
++
++ if (rx_nss != 0xff) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
++ MACSTR " %d", MAC2STR(addr), rx_nss);
++ }
++}
++
++
+ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset, int width, int cf1, int cf2)
+ {
++ /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
++
+ #ifdef NEED_AP_MLME
+- int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
++ int channel, chwidth, is_dfs;
++ u8 seg0_idx = 0, seg1_idx = 0;
++ size_t i;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+- "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+- freq, ht, offset, width, channel_width_to_string(width),
+- cf1, cf2);
++ "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
++ freq, ht, hapd->iconf->ch_switch_vht_config, offset,
++ width, channel_width_to_string(width), cf1, cf2);
+
+ hapd->iface->freq = freq;
+
+@@ -491,21 +823,33 @@
+ seg1_idx = (cf2 - 5000) / 5;
+ break;
+ default:
+- seg0_idx = hostapd_hw_get_channel(hapd, cf1);
+- seg1_idx = hostapd_hw_get_channel(hapd, cf2);
++ ieee80211_freq_to_chan(cf1, &seg0_idx);
++ ieee80211_freq_to_chan(cf2, &seg1_idx);
+ break;
+ }
+
+ hapd->iconf->channel = channel;
+ hapd->iconf->ieee80211n = ht;
+- if (!ht)
++ if (!ht) {
+ hapd->iconf->ieee80211ac = 0;
++ } else if (hapd->iconf->ch_switch_vht_config) {
++ /* CHAN_SWITCH VHT config */
++ if (hapd->iconf->ch_switch_vht_config &
++ CH_SWITCH_VHT_ENABLED)
++ hapd->iconf->ieee80211ac = 1;
++ else if (hapd->iconf->ch_switch_vht_config &
++ CH_SWITCH_VHT_DISABLED)
++ hapd->iconf->ieee80211ac = 0;
++ }
++ hapd->iconf->ch_switch_vht_config = 0;
++
+ hapd->iconf->secondary_channel = offset;
+ hapd->iconf->vht_oper_chwidth = chwidth;
+ hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
+ hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+
+- is_dfs = ieee80211_is_dfs(freq);
++ is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
++ hapd->iface->num_hw_features);
+
+ if (hapd->csa_in_progress &&
+ freq == hapd->cs_freq_params.freq) {
+@@ -518,6 +862,9 @@
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ "freq=%d dfs=%d", freq, is_dfs);
+ }
++
++ for (i = 0; i < hapd->iface->num_bss; i++)
++ hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
+ #endif /* NEED_AP_MLME */
+ }
+
+@@ -539,10 +886,11 @@
+
+
+ #ifdef CONFIG_ACS
+-static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+- struct acs_selected_channels *acs_res)
++void hostapd_acs_channel_selected(struct hostapd_data *hapd,
++ struct acs_selected_channels *acs_res)
+ {
+ int ret, i;
++ int err = 0;
+
+ if (hapd->iconf->channel) {
+ wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+@@ -564,7 +912,8 @@
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver selected to bad hw_mode");
+- return;
++ err = 1;
++ goto out;
+ }
+ }
+
+@@ -574,7 +923,8 @@
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel");
+- return;
++ err = 1;
++ goto out;
+ }
+
+ hapd->iconf->channel = acs_res->pri_channel;
+@@ -588,7 +938,8 @@
+ hapd->iconf->secondary_channel = 1;
+ else {
+ wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+- return;
++ err = 1;
++ goto out;
+ }
+
+ if (hapd->iface->conf->ieee80211ac) {
+@@ -617,7 +968,8 @@
+ }
+ }
+
+- ret = hostapd_acs_completed(hapd->iface, 0);
++out:
++ ret = hostapd_acs_completed(hapd->iface, err);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Possibly channel configuration is invalid");
+@@ -651,7 +1003,7 @@
+
+ #ifdef HOSTAPD
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+ const u8 *bssid,
+ u16 auth_transaction, u16 status,
+@@ -670,9 +1022,35 @@
+
+ hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+
++#ifdef CONFIG_FILS
++static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 resp,
++ struct wpabuf *data, int pub)
++{
++ if (resp == WLAN_STATUS_SUCCESS) {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
++ sta->flags |= WLAN_STA_AUTH;
++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
++ sta->auth_alg = WLAN_AUTH_FILS_SK;
++ mlme_authenticate_indication(hapd, sta);
++ } else {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "authentication failed (FILS)");
++ }
++
++ hostapd_sta_auth(hapd, sta->addr, 2, resp,
++ data ? wpabuf_head(data) : NULL,
++ data ? wpabuf_len(data) : 0);
++ wpabuf_free(data);
++}
++#endif /* CONFIG_FILS */
++
++
+ static void hostapd_notif_auth(struct hostapd_data *hapd,
+ struct auth_info *rx_auth)
+ {
+@@ -691,7 +1069,7 @@
+ }
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+@@ -709,7 +1087,19 @@
+ hostapd_notify_auth_ft_finish, hapd);
+ return;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
++
++#ifdef CONFIG_FILS
++ if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
++ sta->auth_alg = WLAN_AUTH_FILS_SK;
++ handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
++ rx_auth->auth_type, rx_auth->auth_transaction,
++ rx_auth->status_code,
++ hostapd_notify_auth_fils_finish);
++ return;
++ }
++#endif /* CONFIG_FILS */
++
+ fail:
+ hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+ status, resp_ies, resp_ies_len);
+@@ -716,6 +1106,7 @@
+ }
+
+
++#ifndef NEED_AP_MLME
+ static void hostapd_action_rx(struct hostapd_data *hapd,
+ struct rx_mgmt *drv_mgmt)
+ {
+@@ -723,11 +1114,12 @@
+ struct sta_info *sta;
+ size_t plen __maybe_unused;
+ u16 fc;
++ u8 *action __maybe_unused;
+
+- if (drv_mgmt->frame_len < 24 + 1)
++ if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
+ return;
+
+- plen = drv_mgmt->frame_len - 24 - 1;
++ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
+
+ mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
+ fc = le_to_host16(mgmt->frame_control);
+@@ -734,8 +1126,11 @@
+ if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+ return; /* handled by the driver */
+
+- wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
+- mgmt->u.action.category, (int) plen);
++ action = (u8 *) &mgmt->u.action.u;
++ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
++ " da " MACSTR " plen %d",
++ mgmt->u.action.category, *action,
++ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL) {
+@@ -742,26 +1137,24 @@
+ wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+ return;
+ }
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (mgmt->u.action.category == WLAN_ACTION_FT) {
+- const u8 *payload = drv_mgmt->frame + 24 + 1;
+-
+- wpa_ft_action_rx(sta->wpa_sm, payload, plen);
++ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
++ return;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+- if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
+- ieee802_11_sa_query_action(
+- hapd, mgmt->sa,
+- mgmt->u.action.u.sa_query_resp.action,
+- mgmt->u.action.u.sa_query_resp.trans_id);
++ if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
++ ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
++ return;
+ }
+ #endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
++ return;
+ }
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ #ifdef CONFIG_FST
+ if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+ fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+@@ -768,8 +1161,25 @@
+ return;
+ }
+ #endif /* CONFIG_FST */
++#ifdef CONFIG_DPP
++ if (plen >= 2 + 4 &&
++ mgmt->u.action.u.vs_public_action.action ==
++ WLAN_PA_VENDOR_SPECIFIC &&
++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
++ OUI_WFA &&
++ mgmt->u.action.u.vs_public_action.variable[0] ==
++ DPP_OUI_TYPE) {
++ const u8 *pos, *end;
+
++ pos = mgmt->u.action.u.vs_public_action.oui;
++ end = drv_mgmt->frame + drv_mgmt->frame_len;
++ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
++ drv_mgmt->freq);
++ return;
++ }
++#endif /* CONFIG_DPP */
+ }
++#endif /* NEED_AP_MLME */
+
+
+ #ifdef NEED_AP_MLME
+@@ -852,6 +1262,7 @@
+ }
+
+ os_memset(&fi, 0, sizeof(fi));
++ fi.freq = rx_mgmt->freq;
+ fi.datarate = rx_mgmt->datarate;
+ fi.ssi_signal = rx_mgmt->ssi_signal;
+
+@@ -884,11 +1295,24 @@
+ size_t len, u16 stype, int ok)
+ {
+ struct ieee80211_hdr *hdr;
++ struct hostapd_data *orig_hapd = hapd;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+- if (hapd == NULL || hapd == HAPD_BROADCAST)
++ if (!hapd)
+ return;
++ if (hapd == HAPD_BROADCAST) {
++ if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
++ buf[24] != WLAN_ACTION_PUBLIC)
++ return;
++ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
++ if (!hapd || hapd == HAPD_BROADCAST)
++ return;
++ /*
++ * Allow processing of TX status for a Public Action frame that
++ * used wildcard BBSID.
++ */
++ }
+ ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+ }
+
+@@ -935,7 +1359,9 @@
+ ieee802_1x_receive(hapd, src, data, data_len);
+ }
+
++#endif /* HOSTAPD */
+
++
+ static struct hostapd_channel_data * hostapd_get_mode_channel(
+ struct hostapd_iface *iface, unsigned int freq)
+ {
+@@ -944,8 +1370,6 @@
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+- if (!chan)
+- return NULL;
+ if ((unsigned int) chan->freq == freq)
+ return chan;
+ }
+@@ -1009,10 +1433,9 @@
+ }
+
+
+-static void hostapd_event_get_survey(struct hostapd_data *hapd,
+- struct survey_results *survey_results)
++void hostapd_event_get_survey(struct hostapd_iface *iface,
++ struct survey_results *survey_results)
+ {
+- struct hostapd_iface *iface = hapd->iface;
+ struct freq_survey *survey, *tmp;
+ struct hostapd_channel_data *chan;
+
+@@ -1044,6 +1467,7 @@
+ }
+
+
++#ifdef HOSTAPD
+ #ifdef NEED_AP_MLME
+
+ static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+@@ -1070,6 +1494,16 @@
+ }
+
+
++static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
++ struct dfs_event *radar)
++{
++ wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
++ hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
++ radar->chan_offset, radar->chan_width,
++ radar->cf1, radar->cf2);
++}
++
++
+ static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+ {
+@@ -1112,6 +1546,28 @@
+ #endif /* NEED_AP_MLME */
+
+
++static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
++ int istatus,
++ const char *ifname,
++ const u8 *addr)
++{
++ struct sta_info *sta = ap_get_sta(hapd, addr);
++
++ if (sta) {
++ os_free(sta->ifname_wds);
++ if (istatus == INTERFACE_ADDED)
++ sta->ifname_wds = os_strdup(ifname);
++ else
++ sta->ifname_wds = NULL;
++ }
++
++ wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
++ istatus == INTERFACE_ADDED ?
++ WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
++ ifname, MAC2STR(addr));
++}
++
++
+ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+ {
+@@ -1185,10 +1641,10 @@
+ if (!data->rx_mgmt.frame)
+ break;
+ #ifdef NEED_AP_MLME
+- if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
+- break;
++ hostapd_mgmt_rx(hapd, &data->rx_mgmt);
++#else /* NEED_AP_MLME */
++ hostapd_action_rx(hapd, &data->rx_mgmt);
+ #endif /* NEED_AP_MLME */
+- hostapd_action_rx(hapd, &data->rx_mgmt);
+ break;
+ case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+@@ -1251,7 +1707,7 @@
+ data->connect_failed_reason.code);
+ break;
+ case EVENT_SURVEY:
+- hostapd_event_get_survey(hapd, &data->survey_results);
++ hostapd_event_get_survey(hapd->iface, &data->survey_results);
+ break;
+ #ifdef NEED_AP_MLME
+ case EVENT_INTERFACE_UNAVAILABLE:
+@@ -1262,6 +1718,11 @@
+ break;
+ hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+ break;
++ case EVENT_DFS_PRE_CAC_EXPIRED:
++ if (!data)
++ break;
++ hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
++ break;
+ case EVENT_DFS_CAC_FINISHED:
+ if (!data)
+ break;
+@@ -1299,9 +1760,17 @@
+ * Try to re-enable interface if the driver stopped it
+ * when the interface got disabled.
+ */
+- wpa_auth_reconfig_group_keys(hapd->wpa_auth);
++ if (hapd->wpa_auth)
++ wpa_auth_reconfig_group_keys(hapd->wpa_auth);
++ else
++ hostapd_reconfig_encryption(hapd);
+ hapd->reenable_beacon = 1;
+ ieee802_11_set_beacon(hapd);
++#ifdef NEED_AP_MLME
++ } else if (hapd->disabled && hapd->iface->cac_started) {
++ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
++ hostapd_handle_dfs(hapd->iface);
++#endif /* NEED_AP_MLME */
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+@@ -1315,6 +1784,18 @@
+ &data->acs_selected_channels);
+ break;
+ #endif /* CONFIG_ACS */
++ case EVENT_STATION_OPMODE_CHANGED:
++ hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
++ data->sta_opmode.smps_mode,
++ data->sta_opmode.chan_width,
++ data->sta_opmode.rx_nss);
++ break;
++ case EVENT_WDS_STA_INTERFACE_STATUS:
++ hostapd_event_wds_sta_interface_status(
++ hapd, data->wds_sta_interface.istatus,
++ data->wds_sta_interface.ifname,
++ data->wds_sta_interface.sta_addr);
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+ break;
+@@ -1321,4 +1802,31 @@
+ }
+ }
+
++
++void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data)
++{
++ struct hapd_interfaces *interfaces = ctx;
++ struct hostapd_data *hapd;
++
++ if (event != EVENT_INTERFACE_STATUS)
++ return;
++
++ hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
++ if (hapd && hapd->driver && hapd->driver->get_ifindex &&
++ hapd->drv_priv) {
++ unsigned int ifindex;
++
++ ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
++ if (ifindex != data->interface_status.ifindex) {
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
++ "interface status ifindex %d mismatch (%d)",
++ ifindex, data->interface_status.ifindex);
++ return;
++ }
++ }
++ if (hapd)
++ wpa_supplicant_event(hapd, event, data);
++}
++
+ #endif /* HOSTAPD */
+--- contrib/wpa/src/ap/eap_user_db.c.orig
++++ contrib/wpa/src/ap/eap_user_db.c
+@@ -91,6 +91,8 @@
+ set_user_methods(user, argv[i]);
+ } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+ user->remediation = strlen(argv[i]) > 0;
++ } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
++ user->t_c_timestamp = strtol(argv[i], NULL, 10);
+ }
+ }
+
+@@ -137,6 +139,7 @@
+ struct hostapd_eap_user *user = NULL;
+ char id_str[256], cmd[300];
+ size_t i;
++ int res;
+
+ if (identity_len >= sizeof(id_str)) {
+ wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
+@@ -172,6 +175,7 @@
+ if (hapd->tmp_eap_user.identity == NULL)
+ return NULL;
+ os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
++ hapd->tmp_eap_user.identity_len = identity_len;
+
+ if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
+ wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
+@@ -180,9 +184,12 @@
+ return NULL;
+ }
+
+- os_snprintf(cmd, sizeof(cmd),
+- "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+- id_str, phase2);
++ res = os_snprintf(cmd, sizeof(cmd),
++ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
++ id_str, phase2);
++ if (os_snprintf_error(sizeof(cmd), res))
++ goto fail;
++
+ wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+ if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
+ SQLITE_OK) {
+@@ -212,6 +219,7 @@
+ }
+ }
+
++fail:
+ sqlite3_close(db);
+
+ return user;
+--- contrib/wpa/src/ap/eth_p_oui.c.orig
++++ contrib/wpa/src/ap/eth_p_oui.c
+@@ -0,0 +1,191 @@
++/*
++ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
++ * Copyright (c) 2016, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "l2_packet/l2_packet.h"
++#include "hostapd.h"
++#include "eth_p_oui.h"
++
++/*
++ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
++ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
++ * vendor-specific subtype 0x0001.
++ */
++static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
++
++struct eth_p_oui_iface {
++ struct dl_list list;
++ char ifname[IFNAMSIZ + 1];
++ struct l2_packet_data *l2;
++ struct dl_list receiver;
++};
++
++struct eth_p_oui_ctx {
++ struct dl_list list;
++ struct eth_p_oui_iface *iface;
++ /* all data needed to deliver and unregister */
++ u8 oui_suffix; /* last byte of OUI */
++ void (*rx_callback)(void *ctx, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix,
++ const u8 *buf, size_t len);
++ void *rx_callback_ctx;
++};
++
++
++void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
++ const u8 *dst_addr, const u8 *buf, size_t len)
++{
++ ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
++ ctx->oui_suffix, buf, len);
++}
++
++
++static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
++{
++ struct eth_p_oui_iface *iface = ctx;
++ struct eth_p_oui_ctx *receiver;
++ const struct l2_ethhdr *ethhdr;
++
++ if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
++ /* too short packet */
++ return;
++ }
++
++ ethhdr = (struct l2_ethhdr *) buf;
++ /* trim eth_hdr from buf and len */
++ buf += sizeof(*ethhdr);
++ len -= sizeof(*ethhdr);
++
++ /* verify OUI and vendor-specific subtype match */
++ if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
++ return;
++ buf += sizeof(global_oui);
++ len -= sizeof(global_oui);
++
++ dl_list_for_each(receiver, &iface->receiver,
++ struct eth_p_oui_ctx, list) {
++ if (buf[0] != receiver->oui_suffix)
++ continue;
++
++ eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
++ buf + 1, len - 1);
++ }
++}
++
++
++struct eth_p_oui_ctx *
++eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
++ void (*rx_callback)(void *ctx, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix,
++ const u8 *buf, size_t len),
++ void *rx_callback_ctx)
++{
++ struct eth_p_oui_iface *iface;
++ struct eth_p_oui_ctx *receiver;
++ int found = 0;
++ struct hapd_interfaces *interfaces;
++
++ receiver = os_zalloc(sizeof(*receiver));
++ if (!receiver)
++ goto err;
++
++ receiver->oui_suffix = oui_suffix;
++ receiver->rx_callback = rx_callback;
++ receiver->rx_callback_ctx = rx_callback_ctx;
++
++ interfaces = hapd->iface->interfaces;
++
++ dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
++ list) {
++ if (os_strcmp(iface->ifname, ifname) != 0)
++ continue;
++ found = 1;
++ break;
++ }
++
++ if (!found) {
++ iface = os_zalloc(sizeof(*iface));
++ if (!iface)
++ goto err;
++
++ os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
++ iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
++ iface, 1);
++ if (!iface->l2) {
++ os_free(iface);
++ goto err;
++ }
++ dl_list_init(&iface->receiver);
++
++ dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
++ }
++
++ dl_list_add_tail(&iface->receiver, &receiver->list);
++ receiver->iface = iface;
++
++ return receiver;
++err:
++ os_free(receiver);
++ return NULL;
++}
++
++
++void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
++{
++ struct eth_p_oui_iface *iface;
++
++ if (!ctx)
++ return;
++
++ iface = ctx->iface;
++
++ dl_list_del(&ctx->list);
++ os_free(ctx);
++
++ if (dl_list_empty(&iface->receiver)) {
++ dl_list_del(&iface->list);
++ l2_packet_deinit(iface->l2);
++ os_free(iface);
++ }
++}
++
++
++int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
++ const u8 *dst_addr, const u8 *buf, size_t len)
++{
++ struct eth_p_oui_iface *iface = ctx->iface;
++ u8 *packet, *p;
++ size_t packet_len;
++ int ret;
++ struct l2_ethhdr *ethhdr;
++
++ packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
++ packet = os_zalloc(packet_len);
++ if (!packet)
++ return -1;
++ p = packet;
++
++ ethhdr = (struct l2_ethhdr *) packet;
++ os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
++ os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
++ ethhdr->h_proto = host_to_be16(ETH_P_OUI);
++ p += sizeof(*ethhdr);
++
++ os_memcpy(p, global_oui, sizeof(global_oui));
++ p[sizeof(global_oui)] = ctx->oui_suffix;
++ p += sizeof(global_oui) + 1;
++
++ os_memcpy(p, buf, len);
++
++ ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
++ os_free(packet);
++ return ret;
++}
+--- contrib/wpa/src/ap/eth_p_oui.h.orig
++++ contrib/wpa/src/ap/eth_p_oui.h
+@@ -0,0 +1,28 @@
++/*
++ * hostapd / IEEE 802 OUI Extended Ethertype
++ * Copyright (c) 2016, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef ETH_P_OUI_H
++#define ETH_P_OUI_H
++
++struct eth_p_oui_ctx;
++struct hostapd_data;
++
++/* rx_callback only gets payload after OUI passed as buf */
++struct eth_p_oui_ctx *
++eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
++ void (*rx_callback)(void *ctx, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix,
++ const u8 *buf, size_t len),
++ void *rx_callback_ctx);
++void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
++int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
++ const u8 *dst_addr, const u8 *buf, size_t len);
++void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
++ const u8 *dst_addr, const u8 *buf, size_t len);
++
++#endif /* ETH_P_OUI_H */
+--- contrib/wpa/src/ap/fils_hlp.c.orig
++++ contrib/wpa/src/ap/fils_hlp.c
+@@ -0,0 +1,654 @@
++/*
++ * FILS HLP request processing
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "common/dhcp.h"
++#include "hostapd.h"
++#include "sta_info.h"
++#include "ieee802_11.h"
++#include "fils_hlp.h"
++
++
++static be16 ip_checksum(const void *buf, size_t len)
++{
++ u32 sum = 0;
++ const u16 *pos;
++
++ for (pos = buf; len >= 2; len -= 2)
++ sum += ntohs(*pos++);
++ if (len)
++ sum += ntohs(*pos << 8);
++
++ sum = (sum >> 16) + (sum & 0xffff);
++ sum += sum >> 16;
++ return htons(~sum);
++}
++
++
++static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
++ struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
++{
++ u8 *pos, *end;
++ struct dhcp_data *dhcp;
++ struct sockaddr_in addr;
++ ssize_t res;
++ const u8 *server_id = NULL;
++
++ if (!sta->hlp_dhcp_discover) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: No pending HLP DHCPDISCOVER available");
++ return -1;
++ }
++
++ /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
++ * IP address option with yiaddr. */
++ pos = wpabuf_mhead(sta->hlp_dhcp_discover);
++ end = pos + wpabuf_len(sta->hlp_dhcp_discover);
++ dhcp = (struct dhcp_data *) pos;
++ pos = (u8 *) (dhcp + 1);
++ pos += 4; /* skip magic */
++ while (pos < end && *pos != DHCP_OPT_END) {
++ u8 opt, olen;
++
++ opt = *pos++;
++ if (opt == DHCP_OPT_PAD)
++ continue;
++ if (pos >= end)
++ break;
++ olen = *pos++;
++ if (olen > end - pos)
++ break;
++
++ switch (opt) {
++ case DHCP_OPT_MSG_TYPE:
++ if (olen > 0)
++ *pos = DHCPREQUEST;
++ break;
++ case DHCP_OPT_RAPID_COMMIT:
++ case DHCP_OPT_REQUESTED_IP_ADDRESS:
++ case DHCP_OPT_SERVER_ID:
++ /* Remove option */
++ pos -= 2;
++ os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
++ end -= 2 + olen;
++ olen = 0;
++ break;
++ }
++ pos += olen;
++ }
++ if (pos >= end || *pos != DHCP_OPT_END) {
++ wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
++ return -1;
++ }
++ sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
++
++ /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
++ pos = (u8 *) (dhcpoffer + 1);
++ end = dhcpofferend;
++ pos += 4; /* skip magic */
++ while (pos < end && *pos != DHCP_OPT_END) {
++ u8 opt, olen;
++
++ opt = *pos++;
++ if (opt == DHCP_OPT_PAD)
++ continue;
++ if (pos >= end)
++ break;
++ olen = *pos++;
++ if (olen > end - pos)
++ break;
++
++ switch (opt) {
++ case DHCP_OPT_SERVER_ID:
++ server_id = pos - 2;
++ break;
++ }
++ pos += olen;
++ }
++
++ if (wpabuf_resize(&sta->hlp_dhcp_discover,
++ 6 + 1 + (server_id ? 2 + server_id[1] : 0)))
++ return -1;
++ if (server_id)
++ wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
++ 2 + server_id[1]);
++ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
++ wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
++ wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
++ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
++
++ os_memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
++ addr.sin_port = htons(hapd->conf->dhcp_server_port);
++ res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
++ wpabuf_len(sta->hlp_dhcp_discover), 0,
++ (const struct sockaddr *) &addr, sizeof(addr));
++ if (res < 0) {
++ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
++ strerror(errno));
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG,
++ "FILS: Acting as DHCP rapid commit proxy for %s:%d",
++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++ wpabuf_free(sta->hlp_dhcp_discover);
++ sta->hlp_dhcp_discover = NULL;
++ sta->fils_dhcp_rapid_commit_proxy = 1;
++ return 0;
++}
++
++
++static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
++{
++ struct hostapd_data *hapd = sock_ctx;
++ struct sta_info *sta;
++ u8 buf[1500], *pos, *end, *end_opt = NULL;
++ struct dhcp_data *dhcp;
++ struct sockaddr_in addr;
++ socklen_t addr_len;
++ ssize_t res;
++ u8 msgtype = 0;
++ int rapid_commit = 0;
++ struct iphdr *iph;
++ struct udphdr *udph;
++ struct wpabuf *resp;
++ const u8 *rpos;
++ size_t left, len;
++
++ addr_len = sizeof(addr);
++ res = recvfrom(sd, buf, sizeof(buf), 0,
++ (struct sockaddr *) &addr, &addr_len);
++ if (res < 0) {
++ wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
++ strerror(errno));
++ return;
++ }
++ wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
++ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
++ if ((size_t) res < sizeof(*dhcp))
++ return;
++ dhcp = (struct dhcp_data *) buf;
++ if (dhcp->op != 2)
++ return; /* Not a BOOTREPLY */
++ if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP - DHCP response to unknown relay address 0x%x",
++ dhcp->relay_ip);
++ return;
++ }
++ dhcp->relay_ip = 0;
++ pos = (u8 *) (dhcp + 1);
++ end = &buf[res];
++
++ if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
++ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
++ return;
++ }
++ pos += 4;
++
++ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
++ pos, end - pos);
++ while (pos < end && *pos != DHCP_OPT_END) {
++ u8 opt, olen;
++
++ opt = *pos++;
++ if (opt == DHCP_OPT_PAD)
++ continue;
++ if (pos >= end)
++ break;
++ olen = *pos++;
++ if (olen > end - pos)
++ break;
++
++ switch (opt) {
++ case DHCP_OPT_MSG_TYPE:
++ if (olen > 0)
++ msgtype = pos[0];
++ break;
++ case DHCP_OPT_RAPID_COMMIT:
++ rapid_commit = 1;
++ break;
++ }
++ pos += olen;
++ }
++ if (pos < end && *pos == DHCP_OPT_END)
++ end_opt = pos;
++
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
++ MACSTR ")",
++ msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
++
++ sta = ap_get_sta(hapd, dhcp->hw_addr);
++ if (!sta || !sta->fils_pending_assoc_req) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: No pending HLP DHCP exchange with hw_addr "
++ MACSTR, MAC2STR(dhcp->hw_addr));
++ return;
++ }
++
++ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
++ !rapid_commit) {
++ /* Use hostapd to take care of 4-message exchange and convert
++ * the final DHCPACK to rapid commit version. */
++ if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
++ return;
++ /* failed, so send the server response as-is */
++ } else if (msgtype != DHCPACK) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
++ }
++
++ pos = buf;
++ resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
++ sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
++ if (!resp)
++ return;
++ wpabuf_put_data(resp, sta->addr, ETH_ALEN);
++ wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
++ wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
++ wpabuf_put_be16(resp, ETH_P_IP);
++ iph = wpabuf_put(resp, sizeof(*iph));
++ iph->version = 4;
++ iph->ihl = sizeof(*iph) / 4;
++ iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
++ iph->ttl = 1;
++ iph->protocol = 17; /* UDP */
++ iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
++ iph->daddr = dhcp->client_ip;
++ iph->check = ip_checksum(iph, sizeof(*iph));
++ udph = wpabuf_put(resp, sizeof(*udph));
++ udph->uh_sport = htons(DHCP_SERVER_PORT);
++ udph->uh_dport = htons(DHCP_CLIENT_PORT);
++ udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
++ udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
++ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
++ !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
++ /* Add rapid commit option */
++ wpabuf_put_data(resp, pos, end_opt - pos);
++ wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
++ wpabuf_put_u8(resp, 0);
++ wpabuf_put_data(resp, end_opt, end - end_opt);
++ } else {
++ wpabuf_put_data(resp, pos, end - pos);
++ }
++ if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
++ 2 * wpabuf_len(resp) / 255 + 100)) {
++ wpabuf_free(resp);
++ return;
++ }
++
++ rpos = wpabuf_head(resp);
++ left = wpabuf_len(resp);
++
++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
++ if (left <= 254)
++ len = 1 + left;
++ else
++ len = 255;
++ wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
++ /* Element ID Extension */
++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
++ /* Destination MAC Address, Source MAC Address, HLP Packet.
++ * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
++ * when LPD is used). */
++ wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
++ rpos += len - 1;
++ left -= len - 1;
++ while (left) {
++ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
++ len = left > 255 ? 255 : left;
++ wpabuf_put_u8(sta->fils_hlp_resp, len);
++ wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
++ rpos += len;
++ left -= len;
++ }
++ wpabuf_free(resp);
++
++ if (sta->fils_drv_assoc_finish)
++ hostapd_notify_assoc_fils_finish(hapd, sta);
++ else
++ fils_hlp_finish_assoc(hapd, sta);
++}
++
++
++static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *msg, size_t len)
++{
++ const struct dhcp_data *dhcp;
++ struct wpabuf *dhcp_buf;
++ struct dhcp_data *dhcp_msg;
++ u8 msgtype = 0;
++ int rapid_commit = 0;
++ const u8 *pos = msg, *end;
++ struct sockaddr_in addr;
++ ssize_t res;
++
++ if (len < sizeof(*dhcp))
++ return 0;
++ dhcp = (const struct dhcp_data *) pos;
++ end = pos + len;
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
++ dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
++ ntohl(dhcp->xid));
++ pos += sizeof(*dhcp);
++ if (dhcp->op != 1)
++ return 0; /* Not a BOOTREQUEST */
++
++ if (end - pos < 4)
++ return 0;
++ if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
++ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
++ return 0;
++ }
++ pos += 4;
++
++ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
++ while (pos < end && *pos != DHCP_OPT_END) {
++ u8 opt, olen;
++
++ opt = *pos++;
++ if (opt == DHCP_OPT_PAD)
++ continue;
++ if (pos >= end)
++ break;
++ olen = *pos++;
++ if (olen > end - pos)
++ break;
++
++ switch (opt) {
++ case DHCP_OPT_MSG_TYPE:
++ if (olen > 0)
++ msgtype = pos[0];
++ break;
++ case DHCP_OPT_RAPID_COMMIT:
++ rapid_commit = 1;
++ break;
++ }
++ pos += olen;
++ }
++
++ wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
++ if (msgtype != DHCPDISCOVER)
++ return 0;
++
++ if (hapd->conf->dhcp_server.af != AF_INET ||
++ hapd->conf->dhcp_server.u.v4.s_addr == 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP - no DHCPv4 server configured - drop request");
++ return 0;
++ }
++
++ if (hapd->conf->own_ip_addr.af != AF_INET ||
++ hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
++ return 0;
++ }
++
++ if (hapd->dhcp_sock < 0) {
++ int s;
++
++ s = socket(AF_INET, SOCK_DGRAM, 0);
++ if (s < 0) {
++ wpa_printf(MSG_ERROR,
++ "FILS: Failed to open DHCP socket: %s",
++ strerror(errno));
++ return 0;
++ }
++
++ if (hapd->conf->dhcp_relay_port) {
++ os_memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr =
++ hapd->conf->own_ip_addr.u.v4.s_addr;
++ addr.sin_port = htons(hapd->conf->dhcp_relay_port);
++ if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
++ wpa_printf(MSG_ERROR,
++ "FILS: Failed to bind DHCP socket: %s",
++ strerror(errno));
++ close(s);
++ return 0;
++ }
++ }
++ if (eloop_register_sock(s, EVENT_TYPE_READ,
++ fils_dhcp_handler, NULL, hapd)) {
++ close(s);
++ return 0;
++ }
++
++ hapd->dhcp_sock = s;
++ }
++
++ dhcp_buf = wpabuf_alloc(len);
++ if (!dhcp_buf)
++ return 0;
++ dhcp_msg = wpabuf_put(dhcp_buf, len);
++ os_memcpy(dhcp_msg, msg, len);
++ dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
++ os_memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
++ addr.sin_port = htons(hapd->conf->dhcp_server_port);
++ res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
++ (const struct sockaddr *) &addr, sizeof(addr));
++ if (res < 0) {
++ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
++ strerror(errno));
++ wpabuf_free(dhcp_buf);
++ /* Close the socket to try to recover from error */
++ eloop_unregister_read_sock(hapd->dhcp_sock);
++ close(hapd->dhcp_sock);
++ hapd->dhcp_sock = -1;
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
++ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
++ rapid_commit);
++ if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
++ /* Store a copy of the DHCPDISCOVER for rapid commit proxying
++ * purposes if the server does not support the rapid commit
++ * option. */
++ wpa_printf(MSG_DEBUG,
++ "FILS: Store DHCPDISCOVER for rapid commit proxy");
++ wpabuf_free(sta->hlp_dhcp_discover);
++ sta->hlp_dhcp_discover = dhcp_buf;
++ } else {
++ wpabuf_free(dhcp_buf);
++ }
++
++ return 1;
++}
++
++
++static int fils_process_hlp_udp(struct hostapd_data *hapd,
++ struct sta_info *sta, const u8 *dst,
++ const u8 *pos, size_t len)
++{
++ const struct iphdr *iph;
++ const struct udphdr *udph;
++ u16 sport, dport, ulen;
++
++ if (len < sizeof(*iph) + sizeof(*udph))
++ return 0;
++ iph = (const struct iphdr *) pos;
++ udph = (const struct udphdr *) (iph + 1);
++ sport = ntohs(udph->uh_sport);
++ dport = ntohs(udph->uh_dport);
++ ulen = ntohs(udph->uh_ulen);
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
++ sport, dport, ulen, ntohs(udph->uh_sum));
++ /* TODO: Check UDP checksum */
++ if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
++ return 0;
++
++ if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
++ return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
++ ulen - sizeof(*udph));
++ }
++
++ return 0;
++}
++
++
++static int fils_process_hlp_ip(struct hostapd_data *hapd,
++ struct sta_info *sta, const u8 *dst,
++ const u8 *pos, size_t len)
++{
++ const struct iphdr *iph;
++ u16 tot_len;
++
++ if (len < sizeof(*iph))
++ return 0;
++ iph = (const struct iphdr *) pos;
++ if (ip_checksum(iph, sizeof(*iph)) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
++ return 0;
++ }
++ tot_len = ntohs(iph->tot_len);
++ if (tot_len > len)
++ return 0;
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
++ iph->saddr, iph->daddr, iph->protocol);
++ switch (iph->protocol) {
++ case 17:
++ return fils_process_hlp_udp(hapd, sta, dst, pos, len);
++ }
++
++ return 0;
++}
++
++
++static int fils_process_hlp_req(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *pos, size_t len)
++{
++ const u8 *pkt, *end;
++
++ wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
++ " src=" MACSTR " len=%u)",
++ MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
++ (unsigned int) len);
++ if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Ignore HLP request with unexpected source address"
++ MACSTR, MAC2STR(pos + ETH_ALEN));
++ return 0;
++ }
++
++ end = pos + len;
++ pkt = pos + 2 * ETH_ALEN;
++ if (end - pkt >= 6 &&
++ os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
++ pkt += 6; /* Remove SNAP/LLC header */
++ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
++
++ if (end - pkt < 2)
++ return 0;
++
++ switch (WPA_GET_BE16(pkt)) {
++ case ETH_P_IP:
++ return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
++ end - pkt - 2);
++ }
++
++ return 0;
++}
++
++
++int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *pos, int left)
++{
++ const u8 *end = pos + left;
++ u8 *tmp, *tmp_pos;
++ int ret = 0;
++
++ if (sta->fils_pending_assoc_req &&
++ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
++ /* Do not process FILS HLP request again if the station
++ * retransmits (Re)Association Request frame before the previous
++ * HLP response has either been received or timed out. */
++ wpa_printf(MSG_DEBUG,
++ "FILS: Do not relay another HLP request from "
++ MACSTR
++ " before processing of the already pending one has been completed",
++ MAC2STR(sta->addr));
++ return 1;
++ }
++
++ /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
++ wpabuf_free(sta->hlp_dhcp_discover);
++ sta->hlp_dhcp_discover = NULL;
++ sta->fils_dhcp_rapid_commit_proxy = 0;
++
++ /* Check if there are any FILS HLP Container elements */
++ while (end - pos >= 2) {
++ if (2 + pos[1] > end - pos)
++ return 0;
++ if (pos[0] == WLAN_EID_EXTENSION &&
++ pos[1] >= 1 + 2 * ETH_ALEN &&
++ pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
++ break;
++ pos += 2 + pos[1];
++ }
++ if (end - pos < 2)
++ return 0; /* No FILS HLP Container elements */
++
++ tmp = os_malloc(end - pos);
++ if (!tmp)
++ return 0;
++
++ while (end - pos >= 2) {
++ if (2 + pos[1] > end - pos ||
++ pos[0] != WLAN_EID_EXTENSION ||
++ pos[1] < 1 + 2 * ETH_ALEN ||
++ pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
++ break;
++ tmp_pos = tmp;
++ os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
++ tmp_pos += pos[1] - 1;
++ pos += 2 + pos[1];
++
++ /* Add possible fragments */
++ while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
++ 2 + pos[1] <= end - pos) {
++ os_memcpy(tmp_pos, pos + 2, pos[1]);
++ tmp_pos += pos[1];
++ pos += 2 + pos[1];
++ }
++
++ if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
++ ret = 1;
++ }
++
++ os_free(tmp);
++
++ return ret;
++}
++
++
++void fils_hlp_deinit(struct hostapd_data *hapd)
++{
++ if (hapd->dhcp_sock >= 0) {
++ eloop_unregister_read_sock(hapd->dhcp_sock);
++ close(hapd->dhcp_sock);
++ hapd->dhcp_sock = -1;
++ }
++}
+--- contrib/wpa/src/ap/fils_hlp.h.orig
++++ contrib/wpa/src/ap/fils_hlp.h
+@@ -0,0 +1,27 @@
++/*
++ * FILS HLP request processing
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef FILS_HLP_H
++#define FILS_HLP_H
++
++int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *pos, int left);
++
++#ifdef CONFIG_FILS
++
++void fils_hlp_deinit(struct hostapd_data *hapd);
++
++#else /* CONFIG_FILS */
++
++static inline void fils_hlp_deinit(struct hostapd_data *hapd)
++{
++}
++
++#endif /* CONFIG_FILS */
++
++#endif /* FILS_HLP_H */
+--- contrib/wpa/src/ap/gas_query_ap.c.orig
++++ contrib/wpa/src/ap/gas_query_ap.c
+@@ -0,0 +1,714 @@
++/*
++ * Generic advertisement service (GAS) query (hostapd)
++ * Copyright (c) 2009, Atheros Communications
++ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
++ * Copyright (c) 2011-2014, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "utils/eloop.h"
++#include "utils/list.h"
++#include "common/ieee802_11_defs.h"
++#include "common/gas.h"
++#include "common/wpa_ctrl.h"
++#include "hostapd.h"
++#include "sta_info.h"
++#include "ap_drv_ops.h"
++#include "gas_query_ap.h"
++
++
++/** GAS query timeout in seconds */
++#define GAS_QUERY_TIMEOUT_PERIOD 2
++
++/* GAS query wait-time / duration in ms */
++#define GAS_QUERY_WAIT_TIME_INITIAL 1000
++#define GAS_QUERY_WAIT_TIME_COMEBACK 150
++
++/**
++ * struct gas_query_pending - Pending GAS query
++ */
++struct gas_query_pending {
++ struct dl_list list;
++ struct gas_query_ap *gas;
++ u8 addr[ETH_ALEN];
++ u8 dialog_token;
++ u8 next_frag_id;
++ unsigned int wait_comeback:1;
++ unsigned int offchannel_tx_started:1;
++ unsigned int retry:1;
++ int freq;
++ u16 status_code;
++ struct wpabuf *req;
++ struct wpabuf *adv_proto;
++ struct wpabuf *resp;
++ struct os_reltime last_oper;
++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
++ enum gas_query_ap_result result,
++ const struct wpabuf *adv_proto,
++ const struct wpabuf *resp, u16 status_code);
++ void *ctx;
++ u8 sa[ETH_ALEN];
++};
++
++/**
++ * struct gas_query_ap - Internal GAS query data
++ */
++struct gas_query_ap {
++ struct hostapd_data *hapd;
++ void *msg_ctx;
++ struct dl_list pending; /* struct gas_query_pending */
++ struct gas_query_pending *current;
++};
++
++
++static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
++static void gas_query_timeout(void *eloop_data, void *user_ctx);
++static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
++static void gas_query_tx_initial_req(struct gas_query_ap *gas,
++ struct gas_query_pending *query);
++static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
++
++
++static int ms_from_time(struct os_reltime *last)
++{
++ struct os_reltime now, res;
++
++ os_get_reltime(&now);
++ os_reltime_sub(&now, last, &res);
++ return res.sec * 1000 + res.usec / 1000;
++}
++
++
++/**
++ * gas_query_ap_init - Initialize GAS query component
++ * @hapd: Pointer to hostapd data
++ * Returns: Pointer to GAS query data or %NULL on failure
++ */
++struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
++ void *msg_ctx)
++{
++ struct gas_query_ap *gas;
++
++ gas = os_zalloc(sizeof(*gas));
++ if (!gas)
++ return NULL;
++
++ gas->hapd = hapd;
++ gas->msg_ctx = msg_ctx;
++ dl_list_init(&gas->pending);
++
++ return gas;
++}
++
++
++static const char * gas_result_txt(enum gas_query_ap_result result)
++{
++ switch (result) {
++ case GAS_QUERY_AP_SUCCESS:
++ return "SUCCESS";
++ case GAS_QUERY_AP_FAILURE:
++ return "FAILURE";
++ case GAS_QUERY_AP_TIMEOUT:
++ return "TIMEOUT";
++ case GAS_QUERY_AP_PEER_ERROR:
++ return "PEER_ERROR";
++ case GAS_QUERY_AP_INTERNAL_ERROR:
++ return "INTERNAL_ERROR";
++ case GAS_QUERY_AP_DELETED_AT_DEINIT:
++ return "DELETED_AT_DEINIT";
++ }
++
++ return "N/A";
++}
++
++
++static void gas_query_free(struct gas_query_pending *query, int del_list)
++{
++ if (del_list)
++ dl_list_del(&query->list);
++
++ wpabuf_free(query->req);
++ wpabuf_free(query->adv_proto);
++ wpabuf_free(query->resp);
++ os_free(query);
++}
++
++
++static void gas_query_done(struct gas_query_ap *gas,
++ struct gas_query_pending *query,
++ enum gas_query_ap_result result)
++{
++ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
++ " dialog_token=%u freq=%d status_code=%u result=%s",
++ MAC2STR(query->addr), query->dialog_token, query->freq,
++ query->status_code, gas_result_txt(result));
++ if (gas->current == query)
++ gas->current = NULL;
++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
++ eloop_cancel_timeout(gas_query_timeout, gas, query);
++ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
++ dl_list_del(&query->list);
++ query->cb(query->ctx, query->addr, query->dialog_token, result,
++ query->adv_proto, query->resp, query->status_code);
++ gas_query_free(query, 0);
++}
++
++
++/**
++ * gas_query_ap_deinit - Deinitialize GAS query component
++ * @gas: GAS query data from gas_query_init()
++ */
++void gas_query_ap_deinit(struct gas_query_ap *gas)
++{
++ struct gas_query_pending *query, *next;
++
++ if (gas == NULL)
++ return;
++
++ dl_list_for_each_safe(query, next, &gas->pending,
++ struct gas_query_pending, list)
++ gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
++
++ os_free(gas);
++}
++
++
++static struct gas_query_pending *
++gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
++{
++ struct gas_query_pending *q;
++ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
++ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
++ q->dialog_token == dialog_token)
++ return q;
++ }
++ return NULL;
++}
++
++
++static int gas_query_append(struct gas_query_pending *query, const u8 *data,
++ size_t len)
++{
++ if (wpabuf_resize(&query->resp, len) < 0) {
++ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
++ return -1;
++ }
++ wpabuf_put_data(query->resp, data, len);
++ return 0;
++}
++
++
++void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
++ const u8 *data, size_t data_len, int ok)
++{
++ struct gas_query_pending *query;
++ int dur;
++
++ if (!gas || !gas->current) {
++ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
++ " ok=%d - no query in progress", MAC2STR(dst), ok);
++ return;
++ }
++
++ query = gas->current;
++
++ dur = ms_from_time(&query->last_oper);
++ wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
++ " ok=%d query=%p dialog_token=%u dur=%d ms",
++ MAC2STR(dst), ok, query, query->dialog_token, dur);
++ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
++ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
++ return;
++ }
++ os_get_reltime(&query->last_oper);
++
++ eloop_cancel_timeout(gas_query_timeout, gas, query);
++ if (!ok) {
++ wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
++ eloop_register_timeout(0, 250000, gas_query_timeout,
++ gas, query);
++ } else {
++ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
++ gas_query_timeout, gas, query);
++ }
++ if (query->wait_comeback && !query->retry) {
++ eloop_cancel_timeout(gas_query_rx_comeback_timeout,
++ gas, query);
++ eloop_register_timeout(
++ 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
++ gas_query_rx_comeback_timeout, gas, query);
++ }
++}
++
++
++static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
++{
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, addr);
++ return sta && (sta->flags & WLAN_STA_MFP);
++}
++
++
++static int gas_query_tx(struct gas_query_ap *gas,
++ struct gas_query_pending *query,
++ struct wpabuf *req, unsigned int wait_time)
++{
++ int res, prot = pmf_in_use(gas->hapd, query->addr);
++
++ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
++ "freq=%d prot=%d using src addr " MACSTR,
++ MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
++ query->freq, prot, MAC2STR(query->sa));
++ if (prot) {
++ u8 *categ = wpabuf_mhead_u8(req);
++ *categ = WLAN_ACTION_PROTECTED_DUAL;
++ }
++ os_get_reltime(&query->last_oper);
++ res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
++ query->addr, wpabuf_head(req),
++ wpabuf_len(req));
++ return res;
++}
++
++
++static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
++ struct gas_query_pending *query)
++{
++ struct wpabuf *req;
++ unsigned int wait_time;
++
++ req = gas_build_comeback_req(query->dialog_token);
++ if (req == NULL) {
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ return;
++ }
++
++ wait_time = (query->retry || !query->offchannel_tx_started) ?
++ GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
++
++ if (gas_query_tx(gas, query, req, wait_time) < 0) {
++ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
++ MACSTR, MAC2STR(query->addr));
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ }
++
++ wpabuf_free(req);
++}
++
++
++static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
++{
++ struct gas_query_ap *gas = eloop_data;
++ struct gas_query_pending *query = user_ctx;
++ int dialog_token;
++
++ wpa_printf(MSG_DEBUG,
++ "GAS: No response to comeback request received (retry=%u)",
++ query->retry);
++ if (gas->current != query || query->retry)
++ return;
++ dialog_token = gas_query_new_dialog_token(gas, query->addr);
++ if (dialog_token < 0)
++ return;
++ wpa_printf(MSG_DEBUG,
++ "GAS: Retry GAS query due to comeback response timeout");
++ query->retry = 1;
++ query->dialog_token = dialog_token;
++ *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
++ query->wait_comeback = 0;
++ query->next_frag_id = 0;
++ wpabuf_free(query->adv_proto);
++ query->adv_proto = NULL;
++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
++ eloop_cancel_timeout(gas_query_timeout, gas, query);
++ gas_query_tx_initial_req(gas, query);
++}
++
++
++static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
++{
++ struct gas_query_ap *gas = eloop_data;
++ struct gas_query_pending *query = user_ctx;
++
++ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
++ MAC2STR(query->addr));
++ gas_query_tx_comeback_req(gas, query);
++}
++
++
++static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
++ struct gas_query_pending *query,
++ u16 comeback_delay)
++{
++ unsigned int secs, usecs;
++
++ secs = (comeback_delay * 1024) / 1000000;
++ usecs = comeback_delay * 1024 - secs * 1000000;
++ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
++ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
++ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
++ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
++ gas, query);
++}
++
++
++static void gas_query_rx_initial(struct gas_query_ap *gas,
++ struct gas_query_pending *query,
++ const u8 *adv_proto, const u8 *resp,
++ size_t len, u16 comeback_delay)
++{
++ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
++ MACSTR " (dialog_token=%u comeback_delay=%u)",
++ MAC2STR(query->addr), query->dialog_token, comeback_delay);
++
++ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
++ if (query->adv_proto == NULL) {
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ return;
++ }
++
++ if (comeback_delay) {
++ eloop_cancel_timeout(gas_query_timeout, gas, query);
++ query->wait_comeback = 1;
++ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
++ return;
++ }
++
++ /* Query was completed without comeback mechanism */
++ if (gas_query_append(query, resp, len) < 0) {
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ return;
++ }
++
++ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
++}
++
++
++static void gas_query_rx_comeback(struct gas_query_ap *gas,
++ struct gas_query_pending *query,
++ const u8 *adv_proto, const u8 *resp,
++ size_t len, u8 frag_id, u8 more_frags,
++ u16 comeback_delay)
++{
++ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
++ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
++ "comeback_delay=%u)",
++ MAC2STR(query->addr), query->dialog_token, frag_id,
++ more_frags, comeback_delay);
++ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
++
++ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
++ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
++ wpabuf_len(query->adv_proto)) != 0) {
++ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
++ "between initial and comeback response from "
++ MACSTR, MAC2STR(query->addr));
++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
++ return;
++ }
++
++ if (comeback_delay) {
++ if (frag_id) {
++ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
++ "with non-zero frag_id and comeback_delay "
++ "from " MACSTR, MAC2STR(query->addr));
++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
++ return;
++ }
++ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
++ return;
++ }
++
++ if (frag_id != query->next_frag_id) {
++ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
++ "from " MACSTR, MAC2STR(query->addr));
++ if (frag_id + 1 == query->next_frag_id) {
++ wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
++ "retry of previous fragment");
++ return;
++ }
++ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
++ return;
++ }
++ query->next_frag_id++;
++
++ if (gas_query_append(query, resp, len) < 0) {
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ return;
++ }
++
++ if (more_frags) {
++ gas_query_tx_comeback_req(gas, query);
++ return;
++ }
++
++ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
++}
++
++
++/**
++ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
++ * frame
++ * @gas: GAS query data from gas_query_init()
++ * @sa: Source MAC address of the Action frame
++ * @categ: Category of the Action frame
++ * @data: Payload of the Action frame
++ * @len: Length of @data
++ * @freq: Frequency (in MHz) on which the frame was received
++ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
++ */
++int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
++ const u8 *data, size_t len, int freq)
++{
++ struct gas_query_pending *query;
++ u8 action, dialog_token, frag_id = 0, more_frags = 0;
++ u16 comeback_delay, resp_len;
++ const u8 *pos, *adv_proto;
++ int prot, pmf;
++ unsigned int left;
++
++ if (!gas || len < 4)
++ return -1;
++
++ pos = data;
++ action = *pos++;
++ dialog_token = *pos++;
++
++ if (action != WLAN_PA_GAS_INITIAL_RESP &&
++ action != WLAN_PA_GAS_COMEBACK_RESP)
++ return -1; /* Not a GAS response */
++
++ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
++ pmf = pmf_in_use(gas->hapd, sa);
++ if (prot && !pmf) {
++ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
++ return 0;
++ }
++ if (!prot && pmf) {
++ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
++ return 0;
++ }
++
++ query = gas_query_get_pending(gas, sa, dialog_token);
++ if (query == NULL) {
++ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
++ " dialog token %u", MAC2STR(sa), dialog_token);
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
++ ms_from_time(&query->last_oper), MAC2STR(sa));
++
++ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
++ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
++ MACSTR " dialog token %u when waiting for comeback "
++ "response", MAC2STR(sa), dialog_token);
++ return 0;
++ }
++
++ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
++ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
++ MACSTR " dialog token %u when waiting for initial "
++ "response", MAC2STR(sa), dialog_token);
++ return 0;
++ }
++
++ query->status_code = WPA_GET_LE16(pos);
++ pos += 2;
++
++ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
++ action == WLAN_PA_GAS_COMEBACK_RESP) {
++ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
++ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
++ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
++ "%u failed - status code %u",
++ MAC2STR(sa), dialog_token, query->status_code);
++ gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
++ return 0;
++ }
++
++ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
++ if (pos + 1 > data + len)
++ return 0;
++ frag_id = *pos & 0x7f;
++ more_frags = (*pos & 0x80) >> 7;
++ pos++;
++ }
++
++ /* Comeback Delay */
++ if (pos + 2 > data + len)
++ return 0;
++ comeback_delay = WPA_GET_LE16(pos);
++ pos += 2;
++
++ /* Advertisement Protocol element */
++ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
++ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
++ "Protocol element in the response from " MACSTR,
++ MAC2STR(sa));
++ return 0;
++ }
++
++ if (*pos != WLAN_EID_ADV_PROTO) {
++ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
++ "Protocol element ID %u in response from " MACSTR,
++ *pos, MAC2STR(sa));
++ return 0;
++ }
++
++ adv_proto = pos;
++ pos += 2 + pos[1];
++
++ /* Query Response Length */
++ if (pos + 2 > data + len) {
++ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
++ return 0;
++ }
++ resp_len = WPA_GET_LE16(pos);
++ pos += 2;
++
++ left = data + len - pos;
++ if (resp_len > left) {
++ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
++ "response from " MACSTR, MAC2STR(sa));
++ return 0;
++ }
++
++ if (resp_len < left) {
++ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
++ "after Query Response from " MACSTR,
++ left - resp_len, MAC2STR(sa));
++ }
++
++ if (action == WLAN_PA_GAS_COMEBACK_RESP)
++ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
++ frag_id, more_frags, comeback_delay);
++ else
++ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
++ comeback_delay);
++
++ return 0;
++}
++
++
++static void gas_query_timeout(void *eloop_data, void *user_ctx)
++{
++ struct gas_query_ap *gas = eloop_data;
++ struct gas_query_pending *query = user_ctx;
++
++ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
++ " dialog token %u",
++ MAC2STR(query->addr), query->dialog_token);
++ gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
++}
++
++
++static int gas_query_dialog_token_available(struct gas_query_ap *gas,
++ const u8 *dst, u8 dialog_token)
++{
++ struct gas_query_pending *q;
++ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
++ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
++ dialog_token == q->dialog_token)
++ return 0;
++ }
++
++ return 1;
++}
++
++
++static void gas_query_tx_initial_req(struct gas_query_ap *gas,
++ struct gas_query_pending *query)
++{
++ if (gas_query_tx(gas, query, query->req,
++ GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
++ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
++ MACSTR, MAC2STR(query->addr));
++ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
++ return;
++ }
++ gas->current = query;
++
++ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
++ query->dialog_token);
++ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
++ gas_query_timeout, gas, query);
++}
++
++
++static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
++{
++ static int next_start = 0;
++ int dialog_token;
++
++ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
++ if (gas_query_dialog_token_available(
++ gas, dst, (next_start + dialog_token) % 256))
++ break;
++ }
++ if (dialog_token == 256)
++ return -1; /* Too many pending queries */
++ dialog_token = (next_start + dialog_token) % 256;
++ next_start = (dialog_token + 1) % 256;
++ return dialog_token;
++}
++
++
++/**
++ * gas_query_ap_req - Request a GAS query
++ * @gas: GAS query data from gas_query_init()
++ * @dst: Destination MAC address for the query
++ * @freq: Frequency (in MHz) for the channel on which to send the query
++ * @req: GAS query payload (to be freed by gas_query module in case of success
++ * return)
++ * @cb: Callback function for reporting GAS query result and response
++ * @ctx: Context pointer to use with the @cb call
++ * Returns: dialog token (>= 0) on success or -1 on failure
++ */
++int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
++ struct wpabuf *req,
++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
++ enum gas_query_ap_result result,
++ const struct wpabuf *adv_proto,
++ const struct wpabuf *resp, u16 status_code),
++ void *ctx)
++{
++ struct gas_query_pending *query;
++ int dialog_token;
++
++ if (!gas || wpabuf_len(req) < 3)
++ return -1;
++
++ dialog_token = gas_query_new_dialog_token(gas, dst);
++ if (dialog_token < 0)
++ return -1;
++
++ query = os_zalloc(sizeof(*query));
++ if (query == NULL)
++ return -1;
++
++ query->gas = gas;
++ os_memcpy(query->addr, dst, ETH_ALEN);
++ query->dialog_token = dialog_token;
++ query->freq = freq;
++ query->cb = cb;
++ query->ctx = ctx;
++ query->req = req;
++ dl_list_add(&gas->pending, &query->list);
++
++ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
++
++ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
++ " dialog_token=%u freq=%d",
++ MAC2STR(query->addr), query->dialog_token, query->freq);
++
++ gas_query_tx_initial_req(gas, query);
++
++ return dialog_token;
++}
+--- contrib/wpa/src/ap/gas_query_ap.h.orig
++++ contrib/wpa/src/ap/gas_query_ap.h
+@@ -0,0 +1,43 @@
++/*
++ * Generic advertisement service (GAS) query
++ * Copyright (c) 2009, Atheros Communications
++ * Copyright (c) 2011-2017, Qualcomm Atheros
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef GAS_QUERY_AP_H
++#define GAS_QUERY_AP_H
++
++struct gas_query_ap;
++
++struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
++ void *msg_ctx);
++void gas_query_ap_deinit(struct gas_query_ap *gas);
++int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
++ const u8 *data, size_t len, int freq);
++
++/**
++ * enum gas_query_ap_result - GAS query result
++ */
++enum gas_query_ap_result {
++ GAS_QUERY_AP_SUCCESS,
++ GAS_QUERY_AP_FAILURE,
++ GAS_QUERY_AP_TIMEOUT,
++ GAS_QUERY_AP_PEER_ERROR,
++ GAS_QUERY_AP_INTERNAL_ERROR,
++ GAS_QUERY_AP_DELETED_AT_DEINIT
++};
++
++int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
++ struct wpabuf *req,
++ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
++ enum gas_query_ap_result result,
++ const struct wpabuf *adv_proto,
++ const struct wpabuf *resp, u16 status_code),
++ void *ctx);
++void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
++ const u8 *data, size_t data_len, int ok);
++
++#endif /* GAS_QUERY_AP_H */
+--- contrib/wpa/src/ap/gas_serv.c.orig
++++ contrib/wpa/src/ap/gas_serv.c
+@@ -11,14 +11,31 @@
+ #include "common.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/gas.h"
++#include "common/wpa_ctrl.h"
+ #include "utils/eloop.h"
+ #include "hostapd.h"
+ #include "ap_config.h"
+ #include "ap_drv_ops.h"
++#include "dpp_hostapd.h"
+ #include "sta_info.h"
+ #include "gas_serv.h"
+
+
++#ifdef CONFIG_DPP
++static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
++{
++ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
++ wpabuf_put_u8(buf, 8); /* Length */
++ wpabuf_put_u8(buf, 0x7f);
++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
++ wpabuf_put_u8(buf, 5);
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, DPP_OUI_TYPE);
++ wpabuf_put_u8(buf, 0x01);
++}
++#endif /* CONFIG_DPP */
++
++
+ static void convert_to_protected_dual(struct wpabuf *msg)
+ {
+ u8 *categ = wpabuf_mhead_u8(msg);
+@@ -50,9 +67,12 @@
+ sta->flags |= WLAN_STA_GAS;
+ /*
+ * The default inactivity is 300 seconds. We don't need
+- * it to be that long.
++ * it to be that long. Use five second timeout and increase this
++ * with the comeback_delay for testing cases.
+ */
+- ap_sta_session_timeout(hapd, sta, 5);
++ ap_sta_session_timeout(hapd, sta,
++ hapd->conf->gas_comeback_delay / 1024 +
++ 5);
+ } else {
+ ap_sta_replenish_timeout(hapd, sta, 5);
+ }
+@@ -101,6 +121,7 @@
+ if (sta->gas_dialog[i].dialog_token != dialog_token ||
+ !sta->gas_dialog[i].valid)
+ continue;
++ ap_sta_replenish_timeout(hapd, sta, 5);
+ return &sta->gas_dialog[i];
+ }
+ wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+@@ -160,34 +181,127 @@
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ if (hapd->conf->hs20_osu_providers_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
++ if (hapd->conf->hs20_osu_providers_nai_count)
++ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ if (hapd->conf->hs20_icons_count)
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
++ if (hapd->conf->hs20_operator_icon_count)
++ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+ gas_anqp_set_element_len(buf, len);
+ }
+ #endif /* CONFIG_HS20 */
+
+
++static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
++ u16 infoid)
++{
++ struct anqp_element *elem;
++
++ dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
++ list) {
++ if (elem->infoid == infoid)
++ return elem;
++ }
++
++ return NULL;
++}
++
++
++static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
++ u16 infoid)
++{
++ struct anqp_element *elem;
++
++ elem = get_anqp_elem(hapd, infoid);
++ if (!elem)
++ return;
++ if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
++ wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
++ infoid);
++ return;
++ }
++
++ wpabuf_put_le16(buf, infoid);
++ wpabuf_put_le16(buf, wpabuf_len(elem->payload));
++ wpabuf_put_buf(buf, elem->payload);
++}
++
++
++static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
++ u16 infoid)
++{
++ if (get_anqp_elem(hapd, infoid)) {
++ anqp_add_elem(hapd, buf, infoid);
++ return 1;
++ }
++
++ return 0;
++}
++
++
+ static void anqp_add_capab_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+ {
+ u8 *len;
++ u16 id;
+
++ if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
++ return;
++
+ len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+ wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+- if (hapd->conf->venue_name)
++ if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
+ wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+- if (hapd->conf->network_auth_type)
++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
++ wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
++ if (hapd->conf->network_auth_type ||
++ get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+- if (hapd->conf->roaming_consortium)
++ if (hapd->conf->roaming_consortium ||
++ get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
+ wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+- if (hapd->conf->ipaddr_type_configured)
++ if (hapd->conf->ipaddr_type_configured ||
++ get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+- if (hapd->conf->nai_realm_data)
++ if (hapd->conf->nai_realm_data ||
++ get_anqp_elem(hapd, ANQP_NAI_REALM))
+ wpabuf_put_le16(buf, ANQP_NAI_REALM);
+- if (hapd->conf->anqp_3gpp_cell_net)
++ if (hapd->conf->anqp_3gpp_cell_net ||
++ get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+- if (hapd->conf->domain_name)
++ if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
++ wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
++ if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
++ wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
++ if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
++ wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
++ if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
++ wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
++ if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
++ wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
++ if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
++ wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
++ if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
++ wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
++#ifdef CONFIG_FILS
++ if (!dl_list_empty(&hapd->conf->fils_realms) ||
++ get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
++ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
++#endif /* CONFIG_FILS */
++ if (get_anqp_elem(hapd, ANQP_CAG))
++ wpabuf_put_le16(buf, ANQP_CAG);
++ if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
++ wpabuf_put_le16(buf, ANQP_VENUE_URL);
++ if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
++ wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
++ if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
++ wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
++ for (id = 280; id < 300; id++) {
++ if (get_anqp_elem(hapd, id))
++ wpabuf_put_le16(buf, id);
++ }
+ #ifdef CONFIG_HS20
+ anqp_add_hs_capab_list(hapd, buf);
+ #endif /* CONFIG_HS20 */
+@@ -197,6 +311,9 @@
+
+ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+ {
++ if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
++ return;
++
+ if (hapd->conf->venue_name) {
+ u8 *len;
+ unsigned int i;
+@@ -215,9 +332,35 @@
+ }
+
+
++static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
++{
++ if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
++ return;
++
++ if (hapd->conf->venue_url) {
++ u8 *len;
++ unsigned int i;
++
++ len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
++ for (i = 0; i < hapd->conf->venue_url_count; i++) {
++ struct hostapd_venue_url *url;
++
++ url = &hapd->conf->venue_url[i];
++ wpabuf_put_u8(buf, 1 + url->url_len);
++ wpabuf_put_u8(buf, url->venue_number);
++ wpabuf_put_data(buf, url->url, url->url_len);
++ }
++ gas_anqp_set_element_len(buf, len);
++ }
++}
++
++
+ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+ {
++ if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
++ return;
++
+ if (hapd->conf->network_auth_type) {
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+ wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+@@ -233,6 +376,9 @@
+ unsigned int i;
+ u8 *len;
+
++ if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
++ return;
++
+ len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+ for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+ struct hostapd_roaming_consortium *rc;
+@@ -247,6 +393,9 @@
+ static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+ {
++ if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
++ return;
++
+ if (hapd->conf->ipaddr_type_configured) {
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ wpabuf_put_le16(buf, 1);
+@@ -309,7 +458,7 @@
+
+ pos = home_realm;
+ end = pos + home_realm_len;
+- if (pos + 1 > end) {
++ if (end - pos < 1) {
+ wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+@@ -317,7 +466,7 @@
+ num_realms = *pos++;
+
+ for (i = 0; i < num_realms && num_matching < 10; i++) {
+- if (pos + 2 > end) {
++ if (end - pos < 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+@@ -325,7 +474,7 @@
+ }
+ encoding = *pos++;
+ realm_len = *pos++;
+- if (pos + realm_len > end) {
++ if (realm_len > end - pos) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+@@ -391,6 +540,10 @@
+ const u8 *home_realm, size_t home_realm_len,
+ int nai_realm, int nai_home_realm)
+ {
++ if (nai_realm && !nai_home_realm &&
++ anqp_add_override(hapd, buf, ANQP_NAI_REALM))
++ return;
++
+ if (nai_realm && hapd->conf->nai_realm_data) {
+ u8 *len;
+ unsigned int i, j;
+@@ -424,6 +577,9 @@
+ static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+ {
++ if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
++ return;
++
+ if (hapd->conf->anqp_3gpp_cell_net) {
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+ wpabuf_put_le16(buf,
+@@ -436,6 +592,9 @@
+
+ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+ {
++ if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
++ return;
++
+ if (hapd->conf->domain_name) {
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+ wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+@@ -445,6 +604,36 @@
+ }
+
+
++#ifdef CONFIG_FILS
++static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
++ struct wpabuf *buf)
++{
++ size_t count;
++
++ if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
++ return;
++
++ count = dl_list_len(&hapd->conf->fils_realms);
++ if (count > 10000)
++ count = 10000;
++ if (count) {
++ struct fils_realm *realm;
++
++ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
++ wpabuf_put_le16(buf, 2 * count);
++
++ dl_list_for_each(realm, &hapd->conf->fils_realms,
++ struct fils_realm, list) {
++ if (count == 0)
++ break;
++ wpabuf_put_data(buf, realm->hash, 2);
++ count--;
++ }
++ }
++}
++#endif /* CONFIG_FILS */
++
++
+ #ifdef CONFIG_HS20
+
+ static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+@@ -518,6 +707,29 @@
+ }
+
+
++static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
++ const char *name)
++{
++ size_t j;
++ struct hs20_icon *icon = NULL;
++
++ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
++ if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
++ icon = &bss->hs20_icons[j];
++ }
++ if (!icon)
++ return; /* icon info not found */
++
++ wpabuf_put_le16(buf, icon->width);
++ wpabuf_put_le16(buf, icon->height);
++ wpabuf_put_data(buf, icon->language, 3);
++ wpabuf_put_u8(buf, os_strlen(icon->type));
++ wpabuf_put_str(buf, icon->type);
++ wpabuf_put_u8(buf, os_strlen(icon->name));
++ wpabuf_put_str(buf, icon->name);
++}
++
++
+ static void anqp_add_osu_provider(struct wpabuf *buf,
+ struct hostapd_bss_config *bss,
+ struct hs20_osu_provider *p)
+@@ -546,32 +758,14 @@
+
+ /* OSU Method List */
+ count = wpabuf_put(buf, 1);
+- for (i = 0; p->method_list[i] >= 0; i++)
++ for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
+ wpabuf_put_u8(buf, p->method_list[i]);
+ *count = i;
+
+ /* Icons Available */
+ len2 = wpabuf_put(buf, 2);
+- for (i = 0; i < p->icons_count; i++) {
+- size_t j;
+- struct hs20_icon *icon = NULL;
+-
+- for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+- if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+- 0)
+- icon = &bss->hs20_icons[j];
+- }
+- if (!icon)
+- continue; /* icon info not found */
+-
+- wpabuf_put_le16(buf, icon->width);
+- wpabuf_put_le16(buf, icon->height);
+- wpabuf_put_data(buf, icon->language, 3);
+- wpabuf_put_u8(buf, os_strlen(icon->type));
+- wpabuf_put_str(buf, icon->type);
+- wpabuf_put_u8(buf, os_strlen(icon->name));
+- wpabuf_put_str(buf, icon->name);
+- }
++ for (i = 0; i < p->icons_count; i++)
++ anqp_add_icon(buf, bss, p->icons[i]);
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU_NAI */
+@@ -625,6 +819,40 @@
+ }
+
+
++static void anqp_add_osu_provider_nai(struct wpabuf *buf,
++ struct hs20_osu_provider *p)
++{
++ /* OSU_NAI for shared BSS (Single SSID) */
++ if (p->osu_nai2) {
++ wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
++ wpabuf_put_str(buf, p->osu_nai2);
++ } else {
++ wpabuf_put_u8(buf, 0);
++ }
++}
++
++
++static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
++ struct wpabuf *buf)
++{
++ if (hapd->conf->hs20_osu_providers_nai_count) {
++ size_t i;
++ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
++ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
++ wpabuf_put_u8(buf, 0); /* Reserved */
++
++ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
++ anqp_add_osu_provider_nai(
++ buf, &hapd->conf->hs20_osu_providers[i]);
++ }
++
++ gas_anqp_set_element_len(buf, len);
++ }
++}
++
++
+ static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *name, size_t name_len)
+@@ -680,17 +908,78 @@
+ gas_anqp_set_element_len(buf, len);
+ }
+
++
++static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
++ struct wpabuf *buf)
++{
++ struct hostapd_bss_config *bss = hapd->conf;
++ size_t i;
++ u8 *len;
++
++ if (!bss->hs20_operator_icon_count)
++ return;
++
++ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
++
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
++ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
++ wpabuf_put_u8(buf, 0); /* Reserved */
++
++ for (i = 0; i < bss->hs20_operator_icon_count; i++)
++ anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
++
++ gas_anqp_set_element_len(buf, len);
++}
++
+ #endif /* CONFIG_HS20 */
+
+
++#ifdef CONFIG_MBO
++static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
++ struct wpabuf *buf)
++{
++ if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
++ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
++ wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
++ wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
++ gas_anqp_set_element_len(buf, len);
++ }
++}
++#endif /* CONFIG_MBO */
++
++
++static size_t anqp_get_required_len(struct hostapd_data *hapd,
++ const u16 *infoid,
++ unsigned int num_infoid)
++{
++ size_t len = 0;
++ unsigned int i;
++
++ for (i = 0; i < num_infoid; i++) {
++ struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
++
++ if (elem)
++ len += 2 + 2 + wpabuf_len(elem->payload);
++ }
++
++ return len;
++}
++
++
+ static struct wpabuf *
+ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ unsigned int request,
+ const u8 *home_realm, size_t home_realm_len,
+- const u8 *icon_name, size_t icon_name_len)
++ const u8 *icon_name, size_t icon_name_len,
++ const u16 *extra_req,
++ unsigned int num_extra_req)
+ {
+ struct wpabuf *buf;
+ size_t len;
++ unsigned int i;
+
+ len = 1400;
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+@@ -697,6 +986,11 @@
+ len += 1000;
+ if (request & ANQP_REQ_ICON_REQUEST)
+ len += 65536;
++#ifdef CONFIG_FILS
++ if (request & ANQP_FILS_REALM_INFO)
++ len += 2 * dl_list_len(&hapd->conf->fils_realms);
++#endif /* CONFIG_FILS */
++ len += anqp_get_required_len(hapd, extra_req, num_extra_req);
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+@@ -706,6 +1000,8 @@
+ anqp_add_capab_list(hapd, buf);
+ if (request & ANQP_REQ_VENUE_NAME)
+ anqp_add_venue_name(hapd, buf);
++ if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
+ if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+ anqp_add_network_auth_type(hapd, buf);
+ if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+@@ -718,9 +1014,35 @@
+ request & ANQP_REQ_NAI_HOME_REALM);
+ if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+ anqp_add_3gpp_cellular_network(hapd, buf);
++ if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
++ anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
++ if (request & ANQP_REQ_AP_CIVIC_LOCATION)
++ anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
++ if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
++ anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
+ if (request & ANQP_REQ_DOMAIN_NAME)
+ anqp_add_domain_name(hapd, buf);
++ if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
++ if (request & ANQP_REQ_TDLS_CAPABILITY)
++ anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
++ if (request & ANQP_REQ_EMERGENCY_NAI)
++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
++ for (i = 0; i < num_extra_req; i++) {
++#ifdef CONFIG_FILS
++ if (extra_req[i] == ANQP_FILS_REALM_INFO) {
++ anqp_add_fils_realm_info(hapd, buf);
++ continue;
++ }
++#endif /* CONFIG_FILS */
++ if (extra_req[i] == ANQP_VENUE_URL) {
++ anqp_add_venue_url(hapd, buf);
++ continue;
++ }
++ anqp_add_elem(hapd, buf, extra_req[i]);
++ }
++
+ #ifdef CONFIG_HS20
+ if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+ anqp_add_hs_capab_list(hapd, buf);
+@@ -736,12 +1058,23 @@
+ anqp_add_osu_providers_list(hapd, buf);
+ if (request & ANQP_REQ_ICON_REQUEST)
+ anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
++ if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
++ anqp_add_operator_icon_metadata(hapd, buf);
++ if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
++ anqp_add_osu_providers_nai_list(hapd, buf);
+ #endif /* CONFIG_HS20 */
+
++#ifdef CONFIG_MBO
++ if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
++ anqp_add_mbo_cell_data_conn_pref(hapd, buf);
++#endif /* CONFIG_MBO */
++
+ return buf;
+ }
+
+
++#define ANQP_MAX_EXTRA_REQ 20
++
+ struct anqp_query_info {
+ unsigned int request;
+ const u8 *home_realm_query;
+@@ -749,6 +1082,8 @@
+ const u8 *icon_name;
+ size_t icon_name_len;
+ int p2p_sd;
++ u16 extra_req[ANQP_MAX_EXTRA_REQ];
++ unsigned int num_extra_req;
+ };
+
+
+@@ -776,6 +1111,11 @@
+ set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+ hapd->conf->venue_name != NULL, qi);
+ break;
++ case ANQP_EMERGENCY_CALL_NUMBER:
++ set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
++ "Emergency Call Number",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+ hapd->conf->network_auth_type != NULL, qi);
+@@ -798,13 +1138,65 @@
+ "3GPP Cellular Network",
+ hapd->conf->anqp_3gpp_cell_net != NULL, qi);
+ break;
++ case ANQP_AP_GEOSPATIAL_LOCATION:
++ set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
++ "AP Geospatial Location",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
++ case ANQP_AP_CIVIC_LOCATION:
++ set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
++ "AP Civic Location",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
++ case ANQP_AP_LOCATION_PUBLIC_URI:
++ set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
++ "AP Location Public URI",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
+ case ANQP_DOMAIN_NAME:
+ set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+ hapd->conf->domain_name != NULL, qi);
+ break;
++ case ANQP_EMERGENCY_ALERT_URI:
++ set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
++ "Emergency Alert URI",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
++ case ANQP_TDLS_CAPABILITY:
++ set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
++ "TDLS Capability",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
++ case ANQP_EMERGENCY_NAI:
++ set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
++ "Emergency NAI",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
+ default:
+- wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+- info_id);
++#ifdef CONFIG_FILS
++ if (info_id == ANQP_FILS_REALM_INFO &&
++ !dl_list_empty(&hapd->conf->fils_realms)) {
++ wpa_printf(MSG_DEBUG,
++ "ANQP: FILS Realm Information (local)");
++ } else
++#endif /* CONFIG_FILS */
++ if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
++ wpa_printf(MSG_DEBUG,
++ "ANQP: Venue URL (local)");
++ } else if (!get_anqp_elem(hapd, info_id)) {
++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
++ info_id);
++ break;
++ }
++ if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
++ wpa_printf(MSG_DEBUG,
++ "ANQP: No more room for extra requests - ignore Info Id %u",
++ info_id);
++ break;
++ }
++ wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
++ qi->extra_req[qi->num_extra_req] = info_id;
++ qi->num_extra_req++;
+ break;
+ }
+ }
+@@ -817,7 +1209,7 @@
+ wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+ (unsigned int) (end - pos) / 2);
+
+- while (pos + 2 <= end) {
++ while (end - pos >= 2) {
+ rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+ pos += 2;
+ }
+@@ -857,6 +1249,16 @@
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+ hapd->conf->hs20_osu_providers_count, qi);
+ break;
++ case HS20_STYPE_OPERATOR_ICON_METADATA:
++ set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
++ "Operator Icon Metadata",
++ hapd->conf->hs20_operator_icon_count, qi);
++ break;
++ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
++ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
++ "OSU Providers NAI List",
++ hapd->conf->hs20_osu_providers_nai_count, qi);
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+ subtype);
+@@ -899,52 +1301,15 @@
+ }
+
+
+-static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+- const u8 *pos, const u8 *end,
+- struct anqp_query_info *qi)
++static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
++ const u8 *pos, const u8 *end,
++ struct anqp_query_info *qi)
+ {
+- u32 oui;
+ u8 subtype;
+
+- if (pos + 4 > end) {
+- wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+- "Query element");
++ if (end - pos <= 1)
+ return;
+- }
+
+- oui = WPA_GET_BE24(pos);
+- pos += 3;
+- if (oui != OUI_WFA) {
+- wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+- oui);
+- return;
+- }
+-
+-#ifdef CONFIG_P2P
+- if (*pos == P2P_OUI_TYPE) {
+- /*
+- * This is for P2P SD and will be taken care of by the P2P
+- * implementation. This query needs to be ignored in the generic
+- * GAS server to avoid duplicated response.
+- */
+- wpa_printf(MSG_DEBUG,
+- "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+- *pos);
+- qi->p2p_sd = 1;
+- return;
+- }
+-#endif /* CONFIG_P2P */
+-
+- if (*pos != HS20_ANQP_OUI_TYPE) {
+- wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+- *pos);
+- return;
+- }
+- pos++;
+-
+- if (pos + 1 >= end)
+- return;
+-
+ subtype = *pos++;
+ pos++; /* Reserved */
+ switch (subtype) {
+@@ -971,9 +1336,119 @@
+ #endif /* CONFIG_HS20 */
+
+
++#ifdef CONFIG_P2P
++static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
++ struct anqp_query_info *qi)
++{
++ /*
++ * This is for P2P SD and will be taken care of by the P2P
++ * implementation. This query needs to be ignored in the generic
++ * GAS server to avoid duplicated response.
++ */
++ wpa_printf(MSG_DEBUG,
++ "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
++ P2P_OUI_TYPE);
++ qi->p2p_sd = 1;
++ return;
++}
++#endif /* CONFIG_P2P */
++
++
++#ifdef CONFIG_MBO
++
++static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
++ struct anqp_query_info *qi)
++{
++ switch (subtype) {
++ case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
++ set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
++ "Cellular Data Connection Preference",
++ hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
++ break;
++ default:
++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
++ subtype);
++ break;
++ }
++}
++
++
++static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
++ const u8 *pos, const u8 *end,
++ struct anqp_query_info *qi)
++{
++ u8 subtype;
++
++ if (end - pos < 1)
++ return;
++
++ subtype = *pos++;
++ switch (subtype) {
++ case MBO_ANQP_SUBTYPE_QUERY_LIST:
++ wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
++ while (pos < end) {
++ rx_anqp_mbo_query_list(hapd, *pos, qi);
++ pos++;
++ }
++ break;
++ default:
++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
++ subtype);
++ break;
++ }
++}
++
++#endif /* CONFIG_MBO */
++
++
++static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
++ const u8 *pos, const u8 *end,
++ struct anqp_query_info *qi)
++{
++ u32 oui;
++
++ if (end - pos < 4) {
++ wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
++ "Query element");
++ return;
++ }
++
++ oui = WPA_GET_BE24(pos);
++ pos += 3;
++ if (oui != OUI_WFA) {
++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
++ oui);
++ return;
++ }
++
++ switch (*pos) {
++#ifdef CONFIG_P2P
++ case P2P_OUI_TYPE:
++ rx_anqp_vendor_specific_p2p(hapd, qi);
++ break;
++#endif /* CONFIG_P2P */
++#ifdef CONFIG_HS20
++ case HS20_ANQP_OUI_TYPE:
++ rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
++ break;
++#endif /* CONFIG_HS20 */
++#ifdef CONFIG_MBO
++ case MBO_ANQP_OUI_TYPE:
++ rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
++ break;
++#endif /* CONFIG_MBO */
++ default:
++ wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
++ *pos);
++ break;
++ }
++}
++
++
+ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+- struct anqp_query_info *qi, int prot)
++ struct anqp_query_info *qi, int prot,
++ int std_addr3)
+ {
+ struct wpabuf *buf, *tx_buf;
+
+@@ -980,7 +1455,8 @@
+ buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
+ qi->home_realm_query,
+ qi->home_realm_query_len,
+- qi->icon_name, qi->icon_name_len);
++ qi->icon_name, qi->icon_name_len,
++ qi->extra_req, qi->num_extra_req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+ buf);
+ if (!buf)
+@@ -994,7 +1470,7 @@
+ }
+ #endif /* CONFIG_P2P */
+
+- if (wpabuf_len(buf) > hapd->gas_frag_limit ||
++ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+ hapd->conf->gas_comeback_delay) {
+ struct gas_dialog_info *di;
+ u16 comeback_delay = 1;
+@@ -1033,15 +1509,88 @@
+ return;
+ if (prot)
+ convert_to_protected_dual(tx_buf);
++ if (std_addr3)
++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
++ wpabuf_head(tx_buf),
++ wpabuf_len(tx_buf));
++ else
++ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
++ wpabuf_head(tx_buf),
++ wpabuf_len(tx_buf));
++ wpabuf_free(tx_buf);
++}
++
++
++#ifdef CONFIG_DPP
++static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
++ const u8 *sa, u8 dialog_token,
++ int prot, struct wpabuf *buf)
++{
++ struct wpabuf *tx_buf;
++
++ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
++ hapd->conf->gas_comeback_delay) {
++ struct gas_dialog_info *di;
++ u16 comeback_delay = 1;
++
++ if (hapd->conf->gas_comeback_delay) {
++ /* Testing - allow overriding of the delay value */
++ comeback_delay = hapd->conf->gas_comeback_delay;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: Too long response to fit in initial response - use GAS comeback");
++ di = gas_dialog_create(hapd, sa, dialog_token);
++ if (!di) {
++ wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
++ MACSTR " (dialog token %u)",
++ MAC2STR(sa), dialog_token);
++ wpabuf_free(buf);
++ tx_buf = gas_build_initial_resp(
++ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
++ 0, 10);
++ if (tx_buf)
++ gas_serv_write_dpp_adv_proto(tx_buf);
++ } else {
++ di->prot = prot;
++ di->sd_resp = buf;
++ di->sd_resp_pos = 0;
++ tx_buf = gas_build_initial_resp(
++ dialog_token, WLAN_STATUS_SUCCESS,
++ comeback_delay, 10);
++ if (tx_buf)
++ gas_serv_write_dpp_adv_proto(tx_buf);
++ }
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: GAS Initial response (no comeback)");
++ tx_buf = gas_build_initial_resp(
++ dialog_token, WLAN_STATUS_SUCCESS, 0,
++ 10 + 2 + wpabuf_len(buf));
++ if (tx_buf) {
++ gas_serv_write_dpp_adv_proto(tx_buf);
++ wpabuf_put_le16(tx_buf, wpabuf_len(buf));
++ wpabuf_put_buf(tx_buf, buf);
++ hostapd_dpp_gas_status_handler(hapd, 1);
++ }
++ wpabuf_free(buf);
++ }
++ if (!tx_buf)
++ return;
++ if (prot)
++ convert_to_protected_dual(tx_buf);
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+- wpabuf_head(tx_buf), wpabuf_len(tx_buf));
++ wpabuf_head(tx_buf),
++ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+ }
++#endif /* CONFIG_DPP */
+
+
+ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+ const u8 *sa,
+- const u8 *data, size_t len, int prot)
++ const u8 *data, size_t len, int prot,
++ int std_addr3)
+ {
+ const u8 *pos = data;
+ const u8 *end = data + len;
+@@ -1050,6 +1599,9 @@
+ u16 slen;
+ struct anqp_query_info qi;
+ const u8 *adv_proto;
++#ifdef CONFIG_DPP
++ int dpp = 0;
++#endif /* CONFIG_DPP */
+
+ if (len < 1 + 2)
+ return;
+@@ -1069,14 +1621,23 @@
+ adv_proto = pos++;
+
+ slen = *pos++;
+- next = pos + slen;
+- if (next > end || slen < 2) {
++ if (slen > end - pos || slen < 2) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Invalid IE in GAS Initial Request");
+ return;
+ }
++ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
++#ifdef CONFIG_DPP
++ if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
++ pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
++ pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
++ wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
++ dpp = 1;
++ } else
++#endif /* CONFIG_DPP */
++
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ struct wpabuf *buf;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+@@ -1093,8 +1654,15 @@
+ wpabuf_put_le16(buf, 0); /* Query Response Length */
+ if (prot)
+ convert_to_protected_dual(buf);
+- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+- wpabuf_head(buf), wpabuf_len(buf));
++ if (std_addr3)
++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
++ wpabuf_head(buf),
++ wpabuf_len(buf));
++ else
++ hostapd_drv_send_action_addr3_ap(hapd,
++ hapd->iface->freq, 0,
++ sa, wpabuf_head(buf),
++ wpabuf_len(buf));
+ wpabuf_free(buf);
+ return;
+ }
+@@ -1101,19 +1669,31 @@
+
+ pos = next;
+ /* Query Request */
+- if (pos + 2 > end)
++ if (end - pos < 2)
+ return;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+- if (pos + slen > end)
++ if (slen > end - pos)
+ return;
+ end = pos + slen;
+
++#ifdef CONFIG_DPP
++ if (dpp) {
++ struct wpabuf *msg;
++
++ msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
++ if (!msg)
++ return;
++ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
++ return;
++ }
++#endif /* CONFIG_DPP */
++
+ /* ANQP Query Request */
+ while (pos < end) {
+ u16 info_id, elen;
+
+- if (pos + 4 > end)
++ if (end - pos < 4)
+ return;
+
+ info_id = WPA_GET_LE16(pos);
+@@ -1121,7 +1701,7 @@
+ elen = WPA_GET_LE16(pos);
+ pos += 2;
+
+- if (pos + elen > end) {
++ if (elen > end - pos) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+ return;
+ }
+@@ -1130,11 +1710,9 @@
+ case ANQP_QUERY_LIST:
+ rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+ break;
+-#ifdef CONFIG_HS20
+ case ANQP_VENDOR_SPECIFIC:
+ rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+ break;
+-#endif /* CONFIG_HS20 */
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+ "Request element %u", info_id);
+@@ -1144,13 +1722,15 @@
+ pos += elen;
+ }
+
+- gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
++ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
++ std_addr3);
+ }
+
+
+ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+ const u8 *sa,
+- const u8 *data, size_t len, int prot)
++ const u8 *data, size_t len, int prot,
++ int std_addr3)
+ {
+ struct gas_dialog_info *dialog;
+ struct wpabuf *buf, *tx_buf;
+@@ -1182,8 +1762,8 @@
+ }
+
+ frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+- if (frag_len > hapd->gas_frag_limit) {
+- frag_len = hapd->gas_frag_limit;
++ if (frag_len > hapd->conf->gas_frag_limit) {
++ frag_len = hapd->conf->gas_frag_limit;
+ more = 1;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+@@ -1196,6 +1776,18 @@
+ gas_serv_dialog_clear(dialog);
+ return;
+ }
++#ifdef CONFIG_DPP
++ if (dialog->dpp) {
++ tx_buf = gas_build_comeback_resp(dialog_token,
++ WLAN_STATUS_SUCCESS,
++ dialog->sd_frag_id, more, 0,
++ 10 + frag_len);
++ if (tx_buf) {
++ gas_serv_write_dpp_adv_proto(tx_buf);
++ wpabuf_put_buf(tx_buf, buf);
++ }
++ } else
++#endif /* CONFIG_DPP */
+ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ dialog->sd_frag_id,
+@@ -1219,6 +1811,10 @@
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+ "SD response sent");
++#ifdef CONFIG_DPP
++ if (dialog->dpp)
++ hostapd_dpp_gas_status_handler(hapd, 1);
++#endif /* CONFIG_DPP */
+ gas_serv_dialog_clear(dialog);
+ gas_serv_free_dialogs(hapd, sa);
+ }
+@@ -1226,8 +1822,14 @@
+ send_resp:
+ if (prot)
+ convert_to_protected_dual(tx_buf);
+- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+- wpabuf_head(tx_buf), wpabuf_len(tx_buf));
++ if (std_addr3)
++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
++ wpabuf_head(tx_buf),
++ wpabuf_len(tx_buf));
++ else
++ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
++ wpabuf_head(tx_buf),
++ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+ }
+
+@@ -1238,7 +1840,7 @@
+ struct hostapd_data *hapd = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *sa, *data;
+- int prot;
++ int prot, std_addr3;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ if (len < IEEE80211_HDRLEN + 2)
+@@ -1253,14 +1855,22 @@
+ */
+ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
+ sa = mgmt->sa;
++ if (hapd->conf->gas_address3 == 1)
++ std_addr3 = 1;
++ else if (hapd->conf->gas_address3 == 2)
++ std_addr3 = 0;
++ else
++ std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
+ len -= IEEE80211_HDRLEN + 1;
+ data = buf + IEEE80211_HDRLEN + 1;
+ switch (data[0]) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+- gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
++ gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
++ std_addr3);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+- gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
++ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
++ std_addr3);
+ break;
+ }
+ }
+@@ -1270,9 +1880,6 @@
+ {
+ hapd->public_action_cb2 = gas_serv_rx_public_action;
+ hapd->public_action_cb2_ctx = hapd;
+- hapd->gas_frag_limit = 1400;
+- if (hapd->conf->gas_frag_limit > 0)
+- hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
+ return 0;
+ }
+
+--- contrib/wpa/src/ap/gas_serv.h.orig
++++ contrib/wpa/src/ap/gas_serv.h
+@@ -9,10 +9,13 @@
+ #ifndef GAS_SERV_H
+ #define GAS_SERV_H
+
++/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
+ #define ANQP_REQ_CAPABILITY_LIST \
+ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+ #define ANQP_REQ_VENUE_NAME \
+ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
++#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
++ (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
+ #define ANQP_REQ_NETWORK_AUTH_TYPE \
+ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+ #define ANQP_REQ_ROAMING_CONSORTIUM \
+@@ -23,8 +26,24 @@
+ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+ #define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
++#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
++ (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
++#define ANQP_REQ_AP_CIVIC_LOCATION \
++ (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
++#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
++ (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
+ #define ANQP_REQ_DOMAIN_NAME \
+ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
++#define ANQP_REQ_EMERGENCY_ALERT_URI \
++ (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
++#define ANQP_REQ_TDLS_CAPABILITY \
++ (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
++#define ANQP_REQ_EMERGENCY_NAI \
++ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
++/*
++ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
++ * optimized bitmap.
++ */
+ #define ANQP_REQ_HS_CAPABILITY_LIST \
+ (0x10000 << HS20_STYPE_CAPABILITY_LIST)
+ #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+@@ -41,6 +60,13 @@
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+ #define ANQP_REQ_ICON_REQUEST \
+ (0x10000 << HS20_STYPE_ICON_REQUEST)
++#define ANQP_REQ_OPERATOR_ICON_METADATA \
++ (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
++#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
++ (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
++/* The first MBO ANQP-element can be included in the optimized bitmap. */
++#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
++ (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+ struct gas_dialog_info {
+ u8 valid;
+@@ -49,6 +75,7 @@
+ size_t sd_resp_pos; /* Offset in sd_resp */
+ u8 sd_frag_id;
+ int prot; /* whether Protected Dual of Public Action frame is used */
++ int dpp; /* whether this is a DPP Config Response */
+ };
+
+ struct hostapd_data;
+--- contrib/wpa/src/ap/hostapd.c.orig
++++ contrib/wpa/src/ap/hostapd.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / Initialization and configuration
+- * Copyright (c) 2002-2014, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -12,6 +12,7 @@
+ #include "utils/eloop.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/wpa_ctrl.h"
++#include "common/hw_features_common.h"
+ #include "radius/radius_client.h"
+ #include "radius/radius_das.h"
+ #include "eap_server/tncs.h"
+@@ -30,6 +31,8 @@
+ #include "vlan_init.h"
+ #include "wpa_auth.h"
+ #include "wps_hostapd.h"
++#include "dpp_hostapd.h"
++#include "gas_query_ap.h"
+ #include "hw_features.h"
+ #include "wpa_auth_glue.h"
+ #include "ap_drv_ops.h"
+@@ -42,6 +45,11 @@
+ #include "x_snoop.h"
+ #include "dhcp_snoop.h"
+ #include "ndisc_snoop.h"
++#include "neighbor_db.h"
++#include "rrm.h"
++#include "fils_hlp.h"
++#include "acs.h"
++#include "hs20.h"
+
+
+ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+@@ -49,6 +57,8 @@
+ static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
+ static int setup_interface2(struct hostapd_iface *iface);
+ static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
++static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
++ void *timeout_ctx);
+
+
+ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+@@ -68,10 +78,26 @@
+ }
+
+
++void hostapd_reconfig_encryption(struct hostapd_data *hapd)
++{
++ if (hapd->wpa_auth)
++ return;
++
++ hostapd_set_privacy(hapd, 0);
++ hostapd_setup_encryption(hapd->conf->iface, hapd);
++}
++
++
+ static void hostapd_reload_bss(struct hostapd_data *hapd)
+ {
+ struct hostapd_ssid *ssid;
+
++ if (!hapd->started)
++ return;
++
++ if (hapd->conf->wmm_enabled < 0)
++ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n;
++
+ #ifndef CONFIG_NO_RADIUS
+ radius_client_reconfig(hapd->radius, hapd->conf->radius);
+ #endif /* CONFIG_NO_RADIUS */
+@@ -150,8 +176,27 @@
+ }
+
+
++static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
++ struct hostapd_config *oldconf)
++{
++ size_t i;
++
++ if (newconf->num_bss != oldconf->num_bss)
++ return 1;
++
++ for (i = 0; i < newconf->num_bss; i++) {
++ if (os_strcmp(newconf->bss[i]->iface,
++ oldconf->bss[i]->iface) != 0)
++ return 1;
++ }
++
++ return 0;
++}
++
++
+ int hostapd_reload_config(struct hostapd_iface *iface)
+ {
++ struct hapd_interfaces *interfaces = iface->interfaces;
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+@@ -174,6 +219,35 @@
+ hostapd_clear_old(iface);
+
+ oldconf = hapd->iconf;
++ if (hostapd_iface_conf_changed(newconf, oldconf)) {
++ char *fname;
++ int res;
++
++ wpa_printf(MSG_DEBUG,
++ "Configuration changes include interface/BSS modification - force full disable+enable sequence");
++ fname = os_strdup(iface->config_fname);
++ if (!fname) {
++ hostapd_config_free(newconf);
++ return -1;
++ }
++ hostapd_remove_iface(interfaces, hapd->conf->iface);
++ iface = hostapd_init(interfaces, fname);
++ os_free(fname);
++ hostapd_config_free(newconf);
++ if (!iface) {
++ wpa_printf(MSG_ERROR,
++ "Failed to initialize interface on config reload");
++ return -1;
++ }
++ iface->interfaces = interfaces;
++ interfaces->iface[interfaces->count] = iface;
++ interfaces->count++;
++ res = hostapd_enable_iface(iface);
++ if (res < 0)
++ wpa_printf(MSG_ERROR,
++ "Failed to enable interface on config reload");
++ return res;
++ }
+ iface->conf = newconf;
+
+ for (j = 0; j < iface->num_bss; j++) {
+@@ -203,10 +277,12 @@
+
+
+ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+- char *ifname)
++ const char *ifname)
+ {
+ int i;
+
++ if (!ifname || !hapd->drv_priv)
++ return;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+ 0, NULL, 0, NULL, 0)) {
+@@ -272,10 +348,11 @@
+
+ if (!hapd->started) {
+ wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+- __func__, hapd->conf->iface);
++ __func__, hapd->conf ? hapd->conf->iface : "N/A");
+ return;
+ }
+ hapd->started = 0;
++ hapd->beacon_set_done = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
+ iapp_deinit(hapd->iapp);
+@@ -292,6 +369,10 @@
+ #endif /* CONFIG_NO_RADIUS */
+
+ hostapd_deinit_wps(hapd);
++#ifdef CONFIG_DPP
++ hostapd_dpp_deinit(hapd);
++ gas_query_ap_deinit(hapd->gas);
++#endif /* CONFIG_DPP */
+
+ authsrv_deinit(hapd);
+
+@@ -334,6 +415,23 @@
+ wpabuf_free(hapd->mesh_pending_auth);
+ hapd->mesh_pending_auth = NULL;
+ #endif /* CONFIG_MESH */
++
++ hostapd_clean_rrm(hapd);
++ fils_hlp_deinit(hapd);
++
++#ifdef CONFIG_SAE
++ {
++ struct hostapd_sae_commit_queue *q;
++
++ while ((q = dl_list_first(&hapd->sae_commit_queue,
++ struct hostapd_sae_commit_queue,
++ list))) {
++ dl_list_del(&q->list);
++ os_free(q);
++ }
++ }
++ eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
++#endif /* CONFIG_SAE */
+ }
+
+
+@@ -348,10 +446,12 @@
+ static void hostapd_cleanup(struct hostapd_data *hapd)
+ {
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+- hapd->conf->iface);
++ hapd->conf ? hapd->conf->iface : "N/A");
+ if (hapd->iface->interfaces &&
+- hapd->iface->interfaces->ctrl_iface_deinit)
++ hapd->iface->interfaces->ctrl_iface_deinit) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
+ hapd->iface->interfaces->ctrl_iface_deinit(hapd);
++ }
+ hostapd_free_hapd_data(hapd);
+ }
+
+@@ -367,7 +467,7 @@
+ list))) {
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+- os_free(info);
++ sta_track_del(info);
+ }
+ }
+
+@@ -380,8 +480,11 @@
+ hostapd_stop_setup_timers(iface);
+ #endif /* NEED_AP_MLME */
+ #endif /* CONFIG_IEEE80211N */
++ if (iface->current_mode)
++ acs_cleanup(iface);
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = NULL;
++ iface->current_mode = NULL;
+ os_free(iface->current_rates);
+ iface->current_rates = NULL;
+ os_free(iface->basic_rates);
+@@ -402,6 +505,8 @@
+ {
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
++ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
++ NULL);
+
+ hostapd_cleanup_iface_partial(iface);
+ hostapd_config_free(iface->conf);
+@@ -416,7 +521,7 @@
+
+ static void hostapd_clear_wep(struct hostapd_data *hapd)
+ {
+- if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
++ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
+ hostapd_set_privacy(hapd, 0);
+ hostapd_broadcast_wep_clear(hapd);
+ }
+@@ -477,9 +582,12 @@
+ ret = -1;
+ }
+ }
+- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
+- os_memset(addr, 0xff, ETH_ALEN);
+- hostapd_drv_sta_deauth(hapd, addr, reason);
++ if (hapd->conf && hapd->conf->broadcast_deauth) {
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
++ "Deauthenticate all stations");
++ os_memset(addr, 0xff, ETH_ALEN);
++ hostapd_drv_sta_deauth(hapd, addr, reason);
++ }
+ hostapd_free_stas(hapd);
+
+ return ret;
+@@ -511,6 +619,9 @@
+ if (hostapd_drv_none(hapd))
+ return 0;
+
++ if (iface->conf->use_driver_iface_addr)
++ return 0;
++
+ /* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+ /* Determine the bits necessary to cover the number of BSSIDs. */
+@@ -520,7 +631,7 @@
+ /* Determine the bits necessary to any configured BSSIDs,
+ if they are higher than the number of BSSIDs. */
+ for (j = 0; j < iface->conf->num_bss; j++) {
+- if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
++ if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) {
+ if (j)
+ auto_addr++;
+ continue;
+@@ -562,8 +673,10 @@
+ for (i = 5; i > 5 - j; i--)
+ mask[i] = 0;
+ j = bits % 8;
+- while (j--)
++ while (j) {
++ j--;
+ mask[i] <<= 1;
++ }
+
+ skip_mask_ext:
+ wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+@@ -672,7 +785,7 @@
+
+ if (attr->acct_session_id) {
+ num_attr++;
+- if (attr->acct_session_id_len != 17) {
++ if (attr->acct_session_id_len != 16) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Session-Id cannot match");
+ return NULL;
+@@ -682,10 +795,9 @@
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
+- os_snprintf(buf, sizeof(buf), "%08X-%08X",
+- sta->acct_session_id_hi,
+- sta->acct_session_id_lo);
+- if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long) sta->acct_session_id);
++ if (os_memcmp(attr->acct_session_id, buf, 16) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+@@ -701,7 +813,7 @@
+
+ if (attr->acct_multi_session_id) {
+ num_attr++;
+- if (attr->acct_multi_session_id_len != 17) {
++ if (attr->acct_multi_session_id_len != 16) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+ return NULL;
+@@ -712,14 +824,14 @@
+ if (!sta->radius_das_match)
+ continue;
+ if (!sta->eapol_sm ||
+- !sta->eapol_sm->acct_multi_session_id_hi) {
++ !sta->eapol_sm->acct_multi_session_id) {
+ sta->radius_das_match = 0;
+ continue;
+ }
+- os_snprintf(buf, sizeof(buf), "%08X+%08X",
+- sta->eapol_sm->acct_multi_session_id_hi,
+- sta->eapol_sm->acct_multi_session_id_lo);
+- if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long)
++ sta->eapol_sm->acct_multi_session_id);
++ if (os_memcmp(attr->acct_multi_session_id, buf, 16) !=
+ 0)
+ sta->radius_das_match = 0;
+ else
+@@ -864,6 +976,48 @@
+ return RADIUS_DAS_SUCCESS;
+ }
+
++
++#ifdef CONFIG_HS20
++static enum radius_das_res
++hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ int multi;
++
++ if (hostapd_das_nas_mismatch(hapd, attr))
++ return RADIUS_DAS_NAS_MISMATCH;
++
++ sta = hostapd_das_find_sta(hapd, attr, &multi);
++ if (!sta) {
++ if (multi) {
++ wpa_printf(MSG_DEBUG,
++ "RADIUS DAS: Multiple sessions match - not supported");
++ return RADIUS_DAS_MULTI_SESSION_MATCH;
++ }
++ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
++ return RADIUS_DAS_SESSION_NOT_FOUND;
++ }
++
++ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
++ " - CoA", MAC2STR(sta->addr));
++
++ if (attr->hs20_t_c_filtering) {
++ if (attr->hs20_t_c_filtering[0] & BIT(0)) {
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
++ return RADIUS_DAS_COA_FAILED;
++ }
++
++ hs20_t_c_filtering(hapd, sta, 0);
++ }
++
++ return RADIUS_DAS_SUCCESS;
++}
++#else /* CONFIG_HS20 */
++#define hostapd_das_coa NULL
++#endif /* CONFIG_HS20 */
++
+ #endif /* CONFIG_NO_RADIUS */
+
+
+@@ -905,12 +1059,9 @@
+ hapd->started = 1;
+
+ if (!first || first == -1) {
+- if (hostapd_mac_comp_empty(conf->bssid) == 0) {
+- /* Allocate the next available BSSID. */
+- do {
+- inc_byte_array(hapd->own_addr, ETH_ALEN);
+- } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+- } else {
++ u8 *addr = hapd->own_addr;
++
++ if (!is_zero_ether_addr(conf->bssid)) {
+ /* Allocate the configured BSSID. */
+ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
+
+@@ -922,11 +1073,18 @@
+ "the radio", conf->iface);
+ return -1;
+ }
++ } else if (hapd->iconf->use_driver_iface_addr) {
++ addr = NULL;
++ } else {
++ /* Allocate the next available BSSID. */
++ do {
++ inc_byte_array(hapd->own_addr, ETH_ALEN);
++ } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+ }
+
+ hapd->interface_added = 1;
+ if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+- conf->iface, hapd->own_addr, hapd,
++ conf->iface, addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ conf->bridge[0] ? conf->bridge : NULL,
+ first == -1)) {
+@@ -935,13 +1093,21 @@
+ hapd->interface_added = 0;
+ return -1;
+ }
++
++ if (!addr)
++ os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
+ }
+
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n;
+
++#ifdef CONFIG_IEEE80211R_AP
++ if (is_zero_ether_addr(conf->r1_key_holder))
++ os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
++#endif /* CONFIG_IEEE80211R_AP */
++
+ #ifdef CONFIG_MESH
+- if (hapd->iface->mconf == NULL)
++ if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
+ flush_old_stations = 0;
+ #endif /* CONFIG_MESH */
+
+@@ -1022,8 +1188,11 @@
+ das_conf.time_window = conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ conf->radius_das_require_event_timestamp;
++ das_conf.require_message_authenticator =
++ conf->radius_das_require_message_authenticator;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
++ das_conf.coa = hostapd_das_coa;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (hapd->radius_das == NULL) {
+ wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
+@@ -1040,6 +1209,14 @@
+ if (hostapd_init_wps(hapd, conf))
+ return -1;
+
++#ifdef CONFIG_DPP
++ hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
++ if (!hapd->gas)
++ return -1;
++ if (hostapd_dpp_init(hapd))
++ return -1;
++#endif /* CONFIG_DPP */
++
+ if (authsrv_init(hapd) < 0)
+ return -1;
+
+@@ -1127,7 +1304,7 @@
+ struct hostapd_tx_queue_params *p;
+
+ #ifdef CONFIG_MESH
+- if (iface->mconf == NULL)
++ if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
+ return;
+ #endif /* CONFIG_MESH */
+
+@@ -1508,17 +1685,119 @@
+
+ #endif /* CONFIG_FST */
+
++#ifdef CONFIG_OWE
+
+-/**
+- * hostapd_setup_interface_complete - Complete interface setup
+- *
+- * This function is called when previous steps in the interface setup has been
+- * completed. This can also start operations, e.g., DFS, that will require
+- * additional processing before interface is ready to be enabled. Such
+- * operations will call this function from eloop callbacks when finished.
+- */
+-int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
++static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
+ {
++ struct hostapd_data *hapd = ctx;
++ size_t i;
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *bss = iface->bss[i];
++
++ if (os_strcmp(hapd->conf->owe_transition_ifname,
++ bss->conf->iface) != 0)
++ continue;
++
++ wpa_printf(MSG_DEBUG,
++ "OWE: ifname=%s found transition mode ifname=%s BSSID "
++ MACSTR " SSID %s",
++ hapd->conf->iface, bss->conf->iface,
++ MAC2STR(bss->own_addr),
++ wpa_ssid_txt(bss->conf->ssid.ssid,
++ bss->conf->ssid.ssid_len));
++ if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
++ is_zero_ether_addr(bss->own_addr))
++ continue;
++
++ os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
++ ETH_ALEN);
++ os_memcpy(hapd->conf->owe_transition_ssid,
++ bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
++ hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
++ wpa_printf(MSG_DEBUG,
++ "OWE: Copied transition mode information");
++ return 1;
++ }
++
++ return 0;
++}
++
++
++int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
++{
++ if (hapd->conf->owe_transition_ssid_len > 0 &&
++ !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
++ return 0;
++
++ /* Find transition mode SSID/BSSID information from a BSS operated by
++ * this hostapd instance. */
++ if (!hapd->iface->interfaces ||
++ !hapd->iface->interfaces->for_each_interface)
++ return hostapd_owe_iface_iter(hapd->iface, hapd);
++ else
++ return hapd->iface->interfaces->for_each_interface(
++ hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
++}
++
++
++static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
++{
++ size_t i;
++
++ for (i = 0; i < iface->num_bss; i++) {
++ struct hostapd_data *bss = iface->bss[i];
++ int res;
++
++ if (!bss->conf->owe_transition_ifname[0])
++ continue;
++ res = hostapd_owe_trans_get_info(bss);
++ if (res == 0)
++ continue;
++ wpa_printf(MSG_DEBUG,
++ "OWE: Matching transition mode interface enabled - update beacon data for %s",
++ bss->conf->iface);
++ ieee802_11_set_beacon(bss);
++ }
++
++ return 0;
++}
++
++#endif /* CONFIG_OWE */
++
++
++static void hostapd_owe_update_trans(struct hostapd_iface *iface)
++{
++#ifdef CONFIG_OWE
++ /* Check whether the enabled BSS can complete OWE transition mode
++ * configuration for any pending interface. */
++ if (!iface->interfaces ||
++ !iface->interfaces->for_each_interface)
++ hostapd_owe_iface_iter2(iface, NULL);
++ else
++ iface->interfaces->for_each_interface(
++ iface->interfaces, hostapd_owe_iface_iter2, NULL);
++#endif /* CONFIG_OWE */
++}
++
++
++static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
++ void *timeout_ctx)
++{
++ struct hostapd_iface *iface = eloop_ctx;
++ struct hostapd_data *hapd;
++
++ if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
++ return;
++ hapd = iface->bss[0];
++ if (hapd->setup_complete_cb)
++ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
++}
++
++
++static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
++ int err)
++{
+ struct hostapd_data *hapd = iface->bss[0];
+ size_t j;
+ u8 *prev_addr;
+@@ -1605,15 +1884,17 @@
+ }
+ }
+
+- if (hapd->iconf->rts_threshold > -1 &&
+- hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
++ if (hapd->iconf->rts_threshold >= -1 &&
++ hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
++ hapd->iconf->rts_threshold >= -1) {
+ wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
+ "kernel driver");
+ goto fail;
+ }
+
+- if (hapd->iconf->fragm_threshold > -1 &&
+- hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
++ if (hapd->iconf->fragm_threshold >= -1 &&
++ hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
++ hapd->iconf->fragm_threshold != -1) {
+ wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
+ "for kernel driver");
+ goto fail;
+@@ -1626,14 +1907,17 @@
+ if (j)
+ os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+ if (hostapd_setup_bss(hapd, j == 0)) {
+- do {
++ for (;;) {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+- } while (j-- > 0);
++ if (j == 0)
++ break;
++ j--;
++ }
+ goto fail;
+ }
+- if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
++ if (is_zero_ether_addr(hapd->conf->bssid))
+ prev_addr = hapd->own_addr;
+ }
+ hapd = iface->bss[0];
+@@ -1641,7 +1925,6 @@
+ hostapd_tx_queue_params(iface);
+
+ ap_list_init(iface);
+- dl_list_init(&iface->sta_seen);
+
+ hostapd_set_acl(hapd);
+
+@@ -1692,6 +1975,7 @@
+ #endif /* CONFIG_FST */
+
+ hostapd_set_state(iface, HAPD_IFACE_ENABLED);
++ hostapd_owe_update_trans(iface);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+@@ -1701,6 +1985,9 @@
+ if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+ iface->interfaces->terminate_on_error--;
+
++ for (j = 0; j < iface->num_bss; j++)
++ hostapd_neighbor_set_own_report(iface->bss[j]);
++
+ return 0;
+
+ fail:
+@@ -1713,13 +2000,107 @@
+ iface->fst = NULL;
+ }
+ #endif /* CONFIG_FST */
+- if (iface->interfaces && iface->interfaces->terminate_on_error)
++
++ if (iface->interfaces && iface->interfaces->terminate_on_error) {
+ eloop_terminate();
++ } else if (hapd->setup_complete_cb) {
++ /*
++ * Calling hapd->setup_complete_cb directly may cause iface
++ * deinitialization which may be accessed later by the caller.
++ */
++ eloop_register_timeout(0, 0,
++ hostapd_interface_setup_failure_handler,
++ iface, NULL);
++ }
++
+ return -1;
+ }
+
+
+ /**
++ * hostapd_setup_interface_complete - Complete interface setup
++ *
++ * This function is called when previous steps in the interface setup has been
++ * completed. This can also start operations, e.g., DFS, that will require
++ * additional processing before interface is ready to be enabled. Such
++ * operations will call this function from eloop callbacks when finished.
++ */
++int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
++{
++ struct hapd_interfaces *interfaces = iface->interfaces;
++ struct hostapd_data *hapd = iface->bss[0];
++ unsigned int i;
++ int not_ready_in_sync_ifaces = 0;
++
++ if (!iface->need_to_start_in_sync)
++ return hostapd_setup_interface_complete_sync(iface, err);
++
++ if (err) {
++ wpa_printf(MSG_ERROR, "Interface initialization failed");
++ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
++ iface->need_to_start_in_sync = 0;
++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
++ if (interfaces && interfaces->terminate_on_error)
++ eloop_terminate();
++ return -1;
++ }
++
++ if (iface->ready_to_start_in_sync) {
++ /* Already in ready and waiting. should never happpen */
++ return 0;
++ }
++
++ for (i = 0; i < interfaces->count; i++) {
++ if (interfaces->iface[i]->need_to_start_in_sync &&
++ !interfaces->iface[i]->ready_to_start_in_sync)
++ not_ready_in_sync_ifaces++;
++ }
++
++ /*
++ * Check if this is the last interface, if yes then start all the other
++ * waiting interfaces. If not, add this interface to the waiting list.
++ */
++ if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
++ /*
++ * If this interface went through CAC, do not synchronize, just
++ * start immediately.
++ */
++ iface->need_to_start_in_sync = 0;
++ wpa_printf(MSG_INFO,
++ "%s: Finished CAC - bypass sync and start interface",
++ iface->bss[0]->conf->iface);
++ return hostapd_setup_interface_complete_sync(iface, err);
++ }
++
++ if (not_ready_in_sync_ifaces > 1) {
++ /* need to wait as there are other interfaces still coming up */
++ iface->ready_to_start_in_sync = 1;
++ wpa_printf(MSG_INFO,
++ "%s: Interface waiting to sync with other interfaces",
++ iface->bss[0]->conf->iface);
++ return 0;
++ }
++
++ wpa_printf(MSG_INFO,
++ "%s: Last interface to sync - starting all interfaces",
++ iface->bss[0]->conf->iface);
++ iface->need_to_start_in_sync = 0;
++ hostapd_setup_interface_complete_sync(iface, err);
++ for (i = 0; i < interfaces->count; i++) {
++ if (interfaces->iface[i]->need_to_start_in_sync &&
++ interfaces->iface[i]->ready_to_start_in_sync) {
++ hostapd_setup_interface_complete_sync(
++ interfaces->iface[i], 0);
++ /* Only once the interfaces are sync started */
++ interfaces->iface[i]->need_to_start_in_sync = 0;
++ }
++ }
++
++ return 0;
++}
++
++
++/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+@@ -1776,8 +2157,19 @@
+ hapd->iconf = conf;
+ hapd->conf = bss;
+ hapd->iface = hapd_iface;
+- hapd->driver = hapd->iconf->driver;
++ if (conf)
++ hapd->driver = conf->driver;
+ hapd->ctrl_sock = -1;
++ dl_list_init(&hapd->ctrl_dst);
++ dl_list_init(&hapd->nr_db);
++ hapd->dhcp_sock = -1;
++#ifdef CONFIG_IEEE80211R_AP
++ dl_list_init(&hapd->l2_queue);
++ dl_list_init(&hapd->l2_oui_queue);
++#endif /* CONFIG_IEEE80211R_AP */
++#ifdef CONFIG_SAE
++ dl_list_init(&hapd->sae_commit_queue);
++#endif /* CONFIG_SAE */
+
+ return hapd;
+ }
+@@ -1785,8 +2177,10 @@
+
+ static void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
++ if (!hapd)
++ return;
+ wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+- hapd->conf->iface);
++ hapd->conf ? hapd->conf->iface : "N/A");
+ hostapd_bss_deinit_no_free(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ hostapd_cleanup(hapd);
+@@ -1803,12 +2197,6 @@
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+-#ifdef CONFIG_IEEE80211N
+-#ifdef NEED_AP_MLME
+- hostapd_stop_setup_timers(iface);
+- eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+-#endif /* NEED_AP_MLME */
+-#endif /* CONFIG_IEEE80211N */
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ iface->wait_channel_update = 0;
+
+@@ -1819,8 +2207,18 @@
+ }
+ #endif /* CONFIG_FST */
+
+- for (j = iface->num_bss - 1; j >= 0; j--)
++ for (j = (int) iface->num_bss - 1; j >= 0; j--) {
++ if (!iface->bss)
++ break;
+ hostapd_bss_deinit(iface->bss[j]);
++ }
++
++#ifdef CONFIG_IEEE80211N
++#ifdef NEED_AP_MLME
++ hostapd_stop_setup_timers(iface);
++ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
++#endif /* NEED_AP_MLME */
++#endif /* CONFIG_IEEE80211N */
+ }
+
+
+@@ -1829,6 +2227,8 @@
+ size_t j;
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ for (j = 0; j < iface->num_bss; j++) {
++ if (!iface->bss)
++ break;
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+ __func__, iface->bss[j]);
+ os_free(iface->bss[j]);
+@@ -1837,6 +2237,20 @@
+ }
+
+
++struct hostapd_iface * hostapd_alloc_iface(void)
++{
++ struct hostapd_iface *hapd_iface;
++
++ hapd_iface = os_zalloc(sizeof(*hapd_iface));
++ if (!hapd_iface)
++ return NULL;
++
++ dl_list_init(&hapd_iface->sta_seen);
++
++ return hapd_iface;
++}
++
++
+ /**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+@@ -1854,7 +2268,7 @@
+ struct hostapd_data *hapd;
+ size_t i;
+
+- hapd_iface = os_zalloc(sizeof(*hapd_iface));
++ hapd_iface = hostapd_alloc_iface();
+ if (hapd_iface == NULL)
+ goto fail;
+
+@@ -2158,6 +2572,11 @@
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
++#ifdef NEED_AP_MLME
++ for (j = 0; j < hapd_iface->num_bss; j++)
++ hostapd_cleanup_cs_params(hapd_iface->bss[j]);
++#endif /* NEED_AP_MLME */
++
+ /* same as hostapd_interface_deinit without deinitializing ctrl-iface */
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ struct hostapd_data *hapd = hapd_iface->bss[j];
+@@ -2190,7 +2609,7 @@
+ return NULL;
+ interfaces->iface = iface;
+ hapd_iface = interfaces->iface[interfaces->count] =
+- os_zalloc(sizeof(*hapd_iface));
++ hostapd_alloc_iface();
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "the interface", __func__);
+@@ -2215,7 +2634,7 @@
+ if (conf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "configuration", __func__);
+- return NULL;
++ return NULL;
+ }
+
+ if (driver) {
+@@ -2368,6 +2787,7 @@
+ return -1;
+ }
+ }
++ hostapd_owe_update_trans(hapd_iface);
+ return 0;
+ }
+
+@@ -2557,6 +2977,7 @@
+ }
+
+ hostapd_prune_associations(hapd, sta->addr);
++ ap_sta_clear_disconnect_timeouts(hapd, sta);
+
+ /* IEEE 802.11F (IAPP) */
+ if (hapd->conf->ieee802_11f)
+@@ -2584,15 +3005,28 @@
+ ieee802_1x_new_station(hapd, sta);
+ if (reassoc) {
+ if (sta->auth_alg != WLAN_AUTH_FT &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK &&
++ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
++ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+ } else
+ wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+- if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+- "for " MACSTR " (%d seconds - ap_max_inactivity)",
+- __func__, MAC2STR(sta->addr),
++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
++ if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
++ wpa_printf(MSG_DEBUG,
++ "%s: %s: canceled wired ap_handle_timer timeout for "
++ MACSTR,
++ hapd->conf->iface, __func__,
++ MAC2STR(sta->addr));
++ }
++ } else if (!(hapd->iface->drv_flags &
++ WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
++ wpa_printf(MSG_DEBUG,
++ "%s: %s: reschedule ap_handle_timer timeout for "
++ MACSTR " (%d seconds - ap_max_inactivity)",
++ hapd->conf->iface, __func__, MAC2STR(sta->addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+@@ -2627,12 +3061,23 @@
+ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+ {
+ wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+- iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
+- hostapd_state_text(s));
++ iface->conf ? iface->conf->bss[0]->iface : "N/A",
++ hostapd_state_text(iface->state), hostapd_state_text(s));
+ iface->state = s;
+ }
+
+
++int hostapd_csa_in_progress(struct hostapd_iface *iface)
++{
++ unsigned int i;
++
++ for (i = 0; i < iface->num_bss; i++)
++ if (iface->bss[i]->csa_in_progress)
++ return 1;
++ return 0;
++}
++
++
+ #ifdef NEED_AP_MLME
+
+ static void free_beacon_data(struct beacon_data *beacon)
+@@ -2671,60 +3116,52 @@
+ goto free_ap_params;
+
+ ret = -1;
+- beacon->head = os_malloc(params.head_len);
++ beacon->head = os_memdup(params.head, params.head_len);
+ if (!beacon->head)
+ goto free_ap_extra_ies;
+
+- os_memcpy(beacon->head, params.head, params.head_len);
+ beacon->head_len = params.head_len;
+
+- beacon->tail = os_malloc(params.tail_len);
++ beacon->tail = os_memdup(params.tail, params.tail_len);
+ if (!beacon->tail)
+ goto free_beacon;
+
+- os_memcpy(beacon->tail, params.tail, params.tail_len);
+ beacon->tail_len = params.tail_len;
+
+ if (params.proberesp != NULL) {
+- beacon->probe_resp = os_malloc(params.proberesp_len);
++ beacon->probe_resp = os_memdup(params.proberesp,
++ params.proberesp_len);
+ if (!beacon->probe_resp)
+ goto free_beacon;
+
+- os_memcpy(beacon->probe_resp, params.proberesp,
+- params.proberesp_len);
+ beacon->probe_resp_len = params.proberesp_len;
+ }
+
+ /* copy the extra ies */
+ if (beacon_extra) {
+- beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
++ beacon->beacon_ies = os_memdup(beacon_extra->buf,
++ wpabuf_len(beacon_extra));
+ if (!beacon->beacon_ies)
+ goto free_beacon;
+
+- os_memcpy(beacon->beacon_ies,
+- beacon_extra->buf, wpabuf_len(beacon_extra));
+ beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+ }
+
+ if (proberesp_extra) {
+- beacon->proberesp_ies =
+- os_malloc(wpabuf_len(proberesp_extra));
++ beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
++ wpabuf_len(proberesp_extra));
+ if (!beacon->proberesp_ies)
+ goto free_beacon;
+
+- os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
+- wpabuf_len(proberesp_extra));
+ beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+ }
+
+ if (assocresp_extra) {
+- beacon->assocresp_ies =
+- os_malloc(wpabuf_len(assocresp_extra));
++ beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
++ wpabuf_len(assocresp_extra));
+ if (!beacon->assocresp_ies)
+ goto free_beacon;
+
+- os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
+- wpabuf_len(assocresp_extra));
+ beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+ }
+
+@@ -2744,9 +3181,9 @@
+
+
+ /*
+- * TODO: This flow currently supports only changing frequency within the
+- * same hw_mode. Any other changes to MAC parameters or provided settings (even
+- * width) are not supported.
++ * TODO: This flow currently supports only changing channel and width within
++ * the same hw_mode. Any other changes to MAC parameters or provided settings
++ * are not supported.
+ */
+ static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ struct hostapd_config *conf,
+@@ -2765,15 +3202,44 @@
+ return -1;
+
+ /* if a pointer to old_params is provided we save previous state */
+- if (old_params) {
+- old_params->channel = conf->channel;
+- old_params->ht_enabled = conf->ieee80211n;
+- old_params->sec_channel_offset = conf->secondary_channel;
++ if (old_params &&
++ hostapd_set_freq_params(old_params, conf->hw_mode,
++ hostapd_hw_get_freq(hapd, conf->channel),
++ conf->channel, conf->ieee80211n,
++ conf->ieee80211ac,
++ conf->secondary_channel,
++ conf->vht_oper_chwidth,
++ conf->vht_oper_centr_freq_seg0_idx,
++ conf->vht_oper_centr_freq_seg1_idx,
++ conf->vht_capab))
++ return -1;
++
++ switch (params->bandwidth) {
++ case 0:
++ case 20:
++ case 40:
++ conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
++ break;
++ case 80:
++ if (params->center_freq2)
++ conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
++ else
++ conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
++ break;
++ case 160:
++ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
++ break;
++ default:
++ return -1;
+ }
+
+ conf->channel = channel;
+ conf->ieee80211n = params->ht_enabled;
+ conf->secondary_channel = params->sec_channel_offset;
++ ieee80211_freq_to_chan(params->center_freq1,
++ &conf->vht_oper_centr_freq_seg0_idx);
++ ieee80211_freq_to_chan(params->center_freq2,
++ &conf->vht_oper_centr_freq_seg1_idx);
+
+ /* TODO: maybe call here hostapd_config_check here? */
+
+@@ -2787,11 +3253,43 @@
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_freq_params old_freq;
+ int ret;
++ u8 chan, vht_bandwidth;
+
+ os_memset(&old_freq, 0, sizeof(old_freq));
+ if (!iface || !iface->freq || hapd->csa_in_progress)
+ return -1;
+
++ switch (settings->freq_params.bandwidth) {
++ case 80:
++ if (settings->freq_params.center_freq2)
++ vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
++ else
++ vht_bandwidth = VHT_CHANWIDTH_80MHZ;
++ break;
++ case 160:
++ vht_bandwidth = VHT_CHANWIDTH_160MHZ;
++ break;
++ default:
++ vht_bandwidth = VHT_CHANWIDTH_USE_HT;
++ break;
++ }
++
++ if (ieee80211_freq_to_channel_ext(
++ settings->freq_params.freq,
++ settings->freq_params.sec_channel_offset,
++ vht_bandwidth,
++ &hapd->iface->cs_oper_class,
++ &chan) == NUM_HOSTAPD_MODES) {
++ wpa_printf(MSG_DEBUG,
++ "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
++ settings->freq_params.freq,
++ settings->freq_params.sec_channel_offset,
++ settings->freq_params.vht_enabled);
++ return -1;
++ }
++
++ settings->freq_params.channel = chan;
++
+ ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &settings->freq_params,
+ &old_freq);
+@@ -2818,8 +3316,10 @@
+ return ret;
+ }
+
+- settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+- settings->counter_offset_presp = hapd->cs_c_off_proberesp;
++ settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
++ settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
++ settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
++ settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+
+ return 0;
+ }
+@@ -2833,9 +3333,24 @@
+ hapd->cs_c_off_beacon = 0;
+ hapd->cs_c_off_proberesp = 0;
+ hapd->csa_in_progress = 0;
++ hapd->cs_c_off_ecsa_beacon = 0;
++ hapd->cs_c_off_ecsa_proberesp = 0;
+ }
+
+
++void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled)
++{
++ if (vht_enabled)
++ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
++ else
++ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
++
++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x",
++ hapd->iconf->ch_switch_vht_config);
++}
++
++
+ int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+ {
+@@ -2870,7 +3385,6 @@
+ const struct hostapd_freq_params *freq_params)
+ {
+ int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+- unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+@@ -2912,15 +3426,15 @@
+ /*
+ * cs_params must not be cleared earlier because the freq_params
+ * argument may actually point to one of these.
++ * These params will be cleared during interface disable below.
+ */
+- for (i = 0; i < iface->num_bss; i++)
+- hostapd_cleanup_cs_params(iface->bss[i]);
+-
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ }
+
++#endif /* NEED_AP_MLME */
+
++
+ struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname)
+ {
+@@ -2940,9 +3454,7 @@
+ return NULL;
+ }
+
+-#endif /* NEED_AP_MLME */
+
+-
+ void hostapd_periodic_iface(struct hostapd_iface *iface)
+ {
+ size_t i;
+--- contrib/wpa/src/ap/hostapd.h.orig
++++ contrib/wpa/src/ap/hostapd.h
+@@ -14,6 +14,13 @@
+ #include "ap_config.h"
+ #include "drivers/driver.h"
+
++#define OCE_STA_CFON_ENABLED(hapd) \
++ ((hapd->conf->oce & OCE_STA_CFON) && \
++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
++#define OCE_AP_ENABLED(hapd) \
++ ((hapd->conf->oce & OCE_AP) && \
++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
++
+ struct wpa_ctrl_dst;
+ struct radius_server_data;
+ struct upnp_wps_device_sm;
+@@ -41,7 +48,7 @@
+
+ size_t count;
+ int global_ctrl_sock;
+- struct wpa_ctrl_dst *global_ctrl_dst;
++ struct dl_list global_ctrl_dst;
+ char *global_iface_path;
+ char *global_iface_name;
+ #ifndef CONFIG_NATIVE_WINDOWS
+@@ -53,6 +60,14 @@
+ #ifndef CONFIG_NO_VLAN
+ struct dynamic_iface *vlan_priv;
+ #endif /* CONFIG_NO_VLAN */
++#ifdef CONFIG_ETH_P_OUI
++ struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
++#endif /* CONFIG_ETH_P_OUI */
++ int eloop_initialized;
++
++#ifdef CONFIG_DPP
++ struct dpp_global *dpp;
++#endif /* CONFIG_DPP */
+ };
+
+ enum hostapd_chan_status {
+@@ -75,6 +90,7 @@
+ };
+
+ struct hostapd_frame_info {
++ unsigned int freq;
+ u32 channel;
+ u32 datarate;
+ int ssi_signal; /* dBm */
+@@ -99,7 +115,25 @@
+ u8 peer_addr[ETH_ALEN];
+ };
+
++struct hostapd_neighbor_entry {
++ struct dl_list list;
++ u8 bssid[ETH_ALEN];
++ struct wpa_ssid_value ssid;
++ struct wpabuf *nr;
++ struct wpabuf *lci;
++ struct wpabuf *civic;
++ /* LCI update time */
++ struct os_time lci_date;
++ int stationary;
++};
+
++struct hostapd_sae_commit_queue {
++ struct dl_list list;
++ int rssi;
++ size_t len;
++ u8 msg[];
++};
++
+ /**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+@@ -138,7 +172,7 @@
+ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+
+ struct radius_client_data *radius;
+- u32 acct_session_id_hi, acct_session_id_lo;
++ u64 acct_session_id;
+ struct radius_das_data *radius_das;
+
+ struct iapp_data *iapp;
+@@ -155,7 +189,7 @@
+ int tkip_countermeasures;
+
+ int ctrl_sock;
+- struct wpa_ctrl_dst *ctrl_dst;
++ struct dl_list ctrl_dst;
+
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+@@ -173,6 +207,17 @@
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ struct l2_packet_data *l2;
++
++#ifdef CONFIG_IEEE80211R_AP
++ struct dl_list l2_queue;
++ struct dl_list l2_oui_queue;
++ struct eth_p_oui_ctx *oui_pull;
++ struct eth_p_oui_ctx *oui_resp;
++ struct eth_p_oui_ctx *oui_push;
++ struct eth_p_oui_ctx *oui_sreq;
++ struct eth_p_oui_ctx *oui_sresp;
++#endif /* CONFIG_IEEE80211R_AP */
++
+ struct wps_context *wps;
+
+ int beacon_set_done;
+@@ -228,10 +273,9 @@
+ unsigned int cs_c_off_beacon;
+ unsigned int cs_c_off_proberesp;
+ int csa_in_progress;
++ unsigned int cs_c_off_ecsa_beacon;
++ unsigned int cs_c_off_ecsa_proberesp;
+
+- /* BSS Load */
+- unsigned int bss_load_update_timeout;
+-
+ #ifdef CONFIG_P2P
+ struct p2p_data *p2p;
+ struct p2p_group *p2p_group;
+@@ -246,9 +290,6 @@
+ int noa_start;
+ int noa_duration;
+ #endif /* CONFIG_P2P */
+-#ifdef CONFIG_INTERWORKING
+- size_t gas_frag_limit;
+-#endif /* CONFIG_INTERWORKING */
+ #ifdef CONFIG_PROXYARP
+ struct l2_packet_data *sock_dhcp;
+ struct l2_packet_data *sock_ndisc;
+@@ -256,9 +297,11 @@
+ #ifdef CONFIG_MESH
+ int num_plinks;
+ int max_plinks;
+- void (*mesh_sta_free_cb)(struct sta_info *sta);
++ void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
++ struct sta_info *sta);
+ struct wpabuf *mesh_pending_auth;
+ struct os_reltime mesh_pending_auth_time;
++ u8 mesh_required_peer[ETH_ALEN];
+ #endif /* CONFIG_MESH */
+
+ #ifdef CONFIG_SQLITE
+@@ -269,7 +312,10 @@
+ /** Key used for generating SAE anti-clogging tokens */
+ u8 sae_token_key[8];
+ struct os_reltime last_sae_token_key_update;
++ u16 sae_token_idx;
++ u16 sae_pending_token_idx[256];
+ int dot11RSNASAERetransPeriod; /* msec */
++ struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
+ #endif /* CONFIG_SAE */
+
+ #ifdef CONFIG_TESTING_OPTIONS
+@@ -277,7 +323,62 @@
+ unsigned int ext_eapol_frame_io:1;
+
+ struct l2_packet_data *l2_test;
++
++ enum wpa_alg last_gtk_alg;
++ int last_gtk_key_idx;
++ u8 last_gtk[WPA_GTK_MAX_LEN];
++ size_t last_gtk_len;
++
++#ifdef CONFIG_IEEE80211W
++ enum wpa_alg last_igtk_alg;
++ int last_igtk_key_idx;
++ u8 last_igtk[WPA_IGTK_MAX_LEN];
++ size_t last_igtk_len;
++#endif /* CONFIG_IEEE80211W */
+ #endif /* CONFIG_TESTING_OPTIONS */
++
++#ifdef CONFIG_MBO
++ unsigned int mbo_assoc_disallow;
++#endif /* CONFIG_MBO */
++
++ struct dl_list nr_db;
++
++ u8 beacon_req_token;
++ u8 lci_req_token;
++ u8 range_req_token;
++ unsigned int lci_req_active:1;
++ unsigned int range_req_active:1;
++
++ int dhcp_sock; /* UDP socket used with the DHCP server */
++
++#ifdef CONFIG_DPP
++ int dpp_init_done;
++ struct dpp_authentication *dpp_auth;
++ u8 dpp_allowed_roles;
++ int dpp_qr_mutual;
++ int dpp_auth_ok_on_ack;
++ int dpp_in_response_listen;
++ struct gas_query_ap *gas;
++ struct dpp_pkex *dpp_pkex;
++ struct dpp_bootstrap_info *dpp_pkex_bi;
++ char *dpp_pkex_code;
++ char *dpp_pkex_identifier;
++ char *dpp_pkex_auth_cmd;
++ char *dpp_configurator_params;
++ struct os_reltime dpp_last_init;
++ struct os_reltime dpp_init_iter_start;
++ unsigned int dpp_init_max_tries;
++ unsigned int dpp_init_retry_time;
++ unsigned int dpp_resp_wait_time;
++ unsigned int dpp_resp_max_tries;
++ unsigned int dpp_resp_retry_time;
++#ifdef CONFIG_TESTING_OPTIONS
++ char *dpp_config_obj_override;
++ char *dpp_discovery_override;
++ char *dpp_groups_override;
++ unsigned int dpp_ignore_netaccesskey_mismatch:1;
++#endif /* CONFIG_TESTING_OPTIONS */
++#endif /* CONFIG_DPP */
+ };
+
+
+@@ -285,6 +386,10 @@
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ struct os_reltime last_seen;
++ int ssi_signal;
++#ifdef CONFIG_TAXONOMY
++ struct wpabuf *probe_ie_taxonomy;
++#endif /* CONFIG_TAXONOMY */
+ };
+
+ /**
+@@ -327,6 +432,15 @@
+ */
+ unsigned int driver_ap_teardown:1;
+
++ /*
++ * When set, indicates that this interface is part of list of
++ * interfaces that need to be started together (synchronously).
++ */
++ unsigned int need_to_start_in_sync:1;
++
++ /* Ready to start but waiting for other interfaces to become ready. */
++ unsigned int ready_to_start_in_sync:1;
++
+ int num_ap; /* number of entries in ap_list */
+ struct ap_info *ap_list; /* AP info list head */
+ struct ap_info *ap_hash[STA_HASH_SIZE];
+@@ -402,6 +516,13 @@
+ u64 last_channel_time_busy;
+ u8 channel_utilization;
+
++ unsigned int chan_util_samples_sum;
++ unsigned int chan_util_num_sample_periods;
++ unsigned int chan_util_average;
++
++ /* eCSA IE will be added only if operating class is specified */
++ u8 cs_oper_class;
++
+ unsigned int dfs_cac_ms;
+ struct os_reltime dfs_cac_start;
+
+@@ -418,6 +539,8 @@
+
+ struct dl_list sta_seen; /* struct hostapd_sta_info */
+ unsigned int num_sta_seen;
++
++ u8 dfs_domain;
+ };
+
+ /* hostapd.c */
+@@ -425,6 +548,7 @@
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+ int hostapd_reload_config(struct hostapd_iface *iface);
++void hostapd_reconfig_encryption(struct hostapd_data *hapd);
+ struct hostapd_data *
+ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+@@ -433,6 +557,7 @@
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
++struct hostapd_iface * hostapd_alloc_iface(void);
+ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file);
+ struct hostapd_iface *
+@@ -449,6 +574,8 @@
+ void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+ const char * hostapd_state_text(enum hostapd_iface_state s);
++int hostapd_csa_in_progress(struct hostapd_iface *iface);
++void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
+ int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings);
+ void
+@@ -456,6 +583,7 @@
+ const struct hostapd_freq_params *freq_params);
+ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+ void hostapd_periodic_iface(struct hostapd_iface *iface);
++int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+
+ /* utils.c */
+ int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+@@ -467,6 +595,8 @@
+ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+ /* drv_callbacks.c (TODO: move to somewhere else?) */
++void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
++ struct sta_info *sta);
+ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ielen, int reassoc);
+ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+@@ -478,6 +608,11 @@
+ int ssi_signal);
+ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset, int width, int cf1, int cf2);
++struct survey_results;
++void hostapd_event_get_survey(struct hostapd_iface *iface,
++ struct survey_results *survey_results);
++void hostapd_acs_channel_selected(struct hostapd_data *hapd,
++ struct acs_selected_channels *acs_res);
+
+ const struct hostapd_eap_user *
+ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+@@ -485,6 +620,9 @@
+
+ struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname);
++void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
++ enum smps_mode smps_mode,
++ enum chan_width chan_width, u8 rx_nss);
+
+ #ifdef CONFIG_FST
+ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+--- contrib/wpa/src/ap/hs20.c.orig
++++ contrib/wpa/src/ap/hs20.c
+@@ -11,9 +11,11 @@
+
+ #include "common.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/wpa_ctrl.h"
+ #include "hostapd.h"
+ #include "ap_config.h"
+ #include "ap_drv_ops.h"
++#include "sta_info.h"
+ #include "hs20.h"
+
+
+@@ -23,17 +25,20 @@
+ if (!hapd->conf->hs20)
+ return eid;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+- *eid++ = 7;
++ *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_INDICATION_OUI_TYPE;
+- conf = HS20_VERSION; /* Release Number */
+- conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
++ conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
++ if (hapd->conf->hs20_release >= 2)
++ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ if (hapd->conf->disable_dgaf)
+ conf |= HS20_DGAF_DISABLED;
+ *eid++ = conf;
+- WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+- eid += 2;
++ if (hapd->conf->hs20_release >= 2) {
++ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
++ eid += 2;
++ }
+
+ return eid;
+ }
+@@ -82,6 +87,10 @@
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ if (hapd->conf->ocv)
++ capab |= WPA_CAPABILITY_OCVC;
++#endif /* CONFIG_OCV */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+@@ -175,3 +184,72 @@
+
+ return ret;
+ }
++
++
++int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
++ const u8 *addr, const char *url)
++{
++ struct wpabuf *buf;
++ int ret;
++ size_t url_len;
++
++ if (!url) {
++ wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
++ return -1;
++ }
++
++ url_len = os_strlen(url);
++ if (5 + url_len > 255) {
++ wpa_printf(MSG_INFO,
++ "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
++ url);
++ return -1;
++ }
++
++ buf = wpabuf_alloc(4 + 7 + url_len);
++ if (!buf)
++ return -1;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
++ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
++ wpabuf_put_u8(buf, 1); /* Dialog token */
++ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
++
++ /* Terms and Conditions Acceptance subelement */
++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
++ wpabuf_put_u8(buf, 4 + 1 + url_len);
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
++ wpabuf_put_u8(buf, url_len);
++ wpabuf_put_str(buf, url);
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++
++ wpabuf_free(buf);
++
++ return ret;
++}
++
++
++void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
++ int enabled)
++{
++ if (enabled) {
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: Terms and Conditions filtering required for "
++ MACSTR, MAC2STR(sta->addr));
++ sta->hs20_t_c_filtering = 1;
++ /* TODO: Enable firewall filtering for the STA */
++ wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
++ MAC2STR(sta->addr));
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: Terms and Conditions filtering not required for "
++ MACSTR, MAC2STR(sta->addr));
++ sta->hs20_t_c_filtering = 0;
++ /* TODO: Disable firewall filtering for the STA */
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
++ }
++}
+--- contrib/wpa/src/ap/hs20.h.orig
++++ contrib/wpa/src/ap/hs20.h
+@@ -18,5 +18,9 @@
+ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload);
++int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
++ const u8 *addr, const char *url);
++void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
++ int enabled);
+
+ #endif /* HS20_H */
+--- contrib/wpa/src/ap/hw_features.c.orig
++++ contrib/wpa/src/ap/hw_features.c
+@@ -78,10 +78,12 @@
+ int i, j;
+ u16 num_modes, flags;
+ struct hostapd_hw_modes *modes;
++ u8 dfs_domain;
+
+ if (hostapd_drv_none(hapd))
+ return -1;
+- modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
++ modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
++ &dfs_domain);
+ if (modes == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -91,6 +93,7 @@
+ }
+
+ iface->hw_flags = flags;
++ iface->dfs_domain = dfs_domain;
+
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = modes;
+@@ -226,9 +229,6 @@
+ {
+ int pri_chan, sec_chan;
+
+- if (!iface->conf->secondary_channel)
+- return 1; /* HT40 not used */
+-
+ pri_chan = iface->conf->channel;
+ sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+@@ -329,6 +329,10 @@
+ res = ieee80211n_allowed_ht40_channel_pair(iface);
+ if (!res) {
+ iface->conf->secondary_channel = 0;
++ iface->conf->vht_oper_centr_freq_seg0_idx = 0;
++ iface->conf->vht_oper_centr_freq_seg1_idx = 0;
++ iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
++ res = 1;
+ wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+ }
+
+@@ -472,8 +476,9 @@
+ struct wpa_driver_scan_params params;
+ int ret;
+
+- if (!iface->conf->secondary_channel)
+- return 0; /* HT40 not used */
++ /* Check that HT40 is used and PRI / SEC switch is allowed */
++ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
++ return 0;
+
+ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+ wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+@@ -619,41 +624,6 @@
+
+
+ #ifdef CONFIG_IEEE80211AC
+-
+-static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
+-{
+- u32 req_cap = conf & cap;
+-
+- /*
+- * Make sure we support all requested capabilities.
+- * NOTE: We assume that 'cap' represents a capability mask,
+- * not a discrete value.
+- */
+- if ((hw & req_cap) != req_cap) {
+- wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
+- name);
+- return 0;
+- }
+- return 1;
+-}
+-
+-
+-static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
+- unsigned int shift,
+- const char *name)
+-{
+- u32 hw_max = hw & mask;
+- u32 conf_val = conf & mask;
+-
+- if (conf_val > hw_max) {
+- wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+- name, conf_val >> shift, hw_max >> shift);
+- return 0;
+- }
+- return 1;
+-}
+-
+-
+ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ {
+ struct hostapd_hw_modes *mode = iface->current_mode;
+@@ -681,45 +651,7 @@
+ }
+ }
+
+-#define VHT_CAP_CHECK(cap) \
+- do { \
+- if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
+- return 0; \
+- } while (0)
+-
+-#define VHT_CAP_CHECK_MAX(cap) \
+- do { \
+- if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
+- #cap)) \
+- return 0; \
+- } while (0)
+-
+- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
+- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
+- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+- VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+- VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+- VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+- VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+- VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+- VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+- VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+- VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+- VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+-
+-#undef VHT_CAP_CHECK
+-#undef VHT_CAP_CHECK_MAX
+-
+- return 1;
++ return ieee80211ac_cap_check(hw, conf);
+ }
+ #endif /* CONFIG_IEEE80211AC */
+
+@@ -744,7 +676,8 @@
+ if (!ieee80211n_supported_ht_capab(iface))
+ return -1;
+ #ifdef CONFIG_IEEE80211AC
+- if (!ieee80211ac_supported_vht_capab(iface))
++ if (iface->conf->ieee80211ac &&
++ !ieee80211ac_supported_vht_capab(iface))
+ return -1;
+ #endif /* CONFIG_IEEE80211AC */
+ ret = ieee80211n_check_40mhz(iface);
+@@ -761,28 +694,25 @@
+ static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+ int channel, int primary)
+ {
+- int i;
+ struct hostapd_channel_data *chan;
+
+ if (!iface->current_mode)
+ return 0;
+
+- for (i = 0; i < iface->current_mode->num_channels; i++) {
+- chan = &iface->current_mode->channels[i];
+- if (chan->chan != channel)
+- continue;
++ chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
++ if (!chan)
++ return 0;
+
+- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+- return 1;
++ if ((primary && chan_pri_allowed(chan)) ||
++ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
++ return 1;
+
+- wpa_printf(MSG_DEBUG,
+- "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
+- primary ? "" : "Configured HT40 secondary ",
+- i, chan->chan, chan->flag,
+- chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+- chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+- }
+-
++ wpa_printf(MSG_INFO,
++ "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
++ channel, primary ? "primary" : "secondary",
++ chan->flag,
++ chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
++ chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+ return 0;
+ }
+
+@@ -789,6 +719,14 @@
+
+ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+ {
++ int secondary_chan;
++ struct hostapd_channel_data *pri_chan;
++
++ pri_chan = hw_get_channel_chan(iface->current_mode,
++ iface->conf->channel, NULL);
++ if (!pri_chan)
++ return 0;
++
+ if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+ return 0;
+
+@@ -795,8 +733,27 @@
+ if (!iface->conf->secondary_channel)
+ return 1;
+
+- return hostapd_is_usable_chan(iface, iface->conf->channel +
+- iface->conf->secondary_channel * 4, 0);
++ if (!iface->conf->ht40_plus_minus_allowed)
++ return hostapd_is_usable_chan(
++ iface, iface->conf->channel +
++ iface->conf->secondary_channel * 4, 0);
++
++ /* Both HT40+ and HT40- are set, pick a valid secondary channel */
++ secondary_chan = iface->conf->channel + 4;
++ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
++ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
++ iface->conf->secondary_channel = 1;
++ return 1;
++ }
++
++ secondary_chan = iface->conf->channel - 4;
++ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
++ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
++ iface->conf->secondary_channel = -1;
++ return 1;
++ }
++
++ return 0;
+ }
+
+
+@@ -976,5 +933,19 @@
+
+ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+ {
+- return hw_get_chan(hapd->iface->current_mode, freq);
++ int i, channel;
++ struct hostapd_hw_modes *mode;
++
++ channel = hw_get_chan(hapd->iface->current_mode, freq);
++ if (channel)
++ return channel;
++ /* Check other available modes since the channel list for the current
++ * mode did not include the specified frequency. */
++ for (i = 0; i < hapd->iface->num_hw_features; i++) {
++ mode = &hapd->iface->hw_features[i];
++ channel = hw_get_chan(mode, freq);
++ if (channel)
++ return channel;
++ }
++ return 0;
+ }
+--- contrib/wpa/src/ap/iapp.c.orig
++++ contrib/wpa/src/ap/iapp.c
+@@ -34,11 +34,7 @@
+ #include "utils/includes.h"
+ #include
+ #include
+-#ifdef USE_KERNEL_HEADERS
+-#include
+-#else /* USE_KERNEL_HEADERS */
+ #include
+-#endif /* USE_KERNEL_HEADERS */
+
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+@@ -385,6 +381,7 @@
+ struct sockaddr_in *paddr, uaddr;
+ struct iapp_data *iapp;
+ struct ip_mreqn mreq;
++ int reuseaddr = 1;
+
+ iapp = os_zalloc(sizeof(*iapp));
+ if (iapp == NULL)
+@@ -447,6 +444,18 @@
+ os_memset(&uaddr, 0, sizeof(uaddr));
+ uaddr.sin_family = AF_INET;
+ uaddr.sin_port = htons(IAPP_UDP_PORT);
++
++ if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
++ sizeof(reuseaddr)) < 0) {
++ wpa_printf(MSG_INFO,
++ "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
++ strerror(errno));
++ /*
++ * Ignore this and try to continue. This is fine for single
++ * BSS cases, but may fail if multiple BSSes enable IAPP.
++ */
++ }
++
+ if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
+ sizeof(uaddr)) < 0) {
+ wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+--- contrib/wpa/src/ap/ieee802_11.c.orig
++++ contrib/wpa/src/ap/ieee802_11.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / IEEE 802.11 Management
+- * Copyright (c) 2002-2014, Jouni Malinen
++ * Copyright (c) 2002-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -14,11 +14,15 @@
+ #include "utils/eloop.h"
+ #include "crypto/crypto.h"
+ #include "crypto/sha256.h"
++#include "crypto/sha384.h"
++#include "crypto/sha512.h"
+ #include "crypto/random.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
+ #include "common/wpa_ctrl.h"
+ #include "common/sae.h"
++#include "common/dpp.h"
++#include "common/ocv.h"
+ #include "radius/radius.h"
+ #include "radius/radius_client.h"
+ #include "p2p/p2p.h"
+@@ -42,8 +46,43 @@
+ #include "hw_features.h"
+ #include "ieee802_11.h"
+ #include "dfs.h"
++#include "mbo_ap.h"
++#include "rrm.h"
++#include "taxonomy.h"
++#include "fils_hlp.h"
++#include "dpp_hostapd.h"
++#include "gas_query_ap.h"
+
+
++#ifdef CONFIG_FILS
++static struct wpabuf *
++prepare_auth_resp_fils(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 *resp,
++ struct rsn_pmksa_cache_entry *pmksa,
++ struct wpabuf *erp_resp,
++ const u8 *msk, size_t msk_len,
++ int *is_pub);
++#endif /* CONFIG_FILS */
++static void handle_auth(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt, size_t len,
++ int rssi, int from_queue);
++
++
++u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
++{
++ u8 multi_ap_val = 0;
++
++ if (!hapd->conf->multi_ap)
++ return eid;
++ if (hapd->conf->multi_ap & BACKHAUL_BSS)
++ multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
++ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
++ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
++
++ return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
++}
++
++
+ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+ {
+ u8 *pos = eid;
+@@ -139,6 +178,7 @@
+ int capab = WLAN_CAPABILITY_ESS;
+ int privacy;
+ int dfs;
++ int i;
+
+ /* Check if any of configured channels require DFS */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+@@ -186,8 +226,12 @@
+ (hapd->iconf->spectrum_mgmt_required || dfs))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+- if (hapd->conf->radio_measurements)
+- capab |= IEEE80211_CAP_RRM;
++ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
++ if (hapd->conf->radio_measurements[i]) {
++ capab |= IEEE80211_CAP_RRM;
++ break;
++ }
++ }
+
+ return capab;
+ }
+@@ -207,16 +251,17 @@
+ if (!sta->challenge) {
+ /* Generate a pseudo-random challenge */
+ u8 key[8];
+- struct os_time now;
+- int r;
++
+ sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
+ if (sta->challenge == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+- os_get_time(&now);
+- r = os_random();
+- os_memcpy(key, &now.sec, 4);
+- os_memcpy(key + 4, &r, 4);
++ if (os_get_random(key, sizeof(key)) < 0) {
++ os_free(sta->challenge);
++ sta->challenge = NULL;
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
+ rc4_skip(key, sizeof(key), 0,
+ sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+ }
+@@ -250,19 +295,20 @@
+ #endif /* CONFIG_NO_RC4 */
+
+
+-static void send_auth_reply(struct hostapd_data *hapd,
+- const u8 *dst, const u8 *bssid,
+- u16 auth_alg, u16 auth_transaction, u16 resp,
+- const u8 *ies, size_t ies_len)
++static int send_auth_reply(struct hostapd_data *hapd,
++ const u8 *dst, const u8 *bssid,
++ u16 auth_alg, u16 auth_transaction, u16 resp,
++ const u8 *ies, size_t ies_len, const char *dbg)
+ {
+ struct ieee80211_mgmt *reply;
+ u8 *buf;
+ size_t rlen;
++ int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+ buf = os_zalloc(rlen);
+ if (buf == NULL)
+- return;
++ return -1;
+
+ reply = (struct ieee80211_mgmt *) buf;
+ reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+@@ -279,17 +325,21 @@
+ os_memcpy(reply->u.auth.variable, ies, ies_len);
+
+ wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
+- " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
++ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
+ MAC2STR(dst), auth_alg, auth_transaction,
+- resp, (unsigned long) ies_len);
++ resp, (unsigned long) ies_len, dbg);
+ if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
+- wpa_printf(MSG_INFO, "send_auth_reply: send");
++ wpa_printf(MSG_INFO, "send_auth_reply: send failed");
++ else
++ reply_res = WLAN_STATUS_SUCCESS;
+
+ os_free(buf);
++
++ return reply_res;
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len)
+@@ -296,28 +346,44 @@
+ {
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
++ int reply_res;
+
+- send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
+- status, ies, ies_len);
++ reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
++ auth_transaction, status, ies, ies_len,
++ "auth-ft-finish");
+
+- if (status != WLAN_STATUS_SUCCESS)
+- return;
+-
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL)
+ return;
+
++ if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
++ status != WLAN_STATUS_SUCCESS)) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ return;
++ }
++
++ if (status != WLAN_STATUS_SUCCESS)
++ return;
++
+ hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ sta->flags |= WLAN_STA_AUTH;
+ mlme_authenticate_indication(hapd, sta);
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+
+ #ifdef CONFIG_SAE
+
+-#define dot11RSNASAESync 5 /* attempts */
++static void sae_set_state(struct sta_info *sta, enum sae_state state,
++ const char *reason)
++{
++ wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
++ sae_state_txt(sta->sae->state), sae_state_txt(state),
++ MAC2STR(sta->addr), reason);
++ sta->sae->state = state;
++}
+
+
+ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+@@ -324,8 +390,28 @@
+ struct sta_info *sta, int update)
+ {
+ struct wpabuf *buf;
++ const char *password = NULL;
++ struct sae_password_entry *pw;
++ const char *rx_id = NULL;
+
+- if (hapd->conf->ssid.wpa_passphrase == NULL) {
++ if (sta->sae->tmp)
++ rx_id = sta->sae->tmp->pw_id;
++
++ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
++ if (!is_broadcast_ether_addr(pw->peer_addr) &&
++ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
++ continue;
++ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
++ continue;
++ if (rx_id && pw->identifier &&
++ os_strcmp(rx_id, pw->identifier) != 0)
++ continue;
++ password = pw->password;
++ break;
++ }
++ if (!password)
++ password = hapd->conf->ssid.wpa_passphrase;
++ if (!password) {
+ wpa_printf(MSG_DEBUG, "SAE: No password available");
+ return NULL;
+ }
+@@ -332,18 +418,27 @@
+
+ if (update &&
+ sae_prepare_commit(hapd->own_addr, sta->addr,
+- (u8 *) hapd->conf->ssid.wpa_passphrase,
+- os_strlen(hapd->conf->ssid.wpa_passphrase),
++ (u8 *) password, os_strlen(password), rx_id,
+ sta->sae) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
+
+- buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
++ if (pw && pw->vlan_id) {
++ if (!sta->sae->tmp) {
++ wpa_printf(MSG_INFO,
++ "SAE: No temporary data allocated - cannot store VLAN ID");
++ return NULL;
++ }
++ sta->sae->tmp->vlan_id = pw->vlan_id;
++ }
++
++ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
++ (rx_id ? 3 + os_strlen(rx_id) : 0));
+ if (buf == NULL)
+ return NULL;
+ sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+- sta->sae->tmp->anti_clogging_token : NULL);
++ sta->sae->tmp->anti_clogging_token : NULL, rx_id);
+
+ return buf;
+ }
+@@ -369,18 +464,21 @@
+ const u8 *bssid, int update)
+ {
+ struct wpabuf *data;
++ int reply_res;
+
+ data = auth_build_sae_commit(hapd, sta, update);
++ if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
++ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+- send_auth_reply(hapd, sta->addr, bssid,
+- WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+- wpabuf_head(data), wpabuf_len(data));
++ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
++ WLAN_STATUS_SUCCESS, wpabuf_head(data),
++ wpabuf_len(data), "sae-send-commit");
+
+ wpabuf_free(data);
+
+- return WLAN_STATUS_SUCCESS;
++ return reply_res;
+ }
+
+
+@@ -389,18 +487,19 @@
+ const u8 *bssid)
+ {
+ struct wpabuf *data;
++ int reply_res;
+
+ data = auth_build_sae_confirm(hapd, sta);
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+- send_auth_reply(hapd, sta->addr, bssid,
+- WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+- wpabuf_head(data), wpabuf_len(data));
++ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
++ WLAN_STATUS_SUCCESS, wpabuf_head(data),
++ wpabuf_len(data), "sae-send-confirm");
+
+ wpabuf_free(data);
+
+- return WLAN_STATUS_SUCCESS;
++ return reply_res;
+ }
+
+
+@@ -423,22 +522,58 @@
+ return 1;
+ }
+
++ /* In addition to already existing open SAE sessions, check whether
++ * there are enough pending commit messages in the processing queue to
++ * potentially result in too many open sessions. */
++ if (open + dl_list_len(&hapd->sae_commit_queue) >=
++ hapd->conf->sae_anti_clogging_threshold)
++ return 1;
++
+ return 0;
+ }
+
+
++static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr)
++{
++ u8 hash[SHA256_MAC_LEN];
++
++ hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
++ addr, ETH_ALEN, hash);
++ return hash[0];
++}
++
++
+ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *token, size_t token_len)
+ {
+ u8 mac[SHA256_MAC_LEN];
++ const u8 *addrs[2];
++ size_t len[2];
++ u16 token_idx;
++ u8 idx;
+
+ if (token_len != SHA256_MAC_LEN)
+ return -1;
+- if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+- addr, ETH_ALEN, mac) < 0 ||
+- os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
++ idx = sae_token_hash(hapd, addr);
++ token_idx = hapd->sae_pending_token_idx[idx];
++ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
++ wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from "
++ MACSTR " - token_idx 0x%04x, expected 0x%04x",
++ MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+ return -1;
++ }
+
++ addrs[0] = addr;
++ len[0] = ETH_ALEN;
++ addrs[1] = token;
++ len[1] = 2;
++ if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
++ 2, addrs, len, mac) < 0 ||
++ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
++ return -1;
++
++ hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */
++
+ return 0;
+ }
+
+@@ -449,10 +584,16 @@
+ struct wpabuf *buf;
+ u8 *token;
+ struct os_reltime now;
++ u8 idx[2];
++ const u8 *addrs[2];
++ size_t len[2];
++ u8 p_idx;
++ u16 token_idx;
+
+ os_get_reltime(&now);
+ if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
+- os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
++ os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) ||
++ hapd->sae_token_idx == 0xffff) {
+ if (random_get_bytes(hapd->sae_token_key,
+ sizeof(hapd->sae_token_key)) < 0)
+ return NULL;
+@@ -459,6 +600,9 @@
+ wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
+ hapd->sae_token_key, sizeof(hapd->sae_token_key));
+ hapd->last_sae_token_key_update = now;
++ hapd->sae_token_idx = 0;
++ os_memset(hapd->sae_pending_token_idx, 0,
++ sizeof(hapd->sae_pending_token_idx));
+ }
+
+ buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
+@@ -467,18 +611,34 @@
+
+ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
++ p_idx = sae_token_hash(hapd, addr);
++ token_idx = hapd->sae_pending_token_idx[p_idx];
++ if (!token_idx) {
++ hapd->sae_token_idx++;
++ token_idx = hapd->sae_token_idx;
++ hapd->sae_pending_token_idx[p_idx] = token_idx;
++ }
++ WPA_PUT_BE16(idx, token_idx);
+ token = wpabuf_put(buf, SHA256_MAC_LEN);
+- hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+- addr, ETH_ALEN, token);
++ addrs[0] = addr;
++ len[0] = ETH_ALEN;
++ addrs[1] = idx;
++ len[1] = sizeof(idx);
++ if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
++ 2, addrs, len, token) < 0) {
++ wpabuf_free(buf);
++ return NULL;
++ }
++ WPA_PUT_BE16(token, token_idx);
+
+ return buf;
+ }
+
+
+-static int sae_check_big_sync(struct sta_info *sta)
++static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+- if (sta->sae->sync > dot11RSNASAESync) {
+- sta->sae->state = SAE_NOTHING;
++ if (sta->sae->sync > hapd->conf->sae_sync) {
++ sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
+ sta->sae->sync = 0;
+ return -1;
+ }
+@@ -492,9 +652,13 @@
+ struct sta_info *sta = eloop_data;
+ int ret;
+
+- if (sae_check_big_sync(sta))
++ if (sae_check_big_sync(hapd, sta))
+ return;
+ sta->sae->sync++;
++ wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
++ " (sync=%d state=%s)",
++ MAC2STR(sta->addr), sta->sae->sync,
++ sae_state_txt(sta->sae->state));
+
+ switch (sta->sae->state) {
+ case SAE_COMMITTED:
+@@ -537,21 +701,85 @@
+ }
+
+
++static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 status)
++{
++ struct external_auth params;
++
++ os_memset(¶ms, 0, sizeof(params));
++ params.status = status;
++ params.bssid = sta->addr;
++ if (status == WLAN_STATUS_SUCCESS && sta->sae)
++ params.pmkid = sta->sae->pmkid;
++
++ hostapd_drv_send_external_auth_status(hapd, ¶ms);
++}
++
++
++void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
++{
++#ifndef CONFIG_NO_VLAN
++ struct vlan_description vlan_desc;
++
++ if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
++ wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
++ " to VLAN ID %d",
++ MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
++
++ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
++ vlan_desc.notempty = 1;
++ vlan_desc.untagged = sta->sae->tmp->vlan_id;
++ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
++ wpa_printf(MSG_INFO,
++ "Invalid VLAN ID %d in sae_password",
++ sta->sae->tmp->vlan_id);
++ return;
++ }
++
++ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
++ ap_sta_bind_vlan(hapd, sta) < 0) {
++ wpa_printf(MSG_INFO,
++ "Failed to assign VLAN ID %d from sae_password to "
++ MACSTR, sta->sae->tmp->vlan_id,
++ MAC2STR(sta->addr));
++ return;
++ }
++ }
++#endif /* CONFIG_NO_VLAN */
++
++ sta->flags |= WLAN_STA_AUTH;
++ sta->auth_alg = WLAN_AUTH_SAE;
++ mlme_authenticate_indication(hapd, sta);
++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
++ sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
++ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
++ sta->sae->pmk, sta->sae->pmkid);
++ sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
++}
++
++
+ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+- const u8 *bssid, u8 auth_transaction)
++ const u8 *bssid, u8 auth_transaction, int allow_reuse,
++ int *sta_removed)
+ {
+ int ret;
+
++ *sta_removed = 0;
++
+ if (auth_transaction != 1 && auth_transaction != 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
++ wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
++ MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
++ auth_transaction);
+ switch (sta->sae->state) {
+ case SAE_NOTHING:
+ if (auth_transaction == 1) {
+- ret = auth_sae_send_commit(hapd, sta, bssid, 1);
++ ret = auth_sae_send_commit(hapd, sta, bssid,
++ !allow_reuse);
+ if (ret)
+ return ret;
+- sta->sae->state = SAE_COMMITTED;
++ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+@@ -573,7 +801,8 @@
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+- sta->sae->state = SAE_CONFIRMED;
++ sae_set_state(sta, SAE_CONFIRMED,
++ "Sent Confirm (mesh)");
+ } else {
+ /*
+ * For infrastructure BSS, send only the Commit
+@@ -580,7 +809,7 @@
+ * message now to get alternating sequence of
+ * Authentication frames between the AP and STA.
+ * Confirm will be sent in
+- * Commited -> Confirmed/Accepted transition
++ * Committed -> Confirmed/Accepted transition
+ * when receiving Confirm from STA.
+ */
+ }
+@@ -602,7 +831,7 @@
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+- sta->sae->state = SAE_CONFIRMED;
++ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else if (hapd->conf->mesh & MESH_ENABLED) {
+@@ -610,7 +839,7 @@
+ * In mesh case, follow SAE finite state machine and
+ * send Commit now, if sync count allows.
+ */
+- if (sae_check_big_sync(sta))
++ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+@@ -629,7 +858,7 @@
+ if (ret)
+ return ret;
+
+- sta->sae->state = SAE_CONFIRMED;
++ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+
+ /*
+ * Since this was triggered on Confirm RX, run another
+@@ -636,13 +865,14 @@
+ * step to get to Accepted without waiting for
+ * additional events.
+ */
+- return sae_sm_step(hapd, sta, bssid, auth_transaction);
++ return sae_sm_step(hapd, sta, bssid, auth_transaction,
++ 0, sta_removed);
+ }
+ break;
+ case SAE_CONFIRMED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+- if (sae_check_big_sync(sta))
++ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+@@ -659,23 +889,32 @@
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+- sta->flags |= WLAN_STA_AUTH;
+- sta->auth_alg = WLAN_AUTH_SAE;
+- mlme_authenticate_indication(hapd, sta);
+- wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+- sta->sae->state = SAE_ACCEPTED;
+- wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+- sta->sae->pmk);
++ sta->sae->send_confirm = 0xffff;
++ sae_accept_sta(hapd, sta);
+ }
+ break;
+ case SAE_ACCEPTED:
+- if (auth_transaction == 1) {
++ if (auth_transaction == 1 &&
++ (hapd->conf->mesh & MESH_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+ ") doing reauthentication",
+ MAC2STR(sta->addr));
++ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ ap_free_sta(hapd, sta);
++ *sta_removed = 1;
++ } else if (auth_transaction == 1) {
++ wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
++ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
++ if (ret)
++ return ret;
++ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
++
++ if (sae_process_commit(sta->sae) < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ sta->sae->sync = 0;
++ sae_set_retransmit_timer(hapd, sta);
+ } else {
+- if (sae_check_big_sync(sta))
++ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+@@ -694,26 +933,109 @@
+ }
+
+
++static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
++{
++ struct sae_data *sae = sta->sae;
++ int i, *groups = hapd->conf->sae_groups;
++ int default_groups[] = { 19, 0 };
++
++ if (sae->state != SAE_COMMITTED)
++ return;
++
++ wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
++
++ if (!groups)
++ groups = default_groups;
++ for (i = 0; groups[i] > 0; i++) {
++ if (sae->group == groups[i])
++ break;
++ }
++
++ if (groups[i] <= 0) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Previously selected group not found from the current configuration");
++ return;
++ }
++
++ for (;;) {
++ i++;
++ if (groups[i] <= 0) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: No alternative group enabled");
++ return;
++ }
++
++ if (sae_set_group(sae, groups[i]) < 0)
++ continue;
++
++ break;
++ }
++ wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
++}
++
++
+ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ u16 auth_transaction, u16 status_code)
+ {
+- u16 resp = WLAN_STATUS_SUCCESS;
++ int resp = WLAN_STATUS_SUCCESS;
+ struct wpabuf *data = NULL;
++ int *groups = hapd->conf->sae_groups;
++ int default_groups[] = { 19, 0 };
++ const u8 *pos, *end;
++ int sta_removed = 0;
+
++ if (!groups)
++ groups = default_groups;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
++ wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
++ pos = mgmt->u.auth.variable;
++ end = ((const u8 *) mgmt) + len;
++ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
++ auth_transaction, resp, pos, end - pos,
++ "auth-sae-reflection-attack");
++ goto remove_sta;
++ }
++
++ if (hapd->conf->sae_commit_override && auth_transaction == 1) {
++ wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
++ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
++ auth_transaction, resp,
++ wpabuf_head(hapd->conf->sae_commit_override),
++ wpabuf_len(hapd->conf->sae_commit_override),
++ "sae-commit-override");
++ goto remove_sta;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
+ if (!sta->sae) {
+- if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+- return;
++ if (auth_transaction != 1 ||
++ status_code != WLAN_STATUS_SUCCESS) {
++ resp = -1;
++ goto remove_sta;
++ }
+ sta->sae = os_zalloc(sizeof(*sta->sae));
+- if (sta->sae == NULL)
+- return;
+- sta->sae->state = SAE_NOTHING;
++ if (!sta->sae) {
++ resp = -1;
++ goto remove_sta;
++ }
++ sae_set_state(sta, SAE_NOTHING, "Init");
+ sta->sae->sync = 0;
+ }
+
++ if (sta->mesh_sae_pmksa_caching) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
++ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
++ sta->mesh_sae_pmksa_caching = 0;
++ }
++
+ if (auth_transaction == 1) {
+- const u8 *token = NULL, *pos, *end;
++ const u8 *token = NULL;
+ size_t token_len = 0;
++ int allow_reuse = 0;
++
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "start SAE authentication (RX commit, status=%u)",
+@@ -730,8 +1052,7 @@
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+- resp = sae_group_allowed(sta->sae,
+- hapd->conf->sae_groups,
++ resp = sae_group_allowed(sta->sae, groups,
+ WPA_GET_LE16(pos));
+ if (resp != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_ERROR,
+@@ -746,7 +1067,8 @@
+ if (sta->sae->tmp->anti_clogging_token == NULL) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to alloc for anti-clogging token");
+- return;
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto remove_sta;
+ }
+
+ /*
+@@ -756,42 +1078,93 @@
+ * Authentication frame, and the commit-scalar and
+ * COMMIT-ELEMENT previously sent.
+ */
+- if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
++ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0);
++ if (resp != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to send commit message");
+- return;
++ goto remove_sta;
+ }
+- sta->sae->state = SAE_COMMITTED;
++ sae_set_state(sta, SAE_COMMITTED,
++ "Sent Commit (anti-clogging token case in mesh)");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ return;
+ }
+
++ if ((hapd->conf->mesh & MESH_ENABLED) &&
++ status_code ==
++ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
++ sta->sae->tmp) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Peer did not accept our SAE group");
++ sae_pick_next_group(hapd, sta);
++ goto remove_sta;
++ }
++
+ if (status_code != WLAN_STATUS_SUCCESS)
+- return;
++ goto remove_sta;
+
++ if (!(hapd->conf->mesh & MESH_ENABLED) &&
++ sta->sae->state == SAE_COMMITTED) {
++ /* This is needed in the infrastructure BSS case to
++ * address a sequence where a STA entry may remain in
++ * hostapd across two attempts to do SAE authentication
++ * by the same STA. The second attempt may end up trying
++ * to use a different group and that would not be
++ * allowed if we remain in Committed state with the
++ * previously set parameters. */
++ pos = mgmt->u.auth.variable;
++ end = ((const u8 *) mgmt) + len;
++ if (end - pos >= (int) sizeof(le16) &&
++ sae_group_allowed(sta->sae, groups,
++ WPA_GET_LE16(pos)) ==
++ WLAN_STATUS_SUCCESS) {
++ /* Do not waste resources deriving the same PWE
++ * again since the same group is reused. */
++ sae_set_state(sta, SAE_NOTHING,
++ "Allow previous PWE to be reused");
++ allow_reuse = 1;
++ } else {
++ sae_set_state(sta, SAE_NOTHING,
++ "Clear existing state to allow restart");
++ sae_clear_data(sta->sae);
++ }
++ }
++
+ resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+ ((const u8 *) mgmt) + len -
+ mgmt->u.auth.variable, &token,
+- &token_len, hapd->conf->sae_groups);
++ &token_len, groups);
+ if (resp == SAE_SILENTLY_DISCARD) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Drop commit message from " MACSTR " due to reflection attack",
+ MAC2STR(sta->addr));
+- return;
++ goto remove_sta;
+ }
++
++ if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
++ MACSTR, MAC2STR(sta->addr));
++ sae_clear_retransmit_timer(hapd, sta);
++ sae_set_state(sta, SAE_NOTHING,
++ "Unknown Password Identifier");
++ goto remove_sta;
++ }
++
+ if (token && check_sae_token(hapd, sta->addr, token, token_len)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+ "incorrect token from " MACSTR,
+ MAC2STR(sta->addr));
+- return;
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto remove_sta;
+ }
+
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto reply;
+
+- if (!token && use_sae_anti_clogging(hapd)) {
++ if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Request anti-clogging token from "
+ MACSTR, MAC2STR(sta->addr));
+@@ -799,11 +1172,13 @@
+ sta->addr);
+ resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+ if (hapd->conf->mesh & MESH_ENABLED)
+- sta->sae->state = SAE_NOTHING;
++ sae_set_state(sta, SAE_NOTHING,
++ "Request anti-clogging token case in mesh");
+ goto reply;
+ }
+
+- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
++ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
++ allow_reuse, &sta_removed);
+ } else if (auth_transaction == 2) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -810,17 +1185,42 @@
+ "SAE authentication (RX confirm, status=%u)",
+ status_code);
+ if (status_code != WLAN_STATUS_SUCCESS)
+- return;
++ goto remove_sta;
+ if (sta->sae->state >= SAE_CONFIRMED ||
+ !(hapd->conf->mesh & MESH_ENABLED)) {
+- if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+- ((u8 *) mgmt) + len -
+- mgmt->u.auth.variable) < 0) {
++ const u8 *var;
++ size_t var_len;
++ u16 peer_send_confirm;
++
++ var = mgmt->u.auth.variable;
++ var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
++ if (var_len < 2) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
++
++ peer_send_confirm = WPA_GET_LE16(var);
++
++ if (sta->sae->state == SAE_ACCEPTED &&
++ (peer_send_confirm <= sta->sae->rc ||
++ peer_send_confirm == 0xffff)) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Silently ignore unexpected Confirm from peer "
++ MACSTR
++ " (peer-send-confirm=%u Rc=%u)",
++ MAC2STR(sta->addr),
++ peer_send_confirm, sta->sae->rc);
++ return;
++ }
++
++ if (sae_check_confirm(sta->sae, var, var_len) < 0) {
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto reply;
++ }
++ sta->sae->rc = peer_send_confirm;
+ }
+- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
++ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0,
++ &sta_removed);
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -827,17 +1227,35 @@
+ "unexpected SAE authentication transaction %u (status=%u)",
+ auth_transaction, status_code);
+ if (status_code != WLAN_STATUS_SUCCESS)
+- return;
++ goto remove_sta;
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ }
+
+ reply:
+- if (resp != WLAN_STATUS_SUCCESS) {
++ if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
++ pos = mgmt->u.auth.variable;
++ end = ((const u8 *) mgmt) + len;
++
++ /* Copy the Finite Cyclic Group field from the request if we
++ * rejected it as unsupported group. */
++ if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
++ !data && end - pos >= 2)
++ data = wpabuf_alloc_copy(pos, 2);
++
++ sae_sme_send_external_auth_status(hapd, sta, resp);
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+- data ? wpabuf_len(data) : 0);
++ data ? wpabuf_len(data) : 0, "auth-sae");
+ }
++
++remove_sta:
++ if (!sta_removed && sta->added_unassoc &&
++ (resp != WLAN_STATUS_SUCCESS ||
++ status_code != WLAN_STATUS_SUCCESS)) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
+ wpabuf_free(data);
+ }
+
+@@ -866,7 +1284,7 @@
+ if (ret)
+ return -1;
+
+- sta->sae->state = SAE_COMMITTED;
++ sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+
+@@ -873,20 +1291,749 @@
+ return 0;
+ }
+
++
++void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct hostapd_sae_commit_queue *q;
++ unsigned int queue_len;
++
++ q = dl_list_first(&hapd->sae_commit_queue,
++ struct hostapd_sae_commit_queue, list);
++ if (!q)
++ return;
++ wpa_printf(MSG_DEBUG,
++ "SAE: Process next available message from queue");
++ dl_list_del(&q->list);
++ handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
++ q->rssi, 1);
++ os_free(q);
++
++ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
++ return;
++ queue_len = dl_list_len(&hapd->sae_commit_queue);
++ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
++ hapd, NULL);
++}
++
++
++static void auth_sae_queue(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt, size_t len,
++ int rssi)
++{
++ struct hostapd_sae_commit_queue *q, *q2;
++ unsigned int queue_len;
++ const struct ieee80211_mgmt *mgmt2;
++
++ queue_len = dl_list_len(&hapd->sae_commit_queue);
++ if (queue_len >= 15) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: No more room in message queue - drop the new frame from "
++ MACSTR, MAC2STR(mgmt->sa));
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
++ MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
++ queue_len);
++ q = os_zalloc(sizeof(*q) + len);
++ if (!q)
++ return;
++ q->rssi = rssi;
++ q->len = len;
++ os_memcpy(q->msg, mgmt, len);
++
++ /* Check whether there is already a queued Authentication frame from the
++ * same station with the same transaction number and if so, replace that
++ * queue entry with the new one. This avoids issues with a peer that
++ * sends multiple times (e.g., due to frequent SAE retries). There is no
++ * point in us trying to process the old attempts after a new one has
++ * obsoleted them. */
++ dl_list_for_each(q2, &hapd->sae_commit_queue,
++ struct hostapd_sae_commit_queue, list) {
++ mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
++ if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
++ mgmt->u.auth.auth_transaction ==
++ mgmt2->u.auth.auth_transaction) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Replace queued message from same STA with same transaction number");
++ dl_list_add(&q2->list, &q->list);
++ dl_list_del(&q2->list);
++ os_free(q2);
++ goto queued;
++ }
++ }
++
++ /* No pending identical entry, so add to the end of the queue */
++ dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
++
++queued:
++ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
++ return;
++ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
++ hapd, NULL);
++}
++
++
++static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
++{
++ struct hostapd_sae_commit_queue *q;
++ const struct ieee80211_mgmt *mgmt;
++
++ dl_list_for_each(q, &hapd->sae_commit_queue,
++ struct hostapd_sae_commit_queue, list) {
++ mgmt = (const struct ieee80211_mgmt *) q->msg;
++ if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
++ return 1;
++ }
++
++ return 0;
++}
++
+ #endif /* CONFIG_SAE */
+
+
++static u16 wpa_res_to_status_code(int res)
++{
++ if (res == WPA_INVALID_GROUP)
++ return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
++ if (res == WPA_INVALID_PAIRWISE)
++ return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
++ if (res == WPA_INVALID_AKMP)
++ return WLAN_STATUS_AKMP_NOT_VALID;
++ if (res == WPA_ALLOC_FAIL)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++#ifdef CONFIG_IEEE80211W
++ if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
++ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
++ if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
++ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
++#endif /* CONFIG_IEEE80211W */
++ if (res == WPA_INVALID_MDIE)
++ return WLAN_STATUS_INVALID_MDIE;
++ if (res == WPA_INVALID_PMKID)
++ return WLAN_STATUS_INVALID_PMKID;
++ if (res != WPA_IE_OK)
++ return WLAN_STATUS_INVALID_IE;
++ return WLAN_STATUS_SUCCESS;
++}
++
++
++#ifdef CONFIG_FILS
++
++static void handle_auth_fils_finish(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 resp,
++ struct wpabuf *data, int pub);
++
++void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *pos, size_t len, u16 auth_alg,
++ u16 auth_transaction, u16 status_code,
++ void (*cb)(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 resp,
++ struct wpabuf *data, int pub))
++{
++ u16 resp = WLAN_STATUS_SUCCESS;
++ const u8 *end;
++ struct ieee802_11_elems elems;
++ int res;
++ struct wpa_ie_data rsn;
++ struct rsn_pmksa_cache_entry *pmksa = NULL;
++
++ if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
++ return;
++
++ end = pos + len;
++
++ wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
++ pos, end - pos);
++
++ /* TODO: FILS PK */
++#ifdef CONFIG_FILS_SK_PFS
++ if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
++ u16 group;
++ struct wpabuf *pub;
++ size_t elem_len;
++
++ /* Using FILS PFS */
++
++ /* Finite Cyclic Group */
++ if (end - pos < 2) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: No room for Finite Cyclic Group");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ group = WPA_GET_LE16(pos);
++ pos += 2;
++ if (group != hapd->conf->fils_dh_group) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
++ group, hapd->conf->fils_dh_group);
++ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++ goto fail;
++ }
++
++ crypto_ecdh_deinit(sta->fils_ecdh);
++ sta->fils_ecdh = crypto_ecdh_init(group);
++ if (!sta->fils_ecdh) {
++ wpa_printf(MSG_INFO,
++ "FILS: Could not initialize ECDH with group %d",
++ group);
++ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++ goto fail;
++ }
++
++ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
++ if (!pub) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Failed to derive ECDH public key");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ elem_len = wpabuf_len(pub);
++ wpabuf_free(pub);
++
++ /* Element */
++ if ((size_t) (end - pos) < elem_len) {
++ wpa_printf(MSG_DEBUG, "FILS: No room for Element");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ wpabuf_free(sta->fils_g_sta);
++ sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
++ wpabuf_clear_free(sta->fils_dh_ss);
++ sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
++ pos, elem_len);
++ if (!sta->fils_dh_ss) {
++ wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
++ pos += elem_len;
++ } else {
++ crypto_ecdh_deinit(sta->fils_ecdh);
++ sta->fils_ecdh = NULL;
++ wpabuf_clear_free(sta->fils_dh_ss);
++ sta->fils_dh_ss = NULL;
++ }
++#endif /* CONFIG_FILS_SK_PFS */
++
++ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
++ if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
++ wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ /* RSNE */
++ wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
++ elems.rsn_ie, elems.rsn_ie_len);
++ if (!elems.rsn_ie ||
++ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
++ &rsn) < 0) {
++ wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ if (!sta->wpa_sm)
++ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
++ NULL);
++ if (!sta->wpa_sm) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Failed to initialize RSN state machine");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
++ hapd->iface->freq,
++ elems.rsn_ie - 2, elems.rsn_ie_len + 2,
++ elems.mdie, elems.mdie_len, NULL, 0);
++ resp = wpa_res_to_status_code(res);
++ if (resp != WLAN_STATUS_SUCCESS)
++ goto fail;
++
++ if (!elems.fils_nonce) {
++ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
++ FILS_NONCE_LEN);
++ os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
++
++ /* PMKID List */
++ if (rsn.pmkid && rsn.num_pmkid > 0) {
++ u8 num;
++ const u8 *pmkid;
++
++ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
++ rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
++
++ pmkid = rsn.pmkid;
++ num = rsn.num_pmkid;
++ while (num) {
++ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
++ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
++ pmkid);
++ if (pmksa)
++ break;
++ pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
++ sta->addr,
++ pmkid);
++ if (pmksa)
++ break;
++ pmkid += PMKID_LEN;
++ num--;
++ }
++ }
++ if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
++ wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
++ pmksa = NULL;
++ }
++ if (pmksa)
++ wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
++
++ /* FILS Session */
++ if (!elems.fils_session) {
++ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
++ FILS_SESSION_LEN);
++ os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
++
++ /* FILS Wrapped Data */
++ if (elems.fils_wrapped_data) {
++ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
++ elems.fils_wrapped_data,
++ elems.fils_wrapped_data_len);
++ if (!pmksa) {
++#ifndef CONFIG_NO_RADIUS
++ if (!sta->eapol_sm) {
++ sta->eapol_sm =
++ ieee802_1x_alloc_eapol_sm(hapd, sta);
++ }
++ wpa_printf(MSG_DEBUG,
++ "FILS: Forward EAP-Initiate/Re-auth to authentication server");
++ ieee802_1x_encapsulate_radius(
++ hapd, sta, elems.fils_wrapped_data,
++ elems.fils_wrapped_data_len);
++ sta->fils_pending_cb = cb;
++ wpa_printf(MSG_DEBUG,
++ "FILS: Will send Authentication frame once the response from authentication server is available");
++ sta->flags |= WLAN_STA_PENDING_FILS_ERP;
++ /* Calculate pending PMKID here so that we do not need
++ * to maintain a copy of the EAP-Initiate/Reauth
++ * message. */
++ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
++ elems.fils_wrapped_data,
++ elems.fils_wrapped_data_len,
++ sta->fils_erp_pmkid) == 0)
++ sta->fils_erp_pmkid_set = 1;
++ return;
++#else /* CONFIG_NO_RADIUS */
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++#endif /* CONFIG_NO_RADIUS */
++ }
++ }
++
++fail:
++ if (cb) {
++ struct wpabuf *data;
++ int pub = 0;
++
++ data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
++ NULL, 0, &pub);
++ if (!data) {
++ wpa_printf(MSG_DEBUG,
++ "%s: prepare_auth_resp_fils() returned failure",
++ __func__);
++ }
++
++ cb(hapd, sta, resp, data, pub);
++ }
++}
++
++
++static struct wpabuf *
++prepare_auth_resp_fils(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 *resp,
++ struct rsn_pmksa_cache_entry *pmksa,
++ struct wpabuf *erp_resp,
++ const u8 *msk, size_t msk_len,
++ int *is_pub)
++{
++ u8 fils_nonce[FILS_NONCE_LEN];
++ size_t ielen;
++ struct wpabuf *data = NULL;
++ const u8 *ie;
++ u8 *ie_buf = NULL;
++ const u8 *pmk = NULL;
++ size_t pmk_len = 0;
++ u8 pmk_buf[PMK_LEN_MAX];
++ struct wpabuf *pub = NULL;
++
++ if (*resp != WLAN_STATUS_SUCCESS)
++ goto fail;
++
++ ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
++ if (!ie) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ if (pmksa) {
++ /* Add PMKID of the selected PMKSA into RSNE */
++ ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
++ if (!ie_buf) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ os_memcpy(ie_buf, ie, ielen);
++ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ ie = ie_buf;
++ }
++
++ if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
++ fils_nonce, FILS_NONCE_LEN);
++
++#ifdef CONFIG_FILS_SK_PFS
++ if (sta->fils_dh_ss && sta->fils_ecdh) {
++ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
++ if (!pub) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ }
++#endif /* CONFIG_FILS_SK_PFS */
++
++ data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
++ if (!data) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ /* TODO: FILS PK */
++#ifdef CONFIG_FILS_SK_PFS
++ if (pub) {
++ /* Finite Cyclic Group */
++ wpabuf_put_le16(data, hapd->conf->fils_dh_group);
++
++ /* Element */
++ wpabuf_put_buf(data, pub);
++ }
++#endif /* CONFIG_FILS_SK_PFS */
++
++ /* RSNE */
++ wpabuf_put_data(data, ie, ielen);
++
++ /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
++
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
++ /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
++ int res;
++ int use_sha384 = wpa_key_mgmt_sha384(
++ wpa_auth_sta_key_mgmt(sta->wpa_sm));
++
++ res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
++ wpabuf_put(data, 0),
++ wpabuf_tailroom(data));
++ if (res < 0) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ wpabuf_put(data, res);
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++
++ /* FILS Nonce */
++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
++ wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
++ /* Element ID Extension */
++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
++ wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
++
++ /* FILS Session */
++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
++ wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
++ /* Element ID Extension */
++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
++ wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
++
++ /* FILS Wrapped Data */
++ if (!pmksa && erp_resp) {
++ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
++ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
++ /* Element ID Extension */
++ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA);
++ wpabuf_put_buf(data, erp_resp);
++
++ if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
++ msk, msk_len, sta->fils_snonce, fils_nonce,
++ sta->fils_dh_ss ?
++ wpabuf_head(sta->fils_dh_ss) : NULL,
++ sta->fils_dh_ss ?
++ wpabuf_len(sta->fils_dh_ss) : 0,
++ pmk_buf, &pmk_len)) {
++ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ wpabuf_free(data);
++ data = NULL;
++ goto fail;
++ }
++ pmk = pmk_buf;
++
++ /* Don't use DHss in PTK derivation if PMKSA caching is not
++ * used. */
++ wpabuf_clear_free(sta->fils_dh_ss);
++ sta->fils_dh_ss = NULL;
++
++ if (sta->fils_erp_pmkid_set) {
++ /* TODO: get PMKLifetime from WPA parameters */
++ unsigned int dot11RSNAConfigPMKLifetime = 43200;
++ int session_timeout;
++
++ session_timeout = dot11RSNAConfigPMKLifetime;
++ if (sta->session_timeout_set) {
++ struct os_reltime now, diff;
++
++ os_get_reltime(&now);
++ os_reltime_sub(&sta->session_timeout, &now,
++ &diff);
++ session_timeout = diff.sec;
++ }
++
++ sta->fils_erp_pmkid_set = 0;
++ wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
++ sta->fils_erp_pmkid);
++ if (!hapd->conf->disable_pmksa_caching &&
++ wpa_auth_pmksa_add2(
++ hapd->wpa_auth, sta->addr,
++ pmk, pmk_len,
++ sta->fils_erp_pmkid,
++ session_timeout,
++ wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
++ wpa_printf(MSG_ERROR,
++ "FILS: Failed to add PMKSA cache entry based on ERP");
++ }
++ }
++ } else if (pmksa) {
++ pmk = pmksa->pmk;
++ pmk_len = pmksa->pmk_len;
++ }
++
++ if (!pmk) {
++ wpa_printf(MSG_DEBUG, "FILS: No PMK available");
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ wpabuf_free(data);
++ data = NULL;
++ goto fail;
++ }
++
++ if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
++ sta->fils_snonce, fils_nonce,
++ sta->fils_dh_ss ?
++ wpabuf_head(sta->fils_dh_ss) : NULL,
++ sta->fils_dh_ss ?
++ wpabuf_len(sta->fils_dh_ss) : 0,
++ sta->fils_g_sta, pub) < 0) {
++ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ wpabuf_free(data);
++ data = NULL;
++ goto fail;
++ }
++
++fail:
++ if (is_pub)
++ *is_pub = pub != NULL;
++ os_free(ie_buf);
++ wpabuf_free(pub);
++ wpabuf_clear_free(sta->fils_dh_ss);
++ sta->fils_dh_ss = NULL;
++#ifdef CONFIG_FILS_SK_PFS
++ crypto_ecdh_deinit(sta->fils_ecdh);
++ sta->fils_ecdh = NULL;
++#endif /* CONFIG_FILS_SK_PFS */
++ return data;
++}
++
++
++static void handle_auth_fils_finish(struct hostapd_data *hapd,
++ struct sta_info *sta, u16 resp,
++ struct wpabuf *data, int pub)
++{
++ u16 auth_alg;
++
++ auth_alg = (pub ||
++ resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
++ WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
++ send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
++ data ? wpabuf_head(data) : (u8 *) "",
++ data ? wpabuf_len(data) : 0, "auth-fils-finish");
++ wpabuf_free(data);
++
++ if (resp == WLAN_STATUS_SUCCESS) {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "authentication OK (FILS)");
++ sta->flags |= WLAN_STA_AUTH;
++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
++ sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
++ mlme_authenticate_indication(hapd, sta);
++ }
++}
++
++
++void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
++ struct sta_info *sta, int success,
++ struct wpabuf *erp_resp,
++ const u8 *msk, size_t msk_len)
++{
++ struct wpabuf *data;
++ int pub = 0;
++ u16 resp;
++
++ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
++
++ if (!sta->fils_pending_cb)
++ return;
++ resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
++ data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
++ msk, msk_len, &pub);
++ if (!data) {
++ wpa_printf(MSG_DEBUG,
++ "%s: prepare_auth_resp_fils() returned failure",
++ __func__);
++ }
++ sta->fils_pending_cb(hapd, sta, resp, data, pub);
++}
++
++#endif /* CONFIG_FILS */
++
++
++int
++ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
++ const u8 *msg, size_t len, u32 *session_timeout,
++ u32 *acct_interim_interval,
++ struct vlan_description *vlan_id,
++ struct hostapd_sta_wpa_psk_short **psk,
++ char **identity, char **radius_cui, int is_probe_req)
++{
++ int res;
++
++ os_memset(vlan_id, 0, sizeof(*vlan_id));
++ res = hostapd_allowed_address(hapd, addr, msg, len,
++ session_timeout, acct_interim_interval,
++ vlan_id, psk, identity, radius_cui,
++ is_probe_req);
++
++ if (res == HOSTAPD_ACL_REJECT) {
++ if (!is_probe_req)
++ wpa_printf(MSG_DEBUG,
++ "Station " MACSTR
++ " not allowed to authenticate",
++ MAC2STR(addr));
++ return HOSTAPD_ACL_REJECT;
++ }
++
++ if (res == HOSTAPD_ACL_PENDING) {
++ wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
++ " waiting for an external authentication",
++ MAC2STR(addr));
++ /* Authentication code will re-send the authentication frame
++ * after it has received (and cached) information from the
++ * external source. */
++ return HOSTAPD_ACL_PENDING;
++ }
++
++ return res;
++}
++
++
++static int
++ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
++ int res, u32 session_timeout,
++ u32 acct_interim_interval,
++ struct vlan_description *vlan_id,
++ struct hostapd_sta_wpa_psk_short **psk,
++ char **identity, char **radius_cui)
++{
++ if (vlan_id->notempty &&
++ !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
++ HOSTAPD_LEVEL_INFO,
++ "Invalid VLAN %d%s received from RADIUS server",
++ vlan_id->untagged,
++ vlan_id->tagged[0] ? "+" : "");
++ return -1;
++ }
++ if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
++ return -1;
++ if (sta->vlan_id)
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
++ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
++
++ hostapd_free_psk_list(sta->psk);
++ if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
++ sta->psk = *psk;
++ *psk = NULL;
++ } else {
++ sta->psk = NULL;
++ }
++
++ os_free(sta->identity);
++ sta->identity = *identity;
++ *identity = NULL;
++
++ os_free(sta->radius_cui);
++ sta->radius_cui = *radius_cui;
++ *radius_cui = NULL;
++
++ if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
++ sta->acct_interim_interval = acct_interim_interval;
++ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
++ sta->session_timeout_set = 1;
++ os_get_reltime(&sta->session_timeout);
++ sta->session_timeout.sec += session_timeout;
++ ap_sta_session_timeout(hapd, sta, session_timeout);
++ } else {
++ sta->session_timeout_set = 0;
++ ap_sta_no_session_timeout(hapd, sta);
++ }
++
++ return 0;
++}
++
++
+ static void handle_auth(struct hostapd_data *hapd,
+- const struct ieee80211_mgmt *mgmt, size_t len)
++ const struct ieee80211_mgmt *mgmt, size_t len,
++ int rssi, int from_queue)
+ {
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+- int res;
++ int res, reply_res;
+ u16 fc;
+ const u8 *challenge = NULL;
+ u32 session_timeout, acct_interim_interval;
+- int vlan_id = 0;
++ struct vlan_description vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+ size_t resp_ies_len = 0;
+@@ -924,11 +2071,12 @@
+
+ wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+ "auth_transaction=%d status_code=%d wep=%d%s "
+- "seq_ctrl=0x%x%s",
++ "seq_ctrl=0x%x%s%s",
+ MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ status_code, !!(fc & WLAN_FC_ISWEP),
+ challenge ? " challenge" : "",
+- seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
++ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
++ from_queue ? " (from queue)" : "");
+
+ #ifdef CONFIG_NO_RC4
+ if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+@@ -941,20 +2089,29 @@
+ #endif /* CONFIG_NO_RC4 */
+
+ if (hapd->tkip_countermeasures) {
+- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
++ wpa_printf(MSG_DEBUG,
++ "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+ auth_alg == WLAN_AUTH_OPEN) ||
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_FT) ||
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_SAE
+ (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_SAE) ||
+ #endif /* CONFIG_SAE */
++#ifdef CONFIG_FILS
++ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
++ auth_alg == WLAN_AUTH_FILS_SK) ||
++ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
++ hapd->conf->fils_dh_group &&
++ auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
++#endif /* CONFIG_FILS */
+ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+ auth_alg == WLAN_AUTH_SHARED_KEY))) {
+ wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+@@ -1033,29 +2190,40 @@
+ }
+ }
+
+- res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
+- &session_timeout,
+- &acct_interim_interval, &vlan_id,
+- &psk, &identity, &radius_cui);
+-
++ res = ieee802_11_allowed_address(
++ hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout,
++ &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui,
++ 0);
+ if (res == HOSTAPD_ACL_REJECT) {
+- wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+- MAC2STR(mgmt->sa));
++ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
++ "Ignore Authentication frame from " MACSTR
++ " due to ACL reject", MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+- if (res == HOSTAPD_ACL_PENDING) {
+- wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+- " waiting for an external authentication",
+- MAC2STR(mgmt->sa));
+- /* Authentication code will re-send the authentication frame
+- * after it has received (and cached) information from the
+- * external source. */
++ if (res == HOSTAPD_ACL_PENDING)
+ return;
++
++#ifdef CONFIG_SAE
++ if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
++ (auth_transaction == 1 ||
++ (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
++ /* Handle SAE Authentication commit message through a queue to
++ * provide more control for postponing the needed heavy
++ * processing under a possible DoS attack scenario. In addition,
++ * queue SAE Authentication confirm message if there happens to
++ * be a queued commit message from the same peer. This is needed
++ * to avoid reordering Authentication frames within the same
++ * SAE exchange. */
++ auth_sae_queue(hapd, mgmt, len, rssi);
++ return;
+ }
++#endif /* CONFIG_SAE */
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta) {
++ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
++ sta->ft_over_ds = 0;
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+@@ -1067,6 +2235,15 @@
+ seq_ctrl);
+ return;
+ }
++#ifdef CONFIG_MESH
++ if ((hapd->conf->mesh & MESH_ENABLED) &&
++ sta->plink_state == PLINK_BLOCKED) {
++ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
++ " is blocked - drop Authentication frame",
++ MAC2STR(mgmt->sa));
++ return;
++ }
++#endif /* CONFIG_MESH */
+ } else {
+ #ifdef CONFIG_MESH
+ if (hapd->conf->mesh & MESH_ENABLED) {
+@@ -1073,7 +2250,7 @@
+ /* if the mesh peer is not available, we don't do auth.
+ */
+ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+- " not yet known - drop Authentiation frame",
++ " not yet known - drop Authentication frame",
+ MAC2STR(mgmt->sa));
+ /*
+ * Save a copy of the frame so that it can be processed
+@@ -1088,6 +2265,7 @@
+
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
++ wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+@@ -1094,44 +2272,70 @@
+ }
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = WLAN_FC_STYPE_AUTH;
++#ifdef CONFIG_MBO
++ sta->auth_rssi = rssi;
++#endif /* CONFIG_MBO */
+
+- if (vlan_id > 0) {
+- if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
+- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+- HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+- "%d received from RADIUS server",
+- vlan_id);
+- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+- goto fail;
+- }
+- sta->vlan_id = vlan_id;
+- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+- HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
++ res = ieee802_11_set_radius_info(
++ hapd, sta, res, session_timeout, acct_interim_interval,
++ &vlan_id, &psk, &identity, &radius_cui);
++ if (res) {
++ wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
+ }
+
+- hostapd_free_psk_list(sta->psk);
+- if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+- sta->psk = psk;
+- psk = NULL;
+- } else {
+- sta->psk = NULL;
+- }
+-
+- sta->identity = identity;
+- identity = NULL;
+- sta->radius_cui = radius_cui;
+- radius_cui = NULL;
+-
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+- if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+- sta->acct_interim_interval = acct_interim_interval;
+- if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+- ap_sta_session_timeout(hapd, sta, session_timeout);
+- else
+- ap_sta_no_session_timeout(hapd, sta);
++ /*
++ * If the driver supports full AP client state, add a station to the
++ * driver before sending authentication reply to make sure the driver
++ * has resources, and not to go through the entire authentication and
++ * association handshake, and fail it at the end.
++ *
++ * If this is not the first transaction, in a multi-step authentication
++ * algorithm, the station already exists in the driver
++ * (sta->added_unassoc = 1) so skip it.
++ *
++ * In mesh mode, the station was already added to the driver when the
++ * NEW_PEER_CANDIDATE event is received.
++ *
++ * If PMF was negotiated for the existing association, skip this to
++ * avoid dropping the STA entry and the associated keys. This is needed
++ * to allow the original connection work until the attempt can complete
++ * (re)association, so that unprotected Authentication frame cannot be
++ * used to bypass PMF protection.
++ */
++ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
++ (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
++ !(hapd->conf->mesh & MESH_ENABLED) &&
++ !(sta->added_unassoc)) {
++ /*
++ * If a station that is already associated to the AP, is trying
++ * to authenticate again, remove the STA entry, in order to make
++ * sure the STA PS state gets cleared and configuration gets
++ * updated. To handle this, station's added_unassoc flag is
++ * cleared once the station has completed association.
++ */
++ ap_sta_set_authorized(hapd, sta, 0);
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
++ WLAN_STA_AUTHORIZED);
+
++ if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0,
++ NULL, NULL, sta->flags, 0, 0, 0, 0)) {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_NOTICE,
++ "Could not add STA to kernel driver");
++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++ goto fail;
++ }
++
++ sta->added_unassoc = 1;
++ }
++
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+@@ -1146,6 +2350,9 @@
+ case WLAN_AUTH_SHARED_KEY:
+ resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+ fc & WLAN_FC_ISWEP);
++ if (resp != 0)
++ wpa_printf(MSG_DEBUG,
++ "auth_shared_key() failed: status=%d", resp);
+ sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+ mlme_authenticate_indication(hapd, sta);
+ if (sta->challenge && auth_transaction == 1) {
+@@ -1157,7 +2364,7 @@
+ }
+ break;
+ #endif /* CONFIG_NO_RC4 */
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ case WLAN_AUTH_FT:
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+@@ -1176,7 +2383,7 @@
+ handle_auth_ft_finish, hapd);
+ /* handle_auth_ft_finish() callback will complete auth. */
+ return;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_SAE
+ case WLAN_AUTH_SAE:
+ #ifdef CONFIG_MESH
+@@ -1198,6 +2405,15 @@
+ status_code);
+ return;
+ #endif /* CONFIG_SAE */
++#ifdef CONFIG_FILS
++ case WLAN_AUTH_FILS_SK:
++ case WLAN_AUTH_FILS_SK_PFS:
++ handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
++ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
++ auth_alg, auth_transaction, status_code,
++ handle_auth_fils_finish);
++ return;
++#endif /* CONFIG_FILS */
+ }
+
+ fail:
+@@ -1205,12 +2421,19 @@
+ os_free(radius_cui);
+ hostapd_free_psk_list(psk);
+
+- send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+- auth_transaction + 1, resp, resp_ies, resp_ies_len);
++ reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
++ auth_transaction + 1, resp, resp_ies,
++ resp_ies_len, "handle-auth");
++
++ if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
++ reply_res != WLAN_STATUS_SUCCESS)) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
+ }
+
+
+-static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
++int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ int i, j = 32, aid;
+
+@@ -1220,6 +2443,9 @@
+ return 0;
+ }
+
++ if (TEST_FAIL())
++ return -1;
++
+ for (i = 0; i < AID_WORDS; i++) {
+ if (hapd->sta_aid[i] == (u32) -1)
+ continue;
+@@ -1286,10 +2512,68 @@
+ return WLAN_STATUS_SUCCESS;
+ }
+
++static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *multi_ap_ie, size_t multi_ap_len)
++{
++ u8 multi_ap_value = 0;
+
++ sta->flags &= ~WLAN_STA_MULTI_AP;
++
++ if (!hapd->conf->multi_ap)
++ return WLAN_STATUS_SUCCESS;
++
++ if (multi_ap_ie) {
++ const u8 *multi_ap_subelem;
++
++ multi_ap_subelem = get_ie(multi_ap_ie + 4,
++ multi_ap_len - 4,
++ MULTI_AP_SUB_ELEM_TYPE);
++ if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
++ multi_ap_value = multi_ap_subelem[2];
++ } else {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Multi-AP IE has missing or invalid Multi-AP subelement");
++ return WLAN_STATUS_INVALID_IE;
++ }
++ }
++
++ if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Multi-AP IE with unexpected value 0x%02x",
++ multi_ap_value);
++
++ if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
++ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
++ return WLAN_STATUS_SUCCESS;
++
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
++ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
++ }
++
++ if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "Backhaul STA tries to associate with fronthaul-only BSS");
++
++ sta->flags |= WLAN_STA_MULTI_AP;
++ return WLAN_STATUS_SUCCESS;
++}
++
++
+ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+ {
++ /* Supported rates not used in IEEE 802.11ad/DMG */
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
++ return WLAN_STATUS_SUCCESS;
++
+ if (!elems->supp_rates) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -1327,10 +2611,188 @@
+ }
+ #endif /* CONFIG_INTERWORKING */
+
++ if (ext_capab_ie_len > 0) {
++ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
++ os_free(sta->ext_capability);
++ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
++ if (sta->ext_capability) {
++ sta->ext_capability[0] = ext_capab_ie_len;
++ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
++ ext_capab_ie_len);
++ }
++ }
++
+ return WLAN_STATUS_SUCCESS;
+ }
+
+
++#ifdef CONFIG_OWE
++
++static int owe_group_supported(struct hostapd_data *hapd, u16 group)
++{
++ int i;
++ int *groups = hapd->conf->owe_groups;
++
++ if (group != 19 && group != 20 && group != 21)
++ return 0;
++
++ if (!groups)
++ return 1;
++
++ for (i = 0; groups[i] > 0; i++) {
++ if (groups[i] == group)
++ return 1;
++ }
++
++ return 0;
++}
++
++
++static u16 owe_process_assoc_req(struct hostapd_data *hapd,
++ struct sta_info *sta, const u8 *owe_dh,
++ u8 owe_dh_len)
++{
++ struct wpabuf *secret, *pub, *hkey;
++ int res;
++ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
++ const char *info = "OWE Key Generation";
++ const u8 *addr[2];
++ size_t len[2];
++ u16 group;
++ size_t hash_len, prime_len;
++
++ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
++ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
++ return WLAN_STATUS_SUCCESS;
++ }
++
++ group = WPA_GET_LE16(owe_dh);
++ if (!owe_group_supported(hapd, group)) {
++ wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++ }
++ if (group == 19)
++ prime_len = 32;
++ else if (group == 20)
++ prime_len = 48;
++ else if (group == 21)
++ prime_len = 66;
++ else
++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++
++ crypto_ecdh_deinit(sta->owe_ecdh);
++ sta->owe_ecdh = crypto_ecdh_init(group);
++ if (!sta->owe_ecdh)
++ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
++ sta->owe_group = group;
++
++ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
++ owe_dh_len - 2);
++ secret = wpabuf_zeropad(secret, prime_len);
++ if (!secret) {
++ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
++
++ /* prk = HKDF-extract(C | A | group, z) */
++
++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
++ if (!pub) {
++ wpabuf_clear_free(secret);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ /* PMKID = Truncate-128(Hash(C | A)) */
++ addr[0] = owe_dh + 2;
++ len[0] = owe_dh_len - 2;
++ addr[1] = wpabuf_head(pub);
++ len[1] = wpabuf_len(pub);
++ if (group == 19) {
++ res = sha256_vector(2, addr, len, pmkid);
++ hash_len = SHA256_MAC_LEN;
++ } else if (group == 20) {
++ res = sha384_vector(2, addr, len, pmkid);
++ hash_len = SHA384_MAC_LEN;
++ } else if (group == 21) {
++ res = sha512_vector(2, addr, len, pmkid);
++ hash_len = SHA512_MAC_LEN;
++ } else {
++ wpabuf_free(pub);
++ wpabuf_clear_free(secret);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ pub = wpabuf_zeropad(pub, prime_len);
++ if (res < 0 || !pub) {
++ wpabuf_free(pub);
++ wpabuf_clear_free(secret);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
++ if (!hkey) {
++ wpabuf_free(pub);
++ wpabuf_clear_free(secret);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
++ wpabuf_put_buf(hkey, pub); /* A */
++ wpabuf_free(pub);
++ wpabuf_put_le16(hkey, group); /* group */
++ if (group == 19)
++ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
++ wpabuf_head(secret), wpabuf_len(secret), prk);
++ else if (group == 20)
++ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
++ wpabuf_head(secret), wpabuf_len(secret), prk);
++ else if (group == 21)
++ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
++ wpabuf_head(secret), wpabuf_len(secret), prk);
++ wpabuf_clear_free(hkey);
++ wpabuf_clear_free(secret);
++ if (res < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++
++ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
++
++ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
++
++ os_free(sta->owe_pmk);
++ sta->owe_pmk = os_malloc(hash_len);
++ if (!sta->owe_pmk) {
++ os_memset(prk, 0, SHA512_MAC_LEN);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ if (group == 19)
++ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
++ os_strlen(info), sta->owe_pmk, hash_len);
++ else if (group == 20)
++ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
++ os_strlen(info), sta->owe_pmk, hash_len);
++ else if (group == 21)
++ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
++ os_strlen(info), sta->owe_pmk, hash_len);
++ os_memset(prk, 0, SHA512_MAC_LEN);
++ if (res < 0) {
++ os_free(sta->owe_pmk);
++ sta->owe_pmk = NULL;
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ sta->owe_pmk_len = hash_len;
++
++ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
++ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
++ wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
++ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
++
++ return WLAN_STATUS_SUCCESS;
++}
++
++#endif /* CONFIG_OWE */
++
++
+ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ies, size_t ies_len, int reassoc)
+ {
+@@ -1359,6 +2821,11 @@
+ resp = copy_supp_rates(hapd, sta, &elems);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
++
++ resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
++ if (resp != WLAN_STATUS_SUCCESS)
++ return resp;
++
+ #ifdef CONFIG_IEEE80211N
+ resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
+ if (resp != WLAN_STATUS_SUCCESS)
+@@ -1378,6 +2845,10 @@
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
++ resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation);
++ if (resp != WLAN_STATUS_SUCCESS)
++ return resp;
++
+ resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+@@ -1470,34 +2941,24 @@
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
++ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
++ hapd->iface->freq,
+ wpa_ie, wpa_ie_len,
+- elems.mdie, elems.mdie_len);
+- if (res == WPA_INVALID_GROUP)
+- resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+- else if (res == WPA_INVALID_PAIRWISE)
+- resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+- else if (res == WPA_INVALID_AKMP)
+- resp = WLAN_STATUS_AKMP_NOT_VALID;
+- else if (res == WPA_ALLOC_FAIL)
+- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+-#ifdef CONFIG_IEEE80211W
+- else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+- else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+-#endif /* CONFIG_IEEE80211W */
+- else if (res == WPA_INVALID_MDIE)
+- resp = WLAN_STATUS_INVALID_MDIE;
+- else if (res != WPA_IE_OK)
+- resp = WLAN_STATUS_INVALID_IE;
++ elems.mdie, elems.mdie_len,
++ elems.owe_dh, elems.owe_dh_len);
++ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ #ifdef CONFIG_IEEE80211W
+- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
++ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
++ !sta->sa_query_timed_out &&
+ sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
++ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
++ !sta->sa_query_timed_out &&
+ (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA
+@@ -1518,7 +2979,7 @@
+ sta->flags &= ~WLAN_STA_MFP;
+ #endif /* CONFIG_IEEE80211W */
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ if (!reassoc) {
+ wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+@@ -1533,9 +2994,13 @@
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #ifdef CONFIG_SAE
++ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
++ sta->sae->state == SAE_ACCEPTED)
++ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
++
+ if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sta->auth_alg == WLAN_AUTH_OPEN) {
+ struct rsn_pmksa_cache_entry *sa;
+@@ -1559,6 +3024,48 @@
+ }
+ #endif /* CONFIG_SAE */
+
++#ifdef CONFIG_OWE
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
++ elems.owe_dh) {
++ resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
++ elems.owe_dh_len);
++ if (resp != WLAN_STATUS_SUCCESS)
++ return resp;
++ }
++#endif /* CONFIG_OWE */
++
++#ifdef CONFIG_DPP2
++ dpp_pfs_free(sta->dpp_pfs);
++ sta->dpp_pfs = NULL;
++
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
++ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
++ elems.owe_dh) {
++ sta->dpp_pfs = dpp_pfs_init(
++ wpabuf_head(hapd->conf->dpp_netaccesskey),
++ wpabuf_len(hapd->conf->dpp_netaccesskey));
++ if (!sta->dpp_pfs) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not initialize PFS");
++ /* Try to continue without PFS */
++ goto pfs_fail;
++ }
++
++ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
++ elems.owe_dh_len) < 0) {
++ dpp_pfs_free(sta->dpp_pfs);
++ sta->dpp_pfs = NULL;
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ }
++
++ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
++ sta->dpp_pfs->secret : NULL);
++ pfs_fail:
++#endif /* CONFIG_DPP2 */
++
+ #ifdef CONFIG_IEEE80211N
+ if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
+ wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+@@ -1603,10 +3110,28 @@
+ #ifdef CONFIG_HS20
+ wpabuf_free(sta->hs20_ie);
+ if (elems.hs20 && elems.hs20_len > 4) {
++ int release;
++
+ sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+ elems.hs20_len - 4);
+- } else
++ release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
++ if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) {
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: PMF not negotiated by release %d station "
++ MACSTR, release, MAC2STR(sta->addr));
++ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
++ }
++ } else {
+ sta->hs20_ie = NULL;
++ }
++
++ wpabuf_free(sta->roaming_consortium);
++ if (elems.roaming_cons_sel)
++ sta->roaming_consortium = wpabuf_alloc_copy(
++ elems.roaming_cons_sel + 4,
++ elems.roaming_cons_sel_len - 4);
++ else
++ sta->roaming_consortium = NULL;
+ #endif /* CONFIG_HS20 */
+
+ #ifdef CONFIG_FST
+@@ -1617,6 +3142,64 @@
+ sta->mb_ies = NULL;
+ #endif /* CONFIG_FST */
+
++#ifdef CONFIG_MBO
++ mbo_ap_check_sta_assoc(hapd, sta, &elems);
++
++ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
++ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
++ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
++ wpa_printf(MSG_INFO,
++ "MBO: Reject WPA2 association without PMF");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++#endif /* CONFIG_MBO */
++
++#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
++ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
++ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK)) {
++ struct wpa_channel_info ci;
++ int tx_chanwidth;
++ int tx_seg1_idx;
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ if (get_sta_tx_parameters(sta->wpa_sm,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx, &tx_chanwidth,
++ &tx_seg1_idx) < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++
++ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
++ tx_chanwidth, tx_seg1_idx) != 0) {
++ wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ }
++#endif /* CONFIG_FILS && CONFIG_OCV */
++
++ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
++ elems.supp_op_classes_len);
++
++ if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
++ elems.rrm_enabled &&
++ elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
++ os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
++ sizeof(sta->rrm_enabled_capa));
++
++ if (elems.power_capab) {
++ sta->min_tx_power = elems.power_capab[0];
++ sta->max_tx_power = elems.power_capab[1];
++ sta->power_capab = 1;
++ } else {
++ sta->power_capab = 0;
++ }
++
+ return WLAN_STATUS_SUCCESS;
+ }
+
+@@ -1643,22 +3226,131 @@
+ }
+
+
+-static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+- u16 status_code, int reassoc, const u8 *ies,
+- size_t ies_len)
++static int add_associated_sta(struct hostapd_data *hapd,
++ struct sta_info *sta, int reassoc)
+ {
++ struct ieee80211_ht_capabilities ht_cap;
++ struct ieee80211_vht_capabilities vht_cap;
++ int set = 1;
++
++ /*
++ * Remove the STA entry to ensure the STA PS state gets cleared and
++ * configuration gets updated. This is relevant for cases, such as
++ * FT-over-the-DS, where a station re-associates back to the same AP but
++ * skips the authentication flow, or if working with a driver that
++ * does not support full AP client state.
++ *
++ * Skip this if the STA has already completed FT reassociation and the
++ * TK has been configured since the TX/RX PN must not be reset to 0 for
++ * the same key.
++ *
++ * FT-over-the-DS has a special case where the STA entry (and as such,
++ * the TK) has not yet been configured to the driver depending on which
++ * driver interface is used. For that case, allow add-STA operation to
++ * be used (instead of set-STA). This is needed to allow mac80211-based
++ * drivers to accept the STA parameter configuration. Since this is
++ * after a new FT-over-DS exchange, a new TK has been derived, so key
++ * reinstallation is not a concern for this case.
++ */
++ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
++ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
++ MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
++ sta->ft_over_ds, reassoc,
++ !!(sta->flags & WLAN_STA_AUTHORIZED),
++ wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
++ wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
++
++ if (!sta->added_unassoc &&
++ (!(sta->flags & WLAN_STA_AUTHORIZED) ||
++ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
++ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
++ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
++ set = 0;
++
++ /* Do not allow the FT-over-DS exception to be used more than
++ * once per authentication exchange to guarantee a new TK is
++ * used here */
++ sta->ft_over_ds = 0;
++ }
++
++#ifdef CONFIG_IEEE80211N
++ if (sta->flags & WLAN_STA_HT)
++ hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
++#endif /* CONFIG_IEEE80211N */
++#ifdef CONFIG_IEEE80211AC
++ if (sta->flags & WLAN_STA_VHT)
++ hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
++#endif /* CONFIG_IEEE80211AC */
++
++ /*
++ * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
++ * will be set when the ACK frame for the (Re)Association Response frame
++ * is processed (TX status driver event).
++ */
++ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
++ sta->supported_rates, sta->supported_rates_len,
++ sta->listen_interval,
++ sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
++ sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
++ sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
++ sta->vht_opmode, sta->p2p_ie ? 1 : 0,
++ set)) {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
++ "Could not %s STA to kernel driver",
++ set ? "set" : "add");
++
++ if (sta->added_unassoc) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
++
++ return -1;
++ }
++
++ sta->added_unassoc = 0;
++
++ return 0;
++}
++
++
++static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *addr, u16 status_code, int reassoc,
++ const u8 *ies, size_t ies_len, int rssi)
++{
+ int send_len;
+- u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
++ u8 *buf;
++ size_t buflen;
+ struct ieee80211_mgmt *reply;
+ u8 *p;
++ u16 res = WLAN_STATUS_SUCCESS;
+
+- os_memset(buf, 0, sizeof(buf));
++ buflen = sizeof(struct ieee80211_mgmt) + 1024;
++#ifdef CONFIG_FILS
++ if (sta && sta->fils_hlp_resp)
++ buflen += wpabuf_len(sta->fils_hlp_resp);
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_OWE
++ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
++ buflen += 150;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP2
++ if (sta && sta->dpp_pfs)
++ buflen += 5 + sta->dpp_pfs->curve->prime_len;
++#endif /* CONFIG_DPP2 */
++ buf = os_zalloc(buflen);
++ if (!buf) {
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto done;
++ }
+ reply = (struct ieee80211_mgmt *) buf;
+ reply->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+ WLAN_FC_STYPE_ASSOC_RESP));
+- os_memcpy(reply->da, sta->addr, ETH_ALEN);
++ os_memcpy(reply->da, addr, ETH_ALEN);
+ os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
+
+@@ -1667,24 +3359,50 @@
+ reply->u.assoc_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+ reply->u.assoc_resp.status_code = host_to_le16(status_code);
+- reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
++
++ reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
++ BIT(14) | BIT(15));
+ /* Supported rates */
+ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+ /* Extended supported rates */
+ p = hostapd_eid_ext_supp_rates(hapd, p);
+
+-#ifdef CONFIG_IEEE80211R
+- if (status_code == WLAN_STATUS_SUCCESS) {
++#ifdef CONFIG_MBO
++ if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
++ rssi != 0) {
++ int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
++
++ p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
++ delta);
++ }
++#endif /* CONFIG_MBO */
++
++#ifdef CONFIG_IEEE80211R_AP
++ if (sta && status_code == WLAN_STATUS_SUCCESS) {
+ /* IEEE 802.11r: Mobility Domain Information, Fast BSS
+ * Transition Information, RSN, [RIC Response] */
+ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+- buf + sizeof(buf) - p,
++ buf + buflen - p,
+ sta->auth_alg, ies, ies_len);
++ if (!p) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Failed to write AssocResp IEs");
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto done;
++ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
++#ifdef CONFIG_OWE
++ if (sta && status_code == WLAN_STATUS_SUCCESS &&
++ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
++ p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
++ buf + buflen - p,
++ ies, ies_len);
++#endif /* CONFIG_OWE */
++
+ #ifdef CONFIG_IEEE80211W
+- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
++ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+ p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+ #endif /* CONFIG_IEEE80211W */
+
+@@ -1695,7 +3413,23 @@
+
+ #ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+- p = hostapd_eid_vht_capabilities(hapd, p);
++ u32 nsts = 0, sta_nsts;
++
++ if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
++ struct ieee80211_vht_capabilities *capa;
++
++ nsts = (hapd->iface->conf->vht_capab >>
++ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
++ capa = sta->vht_capabilities;
++ sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
++ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
++
++ if (nsts < sta_nsts)
++ nsts = 0;
++ else
++ nsts = sta_nsts;
++ }
++ p = hostapd_eid_vht_capabilities(hapd, p, nsts);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
+ #endif /* CONFIG_IEEE80211AC */
+@@ -1702,7 +3436,7 @@
+
+ p = hostapd_eid_ext_capab(hapd, p);
+ p = hostapd_eid_bss_max_idle_period(hapd, p);
+- if (sta->qos_map_enabled)
++ if (sta && sta->qos_map_enabled)
+ p = hostapd_eid_qos_map_set(hapd, p);
+
+ #ifdef CONFIG_FST
+@@ -1713,17 +3447,51 @@
+ }
+ #endif /* CONFIG_FST */
+
++#ifdef CONFIG_OWE
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
++ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
++ struct wpabuf *pub;
++
++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
++ if (!pub) {
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto done;
++ }
++ /* OWE Diffie-Hellman Parameter element */
++ *p++ = WLAN_EID_EXTENSION; /* Element ID */
++ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
++ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
++ WPA_PUT_LE16(p, sta->owe_group);
++ p += 2;
++ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
++ p += wpabuf_len(pub);
++ wpabuf_free(pub);
++ }
++#endif /* CONFIG_OWE */
++
++#ifdef CONFIG_DPP2
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
++ sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
++ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
++ os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
++ wpabuf_len(sta->dpp_pfs->ie));
++ p += wpabuf_len(sta->dpp_pfs->ie);
++ }
++#endif /* CONFIG_DPP2 */
++
+ #ifdef CONFIG_IEEE80211AC
+- if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
++ if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ p = hostapd_eid_vendor_vht(hapd, p);
+ #endif /* CONFIG_IEEE80211AC */
+
+- if (sta->flags & WLAN_STA_WMM)
++ if (sta && (sta->flags & WLAN_STA_WMM))
+ p = hostapd_eid_wmm(hapd, p);
+
+ #ifdef CONFIG_WPS
+- if ((sta->flags & WLAN_STA_WPS) ||
+- ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
++ if (sta &&
++ ((sta->flags & WLAN_STA_WPS) ||
++ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
+ struct wpabuf *wps = wps_build_assoc_resp_ie();
+ if (wps) {
+ os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+@@ -1733,8 +3501,11 @@
+ }
+ #endif /* CONFIG_WPS */
+
++ if (sta && (sta->flags & WLAN_STA_MULTI_AP))
++ p = hostapd_eid_multi_ap(hapd, p);
++
+ #ifdef CONFIG_P2P
+- if (sta->p2p_ie) {
++ if (sta && sta->p2p_ie && hapd->p2p_group) {
+ struct wpabuf *p2p_resp_ie;
+ enum p2p_status_code status;
+ switch (status_code) {
+@@ -1763,23 +3534,180 @@
+ p = hostapd_eid_p2p_manage(hapd, p);
+ #endif /* CONFIG_P2P_MANAGER */
+
++ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
++
++ if (hapd->conf->assocresp_elements &&
++ (size_t) (buf + buflen - p) >=
++ wpabuf_len(hapd->conf->assocresp_elements)) {
++ os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
++ wpabuf_len(hapd->conf->assocresp_elements));
++ p += wpabuf_len(hapd->conf->assocresp_elements);
++ }
++
+ send_len += p - reply->u.assoc_resp.variable;
+
+- if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
++#ifdef CONFIG_FILS
++ if (sta &&
++ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
++ status_code == WLAN_STATUS_SUCCESS) {
++ struct ieee802_11_elems elems;
++
++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
++ ParseFailed || !elems.fils_session) {
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto done;
++ }
++
++ /* FILS Session */
++ *p++ = WLAN_EID_EXTENSION; /* Element ID */
++ *p++ = 1 + FILS_SESSION_LEN; /* Length */
++ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
++ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
++ send_len += 2 + 1 + FILS_SESSION_LEN;
++
++ send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
++ buflen, sta->fils_hlp_resp);
++ if (send_len < 0) {
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto done;
++ }
++ }
++#endif /* CONFIG_FILS */
++
++ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
+ wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+ strerror(errno));
++ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++done:
++ os_free(buf);
++ return res;
+ }
+
+
++#ifdef CONFIG_OWE
++u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *owe_dh, u8 owe_dh_len,
++ u8 *owe_buf, size_t owe_buf_len, u16 *reason)
++{
++#ifdef CONFIG_TESTING_OPTIONS
++ if (hapd->conf->own_ie_override) {
++ wpa_printf(MSG_DEBUG, "OWE: Using IE override");
++ *reason = WLAN_STATUS_SUCCESS;
++ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
++ owe_buf_len, NULL, 0);
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
++ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
++ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
++ owe_buf_len, NULL, 0);
++ *reason = WLAN_STATUS_SUCCESS;
++ return owe_buf;
++ }
++
++ *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
++ if (*reason != WLAN_STATUS_SUCCESS)
++ return NULL;
++
++ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
++ owe_buf_len, NULL, 0);
++
++ if (sta->owe_ecdh && owe_buf) {
++ struct wpabuf *pub;
++
++ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
++ if (!pub) {
++ *reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ return owe_buf;
++ }
++
++ /* OWE Diffie-Hellman Parameter element */
++ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
++ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
++ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
++ */
++ WPA_PUT_LE16(owe_buf, sta->owe_group);
++ owe_buf += 2;
++ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
++ owe_buf += wpabuf_len(pub);
++ wpabuf_free(pub);
++ }
++
++ return owe_buf;
++}
++#endif /* CONFIG_OWE */
++
++
++#ifdef CONFIG_FILS
++
++void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
++{
++ u16 reply_res;
++
++ wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
++ MAC2STR(sta->addr));
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++ if (!sta->fils_pending_assoc_req)
++ return;
++ reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
++ sta->fils_pending_assoc_is_reassoc,
++ sta->fils_pending_assoc_req,
++ sta->fils_pending_assoc_req_len, 0);
++ os_free(sta->fils_pending_assoc_req);
++ sta->fils_pending_assoc_req = NULL;
++ sta->fils_pending_assoc_req_len = 0;
++ wpabuf_free(sta->fils_hlp_resp);
++ sta->fils_hlp_resp = NULL;
++ wpabuf_free(sta->hlp_dhcp_discover);
++ sta->hlp_dhcp_discover = NULL;
++
++ /*
++ * Remove the station in case transmission of a success response fails.
++ * At this point the station was already added associated to the driver.
++ */
++ if (reply_res != WLAN_STATUS_SUCCESS)
++ hostapd_drv_sta_remove(hapd, sta->addr);
++}
++
++
++void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct sta_info *sta = eloop_data;
++
++ wpa_printf(MSG_DEBUG,
++ "FILS: HLP response timeout - continue with association response for "
++ MACSTR, MAC2STR(sta->addr));
++ if (sta->fils_drv_assoc_finish)
++ hostapd_notify_assoc_fils_finish(hapd, sta);
++ else
++ fils_hlp_finish_assoc(hapd, sta);
++}
++
++#endif /* CONFIG_FILS */
++
++
+ static void handle_assoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+- int reassoc)
++ int reassoc, int rssi)
+ {
+ u16 capab_info, listen_interval, seq_ctrl, fc;
+- u16 resp = WLAN_STATUS_SUCCESS;
++ u16 resp = WLAN_STATUS_SUCCESS, reply_res;
+ const u8 *pos;
+ int left, i;
+ struct sta_info *sta;
++ u8 *tmp = NULL;
++ struct hostapd_sta_wpa_psk_short *psk = NULL;
++ char *identity = NULL;
++ char *radius_cui = NULL;
++#ifdef CONFIG_FILS
++ int delay_assoc = 0;
++#endif /* CONFIG_FILS */
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+@@ -1837,31 +3765,89 @@
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+ (sta->flags & WLAN_STA_AUTH) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+ "prior to authentication since it is using "
+ "over-the-DS FT", MAC2STR(mgmt->sa));
++
++ /*
++ * Mark station as authenticated, to avoid adding station
++ * entry in the driver as associated and not authenticated
++ */
++ sta->flags |= WLAN_STA_AUTH;
+ } else
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_INFO, "Station tried to "
+- "associate before authentication "
+- "(aid=%d flags=0x%x)",
+- sta ? sta->aid : -1,
+- sta ? sta->flags : 0);
+- send_deauth(hapd, mgmt->sa,
+- WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+- return;
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode ==
++ HOSTAPD_MODE_IEEE80211AD) {
++ int acl_res;
++ u32 session_timeout, acct_interim_interval;
++ struct vlan_description vlan_id;
++
++ acl_res = ieee802_11_allowed_address(
++ hapd, mgmt->sa, (const u8 *) mgmt, len,
++ &session_timeout, &acct_interim_interval,
++ &vlan_id, &psk, &identity, &radius_cui, 0);
++ if (acl_res == HOSTAPD_ACL_REJECT) {
++ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
++ "Ignore Association Request frame from "
++ MACSTR " due to ACL reject",
++ MAC2STR(mgmt->sa));
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ if (acl_res == HOSTAPD_ACL_PENDING)
++ return;
++
++ /* DMG/IEEE 802.11ad does not use authentication.
++ * Allocate sta entry upon association. */
++ sta = ap_sta_add(hapd, mgmt->sa);
++ if (!sta) {
++ hostapd_logger(hapd, mgmt->sa,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Failed to add STA");
++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++ goto fail;
++ }
++
++ acl_res = ieee802_11_set_radius_info(
++ hapd, sta, acl_res, session_timeout,
++ acct_interim_interval, &vlan_id, &psk,
++ &identity, &radius_cui);
++ if (acl_res) {
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "Skip authentication for DMG/IEEE 802.11ad");
++ sta->flags |= WLAN_STA_AUTH;
++ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
++ sta->auth_alg = WLAN_AUTH_OPEN;
++ } else {
++ hostapd_logger(hapd, mgmt->sa,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Station tried to associate before authentication (aid=%d flags=0x%x)",
++ sta ? sta->aid : -1,
++ sta ? sta->flags : 0);
++ send_deauth(hapd, mgmt->sa,
++ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
++ return;
++ }
+ }
+
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+- sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+- WLAN_FC_STYPE_ASSOC_REQ) {
++ sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
++ WLAN_FC_STYPE_ASSOC_REQ)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated association frame seq_ctrl=0x%x",
+@@ -1873,7 +3859,7 @@
+ WLAN_FC_STYPE_ASSOC_REQ;
+
+ if (hapd->tkip_countermeasures) {
+- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+@@ -1886,6 +3872,53 @@
+ goto fail;
+ }
+
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++ goto fail;
++ }
++
++ if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
++ rssi < hapd->iconf->rssi_reject_assoc_rssi &&
++ (sta->auth_rssi == 0 ||
++ sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
++ resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
++ goto fail;
++ }
++#endif /* CONFIG_MBO */
++
++ /*
++ * sta->capability is used in check_assoc_ies() for RRM enabled
++ * capability element.
++ */
++ sta->capability = capab_info;
++
++#ifdef CONFIG_FILS
++ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) {
++ int res;
++
++ /* The end of the payload is encrypted. Need to decrypt it
++ * before parsing. */
++
++ tmp = os_memdup(pos, left);
++ if (!tmp) {
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++
++ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
++ len, tmp, left);
++ if (res < 0) {
++ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
++ goto fail;
++ }
++ pos = tmp;
++ left = res;
++ }
++#endif /* CONFIG_FILS */
++
+ /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ * is used */
+ resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+@@ -1899,10 +3932,10 @@
+ goto fail;
+ }
+
+- sta->capability = capab_info;
+ sta->listen_interval = listen_interval;
+
+- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ sta->flags |= WLAN_STA_NONERP;
+ for (i = 0; i < sta->supported_rates_len; i++) {
+ if ((sta->supported_rates[i] & 0x7f) > 22) {
+@@ -1921,7 +3954,8 @@
+ !sta->no_short_slot_time_set) {
+ sta->no_short_slot_time_set = 1;
+ hapd->iface->num_sta_no_short_slot_time++;
+- if (hapd->iface->current_mode->mode ==
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211G &&
+ hapd->iface->num_sta_no_short_slot_time == 1)
+ ieee802_11_set_beacons(hapd->iface);
+@@ -1936,7 +3970,8 @@
+ !sta->no_short_preamble_set) {
+ sta->no_short_preamble_set = 1;
+ hapd->iface->num_sta_no_short_preamble++;
+- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_preamble == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+@@ -1969,8 +4004,99 @@
+ * remove the STA immediately. */
+ sta->timeout_next = STA_NULLFUNC;
+
++#ifdef CONFIG_TAXONOMY
++ taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
++#endif /* CONFIG_TAXONOMY */
++
++ sta->pending_wds_enable = 0;
++
++#ifdef CONFIG_FILS
++ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) {
++ if (fils_process_hlp(hapd, sta, pos, left) > 0)
++ delay_assoc = 1;
++ }
++#endif /* CONFIG_FILS */
++
+ fail:
+- send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
++ os_free(identity);
++ os_free(radius_cui);
++ hostapd_free_psk_list(psk);
++
++ /*
++ * In case of a successful response, add the station to the driver.
++ * Otherwise, the kernel may ignore Data frames before we process the
++ * ACK frame (TX status). In case of a failure, this station will be
++ * removed.
++ *
++ * Note that this is not compliant with the IEEE 802.11 standard that
++ * states that a non-AP station should transition into the
++ * authenticated/associated state only after the station acknowledges
++ * the (Re)Association Response frame. However, still do this as:
++ *
++ * 1. In case the station does not acknowledge the (Re)Association
++ * Response frame, it will be removed.
++ * 2. Data frames will be dropped in the kernel until the station is
++ * set into authorized state, and there are no significant known
++ * issues with processing other non-Data Class 3 frames during this
++ * window.
++ */
++ if (resp == WLAN_STATUS_SUCCESS && sta &&
++ add_associated_sta(hapd, sta, reassoc))
++ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
++
++#ifdef CONFIG_FILS
++ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
++ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
++ sta->fils_pending_assoc_req) {
++ /* Do not reschedule fils_hlp_timeout in case the station
++ * retransmits (Re)Association Request frame while waiting for
++ * the previously started FILS HLP wait, so that the timeout can
++ * be determined from the first pending attempt. */
++ wpa_printf(MSG_DEBUG,
++ "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
++ MACSTR, MAC2STR(sta->addr));
++ os_free(tmp);
++ return;
++ }
++ if (sta) {
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++ os_free(sta->fils_pending_assoc_req);
++ sta->fils_pending_assoc_req = NULL;
++ sta->fils_pending_assoc_req_len = 0;
++ wpabuf_free(sta->fils_hlp_resp);
++ sta->fils_hlp_resp = NULL;
++ }
++ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
++ sta->fils_pending_assoc_req = tmp;
++ sta->fils_pending_assoc_req_len = left;
++ sta->fils_pending_assoc_is_reassoc = reassoc;
++ sta->fils_drv_assoc_finish = 0;
++ wpa_printf(MSG_DEBUG,
++ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
++ MACSTR, MAC2STR(sta->addr));
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++ eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
++ fils_hlp_timeout, hapd, sta);
++ return;
++ }
++#endif /* CONFIG_FILS */
++
++ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
++ left, rssi);
++ os_free(tmp);
++
++ /*
++ * Remove the station in case tranmission of a success response fails
++ * (the STA was added associated to the driver) or if the station was
++ * previously added unassociated.
++ */
++ if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
++ resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
+ }
+
+
+@@ -2007,11 +4133,12 @@
+ /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+ * authenticated. */
+ accounting_sta_stop(hapd, sta);
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
+
+ if (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC) {
+@@ -2023,6 +4150,17 @@
+
+ mlme_disassociate_indication(
+ hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
++
++ /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
++ * disassociation. */
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
++ sta->flags &= ~WLAN_STA_AUTH;
++ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
++ ap_free_sta(hapd, sta);
++ }
+ }
+
+
+@@ -2086,28 +4224,6 @@
+
+
+ #ifdef CONFIG_IEEE80211W
+-
+-static int hostapd_sa_query_action(struct hostapd_data *hapd,
+- const struct ieee80211_mgmt *mgmt,
+- size_t len)
+-{
+- const u8 *end;
+-
+- end = mgmt->u.action.u.sa_query_resp.trans_id +
+- WLAN_SA_QUERY_TR_ID_LEN;
+- if (((u8 *) mgmt) + len < end) {
+- wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
+- "frame (len=%lu)", (unsigned long) len);
+- return 0;
+- }
+-
+- ieee802_11_sa_query_action(hapd, mgmt->sa,
+- mgmt->u.action.u.sa_query_resp.action,
+- mgmt->u.action.u.sa_query_resp.trans_id);
+- return 1;
+-}
+-
+-
+ static int robust_action_frame(u8 category)
+ {
+ return category != WLAN_ACTION_PUBLIC &&
+@@ -2117,12 +4233,13 @@
+
+
+ static int handle_action(struct hostapd_data *hapd,
+- const struct ieee80211_mgmt *mgmt, size_t len)
++ const struct ieee80211_mgmt *mgmt, size_t len,
++ unsigned int freq)
+ {
+ struct sta_info *sta;
+- sta = ap_get_sta(hapd, mgmt->sa);
++ u8 *action __maybe_unused;
+
+- if (len < IEEE80211_HDRLEN + 1) {
++ if (len < IEEE80211_HDRLEN + 2 + 1) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - too short payload (len=%lu)",
+@@ -2130,11 +4247,19 @@
+ return 0;
+ }
+
++ action = (u8 *) &mgmt->u.action.u;
++ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
++ " da " MACSTR " len %d freq %u",
++ mgmt->u.action.category, *action,
++ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
++
++ sta = ap_get_sta(hapd, mgmt->sa);
++
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+ "frame (category=%u) from unassociated STA " MACSTR,
+- MAC2STR(mgmt->sa), mgmt->u.action.category);
++ mgmt->u.action.category, MAC2STR(mgmt->sa));
+ return 0;
+ }
+
+@@ -2171,7 +4296,7 @@
+ }
+
+ switch (mgmt->u.action.category) {
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ case WLAN_ACTION_FT:
+ if (!sta ||
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+@@ -2178,19 +4303,20 @@
+ len - IEEE80211_HDRLEN))
+ break;
+ return 1;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ case WLAN_ACTION_WMM:
+ hostapd_wmm_action(hapd, mgmt, len);
+ return 1;
+ #ifdef CONFIG_IEEE80211W
+ case WLAN_ACTION_SA_QUERY:
+- return hostapd_sa_query_action(hapd, mgmt, len);
++ ieee802_11_sa_query_action(hapd, mgmt, len);
++ return 1;
+ #endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ case WLAN_ACTION_WNM:
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+ return 1;
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ #ifdef CONFIG_FST
+ case WLAN_ACTION_FST:
+ if (hapd->iface->fst)
+@@ -2206,12 +4332,41 @@
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_20_40_BSS_COEX) {
+- wpa_printf(MSG_DEBUG,
+- "HT20/40 coex mgmt frame received from STA "
+- MACSTR, MAC2STR(mgmt->sa));
+ hostapd_2040_coex_action(hapd, mgmt, len);
++ return 1;
+ }
+ #endif /* CONFIG_IEEE80211N */
++#ifdef CONFIG_DPP
++ if (len >= IEEE80211_HDRLEN + 6 &&
++ mgmt->u.action.u.vs_public_action.action ==
++ WLAN_PA_VENDOR_SPECIFIC &&
++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
++ OUI_WFA &&
++ mgmt->u.action.u.vs_public_action.variable[0] ==
++ DPP_OUI_TYPE) {
++ const u8 *pos, *end;
++
++ pos = mgmt->u.action.u.vs_public_action.oui;
++ end = ((const u8 *) mgmt) + len;
++ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
++ freq);
++ return 1;
++ }
++ if (len >= IEEE80211_HDRLEN + 2 &&
++ (mgmt->u.action.u.public_action.action ==
++ WLAN_PA_GAS_INITIAL_RESP ||
++ mgmt->u.action.u.public_action.action ==
++ WLAN_PA_GAS_COMEBACK_RESP)) {
++ const u8 *pos, *end;
++
++ pos = &mgmt->u.action.u.public_action.action;
++ end = ((const u8 *) mgmt) + len;
++ gas_query_ap_rx(hapd->gas, mgmt->sa,
++ mgmt->u.action.category,
++ pos, end - pos, hapd->iface->freq);
++ return 1;
++ }
++#endif /* CONFIG_DPP */
+ if (hapd->public_action_cb) {
+ hapd->public_action_cb(hapd->public_action_cb_ctx,
+ (u8 *) mgmt, len,
+@@ -2233,6 +4388,9 @@
+ return 1;
+ }
+ break;
++ case WLAN_ACTION_RADIO_MEASUREMENT:
++ hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
++ return 1;
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+@@ -2240,8 +4398,9 @@
+ "handle_action - unknown action category %d or invalid "
+ "frame",
+ mgmt->u.action.category);
+- if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) &&
+- !(mgmt->sa[0] & 0x01)) {
++ if (!is_multicast_ether_addr(mgmt->da) &&
++ !(mgmt->u.action.category & 0x80) &&
++ !is_multicast_ether_addr(mgmt->sa)) {
+ struct ieee80211_mgmt *resp;
+
+ /*
+@@ -2251,10 +4410,9 @@
+ */
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
+ "frame back to sender");
+- resp = os_malloc(len);
++ resp = os_memdup(mgmt, len);
+ if (resp == NULL)
+ return 0;
+- os_memcpy(resp, mgmt, len);
+ os_memcpy(resp->da, resp->sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+@@ -2288,13 +4446,19 @@
+ struct hostapd_frame_info *fi)
+ {
+ struct ieee80211_mgmt *mgmt;
+- int broadcast;
+ u16 fc, stype;
+ int ret = 0;
++ unsigned int freq;
++ int ssi_signal = fi ? fi->ssi_signal : 0;
+
+ if (len < 24)
+ return 0;
+
++ if (fi && fi->freq)
++ freq = fi->freq;
++ else
++ freq = hapd->iface->freq;
++
+ mgmt = (struct ieee80211_mgmt *) buf;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+@@ -2304,11 +4468,7 @@
+ return 1;
+ }
+
+- broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
+- mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
+- mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
+-
+- if (!broadcast &&
++ if (!is_broadcast_ether_addr(mgmt->bssid) &&
+ #ifdef CONFIG_P2P
+ /* Invitation responses can be sent with the peer MAC as BSSID */
+ !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+@@ -2325,11 +4485,13 @@
+
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+- handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
++ handle_probe_req(hapd, mgmt, len, ssi_signal);
+ return 1;
+ }
+
+- if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
++ if ((!is_broadcast_ether_addr(mgmt->da) ||
++ stype != WLAN_FC_STYPE_ACTION) &&
++ os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "MGMT: DA=" MACSTR " not our address",
+@@ -2338,22 +4500,22 @@
+ }
+
+ if (hapd->iconf->track_sta_max_num)
+- sta_track_add(hapd->iface, mgmt->sa);
++ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::auth");
+- handle_auth(hapd, mgmt, len);
++ handle_auth(hapd, mgmt, len, ssi_signal, 0);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
+- handle_assoc(hapd, mgmt, len, 0);
++ handle_assoc(hapd, mgmt, len, 0, ssi_signal);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
+- handle_assoc(hapd, mgmt, len, 1);
++ handle_assoc(hapd, mgmt, len, 1, ssi_signal);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+@@ -2368,7 +4530,7 @@
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ wpa_printf(MSG_DEBUG, "mgmt::action");
+- ret = handle_action(hapd, mgmt, len);
++ ret = handle_action(hapd, mgmt, len, freq);
+ break;
+ default:
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+@@ -2388,30 +4550,31 @@
+ u16 auth_alg, auth_transaction, status_code;
+ struct sta_info *sta;
+
++ sta = ap_get_sta(hapd, mgmt->da);
++ if (!sta) {
++ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
++ " not found",
++ MAC2STR(mgmt->da));
++ return;
++ }
++
++ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
++ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
++ status_code = le_to_host16(mgmt->u.auth.status_code);
++
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "did not acknowledge authentication response");
+- return;
++ goto fail;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+ (unsigned long) len);
+- return;
++ goto fail;
+ }
+
+- auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+- auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+- status_code = le_to_host16(mgmt->u.auth.status_code);
+-
+- sta = ap_get_sta(hapd, mgmt->da);
+- if (!sta) {
+- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+- MAC2STR(mgmt->da));
+- return;
+- }
+-
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+@@ -2418,7 +4581,16 @@
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "authenticated");
+ sta->flags |= WLAN_STA_AUTH;
++ if (sta->added_unassoc)
++ hostapd_set_sta_flags(hapd, sta);
++ return;
+ }
++
++fail:
++ if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
+ }
+
+
+@@ -2453,16 +4625,7 @@
+ u16 status;
+ struct sta_info *sta;
+ int new_assoc = 1;
+- struct ieee80211_ht_capabilities ht_cap;
+- struct ieee80211_vht_capabilities vht_cap;
+
+- if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+- sizeof(mgmt->u.assoc_resp))) {
+- wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+- reassoc, (unsigned long) len);
+- return;
+- }
+-
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+@@ -2470,19 +4633,32 @@
+ return;
+ }
+
++ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
++ sizeof(mgmt->u.assoc_resp))) {
++ wpa_printf(MSG_INFO,
++ "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
++ reassoc, (unsigned long) len);
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ return;
++ }
++
++ if (reassoc)
++ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
++ else
++ status = le_to_host16(mgmt->u.assoc_resp.status_code);
++
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
++ /* The STA is added only in case of SUCCESS */
++ if (status == WLAN_STATUS_SUCCESS)
++ hostapd_drv_sta_remove(hapd, sta->addr);
++
+ return;
+ }
+
+- if (reassoc)
+- status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+- else
+- status = le_to_host16(mgmt->u.assoc_resp.status_code);
+-
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
+@@ -2499,11 +4675,15 @@
+ new_assoc = 0;
+ sta->flags |= WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
++ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
++ !hapd->conf->osen) ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK ||
+ sta->auth_alg == WLAN_AUTH_FT) {
+ /*
+- * Open, static WEP, or FT protocol; no separate authorization
+- * step.
++ * Open, static WEP, FT protocol, or FILS; no separate
++ * authorization step.
+ */
+ ap_sta_set_authorized(hapd, sta, 1);
+ }
+@@ -2517,48 +4697,6 @@
+ sta->sa_query_timed_out = 0;
+ #endif /* CONFIG_IEEE80211W */
+
+- /*
+- * Remove the STA entry in order to make sure the STA PS state gets
+- * cleared and configuration gets updated in case of reassociation back
+- * to the same AP.
+- */
+- hostapd_drv_sta_remove(hapd, sta->addr);
+-
+-#ifdef CONFIG_IEEE80211N
+- if (sta->flags & WLAN_STA_HT)
+- hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+-#endif /* CONFIG_IEEE80211N */
+-#ifdef CONFIG_IEEE80211AC
+- if (sta->flags & WLAN_STA_VHT)
+- hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+-#endif /* CONFIG_IEEE80211AC */
+-
+- if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+- sta->supported_rates, sta->supported_rates_len,
+- sta->listen_interval,
+- sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+- sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+- sta->flags, sta->qosinfo, sta->vht_opmode)) {
+- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_NOTICE,
+- "Could not add STA to kernel driver");
+-
+- ap_sta_disconnect(hapd, sta, sta->addr,
+- WLAN_REASON_DISASSOC_AP_BUSY);
+-
+- return;
+- }
+-
+- if (sta->flags & WLAN_STA_WDS) {
+- int ret;
+- char ifname_wds[IFNAMSIZ + 1];
+-
+- ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+- sta->aid, 1);
+- if (!ret)
+- hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+- }
+-
+ if (sta->eapol_sm == NULL) {
+ /*
+ * This STA does not use RADIUS server for EAP authentication,
+@@ -2575,13 +4713,64 @@
+
+ hostapd_set_sta_flags(hapd, sta);
+
++ if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
++ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
++ MACSTR " based on pending request",
++ MAC2STR(sta->addr));
++ sta->pending_wds_enable = 0;
++ sta->flags |= WLAN_STA_WDS;
++ }
++
++ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) {
++ int ret;
++ char ifname_wds[IFNAMSIZ + 1];
++
++ wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
++ MACSTR " (aid %u)",
++ MAC2STR(sta->addr), sta->aid);
++ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
++ sta->aid, 1);
++ if (!ret)
++ hostapd_set_wds_encryption(hapd, sta, ifname_wds);
++ }
++
+ if (sta->auth_alg == WLAN_AUTH_FT)
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+ else
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+ hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
++ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+- ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
++#ifdef CONFIG_FILS
++ if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
++ fils_set_tk(sta->wpa_sm) < 0) {
++ wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
++ ap_sta_disconnect(hapd, sta, sta->addr,
++ WLAN_REASON_UNSPECIFIED);
++ return;
++ }
++#endif /* CONFIG_FILS */
++
++ if (sta->pending_eapol_rx) {
++ struct os_reltime now, age;
++
++ os_get_reltime(&now);
++ os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
++ if (age.sec == 0 && age.usec < 200000) {
++ wpa_printf(MSG_DEBUG,
++ "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
++ MAC2STR(sta->addr));
++ ieee802_1x_receive(
++ hapd, mgmt->da,
++ wpabuf_head(sta->pending_eapol_rx->buf),
++ wpabuf_len(sta->pending_eapol_rx->buf));
++ }
++ wpabuf_free(sta->pending_eapol_rx->buf);
++ os_free(sta->pending_eapol_rx);
++ sta->pending_eapol_rx = NULL;
++ }
+ }
+
+
+@@ -2590,7 +4779,7 @@
+ size_t len, int ok)
+ {
+ struct sta_info *sta;
+- if (mgmt->da[0] & 0x01)
++ if (is_multicast_ether_addr(mgmt->da))
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+@@ -2614,7 +4803,7 @@
+ size_t len, int ok)
+ {
+ struct sta_info *sta;
+- if (mgmt->da[0] & 0x01)
++ if (is_multicast_ether_addr(mgmt->da))
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+@@ -2633,6 +4822,65 @@
+ }
+
+
++static void handle_action_cb(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt,
++ size_t len, int ok)
++{
++ struct sta_info *sta;
++ const struct rrm_measurement_report_element *report;
++
++ if (is_multicast_ether_addr(mgmt->da))
++ return;
++#ifdef CONFIG_DPP
++ if (len >= IEEE80211_HDRLEN + 6 &&
++ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
++ mgmt->u.action.u.vs_public_action.action ==
++ WLAN_PA_VENDOR_SPECIFIC &&
++ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
++ OUI_WFA &&
++ mgmt->u.action.u.vs_public_action.variable[0] ==
++ DPP_OUI_TYPE) {
++ const u8 *pos, *end;
++
++ pos = &mgmt->u.action.u.vs_public_action.variable[1];
++ end = ((const u8 *) mgmt) + len;
++ hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
++ return;
++ }
++ if (len >= IEEE80211_HDRLEN + 2 &&
++ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
++ (mgmt->u.action.u.public_action.action ==
++ WLAN_PA_GAS_INITIAL_REQ ||
++ mgmt->u.action.u.public_action.action ==
++ WLAN_PA_GAS_COMEBACK_REQ)) {
++ const u8 *pos, *end;
++
++ pos = mgmt->u.action.u.public_action.variable;
++ end = ((const u8 *) mgmt) + len;
++ gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
++ return;
++ }
++#endif /* CONFIG_DPP */
++ sta = ap_get_sta(hapd, mgmt->da);
++ if (!sta) {
++ wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
++ " not found", MAC2STR(mgmt->da));
++ return;
++ }
++
++ if (len < 24 + 5 + sizeof(*report))
++ return;
++ report = (const struct rrm_measurement_report_element *)
++ &mgmt->u.action.u.rrm.variable[2];
++ if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
++ mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
++ report->eid == WLAN_EID_MEASURE_REQUEST &&
++ report->len >= 3 &&
++ report->type == MEASURE_TYPE_BEACON)
++ hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
++}
++
++
+ /**
+ * ieee802_11_mgmt_cb - Process management frame TX status callback
+ * @hapd: hostapd BSS data structure (the BSS from which the management frame
+@@ -2650,8 +4898,16 @@
+
+ #ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+- wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
+- stype, ok);
++ size_t hex_len = 2 * len + 1;
++ char *hex = os_malloc(hex_len);
++
++ if (hex) {
++ wpa_snprintf_hex(hex, hex_len, buf, len);
++ wpa_msg(hapd->msg_ctx, MSG_INFO,
++ "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
++ stype, ok, hex);
++ os_free(hex);
++ }
+ return;
+ }
+ #endif /* CONFIG_TESTING_OPTIONS */
+@@ -2670,7 +4926,7 @@
+ handle_assoc_cb(hapd, mgmt, len, 1, ok);
+ break;
+ case WLAN_FC_STYPE_PROBE_RESP:
+- wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
++ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+@@ -2681,7 +4937,8 @@
+ handle_disassoc_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_ACTION:
+- wpa_printf(MSG_DEBUG, "mgmt::action cb");
++ wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
++ handle_action_cb(hapd, mgmt, len, ok);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
+@@ -2779,6 +5036,8 @@
+ }
+ if (sta == NULL)
+ return;
++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
++ MAC2STR(sta->addr));
+ if (!(sta->flags & WLAN_STA_PENDING_POLL))
+ return;
+
+@@ -2794,10 +5053,22 @@
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src);
+- if (sta && (sta->flags & WLAN_STA_ASSOC)) {
++ if (sta &&
++ ((sta->flags & WLAN_STA_ASSOC) ||
++ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+ if (!hapd->conf->wds_sta)
+ return;
+
++ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
++ WLAN_STA_ASSOC_REQ_OK) {
++ wpa_printf(MSG_DEBUG,
++ "Postpone 4-address WDS mode enabling for STA "
++ MACSTR " since TX status for AssocResp is not yet known",
++ MAC2STR(sta->addr));
++ sta->pending_wds_enable = 1;
++ return;
++ }
++
+ if (wds && !(sta->flags & WLAN_STA_WDS)) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+@@ -2817,7 +5088,7 @@
+
+ wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+ MACSTR, MAC2STR(src));
+- if (src[0] & 0x01) {
++ if (is_multicast_ether_addr(src)) {
+ /* Broadcast bit set in SA?! Ignore the frame silently. */
+ return;
+ }
+--- contrib/wpa/src/ap/ieee802_11.h.orig
++++ contrib/wpa/src/ap/ieee802_11.h
+@@ -16,6 +16,8 @@
+ struct ieee80211_ht_capabilities;
+ struct ieee80211_vht_capabilities;
+ struct ieee80211_mgmt;
++struct vlan_description;
++struct hostapd_sta_wpa_psk_short;
+
+ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi);
+@@ -49,9 +51,16 @@
+ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
+ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
++u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
++
+ int hostapd_ht_operation_update(struct hostapd_iface *iface);
+ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id);
+@@ -61,6 +70,7 @@
+ void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap);
++int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
+ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab);
+ u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -71,6 +81,8 @@
+ void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
+ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab);
++u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *vht_oper);
+ u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_opmode);
+ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+@@ -82,8 +94,8 @@
+ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid);
+ void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+- const u8 *sa, const u8 action_type,
+- const u8 *trans_id);
++ const struct ieee80211_mgmt *mgmt,
++ size_t len);
+ u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+ u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+@@ -97,6 +109,7 @@
+ #ifdef CONFIG_SAE
+ void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta);
++void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
+ #else /* CONFIG_SAE */
+ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+@@ -104,4 +117,64 @@
+ }
+ #endif /* CONFIG_SAE */
+
++#ifdef CONFIG_MBO
++
++u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
++
++u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
++
++u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
++ size_t len, int delta);
++
++#else /* CONFIG_MBO */
++
++static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
++ size_t len)
++{
++ return eid;
++}
++
++static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
++{
++ return 0;
++}
++
++#endif /* CONFIG_MBO */
++
++void ap_copy_sta_supp_op_classes(struct sta_info *sta,
++ const u8 *supp_op_classes,
++ size_t supp_op_classes_len);
++
++u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
++void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
++ struct sta_info *sta, int success,
++ struct wpabuf *erp_resp,
++ const u8 *msk, size_t msk_len);
++u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *owe_dh, u8 owe_dh_len,
++ u8 *owe_buf, size_t owe_buf_len, u16 *reason);
++void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
++void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
++void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *pos, size_t len, u16 auth_alg,
++ u16 auth_transaction, u16 status_code,
++ void (*cb)(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ u16 resp, struct wpabuf *data, int pub));
++
++size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
++u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
++int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
++ const u8 *msg, size_t len, u32 *session_timeout,
++ u32 *acct_interim_interval,
++ struct vlan_description *vlan_id,
++ struct hostapd_sta_wpa_psk_short **psk,
++ char **identity, char **radius_cui,
++ int is_probe_req);
++
++int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
++ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
++
++void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
++
+ #endif /* IEEE802_11_H */
+--- contrib/wpa/src/ap/ieee802_11_auth.c.orig
++++ contrib/wpa/src/ap/ieee802_11_auth.c
+@@ -15,7 +15,6 @@
+
+ #include "utils/common.h"
+ #include "utils/eloop.h"
+-#include "crypto/sha1.h"
+ #include "radius/radius.h"
+ #include "radius/radius_client.h"
+ #include "hostapd.h"
+@@ -35,7 +34,7 @@
+ struct hostapd_cached_radius_acl *next;
+ u32 session_timeout;
+ u32 acct_interim_interval;
+- int vlan_id;
++ struct vlan_description vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk;
+ char *identity;
+ char *radius_cui;
+@@ -77,29 +76,20 @@
+ static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+ struct hostapd_sta_wpa_psk_short *src)
+ {
+- struct hostapd_sta_wpa_psk_short **copy_to;
+- struct hostapd_sta_wpa_psk_short *copy_from;
++ if (!psk)
++ return;
+
+- /* Copy PSK linked list */
+- copy_to = psk;
+- copy_from = src;
+- while (copy_from && copy_to) {
+- *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+- if (*copy_to == NULL)
+- break;
+- os_memcpy(*copy_to, copy_from,
+- sizeof(struct hostapd_sta_wpa_psk_short));
+- copy_from = copy_from->next;
+- copy_to = &((*copy_to)->next);
+- }
+- if (copy_to)
+- *copy_to = NULL;
++ if (src)
++ src->ref++;
++
++ *psk = src;
+ }
+
+
+ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+ u32 *session_timeout,
+- u32 *acct_interim_interval, int *vlan_id,
++ u32 *acct_interim_interval,
++ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui)
+ {
+@@ -165,7 +155,10 @@
+ if (msg == NULL)
+ return -1;
+
+- radius_msg_make_authenticator(msg, addr, ETH_ALEN);
++ if (radius_msg_make_authenticator(msg) < 0) {
++ wpa_printf(MSG_INFO, "Could not make Request Authenticator");
++ goto fail;
++ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+@@ -213,6 +206,33 @@
+
+
+ /**
++ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
++ * @hapd: hostapd BSS data
++ * @addr: MAC address of the STA
++ * @vlan_id: Buffer for returning VLAN ID
++ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
++ */
++int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
++ struct vlan_description *vlan_id)
++{
++ if (hostapd_maclist_found(hapd->conf->accept_mac,
++ hapd->conf->num_accept_mac, addr, vlan_id))
++ return HOSTAPD_ACL_ACCEPT;
++
++ if (hostapd_maclist_found(hapd->conf->deny_mac,
++ hapd->conf->num_deny_mac, addr, vlan_id))
++ return HOSTAPD_ACL_REJECT;
++
++ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
++ return HOSTAPD_ACL_ACCEPT;
++ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
++ return HOSTAPD_ACL_REJECT;
++
++ return HOSTAPD_ACL_PENDING;
++}
++
++
++/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+@@ -224,6 +244,7 @@
+ * @psk: Linked list buffer for returning WPA PSK
+ * @identity: Buffer for returning identity (from RADIUS)
+ * @radius_cui: Buffer for returning CUI (from RADIUS)
++ * @is_probe_req: Whether this query for a Probe Request frame
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for freeing the returned *identity and *radius_cui
+@@ -231,16 +252,20 @@
+ */
+ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+- u32 *acct_interim_interval, int *vlan_id,
++ u32 *acct_interim_interval,
++ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+- char **identity, char **radius_cui)
++ char **identity, char **radius_cui,
++ int is_probe_req)
+ {
++ int res;
++
+ if (session_timeout)
+ *session_timeout = 0;
+ if (acct_interim_interval)
+ *acct_interim_interval = 0;
+ if (vlan_id)
+- *vlan_id = 0;
++ os_memset(vlan_id, 0, sizeof(*vlan_id));
+ if (psk)
+ *psk = NULL;
+ if (identity)
+@@ -248,19 +273,10 @@
+ if (radius_cui)
+ *radius_cui = NULL;
+
+- if (hostapd_maclist_found(hapd->conf->accept_mac,
+- hapd->conf->num_accept_mac, addr, vlan_id))
+- return HOSTAPD_ACL_ACCEPT;
++ res = hostapd_check_acl(hapd, addr, vlan_id);
++ if (res != HOSTAPD_ACL_PENDING)
++ return res;
+
+- if (hostapd_maclist_found(hapd->conf->deny_mac,
+- hapd->conf->num_deny_mac, addr, vlan_id))
+- return HOSTAPD_ACL_REJECT;
+-
+- if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+- return HOSTAPD_ACL_ACCEPT;
+- if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+- return HOSTAPD_ACL_REJECT;
+-
+ if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+ #ifdef CONFIG_NO_RADIUS
+ return HOSTAPD_ACL_REJECT;
+@@ -267,11 +283,19 @@
+ #else /* CONFIG_NO_RADIUS */
+ struct hostapd_acl_query_data *query;
+
++ if (is_probe_req) {
++ /* Skip RADIUS queries for Probe Request frames to avoid
++ * excessive load on the authentication server. */
++ return HOSTAPD_ACL_ACCEPT;
++ };
++
++ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
++ vlan_id = NULL;
++
+ /* Check whether ACL cache has an entry for this station */
+- int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+- acct_interim_interval,
+- vlan_id, psk,
+- identity, radius_cui);
++ res = hostapd_acl_cache_get(hapd, addr, session_timeout,
++ acct_interim_interval, vlan_id, psk,
++ identity, radius_cui);
+ if (res == HOSTAPD_ACL_ACCEPT ||
+ res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ return res;
+@@ -314,7 +338,7 @@
+ return HOSTAPD_ACL_REJECT;
+ }
+
+- query->auth_msg = os_malloc(len);
++ query->auth_msg = os_memdup(msg, len);
+ if (query->auth_msg == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+ "auth frame.");
+@@ -321,7 +345,6 @@
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+- os_memcpy(query->auth_msg, msg, len);
+ query->auth_msg_len = len;
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+@@ -419,7 +442,7 @@
+ struct hostapd_cached_radius_acl *cache)
+ {
+ int passphraselen;
+- char *passphrase, *strpassphrase;
++ char *passphrase;
+ size_t i;
+ struct hostapd_sta_wpa_psk_short *psk;
+
+@@ -436,24 +459,42 @@
+ */
+ if (passphrase == NULL)
+ break;
++
+ /*
++ * Passphase should be 8..63 chars (to be hashed with SSID)
++ * or 64 chars hex string (no separate hashing with SSID).
++ */
++
++ if (passphraselen < MIN_PASSPHRASE_LEN ||
++ passphraselen > MAX_PASSPHRASE_LEN + 1)
++ goto free_pass;
++
++ /*
+ * passphrase does not contain the NULL termination.
+ * Add it here as pbkdf2_sha1() requires it.
+ */
+- strpassphrase = os_zalloc(passphraselen + 1);
+ psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+- if (strpassphrase && psk) {
+- os_memcpy(strpassphrase, passphrase, passphraselen);
+- pbkdf2_sha1(strpassphrase,
+- hapd->conf->ssid.ssid,
+- hapd->conf->ssid.ssid_len, 4096,
+- psk->psk, PMK_LEN);
++ if (psk) {
++ if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
++ (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
++ hostapd_logger(hapd, cache->addr,
++ HOSTAPD_MODULE_RADIUS,
++ HOSTAPD_LEVEL_WARNING,
++ "invalid hex string (%d chars) in Tunnel-Password",
++ passphraselen);
++ goto skip;
++ } else if (passphraselen <= MAX_PASSPHRASE_LEN) {
++ os_memcpy(psk->passphrase, passphrase,
++ passphraselen);
++ psk->is_passphrase = 1;
++ }
+ psk->next = cache->psk;
+ cache->psk = psk;
+ psk = NULL;
+ }
+- os_free(strpassphrase);
++skip:
+ os_free(psk);
++free_pass:
+ os_free(passphrase);
+ }
+ }
+@@ -535,7 +576,10 @@
+ cache->acct_interim_interval = 0;
+ }
+
+- cache->vlan_id = radius_msg_get_vlanid(msg);
++ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
++ cache->vlan_id.notempty = !!radius_msg_get_vlanid(
++ msg, &cache->vlan_id.untagged,
++ MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
+
+ decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
+ msg, req, cache);
+@@ -558,17 +602,18 @@
+ !cache->psk)
+ cache->accepted = HOSTAPD_ACL_REJECT;
+
+- if (cache->vlan_id &&
+- !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
++ if (cache->vlan_id.notempty &&
++ !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) {
+ hostapd_logger(hapd, query->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+- "Invalid VLAN ID %d received from RADIUS server",
+- cache->vlan_id);
+- cache->vlan_id = 0;
++ "Invalid VLAN %d%s received from RADIUS server",
++ cache->vlan_id.untagged,
++ cache->vlan_id.tagged[0] ? "+" : "");
++ os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
+ }
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+- !cache->vlan_id)
++ !cache->vlan_id.notempty)
+ cache->accepted = HOSTAPD_ACL_REJECT;
+ } else
+ cache->accepted = HOSTAPD_ACL_REJECT;
+@@ -627,9 +672,11 @@
+
+ #ifndef CONFIG_NO_RADIUS
+ hostapd_acl_cache_free(hapd->acl_cache);
++ hapd->acl_cache = NULL;
+ #endif /* CONFIG_NO_RADIUS */
+
+ query = hapd->acl_queries;
++ hapd->acl_queries = NULL;
+ while (query) {
+ prev = query;
+ query = query->next;
+@@ -640,6 +687,12 @@
+
+ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
+ {
++ if (psk && psk->ref) {
++ /* This will be freed when the last reference is dropped. */
++ psk->ref--;
++ return;
++ }
++
+ while (psk) {
+ struct hostapd_sta_wpa_psk_short *prev = psk;
+ psk = psk->next;
+--- contrib/wpa/src/ap/ieee802_11_auth.h.orig
++++ contrib/wpa/src/ap/ieee802_11_auth.h
+@@ -16,11 +16,15 @@
+ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+ };
+
++int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
++ struct vlan_description *vlan_id);
+ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+- u32 *acct_interim_interval, int *vlan_id,
++ u32 *acct_interim_interval,
++ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+- char **identity, char **radius_cui);
++ char **identity, char **radius_cui,
++ int is_probe_req);
+ int hostapd_acl_init(struct hostapd_data *hapd);
+ void hostapd_acl_deinit(struct hostapd_data *hapd);
+ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+--- contrib/wpa/src/ap/ieee802_11_he.c.orig
++++ contrib/wpa/src/ap/ieee802_11_he.c
+@@ -0,0 +1,119 @@
++/*
++ * hostapd / IEEE 802.11ax HE
++ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/ieee802_11_defs.h"
++#include "hostapd.h"
++#include "ap_config.h"
++#include "beacon.h"
++#include "ieee802_11.h"
++#include "dfs.h"
++
++u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
++{
++ struct ieee80211_he_capabilities *cap;
++ u8 *pos = eid;
++
++ if (!hapd->iface->current_mode)
++ return eid;
++
++ *pos++ = WLAN_EID_EXTENSION;
++ *pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
++ *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
++
++ cap = (struct ieee80211_he_capabilities *) pos;
++ os_memset(cap, 0, sizeof(*cap));
++
++ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
++ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
++ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
++
++ if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
++ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
++ HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
++
++ if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
++ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
++ HE_PHYCAP_MU_BEAMFORMER_CAPAB;
++
++ pos += sizeof(*cap);
++
++ return pos;
++}
++
++
++u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
++{
++ struct ieee80211_he_operation *oper;
++ u8 *pos = eid;
++
++ if (!hapd->iface->current_mode)
++ return eid;
++
++ *pos++ = WLAN_EID_EXTENSION;
++ *pos++ = 1 + sizeof(struct ieee80211_he_operation);
++ *pos++ = WLAN_EID_EXT_HE_OPERATION;
++
++ oper = (struct ieee80211_he_operation *) pos;
++ os_memset(oper, 0, sizeof(*oper));
++
++ if (hapd->iface->conf->he_op.he_bss_color)
++ oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
++
++ if (hapd->iface->conf->he_op.he_default_pe_duration)
++ oper->he_oper_params |=
++ (hapd->iface->conf->he_op.he_default_pe_duration <<
++ HE_OPERATION_DFLT_PE_DURATION_OFFSET);
++
++ if (hapd->iface->conf->he_op.he_twt_required)
++ oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
++
++ if (hapd->iface->conf->he_op.he_rts_threshold)
++ oper->he_oper_params |=
++ (hapd->iface->conf->he_op.he_rts_threshold <<
++ HE_OPERATION_RTS_THRESHOLD_OFFSET);
++
++ /* TODO: conditional MaxBSSID Indicator subfield */
++
++ pos += sizeof(*oper);
++
++ return pos;
++}
++
++
++u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
++{
++ struct ieee80211_he_mu_edca_parameter_set *edca;
++ u8 *pos;
++ size_t i;
++
++ pos = (u8 *) &hapd->iface->conf->he_mu_edca;
++ for (i = 0; i < sizeof(*edca); i++) {
++ if (pos[i])
++ break;
++ }
++ if (i == sizeof(*edca))
++ return eid; /* no MU EDCA Parameters configured */
++
++ pos = eid;
++ *pos++ = WLAN_EID_EXTENSION;
++ *pos++ = 1 + sizeof(*edca);
++ *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
++
++ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
++ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
++
++ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
++ pos, sizeof(*edca));
++
++ pos += sizeof(*edca);
++
++ return pos;
++}
+--- contrib/wpa/src/ap/ieee802_11_ht.c.orig
++++ contrib/wpa/src/ap/ieee802_11_ht.c
+@@ -108,6 +108,29 @@
+ }
+
+
++u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
++{
++ u8 sec_ch;
++
++ if (!hapd->cs_freq_params.channel ||
++ !hapd->cs_freq_params.sec_channel_offset)
++ return eid;
++
++ if (hapd->cs_freq_params.sec_channel_offset == -1)
++ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
++ else if (hapd->cs_freq_params.sec_channel_offset == 1)
++ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
++ else
++ return eid;
++
++ *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
++ *eid++ = 1;
++ *eid++ = sec_ch;
++
++ return eid;
++}
++
++
+ /*
+ op_mode
+ Set to 0 (HT pure) under the followign conditions
+@@ -213,17 +236,29 @@
+ int i;
+ const u8 *start = (const u8 *) mgmt;
+ const u8 *data = start + IEEE80211_HDRLEN + 2;
++ struct sta_info *sta;
+
++ wpa_printf(MSG_DEBUG,
++ "HT: Received 20/40 BSS Coexistence Management frame from "
++ MACSTR, MAC2STR(mgmt->sa));
++
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+ mgmt->u.action.u.public_action.action);
+
+- if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
++ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
++ wpa_printf(MSG_DEBUG,
++ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
+ return;
++ }
+
+- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
++ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
++ wpa_printf(MSG_DEBUG,
++ "Ignore too short 20/40 BSS Coexistence Management frame");
+ return;
++ }
+
++ /* 20/40 BSS Coexistence element */
+ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+ if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+ bc_ie->length < 1) {
+@@ -231,13 +266,35 @@
+ bc_ie->element_id, bc_ie->length);
+ return;
+ }
+- if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
++ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
++ wpa_printf(MSG_DEBUG,
++ "Truncated 20/40 BSS Coexistence element");
+ return;
++ }
+ data += 2 + bc_ie->length;
+
+- wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
+- bc_ie->coex_param);
++ wpa_printf(MSG_DEBUG,
++ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
++ bc_ie->coex_param,
++ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
++ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
++ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
++ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
++ (bc_ie->coex_param & BIT(4)) ?
++ "[OBSSScanExemptionGrant]" : "",
++ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
++ "[Reserved]" : "");
++
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
++ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation
++ */
++ sta = ap_get_sta(hapd, mgmt->sa);
++ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
++ wpa_printf(MSG_DEBUG,
++ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
++ return;
++ }
++
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -246,6 +303,8 @@
+ }
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
++ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation
++ */
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -253,12 +312,16 @@
+ is_ht40_allowed = 0;
+ }
+
+- if (start + len - data >= 3 &&
+- data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
++ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */
++ while (start + len - data >= 3 &&
++ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ u8 ielen = data[1];
+
+- if (ielen > start + len - data - 2)
++ if (ielen > start + len - data - 2) {
++ wpa_printf(MSG_DEBUG,
++ "Truncated 20/40 BSS Intolerant Channel Report element");
+ return;
++ }
+ ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Intolerant Channel Report: Operating Class %u",
+@@ -269,8 +332,10 @@
+ for (i = 0; i < ielen - 1; i++) {
+ u8 chan = ic_report->variable[i];
+
++ if (chan == iface->conf->channel)
++ continue; /* matching own primary channel */
+ if (is_40_allowed(iface, chan))
+- continue;
++ continue; /* not within affected channels */
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -278,6 +343,8 @@
+ chan);
+ is_ht40_allowed = 0;
+ }
++
++ data += 2 + ielen;
+ }
+ wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
+ is_ht40_allowed, iface->num_sta_ht40_intolerant);
+@@ -317,8 +384,8 @@
+ * that did not specify a valid WMM IE in the (Re)Association Request
+ * frame.
+ */
+- if (!ht_capab ||
+- !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
++ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
++ !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
+ sta->flags &= ~WLAN_STA_HT;
+ os_free(sta->ht_capabilities);
+ sta->ht_capabilities = NULL;
+--- contrib/wpa/src/ap/ieee802_11_shared.c.orig
++++ contrib/wpa/src/ap/ieee802_11_shared.c
+@@ -10,10 +10,12 @@
+
+ #include "utils/common.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/ocv.h"
+ #include "hostapd.h"
+ #include "sta_info.h"
+ #include "ap_config.h"
+ #include "ap_drv_ops.h"
++#include "wpa_auth.h"
+ #include "ieee802_11.h"
+
+
+@@ -49,7 +51,12 @@
+ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+ {
+- struct ieee80211_mgmt mgmt;
++#ifdef CONFIG_OCV
++ struct sta_info *sta;
++#endif /* CONFIG_OCV */
++ struct ieee80211_mgmt *mgmt;
++ u8 *oci_ie = NULL;
++ u8 oci_ie_len = 0;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+@@ -57,19 +64,61 @@
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+- os_memset(&mgmt, 0, sizeof(mgmt));
+- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+- WLAN_FC_STYPE_ACTION);
+- os_memcpy(mgmt.da, addr, ETH_ALEN);
+- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+- mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
++#ifdef CONFIG_OCV
++ sta = ap_get_sta(hapd, addr);
++ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
++ struct wpa_channel_info ci;
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info for OCI element in SA Query Request");
++ return;
++ }
++
++ oci_ie_len = OCV_OCI_EXTENDED_LEN;
++ oci_ie = os_zalloc(oci_ie_len);
++ if (!oci_ie) {
++ wpa_printf(MSG_WARNING,
++ "Failed to allocate buffer for OCI element in SA Query Request");
++ return;
++ }
++
++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
++ os_free(oci_ie);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++
++ mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
++ if (!mgmt) {
++ wpa_printf(MSG_DEBUG,
++ "Failed to allocate buffer for SA Query Response frame");
++ os_free(oci_ie);
++ return;
++ }
++
++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
++ WLAN_FC_STYPE_ACTION);
++ os_memcpy(mgmt->da, addr, ETH_ALEN);
++ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
++ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
++ mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
++ mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
++ os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+- if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
++ end = mgmt->u.action.u.sa_query_req.variable;
++#ifdef CONFIG_OCV
++ if (oci_ie_len > 0) {
++ os_memcpy(end, oci_ie, oci_ie_len);
++ end += oci_ie_len;
++ }
++#endif /* CONFIG_OCV */
++ if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0)
+ wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
++
++ os_free(mgmt);
++ os_free(oci_ie);
+ }
+
+
+@@ -77,7 +126,9 @@
+ const u8 *sa, const u8 *trans_id)
+ {
+ struct sta_info *sta;
+- struct ieee80211_mgmt resp;
++ struct ieee80211_mgmt *resp;
++ u8 *oci_ie = NULL;
++ u8 oci_ie_len = 0;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+@@ -92,31 +143,124 @@
+ return;
+ }
+
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sta->wpa_sm)) {
++ struct wpa_channel_info ci;
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info for OCI element in SA Query Response");
++ return;
++ }
++
++ oci_ie_len = OCV_OCI_EXTENDED_LEN;
++ oci_ie = os_zalloc(oci_ie_len);
++ if (!oci_ie) {
++ wpa_printf(MSG_WARNING,
++ "Failed to allocate buffer for for OCI element in SA Query Response");
++ return;
++ }
++
++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
++ os_free(oci_ie);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++
++ resp = os_zalloc(sizeof(*resp) + oci_ie_len);
++ if (!resp) {
++ wpa_printf(MSG_DEBUG,
++ "Failed to allocate buffer for SA Query Response frame");
++ os_free(oci_ie);
++ return;
++ }
++
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(sa));
+
+- os_memset(&resp, 0, sizeof(resp));
+- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+- WLAN_FC_STYPE_ACTION);
+- os_memcpy(resp.da, sa, ETH_ALEN);
+- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+- resp.u.action.category = WLAN_ACTION_SA_QUERY;
+- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+- os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
++ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
++ WLAN_FC_STYPE_ACTION);
++ os_memcpy(resp->da, sa, ETH_ALEN);
++ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
++ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
++ resp->u.action.category = WLAN_ACTION_SA_QUERY;
++ resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
++ os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+- if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
++ end = resp->u.action.u.sa_query_req.variable;
++#ifdef CONFIG_OCV
++ if (oci_ie_len > 0) {
++ os_memcpy(end, oci_ie, oci_ie_len);
++ end += oci_ie_len;
++ }
++#endif /* CONFIG_OCV */
++ if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0)
+ wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
++
++ os_free(resp);
++ os_free(oci_ie);
+ }
+
+
+-void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+- const u8 action_type, const u8 *trans_id)
++void ieee802_11_sa_query_action(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt,
++ size_t len)
+ {
+ struct sta_info *sta;
+ int i;
++ const u8 *sa = mgmt->sa;
++ const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
++ const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
+
++ if (((const u8 *) mgmt) + len <
++ mgmt->u.action.u.sa_query_resp.variable) {
++ wpa_printf(MSG_DEBUG,
++ "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
++ (unsigned long) len);
++ return;
++ }
++
++ sta = ap_get_sta(hapd, sa);
++
++#ifdef CONFIG_OCV
++ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
++ struct ieee802_11_elems elems;
++ struct wpa_channel_info ci;
++ int tx_chanwidth;
++ int tx_seg1_idx;
++ size_t ies_len;
++ const u8 *ies;
++
++ ies = mgmt->u.action.u.sa_query_resp.variable;
++ ies_len = len - (ies - (u8 *) mgmt);
++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
++ ParseFailed) {
++ wpa_printf(MSG_DEBUG,
++ "SA Query: Failed to parse elements");
++ return;
++ }
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info to validate received OCI in SA Query Action frame");
++ return;
++ }
++
++ if (get_sta_tx_parameters(sta->wpa_sm,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx, &tx_chanwidth,
++ &tx_seg1_idx) < 0)
++ return;
++
++ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
++ tx_chanwidth, tx_seg1_idx) != 0) {
++ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++
+ if (action_type == WLAN_SA_QUERY_REQUEST) {
+ ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+ return;
+@@ -135,7 +279,6 @@
+
+ /* MLME-SAQuery.confirm */
+
+- sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+@@ -172,10 +315,16 @@
+ case 0: /* Bits 0-7 */
+ if (hapd->iconf->obss_interval)
+ *pos |= 0x01; /* Bit 0 - Coexistence management */
++ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
++ *pos |= 0x04; /* Bit 2 - Extended Channel Switching */
+ break;
+ case 1: /* Bits 8-15 */
+ if (hapd->conf->proxy_arp)
+ *pos |= 0x10; /* Bit 12 - Proxy ARP */
++ if (hapd->conf->coloc_intf_reporting) {
++ /* Bit 13 - Collocated Interference Reporting */
++ *pos |= 0x20;
++ }
+ break;
+ case 2: /* Bits 16-23 */
+ if (hapd->conf->wnm_sleep_mode)
+@@ -184,9 +333,9 @@
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+ break;
+ case 3: /* Bits 24-31 */
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ *pos |= 0x02; /* Bit 25 - SSID List */
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+@@ -207,11 +356,45 @@
+ if (hapd->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+ #endif /* CONFIG_HS20 */
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled)
++ *pos |= 0x40; /* Bit 46 - WNM-Notification */
++#endif /* CONFIG_MBO */
+ break;
+ case 6: /* Bits 48-55 */
+ if (hapd->conf->ssid.utf8_ssid)
+ *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+ break;
++ case 7: /* Bits 56-63 */
++ break;
++ case 8: /* Bits 64-71 */
++ if (hapd->conf->ftm_responder)
++ *pos |= 0x40; /* Bit 70 - FTM responder */
++ if (hapd->conf->ftm_initiator)
++ *pos |= 0x80; /* Bit 71 - FTM initiator */
++ break;
++ case 9: /* Bits 72-79 */
++#ifdef CONFIG_FILS
++ if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
++ wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
++ *pos |= 0x01;
++#endif /* CONFIG_FILS */
++ break;
++ case 10: /* Bits 80-87 */
++#ifdef CONFIG_SAE
++ if (hapd->conf->wpa &&
++ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
++ int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
++
++ if (in_use)
++ *pos |= 0x02; /* Bit 81 - SAE Password
++ * Identifiers In Use */
++ if (in_use == 2)
++ *pos |= 0x04; /* Bit 82 - SAE Password
++ * Identifiers Used Exclusively */
++ }
++#endif /* CONFIG_SAE */
++ break;
+ }
+ }
+
+@@ -231,14 +414,32 @@
+ len = 1;
+ if (len < 7 && hapd->conf->ssid.utf8_ssid)
+ len = 7;
+-#ifdef CONFIG_WNM
++ if (len < 9 &&
++ (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
++ len = 9;
++#ifdef CONFIG_WNM_AP
+ if (len < 4)
+ len = 4;
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ #ifdef CONFIG_HS20
+ if (hapd->conf->hs20 && len < 6)
+ len = 6;
+ #endif /* CONFIG_HS20 */
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled && len < 6)
++ len = 6;
++#endif /* CONFIG_MBO */
++#ifdef CONFIG_FILS
++ if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
++ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
++ len = 10;
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_SAE
++ if (len < 11 && hapd->conf->wpa &&
++ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
++ hostapd_sae_pw_id_in_use(hapd->conf))
++ len = 11;
++#endif /* CONFIG_SAE */
+ if (len < hapd->iface->extended_capa_len)
+ len = hapd->iface->extended_capa_len;
+ if (len == 0)
+@@ -413,7 +614,7 @@
+ {
+ size_t len;
+
+- if (hapd->conf->time_advertisement != 2)
++ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
+ return eid;
+
+ len = os_strlen(hapd->conf->time_zone);
+@@ -484,7 +685,7 @@
+ {
+ u8 *pos = eid;
+
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ if (hapd->conf->ap_max_inactivity > 0) {
+ unsigned int val;
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+@@ -502,7 +703,300 @@
+ pos += 2;
+ *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ }
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+
+ return pos;
+ }
++
++
++#ifdef CONFIG_MBO
++
++u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
++ size_t len, int delta)
++{
++ u8 mbo[4];
++
++ mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
++ mbo[1] = 2;
++ /* Delta RSSI */
++ mbo[2] = delta;
++ /* Retry delay */
++ mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
++
++ return eid + mbo_add_ie(eid, len, mbo, 4);
++}
++
++
++u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
++{
++ u8 mbo[9], *mbo_pos = mbo;
++ u8 *pos = eid;
++
++ if (!hapd->conf->mbo_enabled &&
++ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
++ return eid;
++
++ if (hapd->conf->mbo_enabled) {
++ *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
++ *mbo_pos++ = 1;
++ /* Not Cellular aware */
++ *mbo_pos++ = 0;
++ }
++
++ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
++ *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = hapd->mbo_assoc_disallow;
++ }
++
++ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
++ u8 ctrl;
++
++ ctrl = OCE_RELEASE;
++ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
++ ctrl |= OCE_IS_STA_CFON;
++
++ *mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
++ *mbo_pos++ = 1;
++ *mbo_pos++ = ctrl;
++ }
++
++ pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
++
++ return pos;
++}
++
++
++u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
++{
++ u8 len;
++
++ if (!hapd->conf->mbo_enabled &&
++ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
++ return 0;
++
++ /*
++ * MBO IE header (6) + Capability Indication attribute (3) +
++ * Association Disallowed attribute (3) = 12
++ */
++ len = 6;
++ if (hapd->conf->mbo_enabled)
++ len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
++
++ /* OCE capability indication attribute (3) */
++ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
++ len += 3;
++
++ return len;
++}
++
++#endif /* CONFIG_MBO */
++
++
++#ifdef CONFIG_OWE
++static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
++{
++ return hapd->conf->owe_transition_ssid_len > 0 &&
++ !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
++}
++#endif /* CONFIG_OWE */
++
++
++size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
++{
++#ifdef CONFIG_OWE
++ if (!hostapd_eid_owe_trans_enabled(hapd))
++ return 0;
++ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
++#else /* CONFIG_OWE */
++ return 0;
++#endif /* CONFIG_OWE */
++}
++
++
++u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
++ size_t len)
++{
++#ifdef CONFIG_OWE
++ u8 *pos = eid;
++ size_t elen;
++
++ if (hapd->conf->owe_transition_ifname[0] &&
++ !hostapd_eid_owe_trans_enabled(hapd))
++ hostapd_owe_trans_get_info(hapd);
++
++ if (!hostapd_eid_owe_trans_enabled(hapd))
++ return pos;
++
++ elen = hostapd_eid_owe_trans_len(hapd);
++ if (len < elen) {
++ wpa_printf(MSG_DEBUG,
++ "OWE: Not enough room in the buffer for OWE IE");
++ return pos;
++ }
++
++ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
++ *pos++ = elen - 2;
++ WPA_PUT_BE24(pos, OUI_WFA);
++ pos += 3;
++ *pos++ = OWE_OUI_TYPE;
++ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
++ pos += ETH_ALEN;
++ *pos++ = hapd->conf->owe_transition_ssid_len;
++ os_memcpy(pos, hapd->conf->owe_transition_ssid,
++ hapd->conf->owe_transition_ssid_len);
++ pos += hapd->conf->owe_transition_ssid_len;
++
++ return pos;
++#else /* CONFIG_OWE */
++ return eid;
++#endif /* CONFIG_OWE */
++}
++
++
++void ap_copy_sta_supp_op_classes(struct sta_info *sta,
++ const u8 *supp_op_classes,
++ size_t supp_op_classes_len)
++{
++ if (!supp_op_classes)
++ return;
++ os_free(sta->supp_op_classes);
++ sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
++ if (!sta->supp_op_classes)
++ return;
++
++ sta->supp_op_classes[0] = supp_op_classes_len;
++ os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
++ supp_op_classes_len);
++}
++
++
++u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
++{
++ u8 *pos = eid;
++#ifdef CONFIG_FILS
++ u8 *len;
++ u16 fils_info = 0;
++ size_t realms;
++ struct fils_realm *realm;
++
++ if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
++ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
++ return pos;
++
++ realms = dl_list_len(&hapd->conf->fils_realms);
++ if (realms > 7)
++ realms = 7; /* 3 bit count field limits this to max 7 */
++
++ *pos++ = WLAN_EID_FILS_INDICATION;
++ len = pos++;
++ /* TODO: B0..B2: Number of Public Key Identifiers */
++ if (hapd->conf->erp_domain) {
++ /* B3..B5: Number of Realm Identifiers */
++ fils_info |= realms << 3;
++ }
++ /* TODO: B6: FILS IP Address Configuration */
++ if (hapd->conf->fils_cache_id_set)
++ fils_info |= BIT(7);
++ if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
++ fils_info |= BIT(8); /* HESSID Included */
++ /* FILS Shared Key Authentication without PFS Supported */
++ fils_info |= BIT(9);
++ if (hapd->conf->fils_dh_group) {
++ /* FILS Shared Key Authentication with PFS Supported */
++ fils_info |= BIT(10);
++ }
++ /* TODO: B11: FILS Public Key Authentication Supported */
++ /* B12..B15: Reserved */
++ WPA_PUT_LE16(pos, fils_info);
++ pos += 2;
++ if (hapd->conf->fils_cache_id_set) {
++ os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
++ pos += FILS_CACHE_ID_LEN;
++ }
++ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
++ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
++ pos += ETH_ALEN;
++ }
++
++ dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
++ list) {
++ if (realms == 0)
++ break;
++ realms--;
++ os_memcpy(pos, realm->hash, 2);
++ pos += 2;
++ }
++ *len = pos - len - 1;
++#endif /* CONFIG_FILS */
++
++ return pos;
++}
++
++
++#ifdef CONFIG_OCV
++int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
++ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
++{
++ int ht_40mhz = 0;
++ int vht_80p80 = 0;
++ int requested_bw;
++
++ if (sta->ht_capabilities)
++ ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
++ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
++
++ if (sta->vht_operation) {
++ struct ieee80211_vht_operation *oper = sta->vht_operation;
++
++ /*
++ * If a VHT Operation element was present, use it to determine
++ * the supported channel bandwidth.
++ */
++ if (oper->vht_op_info_chwidth == 0) {
++ requested_bw = ht_40mhz ? 40 : 20;
++ } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
++ requested_bw = 80;
++ } else {
++ int diff;
++
++ requested_bw = 160;
++ diff = abs((int)
++ oper->vht_op_info_chan_center_freq_seg0_idx -
++ (int)
++ oper->vht_op_info_chan_center_freq_seg1_idx);
++ vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
++ != 0 && diff > 16;
++ }
++ } else if (sta->vht_capabilities) {
++ struct ieee80211_vht_capabilities *capab;
++ int vht_chanwidth;
++
++ capab = sta->vht_capabilities;
++
++ /*
++ * If only the VHT Capabilities element is present (e.g., for
++ * normal clients), use it to determine the supported channel
++ * bandwidth.
++ */
++ vht_chanwidth = capab->vht_capabilities_info &
++ VHT_CAP_SUPP_CHAN_WIDTH_MASK;
++ vht_80p80 = capab->vht_capabilities_info &
++ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
++
++ /* TODO: Also take into account Extended NSS BW Support field */
++ requested_bw = vht_chanwidth ? 160 : 80;
++ } else {
++ requested_bw = ht_40mhz ? 40 : 20;
++ }
++
++ *bandwidth = requested_bw < ap_max_chanwidth ?
++ requested_bw : ap_max_chanwidth;
++
++ *seg1_idx = 0;
++ if (ap_seg1_idx && vht_80p80)
++ *seg1_idx = ap_seg1_idx;
++
++ return 0;
++}
++#endif /* CONFIG_OCV */
+--- contrib/wpa/src/ap/ieee802_11_vht.c.orig
++++ contrib/wpa/src/ap/ieee802_11_vht.c
+@@ -17,9 +17,10 @@
+ #include "sta_info.h"
+ #include "beacon.h"
+ #include "ieee802_11.h"
++#include "dfs.h"
+
+
+-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
++u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+ {
+ struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+@@ -49,6 +50,18 @@
+ cap->vht_capabilities_info = host_to_le32(
+ hapd->iface->conf->vht_capab);
+
++ if (nsts != 0) {
++ u32 hapd_nsts;
++
++ hapd_nsts = le_to_host32(cap->vht_capabilities_info);
++ hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
++ cap->vht_capabilities_info &=
++ ~(host_to_le32(hapd_nsts <<
++ VHT_CAP_BEAMFORMEE_STS_OFFSET));
++ cap->vht_capabilities_info |=
++ host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++ }
++
+ /* Supported MCS set comes from hw */
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+@@ -80,6 +93,26 @@
+ hapd->iconf->vht_oper_centr_freq_seg1_idx;
+
+ oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
++ if (hapd->iconf->vht_oper_chwidth == 2) {
++ /*
++ * Convert 160 MHz channel width to new style as interop
++ * workaround.
++ */
++ oper->vht_op_info_chwidth = 1;
++ oper->vht_op_info_chan_center_freq_seg1_idx =
++ oper->vht_op_info_chan_center_freq_seg0_idx;
++ if (hapd->iconf->channel <
++ hapd->iconf->vht_oper_centr_freq_seg0_idx)
++ oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
++ else
++ oper->vht_op_info_chan_center_freq_seg0_idx += 8;
++ } else if (hapd->iconf->vht_oper_chwidth == 3) {
++ /*
++ * Convert 80+80 MHz channel width to new style as interop
++ * workaround.
++ */
++ oper->vht_op_info_chwidth = 1;
++ }
+
+ /* VHT Basic MCS set comes from hw */
+ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+@@ -131,12 +164,177 @@
+ }
+
+
++u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
++{
++ u8 bw, chan1, chan2 = 0;
++ int freq1;
++
++ if (!hapd->cs_freq_params.channel ||
++ !hapd->cs_freq_params.vht_enabled)
++ return eid;
++
++ /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
++ switch (hapd->cs_freq_params.bandwidth) {
++ case 40:
++ bw = 0;
++ break;
++ case 80:
++ /* check if it's 80+80 */
++ if (!hapd->cs_freq_params.center_freq2)
++ bw = 1;
++ else
++ bw = 3;
++ break;
++ case 160:
++ bw = 2;
++ break;
++ default:
++ /* not valid VHT bandwidth or not in CSA */
++ return eid;
++ }
++
++ freq1 = hapd->cs_freq_params.center_freq1 ?
++ hapd->cs_freq_params.center_freq1 :
++ hapd->cs_freq_params.freq;
++ if (ieee80211_freq_to_chan(freq1, &chan1) !=
++ HOSTAPD_MODE_IEEE80211A)
++ return eid;
++
++ if (hapd->cs_freq_params.center_freq2 &&
++ ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
++ &chan2) != HOSTAPD_MODE_IEEE80211A)
++ return eid;
++
++ *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
++ *eid++ = 5; /* Length of Channel Switch Wrapper */
++ *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
++ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
++ *eid++ = bw; /* New Channel Width */
++ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
++ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
++
++ return eid;
++}
++
++
++u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
++{
++ struct hostapd_iface *iface = hapd->iface;
++ struct hostapd_config *iconf = iface->conf;
++ struct hostapd_hw_modes *mode = iface->current_mode;
++ struct hostapd_channel_data *chan;
++ int dfs, i;
++ u8 channel, tx_pwr_count, local_pwr_constraint;
++ int max_tx_power;
++ u8 tx_pwr;
++
++ if (!mode)
++ return eid;
++
++ if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
++ return eid;
++
++ for (i = 0; i < mode->num_channels; i++) {
++ if (mode->channels[i].freq == iface->freq)
++ break;
++ }
++ if (i == mode->num_channels)
++ return eid;
++
++ switch (iface->conf->vht_oper_chwidth) {
++ case VHT_CHANWIDTH_USE_HT:
++ if (iconf->secondary_channel == 0) {
++ /* Max Transmit Power count = 0 (20 MHz) */
++ tx_pwr_count = 0;
++ } else {
++ /* Max Transmit Power count = 1 (20, 40 MHz) */
++ tx_pwr_count = 1;
++ }
++ break;
++ case VHT_CHANWIDTH_80MHZ:
++ /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
++ tx_pwr_count = 2;
++ break;
++ case VHT_CHANWIDTH_80P80MHZ:
++ case VHT_CHANWIDTH_160MHZ:
++ /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
++ tx_pwr_count = 3;
++ break;
++ default:
++ return eid;
++ }
++
++ /*
++ * Below local_pwr_constraint logic is referred from
++ * hostapd_eid_pwr_constraint.
++ *
++ * Check if DFS is required by regulatory.
++ */
++ dfs = hostapd_is_dfs_required(hapd->iface);
++ if (dfs < 0)
++ dfs = 0;
++
++ /*
++ * In order to meet regulations when TPC is not implemented using
++ * a transmit power that is below the legal maximum (including any
++ * mitigation factor) should help. In this case, indicate 3 dB below
++ * maximum allowed transmit power.
++ */
++ if (hapd->iconf->local_pwr_constraint == -1)
++ local_pwr_constraint = (dfs == 0) ? 0 : 3;
++ else
++ local_pwr_constraint = hapd->iconf->local_pwr_constraint;
++
++ /*
++ * A STA that is not an AP shall use a transmit power less than or
++ * equal to the local maximum transmit power level for the channel.
++ * The local maximum transmit power can be calculated from the formula:
++ * local max TX pwr = max TX pwr - local pwr constraint
++ * Where max TX pwr is maximum transmit power level specified for
++ * channel in Country element and local pwr constraint is specified
++ * for channel in this Power Constraint element.
++ */
++ chan = &mode->channels[i];
++ max_tx_power = chan->max_tx_power - local_pwr_constraint;
++
++ /*
++ * Local Maximum Transmit power is encoded as two's complement
++ * with a 0.5 dB step.
++ */
++ max_tx_power *= 2; /* in 0.5 dB steps */
++ if (max_tx_power > 127) {
++ /* 63.5 has special meaning of 63.5 dBm or higher */
++ max_tx_power = 127;
++ }
++ if (max_tx_power < -128)
++ max_tx_power = -128;
++ if (max_tx_power < 0)
++ tx_pwr = 0x80 + max_tx_power + 128;
++ else
++ tx_pwr = max_tx_power;
++
++ *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
++ *eid++ = 2 + tx_pwr_count;
++
++ /*
++ * Max Transmit Power count and
++ * Max Transmit Power units = 0 (EIRP)
++ */
++ *eid++ = tx_pwr_count;
++
++ for (i = 0; i <= tx_pwr_count; i++)
++ *eid++ = tx_pwr;
++
++ return eid;
++}
++
++
+ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab)
+ {
+ /* Disable VHT caps for STAs associated to no-VHT BSSes. */
+ if (!vht_capab ||
+- hapd->conf->disable_11ac ||
++ !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
+ !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
+ sta->flags &= ~WLAN_STA_VHT;
+ os_free(sta->vht_capabilities);
+@@ -159,6 +357,29 @@
+ }
+
+
++u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
++ const u8 *vht_oper)
++{
++ if (!vht_oper) {
++ os_free(sta->vht_operation);
++ sta->vht_operation = NULL;
++ return WLAN_STATUS_SUCCESS;
++ }
++
++ if (!sta->vht_operation) {
++ sta->vht_operation =
++ os_zalloc(sizeof(struct ieee80211_vht_operation));
++ if (!sta->vht_operation)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ os_memcpy(sta->vht_operation, vht_oper,
++ sizeof(struct ieee80211_vht_operation));
++
++ return WLAN_STATUS_SUCCESS;
++}
++
++
+ u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len)
+ {
+@@ -212,7 +433,7 @@
+ WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+ pos += 4;
+ *pos++ = VENDOR_VHT_SUBTYPE;
+- pos = hostapd_eid_vht_capabilities(hapd, pos);
++ pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+
+ return pos;
+--- contrib/wpa/src/ap/ieee802_1x.c.orig
++++ contrib/wpa/src/ap/ieee802_1x.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+- * Copyright (c) 2002-2012, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -31,9 +31,14 @@
+ #include "ap_drv_ops.h"
+ #include "wps_hostapd.h"
+ #include "hs20.h"
++/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
++#include "ieee802_11.h"
+ #include "ieee802_1x.h"
+
+
++#ifdef CONFIG_HS20
++static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
++#endif /* CONFIG_HS20 */
+ static void ieee802_1x_finished(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ int remediation);
+@@ -219,7 +224,7 @@
+ MAC2STR(sta->addr));
+
+ #ifndef CONFIG_NO_VLAN
+- if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
++ if (sta->vlan_id > 0) {
+ wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+ return;
+ }
+@@ -313,6 +318,7 @@
+ hdr->code != EAP_CODE_INITIATE))
+ return;
+
++ eap_erp_update_identity(sm->eap, eap, len);
+ identity = eap_get_identity(sm->eap, &identity_len);
+ if (identity == NULL)
+ return;
+@@ -402,7 +408,16 @@
+ char buf[128];
+
+ if (!hostapd_config_get_radius_attr(req_attr,
++ RADIUS_ATTR_SERVICE_TYPE) &&
++ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
++ RADIUS_SERVICE_TYPE_FRAMED)) {
++ wpa_printf(MSG_ERROR, "Could not add Service-Type");
++ return -1;
++ }
++
++ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_PORT) &&
++ sta->aid > 0 &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+ return -1;
+@@ -435,9 +450,9 @@
+ return -1;
+ }
+
+- if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
+- os_snprintf(buf, sizeof(buf), "%08X-%08X",
+- sta->acct_session_id_hi, sta->acct_session_id_lo);
++ if (sta->acct_session_id) {
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long) sta->acct_session_id);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+@@ -445,7 +460,22 @@
+ }
+ }
+
+-#ifdef CONFIG_IEEE80211R
++ if ((hapd->conf->wpa & 2) &&
++ !hapd->conf->disable_pmksa_caching &&
++ sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) {
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long)
++ sta->eapol_sm->acct_multi_session_id);
++ if (!radius_msg_add_attr(
++ msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
++ (u8 *) buf, os_strlen(buf))) {
++ wpa_printf(MSG_INFO,
++ "Could not add Acct-Multi-Session-Id");
++ return -1;
++ }
++ }
++
++#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ sta->wpa_sm &&
+ (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+@@ -458,7 +488,7 @@
+ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+ return -1;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+ add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+@@ -475,6 +505,7 @@
+ {
+ char buf[128];
+ struct hostapd_radius_attr *attr;
++ int len;
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IP_ADDRESS) &&
+@@ -506,15 +537,15 @@
+ return -1;
+ }
+
+- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+- MAC2STR(hapd->own_addr),
+- wpa_ssid_txt(hapd->conf->ssid.ssid,
+- hapd->conf->ssid.ssid_len));
+- buf[sizeof(buf) - 1] = '\0';
++ len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
++ MAC2STR(hapd->own_addr));
++ os_memcpy(&buf[len], hapd->conf->ssid.ssid,
++ hapd->conf->ssid.ssid_len);
++ len += hapd->conf->ssid.ssid_len;
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_CALLED_STATION_ID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+- (u8 *) buf, os_strlen(buf))) {
++ (u8 *) buf, len)) {
+ wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+ return -1;
+ }
+@@ -560,9 +591,9 @@
+ }
+
+
+-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+- struct sta_info *sta,
+- const u8 *eap, size_t len)
++void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *eap, size_t len)
+ {
+ struct radius_msg *msg;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+@@ -583,7 +614,10 @@
+ return;
+ }
+
+- radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
++ if (radius_msg_make_authenticator(msg) < 0) {
++ wpa_printf(MSG_INFO, "Could not make Request Authenticator");
++ goto fail;
++ }
+
+ if (sm->identity &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+@@ -648,7 +682,8 @@
+
+ #ifdef CONFIG_HS20
+ if (hapd->conf->hs20) {
+- u8 ver = 1; /* Release 2 */
++ u8 ver = hapd->conf->hs20_release - 1;
++
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+ &ver, 1)) {
+@@ -678,6 +713,41 @@
+ goto fail;
+ }
+ }
++
++ if (sta->roaming_consortium &&
++ !radius_msg_add_wfa(
++ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
++ wpabuf_head(sta->roaming_consortium),
++ wpabuf_len(sta->roaming_consortium))) {
++ wpa_printf(MSG_ERROR,
++ "Could not add HS 2.0 Roaming Consortium");
++ goto fail;
++ }
++
++ if (hapd->conf->t_c_filename) {
++ be32 timestamp;
++
++ if (!radius_msg_add_wfa(
++ msg,
++ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
++ (const u8 *) hapd->conf->t_c_filename,
++ os_strlen(hapd->conf->t_c_filename))) {
++ wpa_printf(MSG_ERROR,
++ "Could not add HS 2.0 T&C Filename");
++ goto fail;
++ }
++
++ timestamp = host_to_be32(hapd->conf->t_c_timestamp);
++ if (!radius_msg_add_wfa(
++ msg,
++ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
++ (const u8 *) ×tamp,
++ sizeof(timestamp))) {
++ wpa_printf(MSG_ERROR,
++ "Could not add HS 2.0 Timestamp");
++ goto fail;
++ }
++ }
+ }
+ #endif /* CONFIG_HS20 */
+
+@@ -814,7 +884,7 @@
+ }
+
+
+-static struct eapol_state_machine *
++struct eapol_state_machine *
+ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ int flags = 0;
+@@ -831,6 +901,29 @@
+ }
+
+
++static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
++ size_t len)
++{
++ if (sta->pending_eapol_rx) {
++ wpabuf_free(sta->pending_eapol_rx->buf);
++ } else {
++ sta->pending_eapol_rx =
++ os_malloc(sizeof(*sta->pending_eapol_rx));
++ if (!sta->pending_eapol_rx)
++ return;
++ }
++
++ sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len);
++ if (!sta->pending_eapol_rx->buf) {
++ os_free(sta->pending_eapol_rx);
++ sta->pending_eapol_rx = NULL;
++ return;
++ }
++
++ os_get_reltime(&sta->pending_eapol_rx->rx_time);
++}
++
++
+ /**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+@@ -861,6 +954,13 @@
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
+ "associated/Pre-authenticating STA");
++
++ if (sta && (sta->flags & WLAN_STA_AUTH)) {
++ wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
++ " for later use", MAC2STR(sta->addr));
++ ieee802_1x_save_eapol(sta, buf, len);
++ }
++
+ return;
+ }
+
+@@ -909,7 +1009,9 @@
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
++ if (key_mgmt != -1 &&
++ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
++ key_mgmt == WPA_KEY_MGMT_DPP)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+ "STA is using PSK");
+ return;
+@@ -1047,18 +1149,20 @@
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPS to PSK.
+ */
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+ return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
++ if (key_mgmt != -1 &&
++ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
++ key_mgmt == WPA_KEY_MGMT_DPP)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPA-EAP to PSK.
+ */
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+ return;
+ }
+
+@@ -1093,7 +1197,7 @@
+
+ sta->eapol_sm->eap_if->portEnabled = TRUE;
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -1106,13 +1210,37 @@
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
++ sta->eapol_sm->portValid = TRUE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+- /* TODO: get vlan_id from R0KH using RRB message */
++ ap_sta_bind_vlan(hapd, sta);
+ return;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
++#ifdef CONFIG_FILS
++ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
++ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sta->auth_alg == WLAN_AUTH_FILS_PK) {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
++ HOSTAPD_LEVEL_DEBUG,
++ "PMK from FILS - skip IEEE 802.1X/EAP");
++ /* Setup EAPOL state machines to already authenticated state
++ * because of existing FILS information. */
++ sta->eapol_sm->keyRun = TRUE;
++ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
++ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
++ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
++ sta->eapol_sm->authSuccess = TRUE;
++ sta->eapol_sm->authFail = FALSE;
++ sta->eapol_sm->portValid = TRUE;
++ if (sta->eapol_sm->eap)
++ eap_sm_notify_cached(sta->eapol_sm->eap);
++ wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
++ return;
++ }
++#endif /* CONFIG_FILS */
++
+ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+@@ -1128,7 +1256,7 @@
+ sta->eapol_sm->authFail = FALSE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+- pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
++ pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
+ ap_sta_bind_vlan(hapd, sta);
+ } else {
+ if (reassoc) {
+@@ -1144,10 +1272,20 @@
+ }
+
+
+-void ieee802_1x_free_station(struct sta_info *sta)
++void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
++#ifdef CONFIG_HS20
++ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
++#endif /* CONFIG_HS20 */
++
++ if (sta->pending_eapol_rx) {
++ wpabuf_free(sta->pending_eapol_rx->buf);
++ os_free(sta->pending_eapol_rx);
++ sta->pending_eapol_rx = NULL;
++ }
++
+ if (sm == NULL)
+ return;
+
+@@ -1156,10 +1294,8 @@
+ #ifndef CONFIG_NO_RADIUS
+ radius_msg_free(sm->last_recv_radius);
+ radius_free_class(&sm->radius_class);
+- wpabuf_free(sm->radius_cui);
+ #endif /* CONFIG_NO_RADIUS */
+
+- os_free(sm->identity);
+ eapol_auth_free(sm);
+ }
+
+@@ -1325,11 +1461,10 @@
+ }
+ } while (class_len < 1);
+
+- nclass[nclass_count].data = os_malloc(class_len);
++ nclass[nclass_count].data = os_memdup(attr_class, class_len);
+ if (nclass[nclass_count].data == NULL)
+ break;
+
+- os_memcpy(nclass[nclass_count].data, attr_class, class_len);
+ nclass[nclass_count].len = class_len;
+ nclass_count++;
+ }
+@@ -1489,6 +1624,33 @@
+ ap_sta_session_warning_timeout(hapd, sta, warning_time);
+ }
+
++
++static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
++ struct sta_info *sta, u8 *pos,
++ size_t len)
++{
++ if (len < 4)
++ return; /* Malformed information */
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
++ pos[0], pos[1], pos[2], pos[3]);
++ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
++}
++
++
++static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
++ struct sta_info *sta, u8 *pos, size_t len)
++{
++ os_free(sta->t_c_url);
++ sta->t_c_url = os_malloc(len + 1);
++ if (!sta->t_c_url)
++ return;
++ os_memcpy(sta->t_c_url, pos, len);
++ sta->t_c_url[len] = '\0';
++ wpa_printf(MSG_DEBUG,
++ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
++}
++
+ #endif /* CONFIG_HS20 */
+
+
+@@ -1536,6 +1698,12 @@
+ ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+ session_timeout);
+ break;
++ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
++ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
++ break;
++ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
++ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
++ break;
+ }
+ }
+ #endif /* CONFIG_HS20 */
+@@ -1575,6 +1743,45 @@
+ }
+
+
++#ifndef CONFIG_NO_VLAN
++static int ieee802_1x_update_vlan(struct radius_msg *msg,
++ struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ struct vlan_description vlan_desc;
++
++ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
++ vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
++ MAX_NUM_TAGGED_VLAN,
++ vlan_desc.tagged);
++
++ if (vlan_desc.notempty &&
++ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
++ sta->eapol_sm->authFail = TRUE;
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
++ HOSTAPD_LEVEL_INFO,
++ "Invalid VLAN %d%s received from RADIUS server",
++ vlan_desc.untagged,
++ vlan_desc.tagged[0] ? "+" : "");
++ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
++ ap_sta_set_vlan(hapd, sta, &vlan_desc);
++ return -1;
++ }
++
++ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
++ !vlan_desc.notempty) {
++ sta->eapol_sm->authFail = TRUE;
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
++ HOSTAPD_LEVEL_INFO,
++ "authentication server did not include required VLAN ID in Access-Accept");
++ return -1;
++ }
++
++ return ap_sta_set_vlan(hapd, sta, &vlan_desc);
++}
++#endif /* CONFIG_NO_VLAN */
++
++
+ /**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+@@ -1592,7 +1799,8 @@
+ struct hostapd_data *hapd = data;
+ struct sta_info *sta;
+ u32 session_timeout = 0, termination_action, acct_interim_interval;
+- int session_timeout_set, vlan_id = 0;
++ int session_timeout_set;
++ u32 reason_code;
+ struct eapol_state_machine *sm;
+ int override_eapReq = 0;
+ struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+@@ -1659,51 +1867,35 @@
+
+ switch (hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+- vlan_id = 0;
+ #ifndef CONFIG_NO_VLAN
+- else
+- vlan_id = radius_msg_get_vlanid(msg);
+- if (vlan_id > 0 &&
+- hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
++ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
++ ieee802_1x_update_vlan(msg, hapd, sta) < 0)
++ break;
++
++ if (sta->vlan_id > 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+- "VLAN ID %d", vlan_id);
+- } else if (vlan_id > 0) {
+- sta->eapol_sm->authFail = TRUE;
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_RADIUS,
+- HOSTAPD_LEVEL_INFO,
+- "Invalid VLAN ID %d received from RADIUS server",
+- vlan_id);
+- break;
+- } else if (hapd->conf->ssid.dynamic_vlan ==
+- DYNAMIC_VLAN_REQUIRED) {
+- sta->eapol_sm->authFail = TRUE;
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_IEEE8021X,
+- HOSTAPD_LEVEL_INFO, "authentication "
+- "server did not include required VLAN "
+- "ID in Access-Accept");
+- break;
++ "VLAN ID %d", sta->vlan_id);
+ }
+-#endif /* CONFIG_NO_VLAN */
+
+- sta->vlan_id = vlan_id;
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ ap_sta_bind_vlan(hapd, sta) < 0)
+ break;
++#endif /* CONFIG_NO_VLAN */
+
+ sta->session_timeout_set = !!session_timeout_set;
+- sta->session_timeout = session_timeout;
++ os_get_reltime(&sta->session_timeout);
++ sta->session_timeout.sec += session_timeout;
+
+ /* RFC 3580, Ch. 3.17 */
+ if (session_timeout_set && termination_action ==
+- RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
++ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
+ sm->reAuthPeriod = session_timeout;
+- } else if (session_timeout_set)
++ else if (session_timeout_set)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
++ else
++ ap_sta_no_session_timeout(hapd, sta);
+
+ sm->eap_if->aaaSuccess = TRUE;
+ override_eapReq = 1;
+@@ -1715,19 +1907,17 @@
+ ieee802_1x_check_hs20(hapd, sta, msg,
+ session_timeout_set ?
+ (int) session_timeout : -1);
+- if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+- !sta->hs20_deauth_requested &&
+- wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
+- session_timeout_set ?
+- (int) session_timeout : -1, sm) == 0) {
+- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+- HOSTAPD_LEVEL_DEBUG,
+- "Added PMKSA cache entry");
+- }
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ sm->eap_if->aaaFail = TRUE;
+ override_eapReq = 1;
++ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
++ &reason_code) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
++ MACSTR, reason_code, MAC2STR(sta->addr));
++ sta->disconnect_reason_code = reason_code;
++ }
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ sm->eap_if->aaaEapReq = TRUE;
+@@ -1754,6 +1944,19 @@
+ if (override_eapReq)
+ sm->eap_if->aaaEapReq = FALSE;
+
++#ifdef CONFIG_FILS
++#ifdef NEED_AP_MLME
++ if (sta->flags & WLAN_STA_PENDING_FILS_ERP) {
++ /* TODO: Add a PMKSA entry on success? */
++ ieee802_11_finish_fils_auth(
++ hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
++ sm->eap_if->aaaEapReqData,
++ sm->eap_if->aaaEapKeyData,
++ sm->eap_if->aaaEapKeyDataLen);
++ }
++#endif /* NEED_AP_MLME */
++#endif /* CONFIG_FILS */
++
+ eapol_auth_step(sm);
+
+ return RADIUS_RX_QUEUED;
+@@ -1841,7 +2044,7 @@
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+ eapol->default_wep_key_idx);
+-
++
+ if (ieee802_1x_rekey_broadcast(hapd)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "failed to generate a "
+@@ -1951,13 +2154,19 @@
+ }
+
+ if (eap_user->password) {
+- user->password = os_malloc(eap_user->password_len);
++ user->password = os_memdup(eap_user->password,
++ eap_user->password_len);
+ if (user->password == NULL)
+ goto out;
+- os_memcpy(user->password, eap_user->password,
+- eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
++ if (eap_user->salt && eap_user->salt_len) {
++ user->salt = os_memdup(eap_user->salt,
++ eap_user->salt_len);
++ if (!user->salt)
++ goto out;
++ user->salt_len = eap_user->salt_len;
++ }
+ }
+ user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
+@@ -2107,6 +2316,7 @@
+ conf.erp_domain = hapd->conf->erp_domain;
+ conf.erp = hapd->conf->eap_server_erp;
+ conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
++ conf.tls_flags = hapd->conf->tls_flags;
+ conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
+ conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
+ conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+@@ -2190,7 +2400,7 @@
+ {
+ eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
+
+- if (hapd->driver != NULL &&
++ if (hapd->driver && hapd->drv_priv &&
+ (hapd->conf->ieee802_1x || hapd->conf->wpa))
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+@@ -2243,6 +2453,16 @@
+ MAC2STR(sta->addr), xhdr->version, xhdr->type,
+ be_to_host16(xhdr->length), ack);
+
++#ifdef CONFIG_WPS
++ if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
++ (sta->flags & WLAN_STA_WPS) &&
++ ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
++ wpa_printf(MSG_DEBUG,
++ "WPS: Indicate EAP completion on ACK for EAP-Failure");
++ hostapd_wps_eap_completed(hapd);
++ }
++#endif /* CONFIG_WPS */
++
+ if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
+ return 0;
+
+@@ -2375,6 +2595,7 @@
+ struct os_reltime diff;
+ const char *name1;
+ const char *name2;
++ char *identity_buf = NULL;
+
+ if (sm == NULL)
+ return 0;
+@@ -2490,31 +2711,41 @@
+
+ /* dot1xAuthSessionStatsTable */
+ os_reltime_age(&sta->acct_session_start, &diff);
++ if (sm->eap && !sm->identity) {
++ const u8 *id;
++ size_t id_len;
++
++ id = eap_get_identity(sm->eap, &id_len);
++ if (id)
++ identity_buf = dup_binstr(id, id_len);
++ }
+ ret = os_snprintf(buf + len, buflen - len,
+ /* TODO: dot1xAuthSessionOctetsRx */
+ /* TODO: dot1xAuthSessionOctetsTx */
+ /* TODO: dot1xAuthSessionFramesRx */
+ /* TODO: dot1xAuthSessionFramesTx */
+- "dot1xAuthSessionId=%08X-%08X\n"
++ "dot1xAuthSessionId=%016llX\n"
+ "dot1xAuthSessionAuthenticMethod=%d\n"
+ "dot1xAuthSessionTime=%u\n"
+ "dot1xAuthSessionTerminateCause=999\n"
+ "dot1xAuthSessionUserName=%s\n",
+- sta->acct_session_id_hi, sta->acct_session_id_lo,
++ (unsigned long long) sta->acct_session_id,
+ (wpa_key_mgmt_wpa_ieee8021x(
+ wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+ 1 : 2,
+ (unsigned int) diff.sec,
+- sm->identity);
++ sm->identity ? (char *) sm->identity :
++ (identity_buf ? identity_buf : "N/A"));
++ os_free(identity_buf);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+- if (sm->acct_multi_session_id_hi) {
++ if (sm->acct_multi_session_id) {
+ ret = os_snprintf(buf + len, buflen - len,
+- "authMultiSessionId=%08X+%08X\n",
+- sm->acct_multi_session_id_hi,
+- sm->acct_multi_session_id_lo);
++ "authMultiSessionId=%016llX\n",
++ (unsigned long long)
++ sm->acct_multi_session_id);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+@@ -2535,6 +2766,43 @@
+ }
+
+
++#ifdef CONFIG_HS20
++static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct sta_info *sta = timeout_ctx;
++
++ if (sta->remediation) {
++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
++ MACSTR " to indicate Subscription Remediation",
++ MAC2STR(sta->addr));
++ hs20_send_wnm_notification(hapd, sta->addr,
++ sta->remediation_method,
++ sta->remediation_url);
++ os_free(sta->remediation_url);
++ sta->remediation_url = NULL;
++ }
++
++ if (sta->hs20_deauth_req) {
++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
++ MACSTR " to indicate imminent deauthentication",
++ MAC2STR(sta->addr));
++ hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
++ sta->hs20_deauth_req);
++ }
++
++ if (sta->hs20_t_c_filtering) {
++ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
++ MACSTR " to indicate Terms and Conditions filtering",
++ MAC2STR(sta->addr));
++ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
++ os_free(sta->t_c_url);
++ sta->t_c_url = NULL;
++ }
++}
++#endif /* CONFIG_HS20 */
++
++
+ static void ieee802_1x_finished(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ int remediation)
+@@ -2544,6 +2812,7 @@
+ /* TODO: get PMKLifetime from WPA parameters */
+ static const int dot11RSNAConfigPMKLifetime = 43200;
+ unsigned int session_timeout;
++ struct os_reltime now, remaining;
+
+ #ifdef CONFIG_HS20
+ if (remediation && !sta->remediation) {
+@@ -2554,37 +2823,27 @@
+ sta->remediation_method = 1; /* SOAP-XML SPP */
+ }
+
+- if (success) {
+- if (sta->remediation) {
+- wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+- "to " MACSTR " to indicate Subscription "
+- "Remediation",
+- MAC2STR(sta->addr));
+- hs20_send_wnm_notification(hapd, sta->addr,
+- sta->remediation_method,
+- sta->remediation_url);
+- os_free(sta->remediation_url);
+- sta->remediation_url = NULL;
+- }
+-
+- if (sta->hs20_deauth_req) {
+- wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+- "to " MACSTR " to indicate imminent "
+- "deauthentication", MAC2STR(sta->addr));
+- hs20_send_wnm_notification_deauth_req(
+- hapd, sta->addr, sta->hs20_deauth_req);
+- }
++ if (success && (sta->remediation || sta->hs20_deauth_req ||
++ sta->hs20_t_c_filtering)) {
++ wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
++ MACSTR " in 100 ms", MAC2STR(sta->addr));
++ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
++ eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send,
++ hapd, sta);
+ }
+ #endif /* CONFIG_HS20 */
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &len);
+- if (sta->session_timeout_set)
+- session_timeout = sta->session_timeout;
+- else
++ if (sta->session_timeout_set) {
++ os_get_reltime(&now);
++ os_reltime_sub(&sta->session_timeout, &now, &remaining);
++ session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
++ } else {
+ session_timeout = dot11RSNAConfigPMKLifetime;
++ }
+ if (success && key && len >= PMK_LEN && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
+- wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
++ wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
+ sta->eapol_sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+@@ -2602,15 +2861,6 @@
+ * EAP-FAST with anonymous provisioning, may require another
+ * EAPOL authentication to be started to complete connection.
+ */
+- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
+- "disconnection after EAP-Failure");
+- /* Add a small sleep to increase likelihood of previously
+- * requested EAP-Failure TX getting out before this should the
+- * driver reorder operations.
+- */
+- os_sleep(0, 10000);
+- ap_sta_disconnect(hapd, sta, sta->addr,
+- WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+- hostapd_wps_eap_completed(hapd);
++ ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
+ }
+ }
+--- contrib/wpa/src/ap/ieee802_1x.h.orig
++++ contrib/wpa/src/ap/ieee802_1x.h
+@@ -21,7 +21,7 @@
+ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+ size_t len);
+ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+-void ieee802_1x_free_station(struct sta_info *sta);
++void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+@@ -57,5 +57,10 @@
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg);
++void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *eap, size_t len);
++struct eapol_state_machine *
++ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
+
+ #endif /* IEEE802_1X_H */
+--- contrib/wpa/src/ap/mbo_ap.c.orig
++++ contrib/wpa/src/ap/mbo_ap.c
+@@ -0,0 +1,244 @@
++/*
++ * hostapd - MBO
++ * Copyright (c) 2016, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/ieee802_11_defs.h"
++#include "common/ieee802_11_common.h"
++#include "hostapd.h"
++#include "sta_info.h"
++#include "mbo_ap.h"
++
++
++void mbo_ap_sta_free(struct sta_info *sta)
++{
++ struct mbo_non_pref_chan_info *info, *prev;
++
++ info = sta->non_pref_chan;
++ sta->non_pref_chan = NULL;
++ while (info) {
++ prev = info;
++ info = info->next;
++ os_free(prev);
++ }
++}
++
++
++static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
++ const u8 *buf, size_t len)
++{
++ struct mbo_non_pref_chan_info *info, *tmp;
++ char channels[200], *pos, *end;
++ size_t num_chan, i;
++ int ret;
++
++ if (len <= 3)
++ return; /* Not enough room for any channels */
++
++ num_chan = len - 3;
++ info = os_zalloc(sizeof(*info) + num_chan);
++ if (!info)
++ return;
++ info->op_class = buf[0];
++ info->pref = buf[len - 2];
++ info->reason_code = buf[len - 1];
++ info->num_channels = num_chan;
++ buf++;
++ os_memcpy(info->channels, buf, num_chan);
++ if (!sta->non_pref_chan) {
++ sta->non_pref_chan = info;
++ } else {
++ tmp = sta->non_pref_chan;
++ while (tmp->next)
++ tmp = tmp->next;
++ tmp->next = info;
++ }
++
++ pos = channels;
++ end = pos + sizeof(channels);
++ *pos = '\0';
++ for (i = 0; i < num_chan; i++) {
++ ret = os_snprintf(pos, end - pos, "%s%u",
++ i == 0 ? "" : " ", buf[i]);
++ if (os_snprintf_error(end - pos, ret)) {
++ *pos = '\0';
++ break;
++ }
++ pos += ret;
++ }
++
++ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
++ " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
++ MAC2STR(sta->addr), info->op_class, info->pref,
++ info->reason_code, channels);
++}
++
++
++void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
++ struct ieee802_11_elems *elems)
++{
++ const u8 *pos, *attr, *end;
++ size_t len;
++
++ if (!hapd->conf->mbo_enabled || !elems->mbo)
++ return;
++
++ pos = elems->mbo + 4;
++ len = elems->mbo_len - 4;
++ wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
++
++ attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
++ if (attr && attr[1] >= 1)
++ sta->cell_capa = attr[2];
++
++ mbo_ap_sta_free(sta);
++ end = pos + len;
++ while (end - pos > 1) {
++ u8 ie_len = pos[1];
++
++ if (2 + ie_len > end - pos)
++ break;
++
++ if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
++ mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
++ pos += 2 + pos[1];
++ }
++}
++
++
++int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
++{
++ char *pos = buf, *end = buf + buflen;
++ int ret;
++ struct mbo_non_pref_chan_info *info;
++ u8 i;
++ unsigned int count = 0;
++
++ if (!sta->cell_capa)
++ return 0;
++
++ ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
++ if (os_snprintf_error(end - pos, ret))
++ return pos - buf;
++ pos += ret;
++
++ for (info = sta->non_pref_chan; info; info = info->next) {
++ char *pos2 = pos;
++
++ ret = os_snprintf(pos2, end - pos2,
++ "non_pref_chan[%u]=%u:%u:%u:",
++ count, info->op_class, info->pref,
++ info->reason_code);
++ count++;
++ if (os_snprintf_error(end - pos2, ret))
++ break;
++ pos2 += ret;
++
++ for (i = 0; i < info->num_channels; i++) {
++ ret = os_snprintf(pos2, end - pos2, "%u%s",
++ info->channels[i],
++ i + 1 < info->num_channels ?
++ "," : "");
++ if (os_snprintf_error(end - pos2, ret)) {
++ pos2 = NULL;
++ break;
++ }
++ pos2 += ret;
++ }
++
++ if (!pos2)
++ break;
++ ret = os_snprintf(pos2, end - pos2, "\n");
++ if (os_snprintf_error(end - pos2, ret))
++ break;
++ pos2 += ret;
++ pos = pos2;
++ }
++
++ return pos - buf;
++}
++
++
++static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
++ const u8 *buf, size_t len)
++{
++ if (len < 1)
++ return;
++ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
++ " updated cellular data capability: %u",
++ MAC2STR(sta->addr), buf[0]);
++ sta->cell_capa = buf[0];
++}
++
++
++static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
++ const u8 *buf, size_t len,
++ int *first_non_pref_chan)
++{
++ switch (type) {
++ case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
++ if (*first_non_pref_chan) {
++ /*
++ * Need to free the previously stored entries now to
++ * allow the update to replace all entries.
++ */
++ *first_non_pref_chan = 0;
++ mbo_ap_sta_free(sta);
++ }
++ mbo_ap_parse_non_pref_chan(sta, buf, len);
++ break;
++ case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
++ mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
++ break;
++ default:
++ wpa_printf(MSG_DEBUG,
++ "MBO: Ignore unknown WNM Notification WFA subelement %u",
++ type);
++ break;
++ }
++}
++
++
++void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
++ const u8 *buf, size_t len)
++{
++ const u8 *pos, *end;
++ u8 ie_len;
++ struct sta_info *sta;
++ int first_non_pref_chan = 1;
++
++ if (!hapd->conf->mbo_enabled)
++ return;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return;
++
++ pos = buf;
++ end = buf + len;
++
++ while (end - pos > 1) {
++ ie_len = pos[1];
++
++ if (2 + ie_len > end - pos)
++ break;
++
++ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
++ ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
++ mbo_ap_wnm_notif_req_elem(sta, pos[5],
++ pos + 6, ie_len - 4,
++ &first_non_pref_chan);
++ else
++ wpa_printf(MSG_DEBUG,
++ "MBO: Ignore unknown WNM Notification element %u (len=%u)",
++ pos[0], pos[1]);
++
++ pos += 2 + pos[1];
++ }
++}
+--- contrib/wpa/src/ap/mbo_ap.h.orig
++++ contrib/wpa/src/ap/mbo_ap.h
+@@ -0,0 +1,51 @@
++/*
++ * MBO related functions and structures
++ * Copyright (c) 2016, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef MBO_AP_H
++#define MBO_AP_H
++
++struct hostapd_data;
++struct sta_info;
++struct ieee802_11_elems;
++
++#ifdef CONFIG_MBO
++
++void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
++ struct ieee802_11_elems *elems);
++int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
++void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
++ const u8 *buf, size_t len);
++void mbo_ap_sta_free(struct sta_info *sta);
++
++#else /* CONFIG_MBO */
++
++static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
++ struct sta_info *sta,
++ struct ieee802_11_elems *elems)
++{
++}
++
++static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
++ size_t buflen)
++{
++ return 0;
++}
++
++static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
++ const u8 *addr,
++ const u8 *buf, size_t len)
++{
++}
++
++static inline void mbo_ap_sta_free(struct sta_info *sta)
++{
++}
++
++#endif /* CONFIG_MBO */
++
++#endif /* MBO_AP_H */
+--- contrib/wpa/src/ap/ndisc_snoop.c.orig
++++ contrib/wpa/src/ap/ndisc_snoop.c
+@@ -17,6 +17,7 @@
+ #include "ap_drv_ops.h"
+ #include "list.h"
+ #include "x_snoop.h"
++#include "ndisc_snoop.h"
+
+ struct ip6addr {
+ struct in6_addr addr;
+@@ -181,4 +182,5 @@
+ void ndisc_snoop_deinit(struct hostapd_data *hapd)
+ {
+ l2_packet_deinit(hapd->sock_ndisc);
++ hapd->sock_ndisc = NULL;
+ }
+--- contrib/wpa/src/ap/neighbor_db.c.orig
++++ contrib/wpa/src/ap/neighbor_db.c
+@@ -0,0 +1,257 @@
++/*
++ * hostapd / Neighboring APs DB
++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "hostapd.h"
++#include "ieee802_11.h"
++#include "neighbor_db.h"
++
++
++struct hostapd_neighbor_entry *
++hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid)
++{
++ struct hostapd_neighbor_entry *nr;
++
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++ list) {
++ if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
++ (!ssid ||
++ (ssid->ssid_len == nr->ssid.ssid_len &&
++ os_memcmp(ssid->ssid, nr->ssid.ssid,
++ ssid->ssid_len) == 0)))
++ return nr;
++ }
++ return NULL;
++}
++
++
++static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
++{
++ wpabuf_free(nr->nr);
++ nr->nr = NULL;
++ wpabuf_free(nr->lci);
++ nr->lci = NULL;
++ wpabuf_free(nr->civic);
++ nr->civic = NULL;
++ os_memset(nr->bssid, 0, sizeof(nr->bssid));
++ os_memset(&nr->ssid, 0, sizeof(nr->ssid));
++ nr->stationary = 0;
++}
++
++
++static struct hostapd_neighbor_entry *
++hostapd_neighbor_add(struct hostapd_data *hapd)
++{
++ struct hostapd_neighbor_entry *nr;
++
++ nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
++ if (!nr)
++ return NULL;
++
++ dl_list_add(&hapd->nr_db, &nr->list);
++
++ return nr;
++}
++
++
++int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid,
++ const struct wpabuf *nr, const struct wpabuf *lci,
++ const struct wpabuf *civic, int stationary)
++{
++ struct hostapd_neighbor_entry *entry;
++
++ entry = hostapd_neighbor_get(hapd, bssid, ssid);
++ if (!entry)
++ entry = hostapd_neighbor_add(hapd);
++ if (!entry)
++ return -1;
++
++ hostapd_neighbor_clear_entry(entry);
++
++ os_memcpy(entry->bssid, bssid, ETH_ALEN);
++ os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
++
++ entry->nr = wpabuf_dup(nr);
++ if (!entry->nr)
++ goto fail;
++
++ if (lci && wpabuf_len(lci)) {
++ entry->lci = wpabuf_dup(lci);
++ if (!entry->lci || os_get_time(&entry->lci_date))
++ goto fail;
++ }
++
++ if (civic && wpabuf_len(civic)) {
++ entry->civic = wpabuf_dup(civic);
++ if (!entry->civic)
++ goto fail;
++ }
++
++ entry->stationary = stationary;
++
++ return 0;
++
++fail:
++ hostapd_neighbor_remove(hapd, bssid, ssid);
++ return -1;
++}
++
++
++int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid)
++{
++ struct hostapd_neighbor_entry *nr;
++
++ nr = hostapd_neighbor_get(hapd, bssid, ssid);
++ if (!nr)
++ return -1;
++
++ hostapd_neighbor_clear_entry(nr);
++ dl_list_del(&nr->list);
++ os_free(nr);
++
++ return 0;
++}
++
++
++void hostapd_free_neighbor_db(struct hostapd_data *hapd)
++{
++ struct hostapd_neighbor_entry *nr, *prev;
++
++ dl_list_for_each_safe(nr, prev, &hapd->nr_db,
++ struct hostapd_neighbor_entry, list) {
++ hostapd_neighbor_clear_entry(nr);
++ dl_list_del(&nr->list);
++ os_free(nr);
++ }
++}
++
++
++#ifdef NEED_AP_MLME
++static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
++ int ht, int vht)
++{
++ if (!ht && !vht)
++ return NR_CHAN_WIDTH_20;
++ if (!hapd->iconf->secondary_channel)
++ return NR_CHAN_WIDTH_20;
++ if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
++ return NR_CHAN_WIDTH_40;
++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
++ return NR_CHAN_WIDTH_80;
++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
++ return NR_CHAN_WIDTH_160;
++ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
++ return NR_CHAN_WIDTH_80P80;
++ return NR_CHAN_WIDTH_20;
++}
++#endif /* NEED_AP_MLME */
++
++
++void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
++{
++#ifdef NEED_AP_MLME
++ u16 capab = hostapd_own_capab_info(hapd);
++ int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
++ int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
++ struct wpa_ssid_value ssid;
++ u8 channel, op_class;
++ u8 center_freq1_idx = 0, center_freq2_idx = 0;
++ enum nr_chan_width width;
++ u32 bssid_info;
++ struct wpabuf *nr;
++
++ if (!(hapd->conf->radio_measurements[0] &
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
++ return;
++
++ bssid_info = 3; /* AP is reachable */
++ bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
++ bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
++
++ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
++ bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
++
++ bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
++
++ if (hapd->conf->wmm_enabled) {
++ bssid_info |= NEI_REP_BSSID_INFO_QOS;
++
++ if (hapd->conf->wmm_uapsd &&
++ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
++ bssid_info |= NEI_REP_BSSID_INFO_APSD;
++ }
++
++ if (ht) {
++ bssid_info |= NEI_REP_BSSID_INFO_HT |
++ NEI_REP_BSSID_INFO_DELAYED_BA;
++
++ /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
++ if (vht)
++ bssid_info |= NEI_REP_BSSID_INFO_VHT;
++ }
++
++ /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
++
++ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
++ hapd->iconf->secondary_channel,
++ hapd->iconf->vht_oper_chwidth,
++ &op_class, &channel) ==
++ NUM_HOSTAPD_MODES)
++ return;
++ width = hostapd_get_nr_chan_width(hapd, ht, vht);
++ if (vht) {
++ center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
++ if (width == NR_CHAN_WIDTH_80P80)
++ center_freq2_idx =
++ hapd->iconf->vht_oper_centr_freq_seg1_idx;
++ } else if (ht) {
++ ieee80211_freq_to_chan(hapd->iface->freq +
++ 10 * hapd->iconf->secondary_channel,
++ ¢er_freq1_idx);
++ }
++
++ ssid.ssid_len = hapd->conf->ssid.ssid_len;
++ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
++
++ /*
++ * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
++ * phy type + wide bandwidth channel subelement.
++ */
++ nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
++ if (!nr)
++ return;
++
++ wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
++ wpabuf_put_le32(nr, bssid_info);
++ wpabuf_put_u8(nr, op_class);
++ wpabuf_put_u8(nr, channel);
++ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
++
++ /*
++ * Wide Bandwidth Channel subelement may be needed to allow the
++ * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
++ * Figure 9-301.
++ */
++ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
++ wpabuf_put_u8(nr, 3);
++ wpabuf_put_u8(nr, width);
++ wpabuf_put_u8(nr, center_freq1_idx);
++ wpabuf_put_u8(nr, center_freq2_idx);
++
++ hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
++ hapd->iconf->civic, hapd->iconf->stationary_ap);
++
++ wpabuf_free(nr);
++#endif /* NEED_AP_MLME */
++}
+--- contrib/wpa/src/ap/neighbor_db.h.orig
++++ contrib/wpa/src/ap/neighbor_db.h
+@@ -0,0 +1,25 @@
++/*
++ * hostapd / Neighboring APs DB
++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef NEIGHBOR_DB_H
++#define NEIGHBOR_DB_H
++
++struct hostapd_neighbor_entry *
++hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid);
++int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid,
++ const struct wpabuf *nr, const struct wpabuf *lci,
++ const struct wpabuf *civic, int stationary);
++void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
++int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid);
++void hostapd_free_neighbor_db(struct hostapd_data *hapd);
++
++#endif /* NEIGHBOR_DB_H */
+--- contrib/wpa/src/ap/peerkey_auth.c.orig
++++ contrib/wpa/src/ap/peerkey_auth.c
+@@ -1,396 +0,0 @@
+-/*
+- * hostapd - PeerKey for Direct Link Setup (DLS)
+- * Copyright (c) 2006-2009, Jouni Malinen
+- *
+- * This software may be distributed under the terms of the BSD license.
+- * See README for more details.
+- */
+-
+-#include "utils/includes.h"
+-
+-#include "utils/common.h"
+-#include "utils/eloop.h"
+-#include "crypto/sha1.h"
+-#include "crypto/sha256.h"
+-#include "crypto/random.h"
+-#include "wpa_auth.h"
+-#include "wpa_auth_i.h"
+-#include "wpa_auth_ie.h"
+-
+-#ifdef CONFIG_PEERKEY
+-
+-static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
+-{
+-#if 0
+- struct wpa_authenticator *wpa_auth = eloop_ctx;
+- struct wpa_stsl_negotiation *neg = timeout_ctx;
+-#endif
+-
+- /* TODO: ? */
+-}
+-
+-
+-struct wpa_stsl_search {
+- const u8 *addr;
+- struct wpa_state_machine *sm;
+-};
+-
+-
+-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
+-{
+- struct wpa_stsl_search *search = ctx;
+- if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
+- search->sm = sm;
+- return 1;
+- }
+- return 0;
+-}
+-
+-
+-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm, const u8 *peer,
+- u16 mui, u16 error_type)
+-{
+- u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
+- 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
+- u8 *pos;
+- struct rsn_error_kde error;
+-
+- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+- "Sending SMK Error");
+-
+- pos = kde;
+-
+- if (peer) {
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
+- NULL, 0);
+- }
+-
+- error.mui = host_to_be16(mui);
+- error.error_type = host_to_be16(error_type);
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
+- (u8 *) &error, sizeof(error), NULL, 0);
+-
+- __wpa_send_eapol(wpa_auth, sm,
+- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+- WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
+- NULL, NULL, kde, pos - kde, 0, 0, 0);
+-}
+-
+-
+-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+- const u8 *key_data, size_t key_data_len)
+-{
+- struct wpa_eapol_ie_parse kde;
+- struct wpa_stsl_search search;
+- u8 *buf, *pos;
+- size_t buf_len;
+-
+- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
+- return;
+- }
+-
+- if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+- kde.mac_addr_len < ETH_ALEN) {
+- wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+- "SMK M1");
+- return;
+- }
+-
+- /* Initiator = sm->addr; Peer = kde.mac_addr */
+-
+- search.addr = kde.mac_addr;
+- search.sm = NULL;
+- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+- 0 || search.sm == NULL) {
+- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+- " aborted - STA not associated anymore",
+- MAC2STR(kde.mac_addr));
+- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+- STK_ERR_STA_NR);
+- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
+- return;
+- }
+-
+- buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+- buf = os_malloc(buf_len);
+- if (buf == NULL)
+- return;
+- /* Initiator RSN IE */
+- os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
+- pos = buf + kde.rsn_ie_len;
+- /* Initiator MAC Address */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
+- NULL, 0);
+-
+- /* SMK M2:
+- * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+- * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
+- */
+-
+- wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
+- "Sending SMK M2");
+-
+- __wpa_send_eapol(wpa_auth, search.sm,
+- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+- WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
+- NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
+-
+- os_free(buf);
+-}
+-
+-
+-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
+- struct wpa_eapol_key *key,
+- struct wpa_eapol_ie_parse *kde,
+- const u8 *smk)
+-{
+- u8 *buf, *pos;
+- size_t buf_len;
+- u32 lifetime;
+-
+- /* SMK M4:
+- * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
+- * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
+- * Lifetime KDE)
+- */
+-
+- buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+- pos = buf = os_malloc(buf_len);
+- if (buf == NULL)
+- return;
+-
+- /* Initiator MAC Address */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
+- NULL, 0);
+-
+- /* Initiator Nonce */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
+- NULL, 0);
+-
+- /* SMK with PNonce */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+- key->key_nonce, WPA_NONCE_LEN);
+-
+- /* Lifetime */
+- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+-
+- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+- "Sending SMK M4");
+-
+- __wpa_send_eapol(wpa_auth, sm,
+- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+- WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
+- NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
+-
+- os_free(buf);
+-}
+-
+-
+-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
+- struct wpa_eapol_key *key,
+- struct wpa_eapol_ie_parse *kde,
+- const u8 *smk, const u8 *peer)
+-{
+- u8 *buf, *pos;
+- size_t buf_len;
+- u32 lifetime;
+-
+- /* SMK M5:
+- * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+- * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
+- * Lifetime KDE))
+- */
+-
+- buf_len = kde->rsn_ie_len +
+- 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
+- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
+- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+- pos = buf = os_malloc(buf_len);
+- if (buf == NULL)
+- return;
+-
+- /* Peer RSN IE */
+- os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
+- pos += kde->rsn_ie_len;
+-
+- /* Peer MAC Address */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
+-
+- /* PNonce */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
+- WPA_NONCE_LEN, NULL, 0);
+-
+- /* SMK and INonce */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
+- kde->nonce, WPA_NONCE_LEN);
+-
+- /* Lifetime */
+- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
+- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
+-
+- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+- "Sending SMK M5");
+-
+- __wpa_send_eapol(wpa_auth, sm,
+- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+- WPA_KEY_INFO_SMK_MESSAGE,
+- NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
+-
+- os_free(buf);
+-}
+-
+-
+-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+- const u8 *key_data, size_t key_data_len)
+-{
+- struct wpa_eapol_ie_parse kde;
+- struct wpa_stsl_search search;
+- u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
+-
+- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
+- return;
+- }
+-
+- if (kde.rsn_ie == NULL ||
+- kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+- kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
+- wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
+- "Nonce KDE in SMK M3");
+- return;
+- }
+-
+- /* Peer = sm->addr; Initiator = kde.mac_addr;
+- * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
+-
+- search.addr = kde.mac_addr;
+- search.sm = NULL;
+- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+- 0 || search.sm == NULL) {
+- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
+- " aborted - STA not associated anymore",
+- MAC2STR(kde.mac_addr));
+- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
+- STK_ERR_STA_NR);
+- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
+- return;
+- }
+-
+- if (random_get_bytes(smk, PMK_LEN)) {
+- wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
+- return;
+- }
+-
+- /* SMK = PRF-256(Random number, "SMK Derivation",
+- * AA || Time || INonce || PNonce)
+- */
+- os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+- pos = buf + ETH_ALEN;
+- wpa_get_ntp_timestamp(pos);
+- pos += 8;
+- os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
+- pos += WPA_NONCE_LEN;
+- os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
+-#ifdef CONFIG_IEEE80211W
+- sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+- smk, PMK_LEN);
+-#else /* CONFIG_IEEE80211W */
+- sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
+- smk, PMK_LEN);
+-#endif /* CONFIG_IEEE80211W */
+-
+- wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
+-
+- wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
+- wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
+-
+- /* Authenticator does not need SMK anymore and it is required to forget
+- * it. */
+- os_memset(smk, 0, sizeof(*smk));
+-}
+-
+-
+-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
+- const u8 *key_data, size_t key_data_len)
+-{
+- struct wpa_eapol_ie_parse kde;
+- struct wpa_stsl_search search;
+- struct rsn_error_kde error;
+- u16 mui, error_type;
+-
+- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
+- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+- return;
+- }
+-
+- if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+- kde.error == NULL || kde.error_len < sizeof(error)) {
+- wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
+- "SMK Error");
+- return;
+- }
+-
+- search.addr = kde.mac_addr;
+- search.sm = NULL;
+- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
+- 0 || search.sm == NULL) {
+- wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
+- "associated for SMK Error message from " MACSTR,
+- MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
+- return;
+- }
+-
+- os_memcpy(&error, kde.error, sizeof(error));
+- mui = be_to_host16(error.mui);
+- error_type = be_to_host16(error.error_type);
+- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+- "STA reported SMK Error: Peer " MACSTR
+- " MUI %d Error Type %d",
+- MAC2STR(kde.mac_addr), mui, error_type);
+-
+- wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
+-}
+-
+-
+-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+- struct wpa_stsl_negotiation *neg)
+-{
+- struct wpa_stsl_negotiation *pos, *prev;
+-
+- if (wpa_auth == NULL)
+- return -1;
+- pos = wpa_auth->stsl_negotiations;
+- prev = NULL;
+- while (pos) {
+- if (pos == neg) {
+- if (prev)
+- prev->next = pos->next;
+- else
+- wpa_auth->stsl_negotiations = pos->next;
+-
+- eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
+- os_free(pos);
+- return 0;
+- }
+- prev = pos;
+- pos = pos->next;
+- }
+-
+- return -1;
+-}
+-
+-#endif /* CONFIG_PEERKEY */
+--- contrib/wpa/src/ap/pmksa_cache_auth.c.orig
++++ contrib/wpa/src/ap/pmksa_cache_auth.c
+@@ -38,6 +38,7 @@
+
+ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+ {
++ os_free(entry->vlan_desc);
+ os_free(entry->identity);
+ wpabuf_free(entry->cui);
+ #ifndef CONFIG_NO_RADIUS
+@@ -91,6 +92,20 @@
+ }
+
+
++/**
++ * pmksa_cache_auth_flush - Flush all PMKSA cache entries
++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
++ */
++void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
++{
++ while (pmksa->pmksa) {
++ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
++ MACSTR, MAC2STR(pmksa->pmksa->spa));
++ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
++ }
++}
++
++
+ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+ {
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+@@ -126,6 +141,8 @@
+ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+ {
++ struct vlan_description *vlan_desc;
++
+ if (eapol == NULL)
+ return;
+
+@@ -146,14 +163,22 @@
+ #endif /* CONFIG_NO_RADIUS */
+
+ entry->eap_type_authsrv = eapol->eap_type_authsrv;
+- entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+
+- entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+- entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo;
++ vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
++ if (vlan_desc && vlan_desc->notempty) {
++ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
++ if (entry->vlan_desc)
++ *entry->vlan_desc = *vlan_desc;
++ } else {
++ entry->vlan_desc = NULL;
++ }
++
++ entry->acct_multi_session_id = eapol->acct_multi_session_id;
+ }
+
+
+-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
++void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
++ struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+ {
+ if (entry == NULL || eapol == NULL)
+@@ -186,10 +211,11 @@
+ }
+
+ eapol->eap_type_authsrv = entry->eap_type_authsrv;
+- ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
++#ifndef CONFIG_NO_VLAN
++ ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
++#endif /* CONFIG_NO_VLAN */
+
+- eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
+- eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
++ eapol->acct_multi_session_id = entry->acct_multi_session_id;
+ }
+
+
+@@ -234,6 +260,7 @@
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
++ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+@@ -250,15 +277,50 @@
+ */
+ struct rsn_pmksa_cache_entry *
+ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+- const u8 *pmk, size_t pmk_len,
++ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+ {
+- struct rsn_pmksa_cache_entry *entry, *pos;
++ struct rsn_pmksa_cache_entry *entry;
++
++ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
++ aa, spa, session_timeout, eapol,
++ akmp);
++
++ if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
++ return NULL;
++
++ return entry;
++}
++
++
++/**
++ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
++ * @pmk: The new pairwise master key
++ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
++ * @pmkid: Calculated PMKID
++ * @kck: Key confirmation key or %NULL if not yet derived
++ * @kck_len: KCK length in bytes
++ * @aa: Authenticator address
++ * @spa: Supplicant address
++ * @session_timeout: Session timeout
++ * @eapol: Pointer to EAPOL state machine data
++ * @akmp: WPA_KEY_MGMT_* used in key derivation
++ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
++ *
++ * This function creates a PMKSA entry.
++ */
++struct rsn_pmksa_cache_entry *
++pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
++ const u8 *kck, size_t kck_len, const u8 *aa,
++ const u8 *spa, int session_timeout,
++ struct eapol_state_machine *eapol, int akmp)
++{
++ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+- if (pmk_len > PMK_LEN)
++ if (pmk_len > PMK_LEN_MAX)
+ return NULL;
+
+ if (wpa_key_mgmt_suite_b(akmp) && !kck)
+@@ -269,13 +331,14 @@
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
++ if (pmkid)
++ os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
++ else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+ else if (wpa_key_mgmt_suite_b(akmp))
+ rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+ else
+- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+- wpa_key_mgmt_sha256(akmp));
++ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
+ os_get_reltime(&now);
+ entry->expiration = now.sec;
+ if (session_timeout > 0)
+@@ -286,9 +349,30 @@
+ os_memcpy(entry->spa, spa, ETH_ALEN);
+ pmksa_cache_from_eapol_data(entry, eapol);
+
++ return entry;
++}
++
++
++/**
++ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
++ * @entry: Pointer to PMKSA cache entry
++ *
++ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
++ * already in the cache for the same Supplicant, this entry will be replaced
++ * with the new entry. PMKID will be calculated based on the PMK.
++ */
++int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
++ struct rsn_pmksa_cache_entry *entry)
++{
++ struct rsn_pmksa_cache_entry *pos;
++
++ if (entry == NULL)
++ return -1;
++
+ /* Replace an old entry for the same STA (if found) with the new entry
+ */
+- pos = pmksa_cache_auth_get(pmksa, spa, NULL);
++ pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
+ if (pos)
+ pmksa_cache_free_entry(pmksa, pos);
+
+@@ -302,7 +386,7 @@
+
+ pmksa_cache_link_entry(pmksa, entry);
+
+- return entry;
++ return 0;
+ }
+
+
+@@ -337,7 +421,13 @@
+ radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+ #endif /* CONFIG_NO_RADIUS */
+ entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+- entry->vlan_id = old_entry->vlan_id;
++ if (old_entry->vlan_desc) {
++ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
++ if (entry->vlan_desc)
++ *entry->vlan_desc = *old_entry->vlan_desc;
++ } else {
++ entry->vlan_desc = NULL;
++ }
+ entry->opportunistic = 1;
+
+ pmksa_cache_link_entry(pmksa, entry);
+@@ -427,7 +517,7 @@
+ if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ continue;
+ rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+- wpa_key_mgmt_sha256(entry->akmp));
++ entry->akmp);
+ if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ }
+@@ -471,12 +561,11 @@
+ if (attr->acct_multi_session_id) {
+ char buf[20];
+
+- if (attr->acct_multi_session_id_len != 17)
++ if (attr->acct_multi_session_id_len != 16)
+ return 0;
+- os_snprintf(buf, sizeof(buf), "%08X+%08X",
+- entry->acct_multi_session_id_hi,
+- entry->acct_multi_session_id_lo);
+- if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
++ os_snprintf(buf, sizeof(buf), "%016llX",
++ (unsigned long long) entry->acct_multi_session_id);
++ if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
+ return 0;
+ match++;
+ }
+@@ -526,3 +615,115 @@
+
+ return found ? 0 : -1;
+ }
++
++
++/**
++ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
++ * @buf: Buffer for the list
++ * @len: Length of the buffer
++ * Returns: Number of bytes written to buffer
++ *
++ * This function is used to generate a text format representation of the
++ * current PMKSA cache contents for the ctrl_iface PMKSA command.
++ */
++int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
++{
++ int i, ret;
++ char *pos = buf;
++ struct rsn_pmksa_cache_entry *entry;
++ struct os_reltime now;
++
++ os_get_reltime(&now);
++ ret = os_snprintf(pos, buf + len - pos,
++ "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
++ if (os_snprintf_error(buf + len - pos, ret))
++ return pos - buf;
++ pos += ret;
++ i = 0;
++ entry = pmksa->pmksa;
++ while (entry) {
++ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
++ i, MAC2STR(entry->spa));
++ if (os_snprintf_error(buf + len - pos, ret))
++ return pos - buf;
++ pos += ret;
++ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
++ PMKID_LEN);
++ ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
++ (int) (entry->expiration - now.sec),
++ entry->opportunistic);
++ if (os_snprintf_error(buf + len - pos, ret))
++ return pos - buf;
++ pos += ret;
++ entry = entry->next;
++ }
++ return pos - buf;
++}
++
++
++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
++#ifdef CONFIG_MESH
++
++/**
++ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
++ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
++ * @addr: MAC address of the peer (NULL means any)
++ * @buf: Buffer for the list
++ * @len: Length of the buffer
++ * Returns: Number of bytes written to buffer
++ *
++ * This function is used to generate a text format representation of the
++ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
++ * in external storage.
++ */
++int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
++ char *buf, size_t len)
++{
++ int ret;
++ char *pos, *end;
++ struct rsn_pmksa_cache_entry *entry;
++ struct os_reltime now;
++
++ pos = buf;
++ end = buf + len;
++ os_get_reltime(&now);
++
++
++ /*
++ * Entry format:
++ *
++ */
++ for (entry = pmksa->pmksa; entry; entry = entry->next) {
++ if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
++ continue;
++
++ ret = os_snprintf(pos, end - pos, MACSTR " ",
++ MAC2STR(entry->spa));
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++ pos += ret;
++
++ pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
++ PMKID_LEN);
++
++ ret = os_snprintf(pos, end - pos, " ");
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++ pos += ret;
++
++ pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
++ entry->pmk_len);
++
++ ret = os_snprintf(pos, end - pos, " %d\n",
++ (int) (entry->expiration - now.sec));
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++ pos += ret;
++ }
++
++ return pos - buf;
++}
++
++#endif /* CONFIG_MESH */
++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+--- contrib/wpa/src/ap/pmksa_cache_auth.h.orig
++++ contrib/wpa/src/ap/pmksa_cache_auth.h
+@@ -17,7 +17,7 @@
+ struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next, *hnext;
+ u8 pmkid[PMKID_LEN];
+- u8 pmk[PMK_LEN];
++ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+@@ -28,14 +28,14 @@
+ struct wpabuf *cui;
+ struct radius_class_data radius_class;
+ u8 eap_type_authsrv;
+- int vlan_id;
++ struct vlan_description *vlan_desc;
+ int opportunistic;
+
+- u32 acct_multi_session_id_hi;
+- u32 acct_multi_session_id_lo;
++ u64 acct_multi_session_id;
+ };
+
+ struct rsn_pmksa_cache;
++struct radius_das_attrs;
+
+ struct rsn_pmksa_cache *
+ pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+@@ -49,19 +49,31 @@
+ const u8 *pmkid);
+ struct rsn_pmksa_cache_entry *
+ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+- const u8 *pmk, size_t pmk_len,
++ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+ struct rsn_pmksa_cache_entry *
++pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
++ const u8 *kck, size_t kck_len, const u8 *aa,
++ const u8 *spa, int session_timeout,
++ struct eapol_state_machine *eapol, int akmp);
++int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
++ struct rsn_pmksa_cache_entry *entry);
++struct rsn_pmksa_cache_entry *
+ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa, const u8 *pmkid);
+-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
++void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
++ struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol);
+ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr);
++int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
++void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
++int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
++ char *buf, size_t len);
+
+ #endif /* PMKSA_CACHE_H */
+--- contrib/wpa/src/ap/rrm.c.orig
++++ contrib/wpa/src/ap/rrm.c
+@@ -0,0 +1,674 @@
++/*
++ * hostapd / Radio Measurement (RRM)
++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
++ * Copyright (c) 2016-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/wpa_ctrl.h"
++#include "hostapd.h"
++#include "ap_drv_ops.h"
++#include "sta_info.h"
++#include "eloop.h"
++#include "neighbor_db.h"
++#include "rrm.h"
++
++#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
++
++
++static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
++{
++ struct hostapd_data *hapd = eloop_data;
++
++ wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
++ hapd->lci_req_token);
++ hapd->lci_req_active = 0;
++}
++
++
++static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
++ const u8 *pos, size_t len)
++{
++ if (!hapd->lci_req_active || hapd->lci_req_token != token) {
++ wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
++ return;
++ }
++
++ hapd->lci_req_active = 0;
++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
++ wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
++}
++
++
++static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
++{
++ struct hostapd_data *hapd = eloop_data;
++
++ wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
++ hapd->range_req_token);
++ hapd->range_req_active = 0;
++}
++
++
++static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
++ const u8 *pos, size_t len)
++{
++ if (!hapd->range_req_active || hapd->range_req_token != token) {
++ wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
++ token);
++ return;
++ }
++
++ hapd->range_req_active = 0;
++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
++ wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
++}
++
++
++static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
++ const u8 *addr, u8 token, u8 rep_mode,
++ const u8 *pos, size_t len)
++{
++ char report[2 * 255 + 1];
++
++ wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
++ token, len, MAC2STR(addr));
++ /* Skip to the beginning of the Beacon report */
++ if (len < 3)
++ return;
++ pos += 3;
++ len -= 3;
++ report[0] = '\0';
++ if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
++ return;
++ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
++ MAC2STR(addr), token, rep_mode, report);
++}
++
++
++static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
++ const u8 *buf, size_t len)
++{
++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++ const u8 *pos, *ie, *end;
++ u8 token, rep_mode;
++
++ end = buf + len;
++ token = mgmt->u.action.u.rrm.dialog_token;
++ pos = mgmt->u.action.u.rrm.variable;
++
++ while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
++ if (ie[1] < 3) {
++ wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
++ break;
++ }
++
++ rep_mode = ie[3];
++ wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
++ rep_mode, ie[4]);
++
++ switch (ie[4]) {
++ case MEASURE_TYPE_LCI:
++ hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
++ break;
++ case MEASURE_TYPE_FTM_RANGE:
++ hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
++ break;
++ case MEASURE_TYPE_BEACON:
++ hostapd_handle_beacon_report(hapd, mgmt->sa, token,
++ rep_mode, ie + 2, ie[1]);
++ break;
++ default:
++ wpa_printf(MSG_DEBUG,
++ "Measurement report type %u is not supported",
++ ie[4]);
++ break;
++ }
++
++ pos = ie + ie[1] + 2;
++ }
++}
++
++
++static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
++{
++ const u8 *subelem;
++
++ /* Range Request element + Location Subject + Maximum Age subelement */
++ if (len < 3 + 1 + 4)
++ return 0;
++
++ /* Subelements are arranged as IEs */
++ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
++ if (subelem && subelem[1] == 2)
++ return WPA_GET_LE16(subelem + 2);
++
++ return 0;
++}
++
++
++static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
++{
++ struct os_time curr, diff;
++ unsigned long diff_l;
++
++ if (nr->stationary || max_age == 0xffff)
++ return 1;
++
++ if (!max_age)
++ return 0;
++
++ if (os_get_time(&curr))
++ return 0;
++
++ os_time_sub(&curr, &nr->lci_date, &diff);
++
++ /* avoid overflow */
++ if (diff.sec > 0xffff)
++ return 0;
++
++ /* LCI age is calculated in 10th of a second units. */
++ diff_l = diff.sec * 10 + diff.usec / 100000;
++
++ return max_age > diff_l;
++}
++
++
++static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
++ struct hostapd_neighbor_entry *nr,
++ int send_lci, int send_civic)
++{
++ size_t len = 2 + wpabuf_len(nr->nr);
++
++ if (send_lci && nr->lci)
++ len += 2 + wpabuf_len(nr->lci);
++
++ if (send_civic && nr->civic)
++ len += 2 + wpabuf_len(nr->civic);
++
++ return len;
++}
++
++
++static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
++ const u8 *addr, u8 dialog_token,
++ struct wpa_ssid_value *ssid, u8 lci,
++ u8 civic, u16 lci_max_age)
++{
++ struct hostapd_neighbor_entry *nr;
++ struct wpabuf *buf;
++ u8 *msmt_token;
++
++ /*
++ * The number and length of the Neighbor Report elements in a Neighbor
++ * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
++ * of RRM header.
++ */
++ buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
++ if (!buf)
++ return;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
++ wpabuf_put_u8(buf, dialog_token);
++
++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
++ list) {
++ int send_lci;
++ size_t len;
++
++ if (ssid->ssid_len != nr->ssid.ssid_len ||
++ os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
++ continue;
++
++ send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
++ len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
++
++ if (len - 2 > 0xff) {
++ wpa_printf(MSG_DEBUG,
++ "NR entry for " MACSTR " exceeds 0xFF bytes",
++ MAC2STR(nr->bssid));
++ continue;
++ }
++
++ if (len > wpabuf_tailroom(buf))
++ break;
++
++ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
++ wpabuf_put_u8(buf, len - 2);
++ wpabuf_put_buf(buf, nr->nr);
++
++ if (send_lci && nr->lci) {
++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
++ wpabuf_put_u8(buf, wpabuf_len(nr->lci));
++ /*
++ * Override measurement token - the first byte of the
++ * Measurement Report element.
++ */
++ msmt_token = wpabuf_put(buf, 0);
++ wpabuf_put_buf(buf, nr->lci);
++ *msmt_token = lci;
++ }
++
++ if (civic && nr->civic) {
++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
++ wpabuf_put_u8(buf, wpabuf_len(nr->civic));
++ /*
++ * Override measurement token - the first byte of the
++ * Measurement Report element.
++ */
++ msmt_token = wpabuf_put(buf, 0);
++ wpabuf_put_buf(buf, nr->civic);
++ *msmt_token = civic;
++ }
++ }
++
++ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++ wpabuf_free(buf);
++}
++
++
++static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
++ const u8 *buf, size_t len)
++{
++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++ const u8 *pos, *ie, *end;
++ struct wpa_ssid_value ssid = {
++ .ssid_len = 0
++ };
++ u8 token;
++ u8 lci = 0, civic = 0; /* Measurement tokens */
++ u16 lci_max_age = 0;
++
++ if (!(hapd->conf->radio_measurements[0] &
++ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
++ return;
++
++ end = buf + len;
++
++ token = mgmt->u.action.u.rrm.dialog_token;
++ pos = mgmt->u.action.u.rrm.variable;
++ len = end - pos;
++
++ ie = get_ie(pos, len, WLAN_EID_SSID);
++ if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
++ ssid.ssid_len = ie[1];
++ os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
++ } else {
++ ssid.ssid_len = hapd->conf->ssid.ssid_len;
++ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
++ }
++
++ while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
++ if (ie[1] < 3)
++ break;
++
++ wpa_printf(MSG_DEBUG,
++ "Neighbor report request, measure type %u",
++ ie[4]);
++
++ switch (ie[4]) { /* Measurement Type */
++ case MEASURE_TYPE_LCI:
++ lci = ie[2]; /* Measurement Token */
++ lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
++ ie[1]);
++ break;
++ case MEASURE_TYPE_LOCATION_CIVIC:
++ civic = ie[2]; /* Measurement token */
++ break;
++ }
++
++ pos = ie + ie[1] + 2;
++ len = end - pos;
++ }
++
++ hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
++ lci_max_age);
++}
++
++
++void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
++ const u8 *buf, size_t len)
++{
++ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
++
++ /*
++ * Check for enough bytes: header + (1B)Category + (1B)Action +
++ * (1B)Dialog Token.
++ */
++ if (len < IEEE80211_HDRLEN + 3)
++ return;
++
++ wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
++ mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
++
++ switch (mgmt->u.action.u.rrm.action) {
++ case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
++ hostapd_handle_radio_msmt_report(hapd, buf, len);
++ break;
++ case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
++ hostapd_handle_nei_report_req(hapd, buf, len);
++ break;
++ default:
++ wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
++ mgmt->u.action.u.rrm.action);
++ break;
++ }
++}
++
++
++int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
++{
++ struct wpabuf *buf;
++ struct sta_info *sta = ap_get_sta(hapd, addr);
++ int ret;
++
++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
++ wpa_printf(MSG_INFO,
++ "Request LCI: Destination address is not connected");
++ return -1;
++ }
++
++ if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
++ wpa_printf(MSG_INFO,
++ "Request LCI: Station does not support LCI in RRM");
++ return -1;
++ }
++
++ if (hapd->lci_req_active) {
++ wpa_printf(MSG_DEBUG,
++ "Request LCI: LCI request is already in process, overriding");
++ hapd->lci_req_active = 0;
++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
++ NULL);
++ }
++
++ /* Measurement request (5) + Measurement element with LCI (10) */
++ buf = wpabuf_alloc(5 + 10);
++ if (!buf)
++ return -1;
++
++ hapd->lci_req_token++;
++ /* For wraparounds - the token must be nonzero */
++ if (!hapd->lci_req_token)
++ hapd->lci_req_token++;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
++ wpabuf_put_u8(buf, hapd->lci_req_token);
++ wpabuf_put_le16(buf, 0); /* Number of repetitions */
++
++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
++ wpabuf_put_u8(buf, 3 + 1 + 4);
++
++ wpabuf_put_u8(buf, 1); /* Measurement Token */
++ /*
++ * Parallel and Enable bits are 0, Duration, Request, and Report are
++ * reserved.
++ */
++ wpabuf_put_u8(buf, 0);
++ wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
++
++ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
++
++ wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
++ wpabuf_put_u8(buf, 2);
++ wpabuf_put_le16(buf, 0xffff);
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++ wpabuf_free(buf);
++ if (ret)
++ return ret;
++
++ hapd->lci_req_active = 1;
++
++ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
++ hostapd_lci_rep_timeout_handler, hapd, NULL);
++
++ return 0;
++}
++
++
++int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
++ u16 random_interval, u8 min_ap,
++ const u8 *responders, unsigned int n_responders)
++{
++ struct wpabuf *buf;
++ struct sta_info *sta;
++ u8 *len;
++ unsigned int i;
++ int ret;
++
++ wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
++ " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
++ random_interval, min_ap, n_responders);
++
++ if (min_ap == 0 || min_ap > n_responders) {
++ wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
++ return -1;
++ }
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
++ wpa_printf(MSG_INFO,
++ "Request range: Destination address is not connected");
++ return -1;
++ }
++
++ if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
++ wpa_printf(MSG_ERROR,
++ "Request range: Destination station does not support FTM range report in RRM");
++ return -1;
++ }
++
++ if (hapd->range_req_active) {
++ wpa_printf(MSG_DEBUG,
++ "Request range: Range request is already in process; overriding");
++ hapd->range_req_active = 0;
++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
++ NULL);
++ }
++
++ /* Action + measurement type + token + reps + EID + len = 7 */
++ buf = wpabuf_alloc(7 + 255);
++ if (!buf)
++ return -1;
++
++ hapd->range_req_token++;
++ if (!hapd->range_req_token) /* For wraparounds */
++ hapd->range_req_token++;
++
++ /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
++ wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
++ wpabuf_put_le16(buf, 0); /* Number of Repetitions */
++
++ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
++ len = wpabuf_put(buf, 1); /* Length will be set later */
++
++ wpabuf_put_u8(buf, 1); /* Measurement Token */
++ /*
++ * Parallel and Enable bits are 0; Duration, Request, and Report are
++ * reserved.
++ */
++ wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
++ wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
++
++ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
++ wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
++ wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
++
++ /* FTM Range Subelements */
++
++ /*
++ * Taking the neighbor report part of the range request from neighbor
++ * database instead of requesting the separate bits of data from the
++ * user.
++ */
++ for (i = 0; i < n_responders; i++) {
++ struct hostapd_neighbor_entry *nr;
++
++ nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
++ NULL);
++ if (!nr) {
++ wpa_printf(MSG_INFO, "Missing neighbor report for "
++ MACSTR, MAC2STR(responders + ETH_ALEN * i));
++ wpabuf_free(buf);
++ return -1;
++ }
++
++ if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
++ wpa_printf(MSG_ERROR, "Too long range request");
++ wpabuf_free(buf);
++ return -1;
++ }
++
++ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
++ wpabuf_put_u8(buf, wpabuf_len(nr->nr));
++ wpabuf_put_buf(buf, nr->nr);
++ }
++
++ /* Action + measurement type + token + reps + EID + len = 7 */
++ *len = wpabuf_len(buf) - 7;
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++ wpabuf_free(buf);
++ if (ret)
++ return ret;
++
++ hapd->range_req_active = 1;
++
++ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
++ hostapd_range_rep_timeout_handler, hapd, NULL);
++
++ return 0;
++}
++
++
++void hostapd_clean_rrm(struct hostapd_data *hapd)
++{
++ hostapd_free_neighbor_db(hapd);
++ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
++ hapd->lci_req_active = 0;
++ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
++ hapd->range_req_active = 0;
++}
++
++
++int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
++ u8 req_mode, const struct wpabuf *req)
++{
++ struct wpabuf *buf;
++ struct sta_info *sta = ap_get_sta(hapd, addr);
++ int ret;
++ enum beacon_report_mode mode;
++ const u8 *pos;
++
++ /* Request data:
++ * Operating Class (1), Channel Number (1), Randomization Interval (2),
++ * Measurement Duration (2), Measurement Mode (1), BSSID (6),
++ * Optional Subelements (variable)
++ */
++ if (wpabuf_len(req) < 13) {
++ wpa_printf(MSG_INFO, "Beacon request: Too short request data");
++ return -1;
++ }
++ pos = wpabuf_head(req);
++ mode = pos[6];
++
++ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
++ wpa_printf(MSG_INFO,
++ "Beacon request: " MACSTR " is not connected",
++ MAC2STR(addr));
++ return -1;
++ }
++
++ switch (mode) {
++ case BEACON_REPORT_MODE_PASSIVE:
++ if (!(sta->rrm_enabled_capa[0] &
++ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
++ wpa_printf(MSG_INFO,
++ "Beacon request: " MACSTR
++ " does not support passive beacon report",
++ MAC2STR(addr));
++ return -1;
++ }
++ break;
++ case BEACON_REPORT_MODE_ACTIVE:
++ if (!(sta->rrm_enabled_capa[0] &
++ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
++ wpa_printf(MSG_INFO,
++ "Beacon request: " MACSTR
++ " does not support active beacon report",
++ MAC2STR(addr));
++ return -1;
++ }
++ break;
++ case BEACON_REPORT_MODE_TABLE:
++ if (!(sta->rrm_enabled_capa[0] &
++ WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
++ wpa_printf(MSG_INFO,
++ "Beacon request: " MACSTR
++ " does not support table beacon report",
++ MAC2STR(addr));
++ return -1;
++ }
++ break;
++ default:
++ wpa_printf(MSG_INFO,
++ "Beacon request: Unknown measurement mode %d", mode);
++ return -1;
++ }
++
++ buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
++ if (!buf)
++ return -1;
++
++ hapd->beacon_req_token++;
++ if (!hapd->beacon_req_token)
++ hapd->beacon_req_token++;
++
++ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
++ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
++ wpabuf_put_u8(buf, hapd->beacon_req_token);
++ wpabuf_put_le16(buf, 0); /* Number of repetitions */
++
++ /* Measurement Request element */
++ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
++ wpabuf_put_u8(buf, 3 + wpabuf_len(req));
++ wpabuf_put_u8(buf, 1); /* Measurement Token */
++ wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
++ wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
++ wpabuf_put_buf(buf, req);
++
++ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
++ wpabuf_head(buf), wpabuf_len(buf));
++ wpabuf_free(buf);
++ if (ret < 0)
++ return ret;
++
++ return hapd->beacon_req_token;
++}
++
++
++void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt,
++ size_t len, int ok)
++{
++ if (len < 24 + 3)
++ return;
++ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
++ " %u ack=%d", MAC2STR(mgmt->da),
++ mgmt->u.action.u.rrm.dialog_token, ok);
++}
+--- contrib/wpa/src/ap/rrm.h.orig
++++ contrib/wpa/src/ap/rrm.h
+@@ -0,0 +1,33 @@
++/*
++ * hostapd / Radio Measurement (RRM)
++ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
++ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef RRM_H
++#define RRM_H
++
++/*
++ * Max measure request length is 255, -6 of the body we have 249 for the
++ * neighbor report elements. Each neighbor report element is at least 2 + 13
++ * bytes, so we can't have more than 16 responders in the request.
++ */
++#define RRM_RANGE_REQ_MAX_RESPONDERS 16
++
++void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
++ const u8 *buf, size_t len);
++int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
++int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
++ u16 random_interval, u8 min_ap,
++ const u8 *responders, unsigned int n_responders);
++void hostapd_clean_rrm(struct hostapd_data *hapd);
++int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
++ u8 req_mode, const struct wpabuf *req);
++void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
++ const struct ieee80211_mgmt *mgmt,
++ size_t len, int ok);
++
++#endif /* RRM_H */
+--- contrib/wpa/src/ap/sta_info.c.orig
++++ contrib/wpa/src/ap/sta_info.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / Station table
+- * Copyright (c) 2002-2013, Jouni Malinen
++ * Copyright (c) 2002-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -13,10 +13,12 @@
+ #include "common/ieee802_11_defs.h"
+ #include "common/wpa_ctrl.h"
+ #include "common/sae.h"
++#include "common/dpp.h"
+ #include "radius/radius.h"
+ #include "radius/radius_client.h"
+ #include "p2p/p2p.h"
+ #include "fst/fst.h"
++#include "crypto/crypto.h"
+ #include "hostapd.h"
+ #include "accounting.h"
+ #include "ieee802_1x.h"
+@@ -32,8 +34,11 @@
+ #include "ap_drv_ops.h"
+ #include "gas_serv.h"
+ #include "wnm_ap.h"
++#include "mbo_ap.h"
+ #include "ndisc_snoop.h"
+ #include "sta_info.h"
++#include "vlan.h"
++#include "wps_hostapd.h"
+
+ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+ struct sta_info *sta);
+@@ -45,6 +50,7 @@
+ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+ #endif /* CONFIG_IEEE80211W */
+ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
++static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
+
+ int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -161,7 +167,7 @@
+ /* just in case */
+ ap_sta_set_authorized(hapd, sta, 0);
+
+- if (sta->flags & WLAN_STA_WDS)
++ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP))
+ hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+ if (sta->ipaddr)
+@@ -169,21 +175,10 @@
+ ap_sta_ip6addr_del(hapd, sta);
+
+ if (!hapd->iface->driver_ap_teardown &&
+- !(sta->flags & WLAN_STA_PREAUTH))
++ !(sta->flags & WLAN_STA_PREAUTH)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+-
+-#ifndef CONFIG_NO_VLAN
+- if (sta->vlan_id_bound) {
+- /*
+- * Need to remove the STA entry before potentially removing the
+- * VLAN.
+- */
+- if (hapd->iface->driver_ap_teardown &&
+- !(sta->flags & WLAN_STA_PREAUTH))
+- hostapd_drv_sta_remove(hapd, sta->addr);
+- vlan_remove_dynamic(hapd, sta->vlan_id_bound);
++ sta->added_unassoc = 0;
+ }
+-#endif /* CONFIG_NO_VLAN */
+
+ ap_sta_hash_del(hapd, sta);
+ ap_sta_list_del(hapd, sta);
+@@ -203,7 +198,8 @@
+ if (sta->no_short_slot_time_set) {
+ sta->no_short_slot_time_set = 0;
+ hapd->iface->num_sta_no_short_slot_time--;
+- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_slot_time == 0)
+ set_beacon++;
+ }
+@@ -211,7 +207,8 @@
+ if (sta->no_short_preamble_set) {
+ sta->no_short_preamble_set = 0;
+ hapd->iface->num_sta_no_short_preamble--;
+- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_preamble == 0)
+ set_beacon++;
+ }
+@@ -231,6 +228,13 @@
+ hapd->iface->num_sta_ht_20mhz--;
+ }
+
++#ifdef CONFIG_TAXONOMY
++ wpabuf_free(sta->probe_ie_taxonomy);
++ sta->probe_ie_taxonomy = NULL;
++ wpabuf_free(sta->assoc_ie_taxonomy);
++ sta->assoc_ie_taxonomy = NULL;
++#endif /* CONFIG_TAXONOMY */
++
+ #ifdef CONFIG_IEEE80211N
+ ht40_intolerant_remove(hapd->iface, sta);
+ #endif /* CONFIG_IEEE80211N */
+@@ -251,7 +255,7 @@
+
+ #ifdef CONFIG_MESH
+ if (hapd->mesh_sta_free_cb)
+- hapd->mesh_sta_free_cb(sta);
++ hapd->mesh_sta_free_cb(hapd, sta);
+ #endif /* CONFIG_MESH */
+
+ if (set_beacon)
+@@ -262,11 +266,10 @@
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+- eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+- eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
++ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sae_clear_retransmit_timer(hapd, sta);
+
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ rsn_preauth_free_station(hapd, sta);
+ #ifndef CONFIG_NO_RADIUS
+@@ -274,6 +277,28 @@
+ radius_client_flush_auth(hapd->radius, sta->addr);
+ #endif /* CONFIG_NO_RADIUS */
+
++#ifndef CONFIG_NO_VLAN
++ /*
++ * sta->wpa_sm->group needs to be released before so that
++ * vlan_remove_dynamic() can check that no stations are left on the
++ * AP_VLAN netdev.
++ */
++ if (sta->vlan_id)
++ vlan_remove_dynamic(hapd, sta->vlan_id);
++ if (sta->vlan_id_bound) {
++ /*
++ * Need to remove the STA entry before potentially removing the
++ * VLAN.
++ */
++ if (hapd->iface->driver_ap_teardown &&
++ !(sta->flags & WLAN_STA_PREAUTH)) {
++ hostapd_drv_sta_remove(hapd, sta->addr);
++ sta->added_unassoc = 0;
++ }
++ vlan_remove_dynamic(hapd, sta->vlan_id_bound);
++ }
++#endif /* CONFIG_NO_VLAN */
++
+ os_free(sta->challenge);
+
+ #ifdef CONFIG_IEEE80211W
+@@ -297,6 +322,7 @@
+ wpabuf_free(sta->wps_ie);
+ wpabuf_free(sta->p2p_ie);
+ wpabuf_free(sta->hs20_ie);
++ wpabuf_free(sta->roaming_consortium);
+ #ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+ #endif /* CONFIG_FST */
+@@ -303,10 +329,12 @@
+
+ os_free(sta->ht_capabilities);
+ os_free(sta->vht_capabilities);
++ os_free(sta->vht_operation);
+ hostapd_free_psk_list(sta->psk);
+ os_free(sta->identity);
+ os_free(sta->radius_cui);
+ os_free(sta->remediation_url);
++ os_free(sta->t_c_url);
+ wpabuf_free(sta->hs20_deauth_req);
+ os_free(sta->hs20_session_info_url);
+
+@@ -315,6 +343,39 @@
+ os_free(sta->sae);
+ #endif /* CONFIG_SAE */
+
++ mbo_ap_sta_free(sta);
++ os_free(sta->supp_op_classes);
++
++#ifdef CONFIG_FILS
++ os_free(sta->fils_pending_assoc_req);
++ wpabuf_free(sta->fils_hlp_resp);
++ wpabuf_free(sta->hlp_dhcp_discover);
++ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
++#ifdef CONFIG_FILS_SK_PFS
++ crypto_ecdh_deinit(sta->fils_ecdh);
++ wpabuf_clear_free(sta->fils_dh_ss);
++ wpabuf_free(sta->fils_g_sta);
++#endif /* CONFIG_FILS_SK_PFS */
++#endif /* CONFIG_FILS */
++
++#ifdef CONFIG_OWE
++ bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
++ crypto_ecdh_deinit(sta->owe_ecdh);
++#endif /* CONFIG_OWE */
++
++#ifdef CONFIG_DPP2
++ dpp_pfs_free(sta->dpp_pfs);
++ sta->dpp_pfs = NULL;
++#endif /* CONFIG_DPP2 */
++
++ os_free(sta->ext_capability);
++
++#ifdef CONFIG_WNM_AP
++ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
++#endif /* CONFIG_WNM_AP */
++
++ os_free(sta->ifname_wds);
++
+ os_free(sta);
+ }
+
+@@ -354,8 +415,8 @@
+ unsigned long next_time = 0;
+ int reason;
+
+- wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
+- __func__, MAC2STR(sta->addr), sta->flags,
++ wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
++ hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
+ sta->timeout_next);
+ if (sta->timeout_next == STA_REMOVE) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+@@ -446,6 +507,13 @@
+ } else if (sta->timeout_next != STA_REMOVE) {
+ int deauth = sta->timeout_next == STA_DEAUTH;
+
++ if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) {
++ /* Cannot disassociate not-associated STA, so move
++ * directly to deauthentication. */
++ sta->timeout_next = STA_DEAUTH;
++ deauth = 1;
++ }
++
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Timeout, sending %s info to STA " MACSTR,
+ deauth ? "deauthentication" : "disassociation",
+@@ -482,7 +550,7 @@
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ accounting_sta_stop(hapd, sta);
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated due to "
+ "inactivity");
+@@ -519,6 +587,8 @@
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
++ wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
+ if (!(sta->flags & WLAN_STA_AUTH)) {
+ if (sta->flags & WLAN_STA_GAS) {
+ wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+@@ -573,18 +643,18 @@
+
+ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+ {
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+- wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR,
+- MAC2STR(sta->addr));
++ wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for "
++ MACSTR, hapd->conf->iface, MAC2STR(sta->addr));
+ if (sta->hs20_session_info_url == NULL)
+ return;
+
+ wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+ sta->hs20_disassoc_timer);
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+ }
+
+
+@@ -619,7 +689,10 @@
+ return NULL;
+ }
+ sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+- accounting_sta_get_id(hapd, sta);
++ if (accounting_sta_get_id(hapd, sta) < 0) {
++ os_free(sta);
++ return NULL;
++ }
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+@@ -640,6 +713,11 @@
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ dl_list_init(&sta->ip6addr);
+
++#ifdef CONFIG_TAXONOMY
++ sta_track_claim_taxonomy_info(hapd->iface, addr,
++ &sta->probe_ie_taxonomy);
++#endif /* CONFIG_TAXONOMY */
++
+ return sta;
+ }
+
+@@ -652,14 +730,16 @@
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+
+- wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
+- MAC2STR(sta->addr));
++ wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver",
++ hapd->conf->iface, MAC2STR(sta->addr));
+ if (hostapd_drv_sta_remove(hapd, sta->addr) &&
+ sta->flags & WLAN_STA_ASSOC) {
+- wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
+- " from kernel driver.", MAC2STR(sta->addr));
++ wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR
++ " from kernel driver",
++ hapd->conf->iface, MAC2STR(sta->addr));
+ return -1;
+ }
++ sta->added_unassoc = 0;
+ return 0;
+ }
+
+@@ -683,6 +763,10 @@
+ if (!sta2)
+ continue;
+
++ wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR
++ " association from another BSS %s",
++ hapd->conf->iface, MAC2STR(sta2->addr),
++ bss->conf->iface);
+ ap_sta_disconnect(bss, sta2, sta2->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+@@ -694,6 +778,8 @@
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
++ wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
+ ap_sta_remove(hapd, sta);
+ mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+ }
+@@ -705,9 +791,17 @@
+ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
++ /* Skip deauthentication in DMG/IEEE 802.11ad */
++ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
++ WLAN_STA_ASSOC_REQ_OK);
++ sta->timeout_next = STA_REMOVE;
++ } else {
++ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
++ sta->timeout_next = STA_DEAUTH;
++ }
+ ap_sta_set_authorized(hapd, sta, 0);
+- sta->timeout_next = STA_DEAUTH;
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+@@ -717,7 +811,9 @@
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
++ wpa_auth_sta_deinit(sta->wpa_sm);
++ sta->wpa_sm = NULL;
+
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+@@ -733,6 +829,8 @@
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
++ wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
+ ap_sta_remove(hapd, sta);
+ mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
+ }
+@@ -741,6 +839,14 @@
+ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+ {
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
++ /* Deauthentication is not used in DMG/IEEE 802.11ad;
++ * disassociate the STA instead. */
++ ap_sta_disassociate(hapd, sta, reason);
++ return;
++ }
++
+ wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+@@ -756,7 +862,7 @@
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+- ieee802_1x_free_station(sta);
++ ieee802_1x_free_station(hapd, sta);
+
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+@@ -784,6 +890,125 @@
+ #endif /* CONFIG_WPS */
+
+
++static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd)
++{
++ struct hostapd_vlan *vlan;
++ int vlan_id = MAX_VLAN_ID + 2;
++
++retry:
++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == vlan_id) {
++ vlan_id++;
++ goto retry;
++ }
++ }
++ return vlan_id;
++}
++
++
++int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
++ struct vlan_description *vlan_desc)
++{
++ struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
++ int old_vlan_id, vlan_id = 0, ret = 0;
++
++ /* Check if there is something to do */
++ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
++ /* This sta is lacking its own vif */
++ } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED &&
++ !hapd->conf->ssid.per_sta_vif && sta->vlan_id) {
++ /* sta->vlan_id needs to be reset */
++ } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) {
++ return 0; /* nothing to change */
++ }
++
++ /* Now the real VLAN changed or the STA just needs its own vif */
++ if (hapd->conf->ssid.per_sta_vif) {
++ /* Assign a new vif, always */
++ /* find a free vlan_id sufficiently big */
++ vlan_id = ap_sta_get_free_vlan_id(hapd);
++ /* Get wildcard VLAN */
++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
++ if (vlan->vlan_id == VLAN_ID_WILDCARD)
++ break;
++ }
++ if (!vlan) {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "per_sta_vif missing wildcard");
++ vlan_id = 0;
++ ret = -1;
++ goto done;
++ }
++ } else if (vlan_desc && vlan_desc->notempty) {
++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
++ if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
++ break;
++ if (vlan->vlan_id == VLAN_ID_WILDCARD)
++ wildcard_vlan = vlan;
++ }
++ if (vlan) {
++ vlan_id = vlan->vlan_id;
++ } else if (wildcard_vlan) {
++ vlan = wildcard_vlan;
++ vlan_id = vlan_desc->untagged;
++ if (vlan_desc->tagged[0]) {
++ /* Tagged VLAN configuration */
++ vlan_id = ap_sta_get_free_vlan_id(hapd);
++ }
++ } else {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "missing vlan and wildcard for vlan=%d%s",
++ vlan_desc->untagged,
++ vlan_desc->tagged[0] ? "+" : "");
++ vlan_id = 0;
++ ret = -1;
++ goto done;
++ }
++ }
++
++ if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
++ vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
++ if (vlan == NULL) {
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "could not add dynamic VLAN interface for vlan=%d%s",
++ vlan_desc ? vlan_desc->untagged : -1,
++ (vlan_desc && vlan_desc->tagged[0]) ?
++ "+" : "");
++ vlan_id = 0;
++ ret = -1;
++ goto done;
++ }
++
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "added new dynamic VLAN interface '%s'",
++ vlan->ifname);
++ } else if (vlan && vlan->dynamic_vlan > 0) {
++ vlan->dynamic_vlan++;
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "updated existing dynamic VLAN interface '%s'",
++ vlan->ifname);
++ }
++done:
++ old_vlan_id = sta->vlan_id;
++ sta->vlan_id = vlan_id;
++ sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL;
++
++ if (vlan_id != old_vlan_id && old_vlan_id)
++ vlan_remove_dynamic(hapd, old_vlan_id);
++
++ return ret;
++}
++
++
+ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ #ifndef CONFIG_NO_VLAN
+@@ -796,20 +1021,11 @@
+ if (hapd->conf->ssid.vlan[0])
+ iface = hapd->conf->ssid.vlan;
+
+- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+- sta->vlan_id = 0;
+- else if (sta->vlan_id > 0) {
+- struct hostapd_vlan *wildcard_vlan = NULL;
+- vlan = hapd->conf->vlan;
+- while (vlan) {
++ if (sta->vlan_id > 0) {
++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (vlan->vlan_id == sta->vlan_id)
+ break;
+- if (vlan->vlan_id == VLAN_ID_WILDCARD)
+- wildcard_vlan = vlan;
+- vlan = vlan->next;
+ }
+- if (!vlan)
+- vlan = wildcard_vlan;
+ if (vlan)
+ iface = vlan->ifname;
+ }
+@@ -829,54 +1045,13 @@
+ sta->vlan_id);
+ ret = -1;
+ goto done;
+- } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
+- vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
+- if (vlan == NULL) {
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_DEBUG, "could not add "
+- "dynamic VLAN interface for vlan_id=%d",
+- sta->vlan_id);
+- ret = -1;
+- goto done;
+- }
+-
+- iface = vlan->ifname;
+- if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_DEBUG, "could not "
+- "configure encryption for dynamic VLAN "
+- "interface for vlan_id=%d",
+- sta->vlan_id);
+- }
+-
+- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+- "interface '%s'", iface);
+- } else if (vlan && vlan->vlan_id == sta->vlan_id) {
+- if (vlan->dynamic_vlan > 0) {
+- vlan->dynamic_vlan++;
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_DEBUG, "updated existing "
+- "dynamic VLAN interface '%s'", iface);
+- }
+-
+- /*
+- * Update encryption configuration for statically generated
+- * VLAN interface. This is only used for static WEP
+- * configuration for the case where hostapd did not yet know
+- * which keys are to be used when the interface was added.
+- */
+- if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
+- hostapd_logger(hapd, sta->addr,
+- HOSTAPD_MODULE_IEEE80211,
+- HOSTAPD_LEVEL_DEBUG, "could not "
+- "configure encryption for VLAN "
+- "interface for vlan_id=%d",
+- sta->vlan_id);
+- }
++ } else if (vlan && vlan->dynamic_vlan > 0) {
++ vlan->dynamic_vlan++;
++ hostapd_logger(hapd, sta->addr,
++ HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_DEBUG,
++ "updated existing dynamic VLAN interface '%s'",
++ iface);
+ }
+
+ /* ref counters have been increased, so mark the station */
+@@ -942,6 +1117,10 @@
+ unsigned int timeout, sec, usec;
+ u8 *trans_id, *nbuf;
+
++ wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR
++ " (count=%d)",
++ hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count);
++
+ if (sta->sa_query_count > 0 &&
+ ap_check_sa_query_timeout(hapd, sta))
+ return;
+@@ -999,6 +1178,32 @@
+ #endif /* CONFIG_IEEE80211W */
+
+
++const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ struct hostapd_wpa_psk *psk;
++ struct hostapd_ssid *ssid;
++ const u8 *pmk;
++ int pmk_len;
++
++ ssid = &hapd->conf->ssid;
++
++ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
++ if (!pmk || pmk_len != PMK_LEN)
++ return NULL;
++
++ for (psk = ssid->wpa_psk; psk; psk = psk->next)
++ if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
++ break;
++ if (!psk)
++ return NULL;
++ if (!psk || !psk->keyid[0])
++ return NULL;
++
++ return psk->keyid;
++}
++
++
+ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+ {
+@@ -1037,7 +1242,11 @@
+ sta->addr, authorized, dev_addr);
+
+ if (authorized) {
++ const char *keyid;
++ char keyid_buf[100];
+ char ip_addr[100];
++
++ keyid_buf[0] = '\0';
+ ip_addr[0] = '\0';
+ #ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+@@ -1048,14 +1257,20 @@
+ }
+ #endif /* CONFIG_P2P */
+
+- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+- buf, ip_addr);
++ keyid = ap_sta_wpa_get_keyid(hapd, sta);
++ if (keyid) {
++ os_snprintf(keyid_buf, sizeof(keyid_buf),
++ " keyid=%s", keyid);
++ }
+
++ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
++ buf, ip_addr, keyid_buf);
++
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+- AP_STA_CONNECTED "%s%s",
+- buf, ip_addr);
++ AP_STA_CONNECTED "%s%s%s",
++ buf, ip_addr, keyid_buf);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
+
+@@ -1080,6 +1295,14 @@
+ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason)
+ {
++ if (sta)
++ wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u",
++ hapd->conf->iface, __func__, MAC2STR(sta->addr),
++ reason);
++ else if (addr)
++ wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u",
++ hapd->conf->iface, __func__, MAC2STR(addr),
++ reason);
+
+ if (sta == NULL && addr)
+ sta = ap_get_sta(hapd, addr);
+@@ -1093,10 +1316,10 @@
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
++ wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+- __func__, MAC2STR(sta->addr),
++ hapd->conf->iface, __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DEAUTH);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+@@ -1103,6 +1326,20 @@
+ ap_handle_timer, hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+
++ if (hapd->iface->current_mode &&
++ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
++ /* Deauthentication is not used in DMG/IEEE 802.11ad;
++ * disassociate the STA instead. */
++ sta->disassoc_reason = reason;
++ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
++ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
++ eloop_register_timeout(hapd->iface->drv_flags &
++ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
++ 2 : 0, 0, ap_sta_disassoc_cb_timeout,
++ hapd, sta);
++ return;
++ }
++
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+@@ -1136,12 +1373,37 @@
+ }
+
+
++void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0)
++ wpa_printf(MSG_DEBUG,
++ "%s: Removed ap_sta_deauth_cb_timeout timeout for "
++ MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
++ if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0)
++ wpa_printf(MSG_DEBUG,
++ "%s: Removed ap_sta_disassoc_cb_timeout timeout for "
++ MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
++ if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
++ {
++ wpa_printf(MSG_DEBUG,
++ "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
++ MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
++ if (sta->flags & WLAN_STA_WPS)
++ hostapd_wps_eap_completed(hapd);
++ }
++}
++
++
+ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+ {
+ int res;
+
+ buf[0] = '\0';
+- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
++ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+ (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+ (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+@@ -1158,6 +1420,7 @@
+ (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+ (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+ (flags & WLAN_STA_GAS ? "[GAS]" : ""),
++ (flags & WLAN_STA_HT ? "[HT]" : ""),
+ (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+ (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+ (flags & WLAN_STA_WNM_SLEEP_MODE ?
+@@ -1167,3 +1430,48 @@
+
+ return res;
+ }
++
++
++static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct sta_info *sta = timeout_ctx;
++ u16 reason;
++
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
++ "IEEE 802.1X: Scheduled disconnection of " MACSTR
++ " after EAP-Failure", MAC2STR(sta->addr));
++
++ reason = sta->disconnect_reason_code;
++ if (!reason)
++ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
++ ap_sta_disconnect(hapd, sta, sta->addr, reason);
++ if (sta->flags & WLAN_STA_WPS)
++ hostapd_wps_eap_completed(hapd);
++}
++
++
++void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
++ "IEEE 802.1X: Force disconnection of " MACSTR
++ " after EAP-Failure in 10 ms", MAC2STR(sta->addr));
++
++ /*
++ * Add a small sleep to increase likelihood of previously requested
++ * EAP-Failure TX getting out before this should the driver reorder
++ * operations.
++ */
++ eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
++ eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb,
++ hapd, sta);
++}
++
++
++int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
++ struct sta_info *sta)
++{
++ return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
++ hapd, sta);
++}
+--- contrib/wpa/src/ap/sta_info.h.orig
++++ contrib/wpa/src/ap/sta_info.h
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / Station table
+- * Copyright (c) 2002-2011, Jouni Malinen
++ * Copyright (c) 2002-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -9,12 +9,11 @@
+ #ifndef STA_INFO_H
+ #define STA_INFO_H
+
+-#ifdef CONFIG_MESH
+-/* needed for mesh_plink_state enum */
+ #include "common/defs.h"
+-#endif /* CONFIG_MESH */
+-
+ #include "list.h"
++#include "vlan.h"
++#include "common/wpa_common.h"
++#include "common/ieee802_11_defs.h"
+
+ /* STA flags */
+ #define WLAN_STA_AUTH BIT(0)
+@@ -36,6 +35,8 @@
+ #define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+ #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+ #define WLAN_STA_VENDOR_VHT BIT(21)
++#define WLAN_STA_PENDING_FILS_ERP BIT(22)
++#define WLAN_STA_MULTI_AP BIT(23)
+ #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+ #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+ #define WLAN_STA_NONERP BIT(31)
+@@ -44,7 +45,22 @@
+ * Supported Rates IEs). */
+ #define WLAN_SUPP_RATES_MAX 32
+
++struct hostapd_data;
+
++struct mbo_non_pref_chan_info {
++ struct mbo_non_pref_chan_info *next;
++ u8 op_class;
++ u8 pref;
++ u8 reason_code;
++ u8 num_channels;
++ u8 channels[];
++};
++
++struct pending_eapol_rx {
++ struct wpabuf *buf;
++ struct os_reltime rx_time;
++};
++
+ struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+@@ -52,6 +68,7 @@
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
++ u16 disconnect_reason_code; /* RADIUS server override */
+ u32 flags; /* Bitfield of WLAN_STA_* */
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+@@ -63,13 +80,22 @@
+ enum mesh_plink_state plink_state;
+ u16 peer_lid;
+ u16 my_lid;
++ u16 peer_aid;
+ u16 mpm_close_reason;
+ int mpm_retries;
+- u8 my_nonce[32];
+- u8 peer_nonce[32];
++ u8 my_nonce[WPA_NONCE_LEN];
++ u8 peer_nonce[WPA_NONCE_LEN];
+ u8 aek[32]; /* SHA256 digest length */
+- u8 mtk[16];
+- u8 mgtk[16];
++ u8 mtk[WPA_TK_MAX_LEN];
++ size_t mtk_len;
++ u8 mgtk_rsc[6];
++ u8 mgtk_key_id;
++ u8 mgtk[WPA_TK_MAX_LEN];
++ size_t mgtk_len;
++ u8 igtk_rsc[6];
++ u8 igtk[WPA_TK_MAX_LEN];
++ size_t igtk_len;
++ u16 igtk_key_id;
+ u8 sae_auth_retry;
+ #endif /* CONFIG_MESH */
+
+@@ -86,6 +112,13 @@
+ unsigned int hs20_deauth_requested:1;
+ unsigned int session_timeout_set:1;
+ unsigned int radius_das_match:1;
++ unsigned int ecsa_supported:1;
++ unsigned int added_unassoc:1;
++ unsigned int pending_wds_enable:1;
++ unsigned int power_capab:1;
++ unsigned int agreed_to_steer:1;
++ unsigned int hs20_t_c_filtering:1;
++ unsigned int ft_over_ds:1;
+
+ u16 auth_alg;
+
+@@ -100,17 +133,20 @@
+ /* IEEE 802.1X related data */
+ struct eapol_state_machine *eapol_sm;
+
+- u32 acct_session_id_hi;
+- u32 acct_session_id_lo;
++ struct pending_eapol_rx *pending_eapol_rx;
++
++ u64 acct_session_id;
+ struct os_reltime acct_session_start;
+ int acct_session_started;
+ int acct_terminate_cause; /* Acct-Terminate-Cause */
+ int acct_interim_interval; /* Acct-Interim-Interval */
++ unsigned int acct_interim_errors;
+
+- unsigned long last_rx_bytes;
+- unsigned long last_tx_bytes;
+- u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+- u32 acct_output_gigawords; /* Acct-Output-Gigawords */
++ /* For extending 32-bit driver counters to 64-bit counters */
++ u32 last_rx_bytes_hi;
++ u32 last_rx_bytes_lo;
++ u32 last_tx_bytes_hi;
++ u32 last_tx_bytes_lo;
+
+ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+@@ -118,6 +154,7 @@
+ struct rsn_preauth_interface *preauth_iface;
+
+ int vlan_id; /* 0: none, >0: VID */
++ struct vlan_description *vlan_desc;
+ int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
+ /* PSKs from RADIUS authentication server */
+ struct hostapd_sta_wpa_psk_short *psk;
+@@ -127,6 +164,7 @@
+
+ struct ieee80211_ht_capabilities *ht_capabilities;
+ struct ieee80211_vht_capabilities *vht_capabilities;
++ struct ieee80211_vht_operation *vht_operation;
+ u8 vht_opmode;
+
+ #ifdef CONFIG_IEEE80211W
+@@ -139,17 +177,20 @@
+ struct os_reltime sa_query_start;
+ #endif /* CONFIG_IEEE80211W */
+
+-#ifdef CONFIG_INTERWORKING
++#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
+ #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+ struct gas_dialog_info *gas_dialog;
+ u8 gas_dialog_next;
+-#endif /* CONFIG_INTERWORKING */
++#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
+
+ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
++ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
++ struct wpabuf *roaming_consortium;
+ u8 remediation_method;
+ char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
++ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
+ struct wpabuf *hs20_deauth_req;
+ char *hs20_session_info_url;
+ int hs20_disassoc_timer;
+@@ -161,9 +202,11 @@
+
+ #ifdef CONFIG_SAE
+ struct sae_data *sae;
++ unsigned int mesh_sae_pmksa_caching:1;
+ #endif /* CONFIG_SAE */
+
+- u32 session_timeout; /* valid only if session_timeout_set == 1 */
++ /* valid only if session_timeout_set == 1 */
++ struct os_reltime session_timeout;
+
+ /* Last Authentication/(Re)Association Request/Action frame sequence
+ * control */
+@@ -170,6 +213,68 @@
+ u16 last_seq_ctrl;
+ /* Last Authentication/(Re)Association Request/Action frame subtype */
+ u8 last_subtype;
++
++#ifdef CONFIG_MBO
++ u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
++ * enum mbo_cellular_capa values */
++ struct mbo_non_pref_chan_info *non_pref_chan;
++ int auth_rssi; /* Last Authentication frame RSSI */
++#endif /* CONFIG_MBO */
++
++ u8 *supp_op_classes; /* Supported Operating Classes element, if
++ * received, starting from the Length field */
++
++ u8 rrm_enabled_capa[5];
++
++ s8 min_tx_power;
++ s8 max_tx_power;
++
++#ifdef CONFIG_TAXONOMY
++ struct wpabuf *probe_ie_taxonomy;
++ struct wpabuf *assoc_ie_taxonomy;
++#endif /* CONFIG_TAXONOMY */
++
++#ifdef CONFIG_FILS
++ u8 fils_snonce[FILS_NONCE_LEN];
++ u8 fils_session[FILS_SESSION_LEN];
++ u8 fils_erp_pmkid[PMKID_LEN];
++ u8 *fils_pending_assoc_req;
++ size_t fils_pending_assoc_req_len;
++ unsigned int fils_pending_assoc_is_reassoc:1;
++ unsigned int fils_dhcp_rapid_commit_proxy:1;
++ unsigned int fils_erp_pmkid_set:1;
++ unsigned int fils_drv_assoc_finish:1;
++ struct wpabuf *fils_hlp_resp;
++ struct wpabuf *hlp_dhcp_discover;
++ void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
++ u16 resp, struct wpabuf *data, int pub);
++#ifdef CONFIG_FILS_SK_PFS
++ struct crypto_ecdh *fils_ecdh;
++#endif /* CONFIG_FILS_SK_PFS */
++ struct wpabuf *fils_dh_ss;
++ struct wpabuf *fils_g_sta;
++#endif /* CONFIG_FILS */
++
++#ifdef CONFIG_OWE
++ u8 *owe_pmk;
++ size_t owe_pmk_len;
++ struct crypto_ecdh *owe_ecdh;
++ u16 owe_group;
++#endif /* CONFIG_OWE */
++
++ u8 *ext_capability;
++ char *ifname_wds; /* WDS ifname, if in use */
++
++#ifdef CONFIG_DPP2
++ struct dpp_pfs *dpp_pfs;
++#endif /* CONFIG_DPP2 */
++
++#ifdef CONFIG_TESTING_OPTIONS
++ enum wpa_alg last_tk_alg;
++ int last_tk_key_idx;
++ u8 last_tk[WPA_TK_MAX_LEN];
++ size_t last_tk_len;
++#endif /* CONFIG_TESTING_OPTIONS */
+ };
+
+
+@@ -180,7 +285,7 @@
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+ #define AP_MAX_INACTIVITY (5 * 60)
+-#define AP_DISASSOC_DELAY (1)
++#define AP_DISASSOC_DELAY (3)
+ #define AP_DEAUTH_DELAY (1)
+ /* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+@@ -189,8 +294,6 @@
+ #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+-struct hostapd_data;
+-
+ int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+@@ -220,9 +323,13 @@
+ struct sta_info *sta, void *ctx);
+ #endif /* CONFIG_WPS */
+ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
++int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
++ struct vlan_description *vlan_desc);
+ void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
++const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
++ struct sta_info *sta);
+ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason);
+
+@@ -235,7 +342,13 @@
+
+ void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
++void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
++ struct sta_info *sta);
+
+ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
++void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
++ struct sta_info *sta);
++int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
++ struct sta_info *sta);
+
+ #endif /* STA_INFO_H */
+--- contrib/wpa/src/ap/taxonomy.c.orig
++++ contrib/wpa/src/ap/taxonomy.c
+@@ -0,0 +1,292 @@
++/*
++ * hostapd / Client taxonomy
++ * Copyright (c) 2015 Google, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
++ * and render them to a descriptive string. The tag number of standard options
++ * is written to the string, while the vendor ID and subtag are written for
++ * vendor options.
++ *
++ * Example strings:
++ * 0,1,50,45,221(00904c,51)
++ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "common/wpa_ctrl.h"
++#include "hostapd.h"
++#include "sta_info.h"
++#include "taxonomy.h"
++
++
++/* Copy a string with no funny schtuff allowed; only alphanumerics. */
++static void no_mischief_strncpy(char *dst, const char *src, size_t n)
++{
++ size_t i;
++
++ for (i = 0; i < n; i++) {
++ unsigned char s = src[i];
++ int is_lower = s >= 'a' && s <= 'z';
++ int is_upper = s >= 'A' && s <= 'Z';
++ int is_digit = s >= '0' && s <= '9';
++
++ if (is_lower || is_upper || is_digit) {
++ /* TODO: if any manufacturer uses Unicode within the
++ * WPS header, it will get mangled here. */
++ dst[i] = s;
++ } else {
++ /* Note that even spaces will be transformed to
++ * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
++ * This is deliberate, to make the string easier to
++ * parse. */
++ dst[i] = '_';
++ }
++ }
++}
++
++
++static int get_wps_name(char *name, size_t name_len,
++ const u8 *data, size_t data_len)
++{
++ /* Inside the WPS IE are a series of attributes, using two byte IDs
++ * and two byte lengths. We're looking for the model name, if
++ * present. */
++ while (data_len >= 4) {
++ u16 id, elen;
++
++ id = WPA_GET_BE16(data);
++ elen = WPA_GET_BE16(data + 2);
++ data += 4;
++ data_len -= 4;
++
++ if (elen > data_len)
++ return 0;
++
++ if (id == 0x1023) {
++ /* Model name, like 'Nexus 7' */
++ size_t n = (elen < name_len) ? elen : name_len;
++ no_mischief_strncpy(name, (const char *) data, n);
++ return n;
++ }
++
++ data += elen;
++ data_len -= elen;
++ }
++
++ return 0;
++}
++
++
++static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
++{
++ char *fpos = fstr;
++ char *fend = fstr + fstr_len;
++ char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
++ char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
++ char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
++ char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
++ char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
++ char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
++#define MAX_EXTCAP 254
++ char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
++ */
++ char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
++#define WPS_NAME_LEN 32
++ char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
++ * NUL */
++ int num = 0;
++ const u8 *ie;
++ size_t ie_len;
++ int ret;
++
++ os_memset(htcap, 0, sizeof(htcap));
++ os_memset(htagg, 0, sizeof(htagg));
++ os_memset(htmcs, 0, sizeof(htmcs));
++ os_memset(vhtcap, 0, sizeof(vhtcap));
++ os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
++ os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
++ os_memset(extcap, 0, sizeof(extcap));
++ os_memset(txpow, 0, sizeof(txpow));
++ os_memset(wps, 0, sizeof(wps));
++ *fpos = '\0';
++
++ if (!ies)
++ return;
++ ie = wpabuf_head(ies);
++ ie_len = wpabuf_len(ies);
++
++ while (ie_len >= 2) {
++ u8 id, elen;
++ char *sep = (num++ == 0) ? "" : ",";
++
++ id = *ie++;
++ elen = *ie++;
++ ie_len -= 2;
++
++ if (elen > ie_len)
++ break;
++
++ if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
++ /* Vendor specific */
++ if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
++ /* WPS */
++ char model_name[WPS_NAME_LEN + 1];
++ const u8 *data = &ie[4];
++ size_t data_len = elen - 4;
++
++ os_memset(model_name, 0, sizeof(model_name));
++ if (get_wps_name(model_name, WPS_NAME_LEN, data,
++ data_len)) {
++ os_snprintf(wps, sizeof(wps),
++ ",wps:%s", model_name);
++ }
++ }
++
++ ret = os_snprintf(fpos, fend - fpos,
++ "%s%d(%02x%02x%02x,%d)",
++ sep, id, ie[0], ie[1], ie[2], ie[3]);
++ } else {
++ if (id == WLAN_EID_HT_CAP && elen >= 2) {
++ /* HT Capabilities (802.11n) */
++ os_snprintf(htcap, sizeof(htcap),
++ ",htcap:%04hx",
++ WPA_GET_LE16(ie));
++ }
++ if (id == WLAN_EID_HT_CAP && elen >= 3) {
++ /* HT Capabilities (802.11n), A-MPDU information
++ */
++ os_snprintf(htagg, sizeof(htagg),
++ ",htagg:%02hx", (u16) ie[2]);
++ }
++ if (id == WLAN_EID_HT_CAP && elen >= 7) {
++ /* HT Capabilities (802.11n), MCS information */
++ os_snprintf(htmcs, sizeof(htmcs),
++ ",htmcs:%08hx",
++ (u16) WPA_GET_LE32(ie + 3));
++ }
++ if (id == WLAN_EID_VHT_CAP && elen >= 4) {
++ /* VHT Capabilities (802.11ac) */
++ os_snprintf(vhtcap, sizeof(vhtcap),
++ ",vhtcap:%08x",
++ WPA_GET_LE32(ie));
++ }
++ if (id == WLAN_EID_VHT_CAP && elen >= 8) {
++ /* VHT Capabilities (802.11ac), RX MCS
++ * information */
++ os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
++ ",vhtrxmcs:%08x",
++ WPA_GET_LE32(ie + 4));
++ }
++ if (id == WLAN_EID_VHT_CAP && elen >= 12) {
++ /* VHT Capabilities (802.11ac), TX MCS
++ * information */
++ os_snprintf(vhttxmcs, sizeof(vhttxmcs),
++ ",vhttxmcs:%08x",
++ WPA_GET_LE32(ie + 8));
++ }
++ if (id == WLAN_EID_EXT_CAPAB) {
++ /* Extended Capabilities */
++ int i;
++ int len = (elen < MAX_EXTCAP) ? elen :
++ MAX_EXTCAP;
++ char *p = extcap;
++
++ p += os_snprintf(extcap, sizeof(extcap),
++ ",extcap:");
++ for (i = 0; i < len; i++) {
++ int lim;
++
++ lim = sizeof(extcap) -
++ os_strlen(extcap);
++ if (lim <= 0)
++ break;
++ p += os_snprintf(p, lim, "%02x",
++ *(ie + i));
++ }
++ }
++ if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
++ /* TX Power */
++ os_snprintf(txpow, sizeof(txpow),
++ ",txpow:%04hx",
++ WPA_GET_LE16(ie));
++ }
++
++ ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
++ }
++ if (os_snprintf_error(fend - fpos, ret))
++ goto fail;
++ fpos += ret;
++
++ ie += elen;
++ ie_len -= elen;
++ }
++
++ ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
++ htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
++ txpow, extcap, wps);
++ if (os_snprintf_error(fend - fpos, ret)) {
++ fail:
++ fstr[0] = '\0';
++ }
++}
++
++
++int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
++ struct sta_info *sta, char *buf, size_t buflen)
++{
++ int ret;
++ char *pos, *end;
++
++ if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
++ return 0;
++
++ ret = os_snprintf(buf, buflen, "wifi4|probe:");
++ if (os_snprintf_error(buflen, ret))
++ return 0;
++ pos = buf + ret;
++ end = buf + buflen;
++
++ ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
++ pos = os_strchr(pos, '\0');
++ if (pos >= end)
++ return 0;
++ ret = os_snprintf(pos, end - pos, "|assoc:");
++ if (os_snprintf_error(end - pos, ret))
++ return 0;
++ pos += ret;
++ ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
++ pos = os_strchr(pos, '\0');
++ return pos - buf;
++}
++
++
++void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *ie, size_t ie_len)
++{
++ wpabuf_free(sta->probe_ie_taxonomy);
++ sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
++}
++
++
++void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
++ struct hostapd_sta_info *info,
++ const u8 *ie, size_t ie_len)
++{
++ wpabuf_free(info->probe_ie_taxonomy);
++ info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
++}
++
++
++void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *ie, size_t ie_len)
++{
++ wpabuf_free(sta->assoc_ie_taxonomy);
++ sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
++}
+--- contrib/wpa/src/ap/taxonomy.h.orig
++++ contrib/wpa/src/ap/taxonomy.h
+@@ -0,0 +1,24 @@
++/*
++ * hostapd / Station client taxonomy
++ * Copyright (c) 2015 Google, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef TAXONOMY_H
++#define TAXONOMY_H
++
++void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *ie, size_t ie_len);
++void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
++ struct hostapd_sta_info *sta,
++ const u8 *ie, size_t ie_len);
++void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
++ struct sta_info *sta,
++ const u8 *ie, size_t ie_len);
++int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
++ struct sta_info *sta, char *buf, size_t buflen);
++
++#endif /* TAXONOMY_H */
+--- contrib/wpa/src/ap/tkip_countermeasures.c.orig
++++ contrib/wpa/src/ap/tkip_countermeasures.c
+@@ -71,6 +71,11 @@
+ struct os_reltime now;
+ int ret = 0;
+
++ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Michael MIC failure detected in received frame%s",
++ local ? " (local)" : "");
++
+ if (addr && local) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta != NULL) {
+--- contrib/wpa/src/ap/vlan.c.orig
++++ contrib/wpa/src/ap/vlan.c
+@@ -0,0 +1,34 @@
++/*
++ * hostapd / VLAN definition
++ * Copyright (c) 2016, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++
++#include "utils/common.h"
++#include "ap/vlan.h"
++
++/* compare the two arguments, NULL is treated as empty
++ * return zero iff they are equal
++ */
++int vlan_compare(struct vlan_description *a, struct vlan_description *b)
++{
++ int i;
++ const int a_empty = !a || !a->notempty;
++ const int b_empty = !b || !b->notempty;
++
++ if (a_empty && b_empty)
++ return 0;
++ if (a_empty || b_empty)
++ return 1;
++ if (a->untagged != b->untagged)
++ return 1;
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
++ if (a->tagged[i] != b->tagged[i])
++ return 1;
++ }
++ return 0;
++}
+--- contrib/wpa/src/ap/vlan.h.orig
++++ contrib/wpa/src/ap/vlan.h
+@@ -0,0 +1,30 @@
++/*
++ * hostapd / VLAN definition
++ * Copyright (c) 2015, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef VLAN_H
++#define VLAN_H
++
++#define MAX_NUM_TAGGED_VLAN 32
++
++struct vlan_description {
++ int notempty; /* 0 : no vlan information present, 1: else */
++ int untagged; /* >0 802.1q vid */
++ int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
++};
++
++#ifndef CONFIG_NO_VLAN
++int vlan_compare(struct vlan_description *a, struct vlan_description *b);
++#else /* CONFIG_NO_VLAN */
++static inline int
++vlan_compare(struct vlan_description *a, struct vlan_description *b)
++{
++ return 0;
++}
++#endif /* CONFIG_NO_VLAN */
++
++#endif /* VLAN_H */
+--- contrib/wpa/src/ap/vlan_full.c.orig
++++ contrib/wpa/src/ap/vlan_full.c
+@@ -0,0 +1,801 @@
++/*
++ * hostapd / VLAN initialization - full dynamic VLAN
++ * Copyright 2003, Instant802 Networks, Inc.
++ * Copyright 2005-2006, Devicescape Software, Inc.
++ * Copyright (c) 2009, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include
++/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
++#undef if_type
++#include
++
++#include "utils/common.h"
++#include "drivers/priv_netlink.h"
++#include "drivers/linux_ioctl.h"
++#include "common/linux_bridge.h"
++#include "common/linux_vlan.h"
++#include "utils/eloop.h"
++#include "hostapd.h"
++#include "ap_config.h"
++#include "ap_drv_ops.h"
++#include "wpa_auth.h"
++#include "vlan_init.h"
++#include "vlan_util.h"
++
++
++struct full_dynamic_vlan {
++ int s; /* socket on which to listen for new/removed interfaces. */
++};
++
++#define DVLAN_CLEAN_BR 0x1
++#define DVLAN_CLEAN_VLAN 0x2
++#define DVLAN_CLEAN_VLAN_PORT 0x4
++
++struct dynamic_iface {
++ char ifname[IFNAMSIZ + 1];
++ int usage;
++ int clean;
++ struct dynamic_iface *next;
++};
++
++
++/* Increment ref counter for ifname and add clean flag.
++ * If not in list, add it only if some flags are given.
++ */
++static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
++ int clean)
++{
++ struct dynamic_iface *next, **dynamic_ifaces;
++ struct hapd_interfaces *interfaces;
++
++ interfaces = hapd->iface->interfaces;
++ dynamic_ifaces = &interfaces->vlan_priv;
++
++ for (next = *dynamic_ifaces; next; next = next->next) {
++ if (os_strcmp(ifname, next->ifname) == 0)
++ break;
++ }
++
++ if (next) {
++ next->usage++;
++ next->clean |= clean;
++ return;
++ }
++
++ if (!clean)
++ return;
++
++ next = os_zalloc(sizeof(*next));
++ if (!next)
++ return;
++ os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
++ next->usage = 1;
++ next->clean = clean;
++ next->next = *dynamic_ifaces;
++ *dynamic_ifaces = next;
++}
++
++
++/* Decrement reference counter for given ifname.
++ * Return clean flag iff reference counter was decreased to zero, else zero
++ */
++static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
++{
++ struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
++ struct hapd_interfaces *interfaces;
++ int clean;
++
++ interfaces = hapd->iface->interfaces;
++ dynamic_ifaces = &interfaces->vlan_priv;
++
++ for (next = *dynamic_ifaces; next; next = next->next) {
++ if (os_strcmp(ifname, next->ifname) == 0)
++ break;
++ prev = next;
++ }
++
++ if (!next)
++ return 0;
++
++ next->usage--;
++ if (next->usage)
++ return 0;
++
++ if (prev)
++ prev->next = next->next;
++ else
++ *dynamic_ifaces = next->next;
++ clean = next->clean;
++ os_free(next);
++
++ return clean;
++}
++
++
++static int ifconfig_down(const char *if_name)
++{
++ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
++ return ifconfig_helper(if_name, 0);
++}
++
++
++/* This value should be 256 ONLY. If it is something else, then hostapd
++ * might crash!, as this value has been hard-coded in 2.4.x kernel
++ * bridging code.
++ */
++#define MAX_BR_PORTS 256
++
++static int br_delif(const char *br_name, const char *if_name)
++{
++ int fd;
++ struct ifreq ifr;
++ unsigned long args[2];
++ int if_index;
++
++ wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ if (linux_br_del_if(fd, br_name, if_name) == 0)
++ goto done;
++
++ if_index = if_nametoindex(if_name);
++
++ if (if_index == 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
++ "interface index for '%s'",
++ __func__, if_name);
++ close(fd);
++ return -1;
++ }
++
++ args[0] = BRCTL_DEL_IF;
++ args[1] = if_index;
++
++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
++ ifr.ifr_data = (void *) args;
++
++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
++ /* No error if interface already removed. */
++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
++ "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
++ "%s", __func__, br_name, if_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++done:
++ close(fd);
++ return 0;
++}
++
++
++/*
++ Add interface 'if_name' to the bridge 'br_name'
++
++ returns -1 on error
++ returns 1 if the interface is already part of the bridge
++ returns 0 otherwise
++*/
++static int br_addif(const char *br_name, const char *if_name)
++{
++ int fd;
++ struct ifreq ifr;
++ unsigned long args[2];
++ int if_index;
++
++ wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ if (linux_br_add_if(fd, br_name, if_name) == 0)
++ goto done;
++ if (errno == EBUSY) {
++ /* The interface is already added. */
++ close(fd);
++ return 1;
++ }
++
++ if_index = if_nametoindex(if_name);
++
++ if (if_index == 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
++ "interface index for '%s'",
++ __func__, if_name);
++ close(fd);
++ return -1;
++ }
++
++ args[0] = BRCTL_ADD_IF;
++ args[1] = if_index;
++
++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
++ ifr.ifr_data = (void *) args;
++
++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
++ if (errno == EBUSY) {
++ /* The interface is already added. */
++ close(fd);
++ return 1;
++ }
++
++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
++ "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
++ "%s", __func__, br_name, if_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++done:
++ close(fd);
++ return 0;
++}
++
++
++static int br_delbr(const char *br_name)
++{
++ int fd;
++ unsigned long arg[2];
++
++ wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ if (linux_br_del(fd, br_name) == 0)
++ goto done;
++
++ arg[0] = BRCTL_DEL_BRIDGE;
++ arg[1] = (unsigned long) br_name;
++
++ if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
++ /* No error if bridge already removed. */
++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
++ "%s: %s", __func__, br_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++done:
++ close(fd);
++ return 0;
++}
++
++
++/*
++ Add a bridge with the name 'br_name'.
++
++ returns -1 on error
++ returns 1 if the bridge already exists
++ returns 0 otherwise
++*/
++static int br_addbr(const char *br_name)
++{
++ int fd;
++ unsigned long arg[4];
++ struct ifreq ifr;
++
++ wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ if (linux_br_add(fd, br_name) == 0)
++ goto done;
++ if (errno == EEXIST) {
++ /* The bridge is already added. */
++ close(fd);
++ return 1;
++ }
++
++ arg[0] = BRCTL_ADD_BRIDGE;
++ arg[1] = (unsigned long) br_name;
++
++ if (ioctl(fd, SIOCGIFBR, arg) < 0) {
++ if (errno == EEXIST) {
++ /* The bridge is already added. */
++ close(fd);
++ return 1;
++ } else {
++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
++ "failed for %s: %s",
++ __func__, br_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++ }
++
++done:
++ /* Decrease forwarding delay to avoid EAPOL timeouts. */
++ os_memset(&ifr, 0, sizeof(ifr));
++ os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
++ arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
++ arg[1] = 1;
++ arg[2] = 0;
++ arg[3] = 0;
++ ifr.ifr_data = (char *) &arg;
++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: "
++ "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
++ "%s: %s", __func__, br_name, strerror(errno));
++ /* Continue anyway */
++ }
++
++ close(fd);
++ return 0;
++}
++
++
++static int br_getnumports(const char *br_name)
++{
++ int fd;
++ int i;
++ int port_cnt = 0;
++ unsigned long arg[4];
++ int ifindices[MAX_BR_PORTS];
++ struct ifreq ifr;
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ arg[0] = BRCTL_GET_PORT_LIST;
++ arg[1] = (unsigned long) ifindices;
++ arg[2] = MAX_BR_PORTS;
++ arg[3] = 0;
++
++ os_memset(ifindices, 0, sizeof(ifindices));
++ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
++ ifr.ifr_data = (void *) arg;
++
++ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
++ "failed for %s: %s",
++ __func__, br_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ for (i = 1; i < MAX_BR_PORTS; i++) {
++ if (ifindices[i] > 0) {
++ port_cnt++;
++ }
++ }
++
++ close(fd);
++ return port_cnt;
++}
++
++
++static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
++ const char *br_name, int vid,
++ struct hostapd_data *hapd)
++{
++ char vlan_ifname[IFNAMSIZ];
++ int clean;
++ int ret;
++
++ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
++ tagged_interface, vid);
++ else
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
++ vid);
++ if (ret >= (int) sizeof(vlan_ifname))
++ wpa_printf(MSG_WARNING,
++ "VLAN: Interface name was truncated to %s",
++ vlan_ifname);
++
++ clean = 0;
++ ifconfig_up(tagged_interface);
++ if (!vlan_add(tagged_interface, vid, vlan_ifname))
++ clean |= DVLAN_CLEAN_VLAN;
++
++ if (!br_addif(br_name, vlan_ifname))
++ clean |= DVLAN_CLEAN_VLAN_PORT;
++
++ dyn_iface_get(hapd, vlan_ifname, clean);
++
++ ifconfig_up(vlan_ifname);
++}
++
++
++static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
++ struct hostapd_vlan *vlan, int vid)
++{
++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
++ int ret;
++
++ if (vlan->bridge[0]) {
++ os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
++ ret = 0;
++ } else if (hapd->conf->vlan_bridge[0]) {
++ ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
++ hapd->conf->vlan_bridge, vid);
++ } else if (tagged_interface) {
++ ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
++ tagged_interface, vid);
++ } else {
++ ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
++ }
++ if (ret >= IFNAMSIZ)
++ wpa_printf(MSG_WARNING,
++ "VLAN: Interface name was truncated to %s",
++ br_name);
++}
++
++
++static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
++ int vid)
++{
++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
++ int vlan_naming = hapd->conf->ssid.vlan_naming;
++
++ dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
++
++ ifconfig_up(br_name);
++
++ if (tagged_interface)
++ vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
++ vid, hapd);
++}
++
++
++void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
++{
++ char br_name[IFNAMSIZ];
++ struct hostapd_vlan *vlan;
++ int untagged, *tagged, i, notempty;
++
++ wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
++
++ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
++ if (vlan->configured ||
++ os_strcmp(ifname, vlan->ifname) != 0)
++ continue;
++ break;
++ }
++ if (!vlan)
++ return;
++
++ vlan->configured = 1;
++
++ notempty = vlan->vlan_desc.notempty;
++ untagged = vlan->vlan_desc.untagged;
++ tagged = vlan->vlan_desc.tagged;
++
++ if (!notempty) {
++ /* Non-VLAN STA */
++ if (hapd->conf->bridge[0] &&
++ !br_addif(hapd->conf->bridge, ifname))
++ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
++ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
++ vlan_bridge_name(br_name, hapd, vlan, untagged);
++
++ vlan_get_bridge(br_name, hapd, untagged);
++
++ if (!br_addif(br_name, ifname))
++ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
++ }
++
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
++ if (tagged[i] == untagged ||
++ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
++ (i > 0 && tagged[i] == tagged[i - 1]))
++ continue;
++ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
++ vlan_get_bridge(br_name, hapd, tagged[i]);
++ vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
++ ifname, br_name, tagged[i], hapd);
++ }
++
++ ifconfig_up(ifname);
++}
++
++
++static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
++ const char *br_name, int vid,
++ struct hostapd_data *hapd)
++{
++ char vlan_ifname[IFNAMSIZ];
++ int clean;
++ int ret;
++
++ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
++ tagged_interface, vid);
++ else
++ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
++ vid);
++ if (ret >= (int) sizeof(vlan_ifname))
++ wpa_printf(MSG_WARNING,
++ "VLAN: Interface name was truncated to %s",
++ vlan_ifname);
++
++
++ clean = dyn_iface_put(hapd, vlan_ifname);
++
++ if (clean & DVLAN_CLEAN_VLAN_PORT)
++ br_delif(br_name, vlan_ifname);
++
++ if (clean & DVLAN_CLEAN_VLAN) {
++ ifconfig_down(vlan_ifname);
++ vlan_rem(vlan_ifname);
++ }
++}
++
++
++static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
++ int vid)
++{
++ int clean;
++ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
++ int vlan_naming = hapd->conf->ssid.vlan_naming;
++
++ if (tagged_interface)
++ vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
++ vid, hapd);
++
++ clean = dyn_iface_put(hapd, br_name);
++ if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
++ ifconfig_down(br_name);
++ br_delbr(br_name);
++ }
++}
++
++
++void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
++{
++ struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
++
++ wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
++
++ first = prev = vlan;
++
++ while (vlan) {
++ if (os_strcmp(ifname, vlan->ifname) != 0) {
++ prev = vlan;
++ vlan = vlan->next;
++ continue;
++ }
++ break;
++ }
++ if (!vlan)
++ return;
++
++ if (vlan->configured) {
++ int notempty = vlan->vlan_desc.notempty;
++ int untagged = vlan->vlan_desc.untagged;
++ int *tagged = vlan->vlan_desc.tagged;
++ char br_name[IFNAMSIZ];
++ int i;
++
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
++ if (tagged[i] == untagged ||
++ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
++ (i > 0 && tagged[i] == tagged[i - 1]))
++ continue;
++ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
++ vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
++ ifname, br_name, tagged[i], hapd);
++ vlan_put_bridge(br_name, hapd, tagged[i]);
++ }
++
++ if (!notempty) {
++ /* Non-VLAN STA */
++ if (hapd->conf->bridge[0] &&
++ (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
++ br_delif(hapd->conf->bridge, ifname);
++ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
++ vlan_bridge_name(br_name, hapd, vlan, untagged);
++
++ if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
++ br_delif(br_name, vlan->ifname);
++
++ vlan_put_bridge(br_name, hapd, untagged);
++ }
++ }
++
++ /*
++ * Ensure this VLAN interface is actually removed even if
++ * NEWLINK message is only received later.
++ */
++ if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
++ wpa_printf(MSG_ERROR,
++ "VLAN: Could not remove VLAN iface: %s: %s",
++ vlan->ifname, strerror(errno));
++
++ if (vlan == first)
++ hapd->conf->vlan = vlan->next;
++ else
++ prev->next = vlan->next;
++
++ os_free(vlan);
++}
++
++
++static void
++vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
++ struct hostapd_data *hapd)
++{
++ struct ifinfomsg *ifi;
++ int attrlen, nlmsg_len, rta_len;
++ struct rtattr *attr;
++ char ifname[IFNAMSIZ + 1];
++
++ if (len < sizeof(*ifi))
++ return;
++
++ ifi = NLMSG_DATA(h);
++
++ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
++
++ attrlen = h->nlmsg_len - nlmsg_len;
++ if (attrlen < 0)
++ return;
++
++ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
++
++ os_memset(ifname, 0, sizeof(ifname));
++ rta_len = RTA_ALIGN(sizeof(struct rtattr));
++ while (RTA_OK(attr, attrlen)) {
++ if (attr->rta_type == IFLA_IFNAME) {
++ int n = attr->rta_len - rta_len;
++ if (n < 0)
++ break;
++
++ if ((size_t) n >= sizeof(ifname))
++ n = sizeof(ifname) - 1;
++ os_memcpy(ifname, ((char *) attr) + rta_len, n);
++
++ }
++
++ attr = RTA_NEXT(attr, attrlen);
++ }
++
++ if (!ifname[0])
++ return;
++ if (del && if_nametoindex(ifname)) {
++ /* interface still exists, race condition ->
++ * iface has just been recreated */
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
++ del ? "DEL" : "NEW",
++ ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
++ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
++ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
++ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
++ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
++
++ if (del)
++ vlan_dellink(ifname, hapd);
++ else
++ vlan_newlink(ifname, hapd);
++}
++
++
++static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++ char buf[8192];
++ int left;
++ struct sockaddr_nl from;
++ socklen_t fromlen;
++ struct nlmsghdr *h;
++ struct hostapd_data *hapd = eloop_ctx;
++
++ fromlen = sizeof(from);
++ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
++ (struct sockaddr *) &from, &fromlen);
++ if (left < 0) {
++ if (errno != EINTR && errno != EAGAIN)
++ wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
++ __func__, strerror(errno));
++ return;
++ }
++
++ h = (struct nlmsghdr *) buf;
++ while (NLMSG_OK(h, left)) {
++ int len, plen;
++
++ len = h->nlmsg_len;
++ plen = len - sizeof(*h);
++ if (len > left || plen < 0) {
++ wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
++ "message: len=%d left=%d plen=%d",
++ len, left, plen);
++ break;
++ }
++
++ switch (h->nlmsg_type) {
++ case RTM_NEWLINK:
++ vlan_read_ifnames(h, plen, 0, hapd);
++ break;
++ case RTM_DELLINK:
++ vlan_read_ifnames(h, plen, 1, hapd);
++ break;
++ }
++
++ h = NLMSG_NEXT(h, left);
++ }
++
++ if (left > 0) {
++ wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
++ "netlink message", __func__, left);
++ }
++}
++
++
++struct full_dynamic_vlan *
++full_dynamic_vlan_init(struct hostapd_data *hapd)
++{
++ struct sockaddr_nl local;
++ struct full_dynamic_vlan *priv;
++
++ priv = os_zalloc(sizeof(*priv));
++ if (priv == NULL)
++ return NULL;
++
++ vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
++ DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
++ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
++ VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
++
++ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
++ if (priv->s < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
++ "NETLINK_ROUTE) failed: %s",
++ __func__, strerror(errno));
++ os_free(priv);
++ return NULL;
++ }
++
++ os_memset(&local, 0, sizeof(local));
++ local.nl_family = AF_NETLINK;
++ local.nl_groups = RTMGRP_LINK;
++ if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
++ __func__, strerror(errno));
++ close(priv->s);
++ os_free(priv);
++ return NULL;
++ }
++
++ if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
++ {
++ close(priv->s);
++ os_free(priv);
++ return NULL;
++ }
++
++ return priv;
++}
++
++
++void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
++{
++ if (priv == NULL)
++ return;
++ eloop_unregister_read_sock(priv->s);
++ close(priv->s);
++ os_free(priv);
++}
+--- contrib/wpa/src/ap/vlan_ifconfig.c.orig
++++ contrib/wpa/src/ap/vlan_ifconfig.c
+@@ -0,0 +1,69 @@
++/*
++ * hostapd / VLAN ifconfig helpers
++ * Copyright 2003, Instant802 Networks, Inc.
++ * Copyright 2005-2006, Devicescape Software, Inc.
++ * Copyright (c) 2009, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include
++#include
++
++#include "utils/common.h"
++#include "vlan_util.h"
++
++
++int ifconfig_helper(const char *if_name, int up)
++{
++ int fd;
++ struct ifreq ifr;
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ os_memset(&ifr, 0, sizeof(ifr));
++ os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
++
++ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
++ "for interface %s: %s",
++ __func__, if_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ if (up)
++ ifr.ifr_flags |= IFF_UP;
++ else
++ ifr.ifr_flags &= ~IFF_UP;
++
++ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
++ "for interface %s (up=%d): %s",
++ __func__, if_name, up, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ close(fd);
++ return 0;
++}
++
++
++int ifconfig_up(const char *if_name)
++{
++ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
++ return ifconfig_helper(if_name, 1);
++}
++
++
++int iface_exists(const char *ifname)
++{
++ return if_nametoindex(ifname);
++}
+--- contrib/wpa/src/ap/vlan_init.c.orig
++++ contrib/wpa/src/ap/vlan_init.c
+@@ -9,921 +9,88 @@
+ */
+
+ #include "utils/includes.h"
+-#ifdef CONFIG_FULL_DYNAMIC_VLAN
+-#include
+-#include
+-#include
+-#include
+-#include
+-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ #include "utils/common.h"
+ #include "hostapd.h"
+ #include "ap_config.h"
+ #include "ap_drv_ops.h"
++#include "wpa_auth.h"
+ #include "vlan_init.h"
+ #include "vlan_util.h"
+
+
+-#ifdef CONFIG_FULL_DYNAMIC_VLAN
+-
+-#include "drivers/priv_netlink.h"
+-#include "utils/eloop.h"
+-
+-
+-struct full_dynamic_vlan {
+- int s; /* socket on which to listen for new/removed interfaces. */
+-};
+-
+-#define DVLAN_CLEAN_BR 0x1
+-#define DVLAN_CLEAN_VLAN 0x2
+-#define DVLAN_CLEAN_VLAN_PORT 0x4
+-
+-struct dynamic_iface {
+- char ifname[IFNAMSIZ + 1];
+- int usage;
+- int clean;
+- struct dynamic_iface *next;
+-};
+-
+-
+-/* Increment ref counter for ifname and add clean flag.
+- * If not in list, add it only if some flags are given.
+- */
+-static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+- int clean)
++static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ int existsok)
+ {
+- struct dynamic_iface *next, **dynamic_ifaces;
+- struct hapd_interfaces *interfaces;
++ int ret, i;
+
+- interfaces = hapd->iface->interfaces;
+- dynamic_ifaces = &interfaces->vlan_priv;
+-
+- for (next = *dynamic_ifaces; next; next = next->next) {
+- if (os_strcmp(ifname, next->ifname) == 0)
+- break;
+- }
+-
+- if (next) {
+- next->usage++;
+- next->clean |= clean;
+- return;
+- }
+-
+- if (!clean)
+- return;
+-
+- next = os_zalloc(sizeof(*next));
+- if (!next)
+- return;
+- os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+- next->usage = 1;
+- next->clean = clean;
+- next->next = *dynamic_ifaces;
+- *dynamic_ifaces = next;
+-}
+-
+-
+-/* Decrement reference counter for given ifname.
+- * Return clean flag iff reference counter was decreased to zero, else zero
+- */
+-static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+-{
+- struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+- struct hapd_interfaces *interfaces;
+- int clean;
+-
+- interfaces = hapd->iface->interfaces;
+- dynamic_ifaces = &interfaces->vlan_priv;
+-
+- for (next = *dynamic_ifaces; next; next = next->next) {
+- if (os_strcmp(ifname, next->ifname) == 0)
+- break;
+- prev = next;
+- }
+-
+- if (!next)
+- return 0;
+-
+- next->usage--;
+- if (next->usage)
+- return 0;
+-
+- if (prev)
+- prev->next = next->next;
+- else
+- *dynamic_ifaces = next->next;
+- clean = next->clean;
+- os_free(next);
+-
+- return clean;
+-}
+-
+-
+-static int ifconfig_helper(const char *if_name, int up)
+-{
+- int fd;
+- struct ifreq ifr;
+-
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
++ for (i = 0; i < NUM_WEP_KEYS; i++) {
++ if (!hapd->conf->ssid.wep.key[i])
++ continue;
++ wpa_printf(MSG_ERROR,
++ "VLAN: Refusing to set up VLAN iface %s with WEP",
++ vlan->ifname);
+ return -1;
+ }
+
+- os_memset(&ifr, 0, sizeof(ifr));
+- os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+-
+- if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+- "for interface %s: %s",
+- __func__, if_name, strerror(errno));
+- close(fd);
++ if (!iface_exists(vlan->ifname))
++ ret = hostapd_vlan_if_add(hapd, vlan->ifname);
++ else if (!existsok)
+ return -1;
+- }
+-
+- if (up)
+- ifr.ifr_flags |= IFF_UP;
+ else
+- ifr.ifr_flags &= ~IFF_UP;
++ ret = 0;
+
+- if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+- "for interface %s (up=%d): %s",
+- __func__, if_name, up, strerror(errno));
+- close(fd);
+- return -1;
+- }
++ if (ret)
++ return ret;
+
+- close(fd);
+- return 0;
+-}
++ ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
++ if (hapd->wpa_auth)
++ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+-static int ifconfig_up(const char *if_name)
+-{
+- wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+- return ifconfig_helper(if_name, 1);
+-}
++ if (ret == 0)
++ return ret;
+
++ wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
++ vlan->vlan_id, ret);
++ if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
++ wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+-static int ifconfig_down(const char *if_name)
+-{
+- wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+- return ifconfig_helper(if_name, 0);
+-}
++ /* group state machine setup failed */
++ if (hostapd_vlan_if_remove(hapd, vlan->ifname))
++ wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+-
+-/*
+- * These are only available in recent linux headers (without the leading
+- * underscore).
+- */
+-#define _GET_VLAN_REALDEV_NAME_CMD 8
+-#define _GET_VLAN_VID_CMD 9
+-
+-/* This value should be 256 ONLY. If it is something else, then hostapd
+- * might crash!, as this value has been hard-coded in 2.4.x kernel
+- * bridging code.
+- */
+-#define MAX_BR_PORTS 256
+-
+-static int br_delif(const char *br_name, const char *if_name)
+-{
+- int fd;
+- struct ifreq ifr;
+- unsigned long args[2];
+- int if_index;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- if_index = if_nametoindex(if_name);
+-
+- if (if_index == 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+- "interface index for '%s'",
+- __func__, if_name);
+- close(fd);
+- return -1;
+- }
+-
+- args[0] = BRCTL_DEL_IF;
+- args[1] = if_index;
+-
+- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+- ifr.ifr_data = (__caddr_t) args;
+-
+- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+- /* No error if interface already removed. */
+- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+- "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+- "%s", __func__, br_name, if_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
++ return ret;
+ }
+
+
+-/*
+- Add interface 'if_name' to the bridge 'br_name'
+-
+- returns -1 on error
+- returns 1 if the interface is already part of the bridge
+- returns 0 otherwise
+-*/
+-static int br_addif(const char *br_name, const char *if_name)
++int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+ {
+- int fd;
+- struct ifreq ifr;
+- unsigned long args[2];
+- int if_index;
++ int ret;
+
+- wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
++ ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
++ if (ret)
++ wpa_printf(MSG_ERROR,
++ "WPA deinitialization for VLAN %d failed (%d)",
++ vlan->vlan_id, ret);
+
+- if_index = if_nametoindex(if_name);
+-
+- if (if_index == 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+- "interface index for '%s'",
+- __func__, if_name);
+- close(fd);
+- return -1;
+- }
+-
+- args[0] = BRCTL_ADD_IF;
+- args[1] = if_index;
+-
+- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+- ifr.ifr_data = (__caddr_t) args;
+-
+- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+- if (errno == EBUSY) {
+- /* The interface is already added. */
+- close(fd);
+- return 1;
+- }
+-
+- wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+- "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+- "%s", __func__, br_name, if_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
++ return hostapd_vlan_if_remove(hapd, vlan->ifname);
+ }
+
+
+-static int br_delbr(const char *br_name)
+-{
+- int fd;
+- unsigned long arg[2];
+-
+- wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- arg[0] = BRCTL_DEL_BRIDGE;
+- arg[1] = (unsigned long) br_name;
+-
+- if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+- /* No error if bridge already removed. */
+- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+- "%s: %s", __func__, br_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
+-}
+-
+-
+-/*
+- Add a bridge with the name 'br_name'.
+-
+- returns -1 on error
+- returns 1 if the bridge already exists
+- returns 0 otherwise
+-*/
+-static int br_addbr(const char *br_name)
+-{
+- int fd;
+- unsigned long arg[4];
+- struct ifreq ifr;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- arg[0] = BRCTL_ADD_BRIDGE;
+- arg[1] = (unsigned long) br_name;
+-
+- if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+- if (errno == EEXIST) {
+- /* The bridge is already added. */
+- close(fd);
+- return 1;
+- } else {
+- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+- "failed for %s: %s",
+- __func__, br_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+- }
+-
+- /* Decrease forwarding delay to avoid EAPOL timeouts. */
+- os_memset(&ifr, 0, sizeof(ifr));
+- os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+- arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+- arg[1] = 1;
+- arg[2] = 0;
+- arg[3] = 0;
+- ifr.ifr_data = (char *) &arg;
+- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: "
+- "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+- "%s: %s", __func__, br_name, strerror(errno));
+- /* Continue anyway */
+- }
+-
+- close(fd);
+- return 0;
+-}
+-
+-
+-static int br_getnumports(const char *br_name)
+-{
+- int fd;
+- int i;
+- int port_cnt = 0;
+- unsigned long arg[4];
+- int ifindices[MAX_BR_PORTS];
+- struct ifreq ifr;
+-
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- arg[0] = BRCTL_GET_PORT_LIST;
+- arg[1] = (unsigned long) ifindices;
+- arg[2] = MAX_BR_PORTS;
+- arg[3] = 0;
+-
+- os_memset(ifindices, 0, sizeof(ifindices));
+- os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+- ifr.ifr_data = (__caddr_t) arg;
+-
+- if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+- "failed for %s: %s",
+- __func__, br_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- for (i = 1; i < MAX_BR_PORTS; i++) {
+- if (ifindices[i] > 0) {
+- port_cnt++;
+- }
+- }
+-
+- close(fd);
+- return port_cnt;
+-}
+-
+-
+-#ifndef CONFIG_VLAN_NETLINK
+-
+-int vlan_rem(const char *if_name)
+-{
+- int fd;
+- struct vlan_ioctl_args if_request;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+- if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+- wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+- if_name);
+- return -1;
+- }
+-
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- os_memset(&if_request, 0, sizeof(if_request));
+-
+- os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+- if_request.cmd = DEL_VLAN_CMD;
+-
+- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+- "%s", __func__, if_name, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
+-}
+-
+-
+-/*
+- Add a vlan interface with VLAN ID 'vid' and tagged interface
+- 'if_name'.
+-
+- returns -1 on error
+- returns 1 if the interface already exists
+- returns 0 otherwise
+-*/
+-int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+-{
+- int fd;
+- struct vlan_ioctl_args if_request;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+- if_name, vid);
+- ifconfig_up(if_name);
+-
+- if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+- wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+- if_name);
+- return -1;
+- }
+-
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- os_memset(&if_request, 0, sizeof(if_request));
+-
+- /* Determine if a suitable vlan device already exists. */
+-
+- os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+- vid);
+-
+- if_request.cmd = _GET_VLAN_VID_CMD;
+-
+- if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
+-
+- if (if_request.u.VID == vid) {
+- if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
+-
+- if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+- os_strncmp(if_request.u.device2, if_name,
+- sizeof(if_request.u.device2)) == 0) {
+- close(fd);
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
+- "if_name %s exists already",
+- if_request.device1);
+- return 1;
+- }
+- }
+- }
+-
+- /* A suitable vlan device does not already exist, add one. */
+-
+- os_memset(&if_request, 0, sizeof(if_request));
+- os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+- if_request.u.VID = vid;
+- if_request.cmd = ADD_VLAN_CMD;
+-
+- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
+- "%s",
+- __func__, if_request.device1, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
+-}
+-
+-
+-static int vlan_set_name_type(unsigned int name_type)
+-{
+- int fd;
+- struct vlan_ioctl_args if_request;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+- name_type);
+- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+- "failed: %s", __func__, strerror(errno));
+- return -1;
+- }
+-
+- os_memset(&if_request, 0, sizeof(if_request));
+-
+- if_request.u.name_type = name_type;
+- if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+- if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
+- "name_type=%u failed: %s",
+- __func__, name_type, strerror(errno));
+- close(fd);
+- return -1;
+- }
+-
+- close(fd);
+- return 0;
+-}
+-
+-#endif /* CONFIG_VLAN_NETLINK */
+-
+-
+-static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+-{
+- char vlan_ifname[IFNAMSIZ];
+- char br_name[IFNAMSIZ];
+- struct hostapd_vlan *vlan = hapd->conf->vlan;
+- char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+- int vlan_naming = hapd->conf->ssid.vlan_naming;
+- int clean;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+-
+- while (vlan) {
+- if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) {
+- vlan->configured = 1;
+-
+- if (hapd->conf->vlan_bridge[0]) {
+- os_snprintf(br_name, sizeof(br_name), "%s%d",
+- hapd->conf->vlan_bridge,
+- vlan->vlan_id);
+- } else if (tagged_interface) {
+- os_snprintf(br_name, sizeof(br_name),
+- "br%s.%d", tagged_interface,
+- vlan->vlan_id);
+- } else {
+- os_snprintf(br_name, sizeof(br_name),
+- "brvlan%d", vlan->vlan_id);
+- }
+-
+- dyn_iface_get(hapd, br_name,
+- br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+-
+- ifconfig_up(br_name);
+-
+- if (tagged_interface) {
+- if (vlan_naming ==
+- DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+- os_snprintf(vlan_ifname,
+- sizeof(vlan_ifname),
+- "%s.%d", tagged_interface,
+- vlan->vlan_id);
+- else
+- os_snprintf(vlan_ifname,
+- sizeof(vlan_ifname),
+- "vlan%d", vlan->vlan_id);
+-
+- clean = 0;
+- ifconfig_up(tagged_interface);
+- if (!vlan_add(tagged_interface, vlan->vlan_id,
+- vlan_ifname))
+- clean |= DVLAN_CLEAN_VLAN;
+-
+- if (!br_addif(br_name, vlan_ifname))
+- clean |= DVLAN_CLEAN_VLAN_PORT;
+-
+- dyn_iface_get(hapd, vlan_ifname, clean);
+-
+- ifconfig_up(vlan_ifname);
+- }
+-
+- if (!br_addif(br_name, ifname))
+- vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+-
+- ifconfig_up(ifname);
+-
+- break;
+- }
+- vlan = vlan->next;
+- }
+-}
+-
+-
+-static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
+-{
+- char vlan_ifname[IFNAMSIZ];
+- char br_name[IFNAMSIZ];
+- struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+- char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+- int vlan_naming = hapd->conf->ssid.vlan_naming;
+- int clean;
+-
+- wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+-
+- first = prev = vlan;
+-
+- while (vlan) {
+- if (os_strcmp(ifname, vlan->ifname) == 0 &&
+- vlan->configured) {
+- if (hapd->conf->vlan_bridge[0]) {
+- os_snprintf(br_name, sizeof(br_name), "%s%d",
+- hapd->conf->vlan_bridge,
+- vlan->vlan_id);
+- } else if (tagged_interface) {
+- os_snprintf(br_name, sizeof(br_name),
+- "br%s.%d", tagged_interface,
+- vlan->vlan_id);
+- } else {
+- os_snprintf(br_name, sizeof(br_name),
+- "brvlan%d", vlan->vlan_id);
+- }
+-
+- if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+- br_delif(br_name, vlan->ifname);
+-
+- if (tagged_interface) {
+- if (vlan_naming ==
+- DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+- os_snprintf(vlan_ifname,
+- sizeof(vlan_ifname),
+- "%s.%d", tagged_interface,
+- vlan->vlan_id);
+- else
+- os_snprintf(vlan_ifname,
+- sizeof(vlan_ifname),
+- "vlan%d", vlan->vlan_id);
+-
+- clean = dyn_iface_put(hapd, vlan_ifname);
+-
+- if (clean & DVLAN_CLEAN_VLAN_PORT)
+- br_delif(br_name, vlan_ifname);
+-
+- if (clean & DVLAN_CLEAN_VLAN) {
+- ifconfig_down(vlan_ifname);
+- vlan_rem(vlan_ifname);
+- }
+- }
+-
+- clean = dyn_iface_put(hapd, br_name);
+- if ((clean & DVLAN_CLEAN_BR) &&
+- br_getnumports(br_name) == 0) {
+- ifconfig_down(br_name);
+- br_delbr(br_name);
+- }
+- }
+-
+- if (os_strcmp(ifname, vlan->ifname) == 0) {
+- if (vlan == first) {
+- hapd->conf->vlan = vlan->next;
+- } else {
+- prev->next = vlan->next;
+- }
+- os_free(vlan);
+-
+- break;
+- }
+- prev = vlan;
+- vlan = vlan->next;
+- }
+-}
+-
+-
+-static void
+-vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+- struct hostapd_data *hapd)
+-{
+- struct ifinfomsg *ifi;
+- int attrlen, nlmsg_len, rta_len;
+- struct rtattr *attr;
+- char ifname[IFNAMSIZ + 1];
+-
+- if (len < sizeof(*ifi))
+- return;
+-
+- ifi = NLMSG_DATA(h);
+-
+- nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+-
+- attrlen = h->nlmsg_len - nlmsg_len;
+- if (attrlen < 0)
+- return;
+-
+- attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+-
+- os_memset(ifname, 0, sizeof(ifname));
+- rta_len = RTA_ALIGN(sizeof(struct rtattr));
+- while (RTA_OK(attr, attrlen)) {
+- if (attr->rta_type == IFLA_IFNAME) {
+- int n = attr->rta_len - rta_len;
+- if (n < 0)
+- break;
+-
+- if ((size_t) n >= sizeof(ifname))
+- n = sizeof(ifname) - 1;
+- os_memcpy(ifname, ((char *) attr) + rta_len, n);
+-
+- }
+-
+- attr = RTA_NEXT(attr, attrlen);
+- }
+-
+- if (!ifname[0])
+- return;
+- if (del && if_nametoindex(ifname)) {
+- /* interface still exists, race condition ->
+- * iface has just been recreated */
+- return;
+- }
+-
+- wpa_printf(MSG_DEBUG,
+- "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+- del ? "DEL" : "NEW",
+- ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+- (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+- (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+- (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+- (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+-
+- if (del)
+- vlan_dellink(ifname, hapd);
+- else
+- vlan_newlink(ifname, hapd);
+-}
+-
+-
+-static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+-{
+- char buf[8192];
+- int left;
+- struct sockaddr_nl from;
+- socklen_t fromlen;
+- struct nlmsghdr *h;
+- struct hostapd_data *hapd = eloop_ctx;
+-
+- fromlen = sizeof(from);
+- left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+- (struct sockaddr *) &from, &fromlen);
+- if (left < 0) {
+- if (errno != EINTR && errno != EAGAIN)
+- wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+- __func__, strerror(errno));
+- return;
+- }
+-
+- h = (struct nlmsghdr *) buf;
+- while (NLMSG_OK(h, left)) {
+- int len, plen;
+-
+- len = h->nlmsg_len;
+- plen = len - sizeof(*h);
+- if (len > left || plen < 0) {
+- wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+- "message: len=%d left=%d plen=%d",
+- len, left, plen);
+- break;
+- }
+-
+- switch (h->nlmsg_type) {
+- case RTM_NEWLINK:
+- vlan_read_ifnames(h, plen, 0, hapd);
+- break;
+- case RTM_DELLINK:
+- vlan_read_ifnames(h, plen, 1, hapd);
+- break;
+- }
+-
+- h = NLMSG_NEXT(h, left);
+- }
+-
+- if (left > 0) {
+- wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+- "netlink message", __func__, left);
+- }
+-}
+-
+-
+-static struct full_dynamic_vlan *
+-full_dynamic_vlan_init(struct hostapd_data *hapd)
+-{
+- struct sockaddr_nl local;
+- struct full_dynamic_vlan *priv;
+-
+- priv = os_zalloc(sizeof(*priv));
+- if (priv == NULL)
+- return NULL;
+-
+-#ifndef CONFIG_VLAN_NETLINK
+- vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+- DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+- VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+- VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+-#endif /* CONFIG_VLAN_NETLINK */
+-
+- priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+- if (priv->s < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+- "NETLINK_ROUTE) failed: %s",
+- __func__, strerror(errno));
+- os_free(priv);
+- return NULL;
+- }
+-
+- os_memset(&local, 0, sizeof(local));
+- local.nl_family = AF_NETLINK;
+- local.nl_groups = RTMGRP_LINK;
+- if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+- wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+- __func__, strerror(errno));
+- close(priv->s);
+- os_free(priv);
+- return NULL;
+- }
+-
+- if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+- {
+- close(priv->s);
+- os_free(priv);
+- return NULL;
+- }
+-
+- return priv;
+-}
+-
+-
+-static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+-{
+- if (priv == NULL)
+- return;
+- eloop_unregister_read_sock(priv->s);
+- close(priv->s);
+- os_free(priv);
+-}
+-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+-
+-
+-int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
+-{
+- int i;
+-
+- if (dyn_vlan == NULL)
+- return 0;
+-
+- /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
+- * functions for setting up dynamic broadcast keys. */
+- for (i = 0; i < 4; i++) {
+- if (hapd->conf->ssid.wep.key[i] &&
+- hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+- i == hapd->conf->ssid.wep.idx, NULL, 0,
+- hapd->conf->ssid.wep.key[i],
+- hapd->conf->ssid.wep.len[i]))
+- {
+- wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
+- "encryption for dynamic VLAN");
+- return -1;
+- }
+- }
+-
+- return 0;
+-}
+-
+-
+ static int vlan_dynamic_add(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan)
+ {
+ while (vlan) {
+ if (vlan->vlan_id != VLAN_ID_WILDCARD) {
+- if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
+- if (errno != EEXIST) {
+- wpa_printf(MSG_ERROR, "VLAN: Could "
+- "not add VLAN %s: %s",
+- vlan->ifname,
+- strerror(errno));
+- return -1;
+- }
++ if (vlan_if_add(hapd, vlan, 1)) {
++ wpa_printf(MSG_ERROR,
++ "VLAN: Could not add VLAN %s: %s",
++ vlan->ifname, strerror(errno));
++ return -1;
+ }
+ #ifdef CONFIG_FULL_DYNAMIC_VLAN
+- ifconfig_up(vlan->ifname);
++ vlan_newlink(vlan->ifname, hapd);
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ }
+
+@@ -942,15 +109,17 @@
+ while (vlan) {
+ next = vlan->next;
+
++#ifdef CONFIG_FULL_DYNAMIC_VLAN
++ /* vlan_dellink() takes care of cleanup and interface removal */
++ if (vlan->vlan_id != VLAN_ID_WILDCARD)
++ vlan_dellink(vlan->ifname, hapd);
++#else /* CONFIG_FULL_DYNAMIC_VLAN */
+ if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+- hostapd_vlan_if_remove(hapd, vlan->ifname)) {
++ vlan_if_remove(hapd, vlan)) {
+ wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+ "iface: %s: %s",
+ vlan->ifname, strerror(errno));
+ }
+-#ifdef CONFIG_FULL_DYNAMIC_VLAN
+- if (vlan->clean)
+- vlan_dellink(vlan->ifname, hapd);
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ vlan = next;
+@@ -964,10 +133,13 @@
+ hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+- if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
++ if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
++ hapd->conf->ssid.per_sta_vif) &&
+ !hapd->conf->vlan) {
+ /* dynamic vlans enabled but no (or empty) vlan_file given */
+ struct hostapd_vlan *vlan;
++ int ret;
++
+ vlan = os_zalloc(sizeof(*vlan));
+ if (vlan == NULL) {
+ wpa_printf(MSG_ERROR, "Out of memory while assigning "
+@@ -976,8 +148,16 @@
+ }
+
+ vlan->vlan_id = VLAN_ID_WILDCARD;
+- os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+- hapd->conf->iface);
++ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
++ hapd->conf->iface);
++ if (ret >= (int) sizeof(vlan->ifname)) {
++ wpa_printf(MSG_WARNING,
++ "VLAN: Interface name was truncated to %s",
++ vlan->ifname);
++ } else if (ret < 0) {
++ os_free(vlan);
++ return ret;
++ }
+ vlan->next = hapd->conf->vlan;
+ hapd->conf->vlan = vlan;
+ }
+@@ -1002,50 +182,51 @@
+
+ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+- int vlan_id)
++ int vlan_id,
++ struct vlan_description *vlan_desc)
+ {
+- struct hostapd_vlan *n = NULL;
+- char *ifname, *pos;
++ struct hostapd_vlan *n;
++ char ifname[IFNAMSIZ + 1], *pos;
++ int ret;
+
+- if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+- vlan->vlan_id != VLAN_ID_WILDCARD)
++ if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+ __func__, vlan_id, vlan->ifname);
+- ifname = os_strdup(vlan->ifname);
+- if (ifname == NULL)
+- return NULL;
++ os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
+ pos = os_strchr(ifname, '#');
+ if (pos == NULL)
+- goto free_ifname;
++ return NULL;
+ *pos++ = '\0';
+
+ n = os_zalloc(sizeof(*n));
+ if (n == NULL)
+- goto free_ifname;
++ return NULL;
+
+ n->vlan_id = vlan_id;
++ if (vlan_desc)
++ n->vlan_desc = *vlan_desc;
+ n->dynamic_vlan = 1;
+
+- os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
+- pos);
+-
+- if (hostapd_vlan_if_add(hapd, n->ifname)) {
++ ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
++ ifname, vlan_id, pos);
++ if (os_snprintf_error(sizeof(n->ifname), ret)) {
+ os_free(n);
+- n = NULL;
+- goto free_ifname;
++ return NULL;
+ }
++ os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
+
+ n->next = hapd->conf->vlan;
+ hapd->conf->vlan = n;
+
+-#ifdef CONFIG_FULL_DYNAMIC_VLAN
+- ifconfig_up(n->ifname);
+-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
++ /* hapd->conf->vlan needs this new VLAN here for WPA setup */
++ if (vlan_if_add(hapd, n, 0)) {
++ hapd->conf->vlan = n->next;
++ os_free(n);
++ n = NULL;
++ }
+
+-free_ifname:
+- os_free(ifname);
+ return n;
+ }
+
+@@ -1054,7 +235,7 @@
+ {
+ struct hostapd_vlan *vlan;
+
+- if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
++ if (vlan_id <= 0)
+ return 1;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
+@@ -1073,7 +254,7 @@
+ return 1;
+
+ if (vlan->dynamic_vlan == 0) {
+- hostapd_vlan_if_remove(hapd, vlan->ifname);
++ vlan_if_remove(hapd, vlan);
+ #ifdef CONFIG_FULL_DYNAMIC_VLAN
+ vlan_dellink(vlan->ifname, hapd);
+ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
+--- contrib/wpa/src/ap/vlan_init.h.orig
++++ contrib/wpa/src/ap/vlan_init.h
+@@ -15,10 +15,9 @@
+ void vlan_deinit(struct hostapd_data *hapd);
+ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+- int vlan_id);
++ int vlan_id,
++ struct vlan_description *vlan_desc);
+ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+-int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+- const char *dyn_vlan);
+ #else /* CONFIG_NO_VLAN */
+ static inline int vlan_init(struct hostapd_data *hapd)
+ {
+@@ -29,9 +28,9 @@
+ {
+ }
+
+-static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+- struct hostapd_vlan *vlan,
+- int vlan_id)
++static inline struct hostapd_vlan *
++vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
++ int vlan_id, struct vlan_description *vlan_desc)
+ {
+ return NULL;
+ }
+@@ -40,12 +39,6 @@
+ {
+ return -1;
+ }
+-
+-static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+- const char *dyn_vlan)
+-{
+- return -1;
+-}
+ #endif /* CONFIG_NO_VLAN */
+
+ #endif /* VLAN_INIT_H */
+--- contrib/wpa/src/ap/vlan_ioctl.c.orig
++++ contrib/wpa/src/ap/vlan_ioctl.c
+@@ -0,0 +1,155 @@
++/*
++ * hostapd / VLAN ioctl API
++ * Copyright 2003, Instant802 Networks, Inc.
++ * Copyright 2005-2006, Devicescape Software, Inc.
++ * Copyright (c) 2009, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include
++
++#include "utils/common.h"
++#include "common/linux_vlan.h"
++#include "vlan_util.h"
++
++
++int vlan_rem(const char *if_name)
++{
++ int fd;
++ struct vlan_ioctl_args if_request;
++
++ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
++ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
++ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
++ if_name);
++ return -1;
++ }
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ os_memset(&if_request, 0, sizeof(if_request));
++
++ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
++ if_request.cmd = DEL_VLAN_CMD;
++
++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
++ "%s", __func__, if_name, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ close(fd);
++ return 0;
++}
++
++
++/*
++ Add a vlan interface with VLAN ID 'vid' and tagged interface
++ 'if_name'.
++
++ returns -1 on error
++ returns 1 if the interface already exists
++ returns 0 otherwise
++*/
++int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
++{
++ int fd;
++ struct vlan_ioctl_args if_request;
++
++ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
++ if_name, vid);
++ ifconfig_up(if_name);
++
++ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
++ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
++ if_name);
++ return -1;
++ }
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
++ "failed: %s", __func__, strerror(errno));
++ return -1;
++ }
++
++ os_memset(&if_request, 0, sizeof(if_request));
++
++ /* Determine if a suitable vlan device already exists. */
++
++ os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
++ vid);
++
++ if_request.cmd = GET_VLAN_VID_CMD;
++
++ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
++ if_request.u.VID == vid) {
++ if_request.cmd = GET_VLAN_REALDEV_NAME_CMD;
++
++ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
++ os_strncmp(if_request.u.device2, if_name,
++ sizeof(if_request.u.device2)) == 0) {
++ close(fd);
++ wpa_printf(MSG_DEBUG,
++ "VLAN: vlan_add: if_name %s exists already",
++ if_request.device1);
++ return 1;
++ }
++ }
++
++ /* A suitable vlan device does not already exist, add one. */
++
++ os_memset(&if_request, 0, sizeof(if_request));
++ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
++ if_request.u.VID = vid;
++ if_request.cmd = ADD_VLAN_CMD;
++
++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
++ wpa_printf(MSG_ERROR,
++ "VLAN: %s: ADD_VLAN_CMD failed for %s: %s",
++ __func__, if_request.device1, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ close(fd);
++ return 0;
++}
++
++
++int vlan_set_name_type(unsigned int name_type)
++{
++ int fd;
++ struct vlan_ioctl_args if_request;
++
++ wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
++ name_type);
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ wpa_printf(MSG_ERROR,
++ "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++
++ os_memset(&if_request, 0, sizeof(if_request));
++
++ if_request.u.name_type = name_type;
++ if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
++ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
++ wpa_printf(MSG_ERROR,
++ "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s",
++ __func__, name_type, strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ close(fd);
++ return 0;
++}
+--- contrib/wpa/src/ap/vlan_util.c.orig
++++ contrib/wpa/src/ap/vlan_util.c
+@@ -7,18 +7,10 @@
+ */
+
+ #include "utils/includes.h"
+-#include
+-#include
+-#include
+-#include
+-#include
+-#include
+ #include
+ #include
+
+ #include "utils/common.h"
+-#include "utils/eloop.h"
+-#include "hostapd.h"
+ #include "vlan_util.h"
+
+ /*
+@@ -33,7 +25,6 @@
+ {
+ int err, ret = -1;
+ struct nl_sock *handle = NULL;
+- struct nl_cache *cache = NULL;
+ struct rtnl_link *rlink = NULL;
+ int if_idx = 0;
+
+@@ -65,22 +56,19 @@
+ goto vlan_add_error;
+ }
+
+- err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
++ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+ if (err < 0) {
+- cache = NULL;
+- wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+- nl_geterror(err));
+- goto vlan_add_error;
+- }
+-
+- if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+ if_name);
+ goto vlan_add_error;
+ }
++ if_idx = rtnl_link_get_ifindex(rlink);
++ rtnl_link_put(rlink);
++ rlink = NULL;
+
+- if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
++ err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink);
++ if (err >= 0) {
+ /* link does exist */
+ rtnl_link_put(rlink);
+ rlink = NULL;
+@@ -127,8 +115,6 @@
+ vlan_add_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+- if (cache)
+- nl_cache_free(cache);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+@@ -139,7 +125,6 @@
+ {
+ int err, ret = -1;
+ struct nl_sock *handle = NULL;
+- struct nl_cache *cache = NULL;
+ struct rtnl_link *rlink = NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+@@ -157,15 +142,8 @@
+ goto vlan_rem_error;
+ }
+
+- err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
++ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+ if (err < 0) {
+- cache = NULL;
+- wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+- nl_geterror(err));
+- goto vlan_rem_error;
+- }
+-
+- if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+ if_name);
+@@ -184,9 +162,13 @@
+ vlan_rem_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+- if (cache)
+- nl_cache_free(cache);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+ }
++
++
++int vlan_set_name_type(unsigned int name_type)
++{
++ return 0;
++}
+--- contrib/wpa/src/ap/vlan_util.h.orig
++++ contrib/wpa/src/ap/vlan_util.h
+@@ -1,5 +1,5 @@
+ /*
+- * hostapd / VLAN netlink api
++ * hostapd / VLAN netlink/ioctl api
+ * Copyright (c) 2012, Michael Braun
+ *
+ * This software may be distributed under the terms of the BSD license.
+@@ -9,7 +9,23 @@
+ #ifndef VLAN_UTIL_H
+ #define VLAN_UTIL_H
+
++struct hostapd_data;
++struct hostapd_vlan;
++struct full_dynamic_vlan;
++
+ int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+ int vlan_rem(const char *if_name);
++int vlan_set_name_type(unsigned int name_type);
+
++int ifconfig_helper(const char *if_name, int up);
++int ifconfig_up(const char *if_name);
++int iface_exists(const char *ifname);
++int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
++
++struct full_dynamic_vlan *
++full_dynamic_vlan_init(struct hostapd_data *hapd);
++void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
++void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
++void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
++
+ #endif /* VLAN_UTIL_H */
+--- contrib/wpa/src/ap/wmm.c.orig
++++ contrib/wpa/src/ap/wmm.c
+@@ -21,11 +21,6 @@
+ #include "wmm.h"
+
+
+-/* TODO: maintain separate sequence and fragment numbers for each AC
+- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
+- * if only WMM stations are receiving a certain group */
+-
+-
+ static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+ {
+ u8 ret;
+@@ -157,8 +152,9 @@
+
+ int wmm_process_tspec(struct wmm_tspec_element *tspec)
+ {
+- int medium_time, pps, duration;
+- int up, psb, dir, tid;
++ u64 medium_time;
++ unsigned int pps, duration;
++ unsigned int up, psb, dir, tid;
+ u16 val, surplus;
+
+ up = (tspec->ts_info[1] >> 3) & 0x07;
+@@ -206,8 +202,9 @@
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+
+- medium_time = surplus * pps * duration / 0x2000;
+- wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
++ medium_time = (u64) surplus * pps * duration / 0x2000;
++ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
++ (unsigned long) medium_time);
+
+ /*
+ * TODO: store list of granted (and still active) TSPECs and check
+--- contrib/wpa/src/ap/wnm_ap.c.orig
++++ contrib/wpa/src/ap/wnm_ap.c
+@@ -12,11 +12,13 @@
+ #include "utils/eloop.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/wpa_ctrl.h"
++#include "common/ocv.h"
+ #include "ap/hostapd.h"
+ #include "ap/sta_info.h"
+ #include "ap/ap_config.h"
+ #include "ap/ap_drv_ops.h"
+ #include "ap/wpa_auth.h"
++#include "mbo_ap.h"
+ #include "wnm_ap.h"
+
+ #define MAX_TFS_IE_LEN 1024
+@@ -53,8 +55,8 @@
+ size_t gtk_elem_len = 0;
+ size_t igtk_elem_len = 0;
+ struct wnm_sleep_element wnmsleep_ie;
+- u8 *wnmtfs_ie;
+- u8 wnmsleep_ie_len;
++ u8 *wnmtfs_ie, *oci_ie;
++ u8 wnmsleep_ie_len, oci_ie_len;
+ u16 wnmtfs_ie_len;
+ u8 *pos;
+ struct sta_info *sta;
+@@ -87,14 +89,47 @@
+ wnmtfs_ie = NULL;
+ }
+
++ oci_ie = NULL;
++ oci_ie_len = 0;
++#ifdef CONFIG_OCV
++ if (action_type == WNM_SLEEP_MODE_EXIT &&
++ wpa_auth_uses_ocv(sta->wpa_sm)) {
++ struct wpa_channel_info ci;
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
++ os_free(wnmtfs_ie);
++ return -1;
++ }
++
++ oci_ie_len = OCV_OCI_EXTENDED_LEN;
++ oci_ie = os_zalloc(oci_ie_len);
++ if (!oci_ie) {
++ wpa_printf(MSG_WARNING,
++ "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
++ os_free(wnmtfs_ie);
++ return -1;
++ }
++
++ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
++ os_free(wnmtfs_ie);
++ os_free(oci_ie);
++ return -1;
++ }
++ }
++#endif /* CONFIG_OCV */
++
+ #define MAX_GTK_SUBELEM_LEN 45
+ #define MAX_IGTK_SUBELEM_LEN 26
+ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+- MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
++ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
++ oci_ie_len);
+ if (mgmt == NULL) {
+ wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+ "WNM-Sleep Response action frame");
+- return -1;
++ res = -1;
++ goto fail;
+ }
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+@@ -107,6 +142,7 @@
+ pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+ /* add key data if MFP is enabled */
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
++ hapd->conf->wnm_sleep_mode_no_keys ||
+ action_type != WNM_SLEEP_MODE_EXIT) {
+ mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+ } else {
+@@ -116,11 +152,8 @@
+ (int) gtk_elem_len);
+ #ifdef CONFIG_IEEE80211W
+ res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+- if (res < 0) {
+- os_free(wnmtfs_ie);
+- os_free(mgmt);
+- return -1;
+- }
++ if (res < 0)
++ goto fail;
+ igtk_elem_len = res;
+ pos += igtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+@@ -134,11 +167,18 @@
+ os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+ /* copy TFS IE here */
+ pos += wnmsleep_ie_len;
+- if (wnmtfs_ie)
++ if (wnmtfs_ie) {
+ os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
++ pos += wnmtfs_ie_len;
++ }
++#ifdef CONFIG_OCV
++ /* copy OCV OCI here */
++ if (oci_ie_len > 0)
++ os_memcpy(pos, oci_ie, oci_ie_len);
++#endif /* CONFIG_OCV */
+
+ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+- igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
++ igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
+
+ /* In driver, response frame should be forced to sent when STA is in
+ * PS mode */
+@@ -174,7 +214,8 @@
+ wpa_set_wnmsleep(sta->wpa_sm, 0);
+ hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
+ addr, NULL, NULL);
+- if (!wpa_auth_uses_mfp(sta->wpa_sm))
++ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
++ hapd->conf->wnm_sleep_mode_no_keys)
+ wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+ }
+ } else
+@@ -182,7 +223,9 @@
+
+ #undef MAX_GTK_SUBELEM_LEN
+ #undef MAX_IGTK_SUBELEM_LEN
++fail:
+ os_free(wnmtfs_ie);
++ os_free(oci_ie);
+ os_free(mgmt);
+ return res;
+ }
+@@ -199,18 +242,44 @@
+ u8 *tfsreq_ie_start = NULL;
+ u8 *tfsreq_ie_end = NULL;
+ u16 tfsreq_ie_len = 0;
++#ifdef CONFIG_OCV
++ struct sta_info *sta;
++ const u8 *oci_ie = NULL;
++ u8 oci_ie_len = 0;
++#endif /* CONFIG_OCV */
+
++ if (!hapd->conf->wnm_sleep_mode) {
++ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
++ MACSTR " since WNM-Sleep Mode is disabled",
++ MAC2STR(addr));
++ return;
++ }
++
++ if (len < 1) {
++ wpa_printf(MSG_DEBUG,
++ "WNM: Ignore too short WNM-Sleep Mode Request from "
++ MACSTR, MAC2STR(addr));
++ return;
++ }
++
+ dialog_token = *pos++;
+ while (pos + 1 < frm + len) {
+ u8 ie_len = pos[1];
+ if (pos + 2 + ie_len > frm + len)
+ break;
+- if (*pos == WLAN_EID_WNMSLEEP)
++ if (*pos == WLAN_EID_WNMSLEEP &&
++ ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
+ wnmsleep_ie = (struct wnm_sleep_element *) pos;
+ else if (*pos == WLAN_EID_TFS_REQ) {
+ if (!tfsreq_ie_start)
+ tfsreq_ie_start = (u8 *) pos;
+ tfsreq_ie_end = (u8 *) pos;
++#ifdef CONFIG_OCV
++ } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
++ pos[2] == WLAN_EID_EXT_OCV_OCI) {
++ oci_ie = pos + 3;
++ oci_ie_len = ie_len - 1;
++#endif /* CONFIG_OCV */
+ } else
+ wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+ *pos);
+@@ -222,6 +291,27 @@
+ return;
+ }
+
++#ifdef CONFIG_OCV
++ sta = ap_get_sta(hapd, addr);
++ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
++ sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
++ struct wpa_channel_info ci;
++
++ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
++ return;
++ }
++
++ if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx) != 0) {
++ wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+ tfsreq_ie_start && tfsreq_ie_end &&
+ tfsreq_ie_end - tfsreq_ie_start >= 0) {
+@@ -249,20 +339,14 @@
+
+ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ const u8 *addr,
+- u8 dialog_token,
+- const char *url)
++ u8 dialog_token)
+ {
+ struct ieee80211_mgmt *mgmt;
+- size_t url_len, len;
++ size_t len;
+ u8 *pos;
+ int res;
+
+- if (url)
+- url_len = os_strlen(url);
+- else
+- url_len = 0;
+-
+- mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
++ mgmt = os_zalloc(sizeof(*mgmt));
+ if (mgmt == NULL)
+ return -1;
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+@@ -277,11 +361,6 @@
+ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+- if (url) {
+- *pos++ += url_len;
+- os_memcpy(pos, url, url_len);
+- pos += url_len;
+- }
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+@@ -305,7 +384,21 @@
+ {
+ u8 dialog_token, reason;
+ const u8 *pos, *end;
++ int enabled = hapd->conf->bss_transition;
+
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled)
++ enabled = 1;
++#endif /* CONFIG_MBO */
++ if (!enabled) {
++ wpa_printf(MSG_DEBUG,
++ "Ignore BSS Transition Management Query from "
++ MACSTR
++ " since BSS Transition Management is disabled",
++ MAC2STR(addr));
++ return;
++ }
++
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+ MACSTR, MAC2STR(addr));
+@@ -324,10 +417,23 @@
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+
+- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
++ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+ }
+
+
++void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct sta_info *sta = timeout_ctx;
++
++ if (sta->agreed_to_steer) {
++ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
++ hapd->conf->iface, MAC2STR(sta->addr));
++ sta->agreed_to_steer = 0;
++ }
++}
++
++
+ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm,
+ size_t len)
+@@ -334,7 +440,22 @@
+ {
+ u8 dialog_token, status_code, bss_termination_delay;
+ const u8 *pos, *end;
++ int enabled = hapd->conf->bss_transition;
++ struct sta_info *sta;
+
++#ifdef CONFIG_MBO
++ if (hapd->conf->mbo_enabled)
++ enabled = 1;
++#endif /* CONFIG_MBO */
++ if (!enabled) {
++ wpa_printf(MSG_DEBUG,
++ "Ignore BSS Transition Management Response from "
++ MACSTR
++ " since BSS Transition Management is disabled",
++ MAC2STR(addr));
++ return;
++ }
++
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+ MACSTR, MAC2STR(addr));
+@@ -352,11 +473,23 @@
+ "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+ status_code, bss_termination_delay);
+
++ sta = ap_get_sta(hapd, addr);
++ if (!sta) {
++ wpa_printf(MSG_DEBUG, "Station " MACSTR
++ " not found for received BSS TM Response",
++ MAC2STR(addr));
++ return;
++ }
++
+ if (status_code == WNM_BSS_TM_ACCEPT) {
+ if (end - pos < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ return;
+ }
++ sta->agreed_to_steer = 1;
++ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
++ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
++ hapd, sta);
+ wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+ MAC2STR(pos));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+@@ -366,6 +499,7 @@
+ MAC2STR(pos));
+ pos += ETH_ALEN;
+ } else {
++ sta->agreed_to_steer = 0;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+ " status_code=%u bss_termination_delay=%u",
+ MAC2STR(addr), status_code, bss_termination_delay);
+@@ -376,6 +510,71 @@
+ }
+
+
++static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
++ const u8 *addr, const u8 *buf,
++ size_t len)
++{
++ u8 dialog_token, type;
++
++ if (len < 2)
++ return;
++ dialog_token = *buf++;
++ type = *buf++;
++ len -= 2;
++
++ wpa_printf(MSG_DEBUG,
++ "WNM: Received WNM Notification Request frame from "
++ MACSTR " (dialog_token=%u type=%u)",
++ MAC2STR(addr), dialog_token, type);
++ wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
++ buf, len);
++ if (type == WLAN_EID_VENDOR_SPECIFIC)
++ mbo_ap_wnm_notification_req(hapd, addr, buf, len);
++}
++
++
++static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
++ const u8 *addr, const u8 *buf,
++ size_t len)
++{
++ u8 dialog_token;
++ char *hex;
++ size_t hex_len;
++
++ if (!hapd->conf->coloc_intf_reporting) {
++ wpa_printf(MSG_DEBUG,
++ "WNM: Ignore unexpected Collocated Interference Report from "
++ MACSTR, MAC2STR(addr));
++ return;
++ }
++
++ if (len < 1) {
++ wpa_printf(MSG_DEBUG,
++ "WNM: Ignore too short Collocated Interference Report from "
++ MACSTR, MAC2STR(addr));
++ return;
++ }
++ dialog_token = *buf++;
++ len--;
++
++ wpa_printf(MSG_DEBUG,
++ "WNM: Received Collocated Interference Report frame from "
++ MACSTR " (dialog_token=%u)",
++ MAC2STR(addr), dialog_token);
++ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
++ buf, len);
++
++ hex_len = 2 * len + 1;
++ hex = os_malloc(hex_len);
++ if (!hex)
++ return;
++ wpa_snprintf_hex(hex, hex_len, buf, len);
++ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
++ MAC2STR(addr), dialog_token, hex);
++ os_free(hex);
++}
++
++
+ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+ {
+@@ -402,6 +601,14 @@
+ case WNM_SLEEP_MODE_REQ:
+ ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
+ return 0;
++ case WNM_NOTIFICATION_REQ:
++ ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
++ plen);
++ return 0;
++ case WNM_COLLOCATED_INTERFERENCE_REPORT:
++ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
++ plen);
++ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+@@ -527,7 +734,8 @@
+ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, const char *url,
+- const u8 *nei_rep, size_t nei_rep_len)
++ const u8 *nei_rep, size_t nei_rep_len,
++ const u8 *mbo_attrs, size_t mbo_len)
+ {
+ u8 *buf, *pos;
+ struct ieee80211_mgmt *mgmt;
+@@ -536,7 +744,7 @@
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+ MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+- buf = os_zalloc(1000 + nei_rep_len);
++ buf = os_zalloc(1000 + nei_rep_len + mbo_len);
+ if (buf == NULL)
+ return -1;
+ mgmt = (struct ieee80211_mgmt *) buf;
+@@ -579,6 +787,11 @@
+ pos += nei_rep_len;
+ }
+
++ if (mbo_len > 0) {
++ pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
++ mbo_len);
++ }
++
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send BSS Transition Management Request frame");
+@@ -594,3 +807,40 @@
+
+ return 0;
+ }
++
++
++int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
++ unsigned int auto_report, unsigned int timeout)
++{
++ u8 buf[100], *pos;
++ struct ieee80211_mgmt *mgmt;
++ u8 dialog_token = 1;
++
++ if (auto_report > 3 || timeout > 63)
++ return -1;
++ os_memset(buf, 0, sizeof(buf));
++ mgmt = (struct ieee80211_mgmt *) buf;
++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
++ WLAN_FC_STYPE_ACTION);
++ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
++ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
++ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
++ mgmt->u.action.category = WLAN_ACTION_WNM;
++ mgmt->u.action.u.coloc_intf_req.action =
++ WNM_COLLOCATED_INTERFERENCE_REQ;
++ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
++ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
++ pos = &mgmt->u.action.u.coloc_intf_req.req_info;
++ pos++;
++
++ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
++ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
++ MAC2STR(sta->addr), dialog_token, auto_report, timeout);
++ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "WNM: Failed to send Collocated Interference Request frame");
++ return -1;
++ }
++
++ return 0;
++}
+--- contrib/wpa/src/ap/wnm_ap.h.orig
++++ contrib/wpa/src/ap/wnm_ap.h
+@@ -21,6 +21,10 @@
+ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, const char *url,
+- const u8 *nei_rep, size_t nei_rep_len);
++ const u8 *nei_rep, size_t nei_rep_len,
++ const u8 *mbo_attrs, size_t mbo_len);
++void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
++int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
++ unsigned int auto_report, unsigned int timeout);
+
+ #endif /* WNM_AP_H */
+--- contrib/wpa/src/ap/wpa_auth.c.orig
++++ contrib/wpa/src/ap/wpa_auth.c
+@@ -1,6 +1,6 @@
+ /*
+ * IEEE 802.11 RSN / WPA Authenticator
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -13,12 +13,17 @@
+ #include "utils/state_machine.h"
+ #include "utils/bitfield.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/ocv.h"
++#include "crypto/aes.h"
+ #include "crypto/aes_wrap.h"
++#include "crypto/aes_siv.h"
+ #include "crypto/crypto.h"
+ #include "crypto/sha1.h"
+ #include "crypto/sha256.h"
++#include "crypto/sha384.h"
+ #include "crypto/random.h"
+ #include "eapol_auth/eapol_auth_sm.h"
++#include "drivers/driver.h"
+ #include "ap_config.h"
+ #include "ieee802_11.h"
+ #include "wpa_auth.h"
+@@ -33,8 +38,14 @@
+
+ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+ static int wpa_sm_step(struct wpa_state_machine *sm);
+-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+- size_t data_len);
++static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
++ u8 *data, size_t data_len);
++#ifdef CONFIG_FILS
++static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
++ u8 *buf, size_t buf_len, u16 *_key_data_len);
++static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
++ const struct wpabuf *hlp);
++#endif /* CONFIG_FILS */
+ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+@@ -44,7 +55,8 @@
+ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+- const u8 *pmk, struct wpa_ptk *ptk);
++ const u8 *pmk, unsigned int pmk_len,
++ struct wpa_ptk *ptk);
+ static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+ static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+@@ -51,12 +63,12 @@
+ struct wpa_group *group);
+ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
++static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+
+-static const u32 dot11RSNAConfigGroupUpdateCount = 4;
+-static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
+ static const u32 eapol_key_timeout_first = 100; /* ms */
+ static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+ static const u32 eapol_key_timeout_first_group = 500; /* ms */
++static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
+
+ /* TODO: make these configurable */
+ static const int dot11RSNAConfigPMKLifetime = 43200;
+@@ -67,8 +79,8 @@
+ static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+ {
+- if (wpa_auth->cb.mic_failure_report)
+- return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
++ if (wpa_auth->cb->mic_failure_report)
++ return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
+ return 0;
+ }
+
+@@ -76,8 +88,8 @@
+ static inline void wpa_auth_psk_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+ {
+- if (wpa_auth->cb.psk_failure_report)
+- wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
++ if (wpa_auth->cb->psk_failure_report)
++ wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
+ }
+
+
+@@ -85,8 +97,8 @@
+ const u8 *addr, wpa_eapol_variable var,
+ int value)
+ {
+- if (wpa_auth->cb.set_eapol)
+- wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
++ if (wpa_auth->cb->set_eapol)
++ wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
+ }
+
+
+@@ -93,9 +105,9 @@
+ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, wpa_eapol_variable var)
+ {
+- if (wpa_auth->cb.get_eapol == NULL)
++ if (wpa_auth->cb->get_eapol == NULL)
+ return -1;
+- return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
++ return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
+ }
+
+
+@@ -102,12 +114,13 @@
+ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr,
+ const u8 *p2p_dev_addr,
+- const u8 *prev_psk)
++ const u8 *prev_psk, size_t *psk_len,
++ int *vlan_id)
+ {
+- if (wpa_auth->cb.get_psk == NULL)
++ if (wpa_auth->cb->get_psk == NULL)
+ return NULL;
+- return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+- prev_psk);
++ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
++ prev_psk, psk_len, vlan_id);
+ }
+
+
+@@ -114,9 +127,9 @@
+ static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u8 *msk, size_t *len)
+ {
+- if (wpa_auth->cb.get_msk == NULL)
++ if (wpa_auth->cb->get_msk == NULL)
+ return -1;
+- return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
++ return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
+ }
+
+
+@@ -125,10 +138,10 @@
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len)
+ {
+- if (wpa_auth->cb.set_key == NULL)
++ if (wpa_auth->cb->set_key == NULL)
+ return -1;
+- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+- key, key_len);
++ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
++ key, key_len);
+ }
+
+
+@@ -135,9 +148,9 @@
+ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+ {
+- if (wpa_auth->cb.get_seqnum == NULL)
++ if (wpa_auth->cb->get_seqnum == NULL)
+ return -1;
+- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
++ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+ }
+
+
+@@ -145,10 +158,10 @@
+ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt)
+ {
+- if (wpa_auth->cb.send_eapol == NULL)
++ if (wpa_auth->cb->send_eapol == NULL)
+ return -1;
+- return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
+- encrypt);
++ return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
++ encrypt);
+ }
+
+
+@@ -156,9 +169,9 @@
+ static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+ const u8 *addr)
+ {
+- if (wpa_auth->cb.start_ampe == NULL)
++ if (wpa_auth->cb->start_ampe == NULL)
+ return -1;
+- return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
++ return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
+ }
+ #endif /* CONFIG_MESH */
+
+@@ -167,9 +180,9 @@
+ int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx)
+ {
+- if (wpa_auth->cb.for_each_sta == NULL)
++ if (wpa_auth->cb->for_each_sta == NULL)
+ return 0;
+- return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
++ return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
+ }
+
+
+@@ -177,9 +190,9 @@
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx)
+ {
+- if (wpa_auth->cb.for_each_auth == NULL)
++ if (wpa_auth->cb->for_each_auth == NULL)
+ return 0;
+- return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
++ return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
+ }
+
+
+@@ -186,9 +199,9 @@
+ void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *txt)
+ {
+- if (wpa_auth->cb.logger == NULL)
++ if (wpa_auth->cb->logger == NULL)
+ return;
+- wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
++ wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
+ }
+
+
+@@ -199,7 +212,7 @@
+ int maxlen;
+ va_list ap;
+
+- if (wpa_auth->cb.logger == NULL)
++ if (wpa_auth->cb->logger == NULL)
+ return;
+
+ maxlen = os_strlen(fmt) + 100;
+@@ -218,33 +231,36 @@
+
+
+ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+- const u8 *addr)
++ const u8 *addr, u16 reason)
+ {
+- if (wpa_auth->cb.disconnect == NULL)
++ if (wpa_auth->cb->disconnect == NULL)
+ return;
+- wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
+- wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
+- WLAN_REASON_PREV_AUTH_NOT_VALID);
++ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
++ MAC2STR(addr), reason);
++ wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
+ }
+
+
+-static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
++#ifdef CONFIG_OCV
++static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
++ struct wpa_channel_info *ci)
+ {
+- int ret = 0;
+-#ifdef CONFIG_IEEE80211R
+- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+- ret = 1;
+-#endif /* CONFIG_IEEE80211R */
+-#ifdef CONFIG_IEEE80211W
+- if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
+- ret = 1;
+-#endif /* CONFIG_IEEE80211W */
+- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+- ret = 1;
+- return ret;
++ if (!wpa_auth->cb->channel_info)
++ return -1;
++ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+ }
++#endif /* CONFIG_OCV */
+
+
++static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
++ const u8 *addr, int vlan_id)
++{
++ if (!wpa_auth->cb->update_vlan)
++ return -1;
++ return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
++}
++
++
+ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+ {
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+@@ -304,6 +320,19 @@
+ }
+
+
++void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
++{
++ if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
++ wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
++ MACSTR " (%d seconds)", MAC2STR(sm->addr),
++ sm->wpa_auth->conf.wpa_ptk_rekey);
++ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
++ eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
++ wpa_rekey_ptk, sm->wpa_auth, sm);
++ }
++}
++
++
+ static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
+ {
+ if (sm->pmksa == ctx)
+@@ -339,6 +368,10 @@
+ wpa_get_ntp_timestamp(buf + ETH_ALEN);
+ ptr = (unsigned long) group;
+ os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
++#ifdef TEST_FUZZ
++ os_memset(buf + ETH_ALEN, 0xab, 8);
++ os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr));
++#endif /* TEST_FUZZ */
+ if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+ return -1;
+
+@@ -408,7 +441,8 @@
+ */
+ struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+- struct wpa_auth_callbacks *cb)
++ const struct wpa_auth_callbacks *cb,
++ void *cb_ctx)
+ {
+ struct wpa_authenticator *wpa_auth;
+
+@@ -417,7 +451,8 @@
+ return NULL;
+ os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+- os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
++ wpa_auth->cb = cb;
++ wpa_auth->cb_ctx = cb_ctx;
+
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+@@ -442,7 +477,7 @@
+ return NULL;
+ }
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+ if (wpa_auth->ft_pmk_cache == NULL) {
+ wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+@@ -452,7 +487,7 @@
+ os_free(wpa_auth);
+ return NULL;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ if (wpa_auth->conf.wpa_gmk_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+@@ -505,17 +540,13 @@
+ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+-#ifdef CONFIG_PEERKEY
+- while (wpa_auth->stsl_negotiations)
+- wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
+-#endif /* CONFIG_PEERKEY */
+-
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+ wpa_auth->ft_pmk_cache = NULL;
+-#endif /* CONFIG_IEEE80211R */
++ wpa_ft_deinit(wpa_auth);
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #ifdef CONFIG_P2P
+ bitfield_free(wpa_auth->ip_pool);
+@@ -598,7 +629,7 @@
+ if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+ return -1;
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (sm->ft_completed) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "FT authentication already completed - do not "
+@@ -605,10 +636,22 @@
+ "start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
++ sm->Pair = TRUE;
+ return 0;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
++#ifdef CONFIG_FILS
++ if (sm->fils_completed) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
++ "FILS authentication already completed - do not start 4-way handshake");
++ /* Go to PTKINITDONE state to allow GTK rekeying */
++ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
++ sm->Pair = TRUE;
++ return 0;
++ }
++#endif /* CONFIG_FILS */
++
+ if (sm->started) {
+ os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+ sm->ReAuthenticationRequest = TRUE;
+@@ -659,14 +702,17 @@
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ }
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ os_free(sm->assoc_resp_ftie);
+ wpabuf_free(sm->ft_pending_req_ies);
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ os_free(sm->last_rx_eapol_key);
+ os_free(sm->wpa_ie);
+ wpa_group_put(sm->wpa_auth, sm->group);
+- os_free(sm);
++#ifdef CONFIG_DPP2
++ wpabuf_clear_free(sm->dpp_z);
++#endif /* CONFIG_DPP2 */
++ bin_clear_free(sm, sizeof(*sm));
+ }
+
+
+@@ -679,9 +725,10 @@
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "strict rekeying - force GTK rekey since STA "
+ "is leaving");
+- eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
+- eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+- NULL);
++ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
++ sm->wpa_auth, NULL) == -1)
++ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
++ NULL);
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+@@ -688,6 +735,9 @@
+ sm->pending_1_of_4_timeout = 0;
+ eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
++#ifdef CONFIG_IEEE80211R_AP
++ wpa_ft_sta_deinit(sm);
++#endif /* CONFIG_IEEE80211R_AP */
+ if (sm->in_step_loop) {
+ /* Must not free state machine while wpa_sm_step() is running.
+ * Freeing will be completed in the end of wpa_sm_step(). */
+@@ -738,7 +788,7 @@
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+@@ -785,7 +835,7 @@
+
+ return 0;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+
+ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+@@ -827,25 +877,44 @@
+ struct wpa_ptk PTK;
+ int ok = 0;
+ const u8 *pmk = NULL;
++ size_t pmk_len;
++ int vlan_id = 0;
+
++ os_memset(&PTK, 0, sizeof(PTK));
+ for (;;) {
+- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
++ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+- sm->p2p_dev_addr, pmk);
++ sm->p2p_dev_addr, pmk, &pmk_len,
++ &vlan_id);
+ if (pmk == NULL)
+ break;
+- } else
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
++ os_memcpy(sm->xxkey, pmk, pmk_len);
++ sm->xxkey_len = pmk_len;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++ } else {
+ pmk = sm->PMK;
++ pmk_len = sm->pmk_len;
++ }
+
+- wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
++ if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
++ break;
+
+- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+- == 0) {
++ if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
++ data, data_len) == 0) {
++ if (sm->PMK != pmk) {
++ os_memcpy(sm->PMK, pmk, pmk_len);
++ sm->pmk_len = pmk_len;
++ }
+ ok = 1;
+ break;
+ }
+
+- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
++ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
++ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
+ break;
+ }
+
+@@ -858,6 +927,11 @@
+ wpa_printf(MSG_DEBUG,
+ "WPA: Earlier SNonce resulted in matching MIC");
+ sm->alt_snonce_valid = 0;
++
++ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
++ wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
++ return -1;
++
+ os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+ os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+ sm->PTK_valid = TRUE;
+@@ -872,39 +946,41 @@
+ {
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+- struct wpa_eapol_key_192 *key192;
+ u16 key_info, key_data_length;
+- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
+- SMK_M1, SMK_M3, SMK_ERROR } msg;
++ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
+ char *msgtxt;
+ struct wpa_eapol_ie_parse kde;
+- int ft;
+- const u8 *eapol_key_ie, *key_data;
+- size_t eapol_key_ie_len, keyhdrlen, mic_len;
++ const u8 *key_data;
++ size_t keyhdrlen, mic_len;
++ u8 *mic;
+
+ if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+ return;
++ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
+
+- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
++ keyhdrlen = sizeof(*key) + mic_len + 2;
+
+- if (data_len < sizeof(*hdr) + keyhdrlen)
++ if (data_len < sizeof(*hdr) + keyhdrlen) {
++ wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
+ return;
++ }
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
++ mic = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
+- if (mic_len == 24) {
+- key_data = (const u8 *) (key192 + 1);
+- key_data_length = WPA_GET_BE16(key192->key_data_length);
+- } else {
+- key_data = (const u8 *) (key + 1);
+- key_data_length = WPA_GET_BE16(key->key_data_length);
+- }
++ key_data = mic + mic_len + 2;
++ key_data_length = WPA_GET_BE16(mic + mic_len);
+ wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+- " key_info=0x%x type=%u key_data_length=%u",
+- MAC2STR(sm->addr), key_info, key->type, key_data_length);
++ " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
++ MAC2STR(sm->addr), key_info, key->type,
++ (unsigned int) mic_len, key_data_length);
++ wpa_hexdump(MSG_MSGDUMP,
++ "WPA: EAPOL-Key header (ending before Key MIC)",
++ key, sizeof(*key));
++ wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
++ mic, mic_len);
+ if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
+ wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+ "key_data overflow (%d > %lu)",
+@@ -945,25 +1021,20 @@
+ /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+ * are set */
+
+- if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
+- (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
+- if (key_info & WPA_KEY_INFO_ERROR) {
+- msg = SMK_ERROR;
+- msgtxt = "SMK Error";
+- } else {
+- msg = SMK_M1;
+- msgtxt = "SMK M1";
+- }
+- } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+- msg = SMK_M3;
+- msgtxt = "SMK M3";
+- } else if (key_info & WPA_KEY_INFO_REQUEST) {
++ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
++ wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
++ return;
++ }
++
++ if (key_info & WPA_KEY_INFO_REQUEST) {
+ msg = REQUEST;
+ msgtxt = "Request";
+ } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ msg = GROUP_2;
+ msgtxt = "2/2 Group";
+- } else if (key_data_length == 0) {
++ } else if (key_data_length == 0 ||
++ (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
++ key_data_length == AES_BLOCK_SIZE)) {
+ msg = PAIRWISE_4;
+ msgtxt = "4/4 Pairwise";
+ } else {
+@@ -971,15 +1042,13 @@
+ msgtxt = "2/4 Pairwise";
+ }
+
+- /* TODO: key_info type validation for PeerKey */
+ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
+ msg == GROUP_2) {
+ u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (sm->pairwise == WPA_CIPHER_CCMP ||
+ sm->pairwise == WPA_CIPHER_GCMP) {
+- if (wpa_use_aes_cmac(sm) &&
+- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
++ if (wpa_use_cmac(sm->wpa_key_mgmt) &&
++ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, sm->addr,
+ LOGGER_WARNING,
+@@ -989,7 +1058,8 @@
+ return;
+ }
+
+- if (!wpa_use_aes_cmac(sm) &&
++ if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
++ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, sm->addr,
+ LOGGER_WARNING,
+@@ -999,7 +1069,7 @@
+ }
+ }
+
+- if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
++ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+@@ -1087,6 +1157,15 @@
+ }
+
+ continue_processing:
++#ifdef CONFIG_FILS
++ if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
++ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
++ "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
++ return;
++ }
++#endif /* CONFIG_FILS */
++
+ switch (msg) {
+ case PAIRWISE_2:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+@@ -1114,70 +1193,10 @@
+ "collect more entropy for random number "
+ "generation");
+ random_mark_pool_ready();
+- wpa_sta_disconnect(wpa_auth, sm->addr);
++ wpa_sta_disconnect(wpa_auth, sm->addr,
++ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+- if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+- "received EAPOL-Key msg 2/4 with "
+- "invalid Key Data contents");
+- return;
+- }
+- if (kde.rsn_ie) {
+- eapol_key_ie = kde.rsn_ie;
+- eapol_key_ie_len = kde.rsn_ie_len;
+- } else if (kde.osen) {
+- eapol_key_ie = kde.osen;
+- eapol_key_ie_len = kde.osen_len;
+- } else {
+- eapol_key_ie = kde.wpa_ie;
+- eapol_key_ie_len = kde.wpa_ie_len;
+- }
+- ft = sm->wpa == WPA_VERSION_WPA2 &&
+- wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+- if (sm->wpa_ie == NULL ||
+- wpa_compare_rsn_ie(ft,
+- sm->wpa_ie, sm->wpa_ie_len,
+- eapol_key_ie, eapol_key_ie_len)) {
+- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+- "WPA IE from (Re)AssocReq did not "
+- "match with msg 2/4");
+- if (sm->wpa_ie) {
+- wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+- sm->wpa_ie, sm->wpa_ie_len);
+- }
+- wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+- eapol_key_ie, eapol_key_ie_len);
+- /* MLME-DEAUTHENTICATE.request */
+- wpa_sta_disconnect(wpa_auth, sm->addr);
+- return;
+- }
+-#ifdef CONFIG_IEEE80211R
+- if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+- wpa_sta_disconnect(wpa_auth, sm->addr);
+- return;
+- }
+-#endif /* CONFIG_IEEE80211R */
+-#ifdef CONFIG_P2P
+- if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+- wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+- int idx;
+- wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+- "EAPOL-Key exchange");
+- idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+- if (idx >= 0) {
+- u32 start = WPA_GET_BE32(wpa_auth->conf.
+- ip_addr_start);
+- bitfield_set(wpa_auth->ip_pool, idx);
+- WPA_PUT_BE32(sm->ip_addr, start + idx);
+- wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+- "address %u.%u.%u.%u to " MACSTR,
+- sm->ip_addr[0], sm->ip_addr[1],
+- sm->ip_addr[2], sm->ip_addr[3],
+- MAC2STR(sm->addr));
+- }
+- }
+-#endif /* CONFIG_P2P */
+ break;
+ case PAIRWISE_4:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
+@@ -1199,28 +1218,6 @@
+ return;
+ }
+ break;
+-#ifdef CONFIG_PEERKEY
+- case SMK_M1:
+- case SMK_M3:
+- case SMK_ERROR:
+- if (!wpa_auth->conf.peerkey) {
+- wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
+- "PeerKey use disabled - ignoring message");
+- return;
+- }
+- if (!sm->PTK_valid) {
+- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+- "received EAPOL-Key msg SMK in "
+- "invalid state - dropped");
+- return;
+- }
+- break;
+-#else /* CONFIG_PEERKEY */
+- case SMK_M1:
+- case SMK_M3:
+- case SMK_ERROR:
+- return; /* STSL disabled - ignore SMK messages */
+-#endif /* CONFIG_PEERKEY */
+ case REQUEST:
+ break;
+ }
+@@ -1234,22 +1231,55 @@
+ return;
+ }
+
+- if (!(key_info & WPA_KEY_INFO_MIC)) {
++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
++ !(key_info & WPA_KEY_INFO_MIC)) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received invalid EAPOL-Key: Key MIC not set");
+ return;
+ }
+
++#ifdef CONFIG_FILS
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
++ (key_info & WPA_KEY_INFO_MIC)) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ "received invalid EAPOL-Key: Key MIC set");
++ return;
++ }
++#endif /* CONFIG_FILS */
++
+ sm->MICVerified = FALSE;
+ if (sm->PTK_valid && !sm->update_snonce) {
+- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+- data_len) &&
++ if (mic_len &&
++ wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
++ data, data_len) &&
+ (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+ wpa_try_alt_snonce(sm, data, data_len))) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key with invalid MIC");
++#ifdef TEST_FUZZ
++ wpa_printf(MSG_INFO,
++ "TEST: Ignore Key MIC failure for fuzz testing");
++ goto continue_fuzz;
++#endif /* TEST_FUZZ */
+ return;
+ }
++#ifdef CONFIG_FILS
++ if (!mic_len &&
++ wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
++ &key_data_length) < 0) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ "received EAPOL-Key with invalid MIC");
++#ifdef TEST_FUZZ
++ wpa_printf(MSG_INFO,
++ "TEST: Ignore Key MIC failure for fuzz testing");
++ goto continue_fuzz;
++#endif /* TEST_FUZZ */
++ return;
++ }
++#endif /* CONFIG_FILS */
++#ifdef TEST_FUZZ
++ continue_fuzz:
++#endif /* TEST_FUZZ */
+ sm->MICVerified = TRUE;
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
+@@ -1272,12 +1302,7 @@
+ * even though MAC address KDE is not normally encrypted,
+ * supplicant is allowed to encrypt it.
+ */
+- if (msg == SMK_ERROR) {
+-#ifdef CONFIG_PEERKEY
+- wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
+-#endif /* CONFIG_PEERKEY */
+- return;
+- } else if (key_info & WPA_KEY_INFO_ERROR) {
++ if (key_info & WPA_KEY_INFO_ERROR) {
+ if (wpa_receive_error_report(
+ wpa_auth, sm,
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+@@ -1287,11 +1312,6 @@
+ "received EAPOL-Key Request for new "
+ "4-Way Handshake");
+ wpa_request_new_ptk(sm);
+-#ifdef CONFIG_PEERKEY
+- } else if (msg == SMK_M1) {
+- wpa_smk_m1(wpa_auth, sm, key, key_data,
+- key_data_length);
+-#endif /* CONFIG_PEERKEY */
+ } else if (key_data_length > 0 &&
+ wpa_parse_kde_ies(key_data, key_data_length,
+ &kde) == 0 &&
+@@ -1330,18 +1350,10 @@
+ wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
+ }
+
+-#ifdef CONFIG_PEERKEY
+- if (msg == SMK_M3) {
+- wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
+- return;
+- }
+-#endif /* CONFIG_PEERKEY */
+-
+ os_free(sm->last_rx_eapol_key);
+- sm->last_rx_eapol_key = os_malloc(data_len);
++ sm->last_rx_eapol_key = os_memdup(data, data_len);
+ if (sm->last_rx_eapol_key == NULL)
+ return;
+- os_memcpy(sm->last_rx_eapol_key, data, data_len);
+ sm->last_rx_eapol_key_len = data_len;
+
+ sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
+@@ -1356,7 +1368,7 @@
+ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+ const u8 *gnonce, u8 *gtk, size_t gtk_len)
+ {
+- u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
++ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
+ u8 *pos;
+ int ret = 0;
+
+@@ -1367,21 +1379,33 @@
+ * is done only at the Authenticator and as such, does not need to be
+ * exactly same.
+ */
++ os_memset(data, 0, sizeof(data));
+ os_memcpy(data, addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+ pos = data + ETH_ALEN + WPA_NONCE_LEN;
+ wpa_get_ntp_timestamp(pos);
++#ifdef TEST_FUZZ
++ os_memset(pos, 0xef, 8);
++#endif /* TEST_FUZZ */
+ pos += 8;
+- if (random_get_bytes(pos, 16) < 0)
++ if (random_get_bytes(pos, gtk_len) < 0)
+ ret = -1;
+
+-#ifdef CONFIG_IEEE80211W
+- sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
+-#else /* CONFIG_IEEE80211W */
+- if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+- < 0)
++#ifdef CONFIG_SHA384
++ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
++ gtk, gtk_len) < 0)
+ ret = -1;
+-#endif /* CONFIG_IEEE80211W */
++#else /* CONFIG_SHA384 */
++#ifdef CONFIG_SHA256
++ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
++ gtk, gtk_len) < 0)
++ ret = -1;
++#else /* CONFIG_SHA256 */
++ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
++ gtk, gtk_len) < 0)
++ ret = -1;
++#endif /* CONFIG_SHA256 */
++#endif /* CONFIG_SHA384 */
+
+ return ret;
+ }
+@@ -1407,7 +1431,6 @@
+ {
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+- struct wpa_eapol_key_192 *key192;
+ size_t len, mic_len, keyhdrlen;
+ int alg;
+ int key_data_len, pad_len = 0;
+@@ -1414,19 +1437,18 @@
+ u8 *buf, *pos;
+ int version, pairwise;
+ int i;
+- u8 *key_data;
++ u8 *key_mic, *key_data;
+
+- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
++ keyhdrlen = sizeof(*key) + mic_len + 2;
+
+ len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
+
+ if (force_version)
+ version = force_version;
+- else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
++ else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+ version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+- else if (wpa_use_aes_cmac(sm))
++ else if (wpa_use_cmac(sm->wpa_key_mgmt))
+ version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+ else if (sm->pairwise != WPA_CIPHER_TKIP)
+ version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+@@ -1448,8 +1470,7 @@
+ key_data_len = kde_len;
+
+ if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
++ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+ pad_len = key_data_len % 8;
+ if (pad_len)
+@@ -1458,6 +1479,8 @@
+ }
+
+ len += key_data_len;
++ if (!mic_len && encr)
++ len += AES_BLOCK_SIZE;
+
+ hdr = os_zalloc(len);
+ if (hdr == NULL)
+@@ -1466,7 +1489,7 @@
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = host_to_be16(len - sizeof(*hdr));
+ key = (struct wpa_eapol_key *) (hdr + 1);
+- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
++ key_mic = (u8 *) (key + 1);
+ key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
+
+ key->type = sm->wpa == WPA_VERSION_WPA2 ?
+@@ -1479,11 +1502,11 @@
+ WPA_PUT_BE16(key->key_info, key_info);
+
+ alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
+- WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+- if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
++ if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
+ WPA_PUT_BE16(key->key_length, 0);
++ else
++ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+
+- /* FIX: STSL: what to use as key_replay_counter? */
+ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+ sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+ os_memcpy(sm->key_replay[i].counter,
+@@ -1505,10 +1528,31 @@
+
+ if (kde && !encr) {
+ os_memcpy(key_data, kde, kde_len);
+- if (mic_len == 24)
+- WPA_PUT_BE16(key192->key_data_length, kde_len);
+- else
+- WPA_PUT_BE16(key->key_data_length, kde_len);
++ WPA_PUT_BE16(key_mic + mic_len, kde_len);
++#ifdef CONFIG_FILS
++ } else if (!mic_len && kde) {
++ const u8 *aad[1];
++ size_t aad_len[1];
++
++ WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
++ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
++ kde, kde_len);
++
++ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
++ sm->PTK.kek, sm->PTK.kek_len);
++ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
++ * to Key Data (exclusive). */
++ aad[0] = (u8 *) hdr;
++ aad_len[0] = key_mic + 2 - (u8 *) hdr;
++ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
++ 1, aad, aad_len, key_mic + 2) < 0) {
++ wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
++ return;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
++ key_mic + 2, AES_BLOCK_SIZE + kde_len);
++#endif /* CONFIG_FILS */
+ } else if (encr && kde) {
+ buf = os_zalloc(key_data_len);
+ if (buf == NULL) {
+@@ -1525,9 +1569,11 @@
+ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ buf, key_data_len);
+ if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
++ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
++ (unsigned int) sm->PTK.kek_len);
+ if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+ (key_data_len - 8) / 8, buf, key_data)) {
+ os_free(hdr);
+@@ -1534,15 +1580,13 @@
+ os_free(buf);
+ return;
+ }
+- if (mic_len == 24)
+- WPA_PUT_BE16(key192->key_data_length,
+- key_data_len);
+- else
+- WPA_PUT_BE16(key->key_data_length,
+- key_data_len);
++ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+ #ifndef CONFIG_NO_RC4
+ } else if (sm->PTK.kek_len == 16) {
+ u8 ek[32];
++
++ wpa_printf(MSG_DEBUG,
++ "WPA: Encrypt Key Data using RC4");
+ os_memcpy(key->key_iv,
+ sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+ inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+@@ -1550,12 +1594,7 @@
+ os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+ os_memcpy(key_data, buf, key_data_len);
+ rc4_skip(ek, 32, 256, key_data, key_data_len);
+- if (mic_len == 24)
+- WPA_PUT_BE16(key192->key_data_length,
+- key_data_len);
+- else
+- WPA_PUT_BE16(key->key_data_length,
+- key_data_len);
++ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+ #endif /* CONFIG_NO_RC4 */
+ } else {
+ os_free(hdr);
+@@ -1566,9 +1605,7 @@
+ }
+
+ if (key_info & WPA_KEY_INFO_MIC) {
+- u8 *key_mic;
+-
+- if (!sm->PTK_valid) {
++ if (!sm->PTK_valid || !mic_len) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTK not valid when sending EAPOL-Key "
+ "frame");
+@@ -1576,10 +1613,12 @@
+ return;
+ }
+
+- key_mic = key192->key_mic; /* same offset for key and key192 */
+- wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+- sm->wpa_key_mgmt, version,
+- (u8 *) hdr, len, key_mic);
++ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
++ sm->wpa_key_mgmt, version,
++ (u8 *) hdr, len, key_mic) < 0) {
++ os_free(hdr);
++ return;
++ }
+ #ifdef CONFIG_TESTING_OPTIONS
+ if (!pairwise &&
+ wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
+@@ -1608,7 +1647,7 @@
+ {
+ int timeout_ms;
+ int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+- int ctr;
++ u32 ctr;
+
+ if (sm == NULL)
+ return;
+@@ -1622,25 +1661,30 @@
+ eapol_key_timeout_first_group;
+ else
+ timeout_ms = eapol_key_timeout_subseq;
++ if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
++ (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
++ timeout_ms = eapol_key_timeout_no_retrans;
+ if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+ sm->pending_1_of_4_timeout = 1;
++#ifdef TEST_FUZZ
++ timeout_ms = 1;
++#endif /* TEST_FUZZ */
+ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+- "counter %d)", timeout_ms, ctr);
++ "counter %u)", timeout_ms, ctr);
+ eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+ }
+
+
+-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+- size_t data_len)
++static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
++ u8 *data, size_t data_len)
+ {
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+- struct wpa_eapol_key_192 *key192;
+ u16 key_info;
+ int ret = 0;
+- u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+- size_t mic_len = wpa_mic_len(akmp);
++ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
++ size_t mic_len = wpa_mic_len(akmp, pmk_len);
+
+ if (data_len < sizeof(*hdr) + sizeof(*key))
+ return -1;
+@@ -1647,16 +1691,16 @@
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
++ mic_pos = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
+- os_memcpy(mic, key192->key_mic, mic_len);
+- os_memset(key192->key_mic, 0, mic_len);
++ os_memcpy(mic, mic_pos, mic_len);
++ os_memset(mic_pos, 0, mic_len);
+ if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+ key_info & WPA_KEY_INFO_TYPE_MASK,
+- data, data_len, key192->key_mic) ||
+- os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
++ data, data_len, mic_pos) ||
++ os_memcmp_const(mic, mic_pos, mic_len) != 0)
+ ret = -1;
+- os_memcpy(key192->key_mic, mic, mic_len);
++ os_memcpy(mic_pos, mic, mic_len);
+ return ret;
+ }
+
+@@ -1665,7 +1709,10 @@
+ {
+ sm->PTK_valid = FALSE;
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+- wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
++ if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
++ 0))
++ wpa_printf(MSG_DEBUG,
++ "RSN: PTK removal from the driver failed");
+ sm->pairwise_set = FALSE;
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ }
+@@ -1696,6 +1743,14 @@
+ case WPA_DEAUTH:
+ case WPA_DISASSOC:
+ sm->DeauthenticationRequest = TRUE;
++#ifdef CONFIG_IEEE80211R_AP
++ os_memset(sm->PMK, 0, sizeof(sm->PMK));
++ sm->pmk_len = 0;
++ os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
++ sm->xxkey_len = 0;
++ os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
++ sm->pmk_r1_len = 0;
++#endif /* CONFIG_IEEE80211R_AP */
+ break;
+ case WPA_REAUTH:
+ case WPA_REAUTH_EAPOL:
+@@ -1729,7 +1784,7 @@
+ sm->ReAuthenticationRequest = TRUE;
+ break;
+ case WPA_ASSOC_FT:
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+ "after association");
+ wpa_ft_install_ptk(sm);
+@@ -1736,20 +1791,39 @@
+
+ /* Using FT protocol, not WPA auth state machine */
+ sm->ft_completed = 1;
++ wpa_auth_set_ptk_rekey_timer(sm);
+ return 0;
+-#else /* CONFIG_IEEE80211R */
++#else /* CONFIG_IEEE80211R_AP */
+ break;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
++ case WPA_ASSOC_FILS:
++#ifdef CONFIG_FILS
++ wpa_printf(MSG_DEBUG,
++ "FILS: TK configuration after association");
++ fils_set_tk(sm);
++ sm->fils_completed = 1;
++ return 0;
++#else /* CONFIG_FILS */
++ break;
++#endif /* CONFIG_FILS */
++ case WPA_DRV_STA_REMOVED:
++ sm->tk_already_set = FALSE;
++ return 0;
+ }
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ sm->ft_completed = 0;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_frame_prot && event == WPA_AUTH)
+ remove_ptk = 0;
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_FILS
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
++ (event == WPA_AUTH || event == WPA_ASSOC))
++ remove_ptk = 0;
++#endif /* CONFIG_FILS */
+
+ if (remove_ptk) {
+ sm->PTK_valid = FALSE;
+@@ -1794,7 +1868,9 @@
+ wpa_remove_ptk(sm);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+ sm->TimeoutCtr = 0;
+- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 0);
+ }
+@@ -1803,9 +1879,14 @@
+
+ SM_STATE(WPA_PTK, DISCONNECT)
+ {
++ u16 reason = sm->disconnect_reason;
++
+ SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+ sm->Disconnect = FALSE;
+- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
++ sm->disconnect_reason = 0;
++ if (!reason)
++ reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
++ wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
+ }
+
+
+@@ -1914,25 +1995,54 @@
+ size_t len = 2 * PMK_LEN;
+
+ SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ sm->xxkey_len = 0;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ if (sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+- os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
++ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
++ sm->pmk_len = sm->pmksa->pmk_len;
++#ifdef CONFIG_DPP
++ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No PMKSA cache entry for STA - reject connection");
++ sm->Disconnect = TRUE;
++ sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
++ return;
++#endif /* CONFIG_DPP */
+ } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
++ unsigned int pmk_len;
++
++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
++ pmk_len = PMK_LEN_SUITE_B_192;
++ else
++ pmk_len = PMK_LEN;
+ wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
+- "(len=%lu)", (unsigned long) len);
+- os_memcpy(sm->PMK, msk, PMK_LEN);
+-#ifdef CONFIG_IEEE80211R
++ "(MSK len=%lu PMK len=%u)", (unsigned long) len,
++ pmk_len);
++ if (len < pmk_len) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: MSK not long enough (%u) to create PMK (%u)",
++ (unsigned int) len, (unsigned int) pmk_len);
++ sm->Disconnect = TRUE;
++ return;
++ }
++ os_memcpy(sm->PMK, msk, pmk_len);
++ sm->pmk_len = pmk_len;
++#ifdef CONFIG_IEEE80211R_AP
+ if (len >= 2 * PMK_LEN) {
+- os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+- sm->xxkey_len = PMK_LEN;
++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
++ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
++ sm->xxkey_len = SHA384_MAC_LEN;
++ } else {
++ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
++ sm->xxkey_len = PMK_LEN;
++ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+- sm->wpa_auth->cb.get_msk);
++ sm->wpa_auth->cb->get_msk);
+ sm->Disconnect = TRUE;
+ return;
+ }
+@@ -1954,15 +2064,30 @@
+ SM_STATE(WPA_PTK, INITPSK)
+ {
+ const u8 *psk;
++ size_t psk_len;
++
+ SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
++ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
++ &psk_len, NULL);
+ if (psk) {
+- os_memcpy(sm->PMK, psk, PMK_LEN);
+-#ifdef CONFIG_IEEE80211R
++ os_memcpy(sm->PMK, psk, psk_len);
++ sm->pmk_len = psk_len;
++#ifdef CONFIG_IEEE80211R_AP
+ os_memcpy(sm->xxkey, psk, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ }
++#ifdef CONFIG_SAE
++ if (wpa_auth_uses_sae(sm) && sm->pmksa) {
++ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
++ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
++ sm->pmk_len = sm->pmksa->pmk_len;
++#ifdef CONFIG_IEEE80211R_AP
++ os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
++ sm->xxkey_len = sm->pmksa->pmk_len;
++#endif /* CONFIG_IEEE80211R_AP */
++ }
++#endif /* CONFIG_SAE */
+ sm->req_replay_counter_used = 0;
+ }
+
+@@ -1978,7 +2103,7 @@
+ sm->alt_snonce_valid = FALSE;
+
+ sm->TimeoutCtr++;
+- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
++ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+@@ -1987,11 +2112,23 @@
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake");
+ /*
+- * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
+- * one possible PSK for this STA.
++ * For infrastructure BSS cases, it is better for the AP not to include
++ * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
++ * offline search for the passphrase/PSK without having to be able to
++ * capture a 4-way handshake from a STA that has access to the network.
++ *
++ * For IBSS cases, addition of PMKID KDE could be considered even with
++ * WPA2-PSK cases that use multiple PSKs, but only if there is a single
++ * possible PSK for this STA. However, this should not be done unless
++ * there is support for using that information on the supplicant side.
++ * The concern about exposing PMKID unnecessarily in infrastructure BSS
++ * cases would also apply here, but at least in the IBSS case, this
++ * would cover a potential real use case.
+ */
+ if (sm->wpa == WPA_VERSION_WPA2 &&
+- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
++ (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
++ (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
++ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+ pmkid = buf;
+ pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+@@ -1999,19 +2136,65 @@
+ pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+ RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+ if (sm->pmksa) {
++ wpa_hexdump(MSG_DEBUG,
++ "RSN: Message 1/4 PMKID from PMKSA entry",
++ sm->pmksa->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmksa->pmkid, PMKID_LEN);
+ } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+ /* No KCK available to derive PMKID */
++ wpa_printf(MSG_DEBUG,
++ "RSN: No KCK available to derive PMKID for message 1/4");
+ pmkid = NULL;
++#ifdef CONFIG_FILS
++ } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ if (sm->pmkid_set) {
++ wpa_hexdump(MSG_DEBUG,
++ "RSN: Message 1/4 PMKID from FILS/ERP",
++ sm->pmkid, PMKID_LEN);
++ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
++ sm->pmkid, PMKID_LEN);
++ } else {
++ /* No PMKID available */
++ wpa_printf(MSG_DEBUG,
++ "RSN: No FILS/ERP PMKID available for message 1/4");
++ pmkid = NULL;
++ }
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_IEEE80211R_AP
++ } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
++ sm->ft_completed) {
++ wpa_printf(MSG_DEBUG,
++ "FT: No PMKID in message 1/4 when using FT protocol");
++ pmkid = NULL;
++ pmkid_len = 0;
++#endif /* CONFIG_IEEE80211R_AP */
++#ifdef CONFIG_SAE
++ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
++ if (sm->pmkid_set) {
++ wpa_hexdump(MSG_DEBUG,
++ "RSN: Message 1/4 PMKID from SAE",
++ sm->pmkid, PMKID_LEN);
++ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
++ sm->pmkid, PMKID_LEN);
++ } else {
++ /* No PMKID available */
++ wpa_printf(MSG_DEBUG,
++ "RSN: No SAE PMKID available for message 1/4");
++ pmkid = NULL;
++ }
++#endif /* CONFIG_SAE */
+ } else {
+ /*
+ * Calculate PMKID since no PMKSA cache entry was
+ * available with pre-calculated PMKID.
+ */
+- rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
++ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
+ sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+- wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
++ sm->wpa_key_mgmt);
++ wpa_hexdump(MSG_DEBUG,
++ "RSN: Message 1/4 PMKID derived from PMK",
++ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
+ }
+ }
+ wpa_send_eapol(sm->wpa_auth, sm,
+@@ -2021,53 +2204,682 @@
+
+
+ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+- const u8 *pmk, struct wpa_ptk *ptk)
++ const u8 *pmk, unsigned int pmk_len,
++ struct wpa_ptk *ptk)
+ {
+-#ifdef CONFIG_IEEE80211R
+- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+- return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+-#endif /* CONFIG_IEEE80211R */
++ const u8 *z = NULL;
++ size_t z_len = 0;
+
+- return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
++ if (sm->ft_completed) {
++ u8 ptk_name[WPA_PMK_NAME_LEN];
++
++ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
++ sm->SNonce, sm->ANonce,
++ sm->addr, sm->wpa_auth->addr,
++ sm->pmk_r1_name,
++ ptk, ptk_name,
++ sm->wpa_key_mgmt,
++ sm->pairwise);
++ }
++ return wpa_auth_derive_ptk_ft(sm, ptk);
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++
++#ifdef CONFIG_DPP2
++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
++ z = wpabuf_head(sm->dpp_z);
++ z_len = wpabuf_len(sm->dpp_z);
++ }
++#endif /* CONFIG_DPP2 */
++
++ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+ sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+- ptk, sm->wpa_key_mgmt, sm->pairwise);
++ ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len);
+ }
+
+
++#ifdef CONFIG_FILS
++
++int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
++ size_t pmk_len, const u8 *snonce, const u8 *anonce,
++ const u8 *dhss, size_t dhss_len,
++ struct wpabuf *g_sta, struct wpabuf *g_ap)
++{
++ u8 ick[FILS_ICK_MAX_LEN];
++ size_t ick_len;
++ int res;
++ u8 fils_ft[FILS_FT_MAX_LEN];
++ size_t fils_ft_len = 0;
++
++ res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
++ snonce, anonce, dhss, dhss_len,
++ &sm->PTK, ick, &ick_len,
++ sm->wpa_key_mgmt, sm->pairwise,
++ fils_ft, &fils_ft_len);
++ if (res < 0)
++ return res;
++ sm->PTK_valid = TRUE;
++ sm->tk_already_set = FALSE;
++
++#ifdef CONFIG_IEEE80211R_AP
++ if (fils_ft_len) {
++ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
++ struct wpa_auth_config *conf = &wpa_auth->conf;
++ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
++ size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
++
++ if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
++ conf->ssid, conf->ssid_len,
++ conf->mobility_domain,
++ conf->r0_key_holder,
++ conf->r0_key_holder_len,
++ sm->addr, pmk_r0, pmk_r0_name,
++ use_sha384) < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
++ pmk_r0, pmk_r0_len);
++ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
++ pmk_r0_name, WPA_PMK_NAME_LEN);
++ wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
++ os_memset(fils_ft, 0, sizeof(fils_ft));
++
++ res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
++ sm->addr, sm->pmk_r1_name,
++ use_sha384);
++ os_memset(pmk_r0, 0, PMK_LEN_MAX);
++ if (res < 0)
++ return -1;
++ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
++ WPA_PMK_NAME_LEN);
++ sm->pmk_r1_name_valid = 1;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++
++ res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
++ sm->addr, sm->wpa_auth->addr,
++ g_sta ? wpabuf_head(g_sta) : NULL,
++ g_sta ? wpabuf_len(g_sta) : 0,
++ g_ap ? wpabuf_head(g_ap) : NULL,
++ g_ap ? wpabuf_len(g_ap) : 0,
++ sm->wpa_key_mgmt, sm->fils_key_auth_sta,
++ sm->fils_key_auth_ap,
++ &sm->fils_key_auth_len);
++ os_memset(ick, 0, sizeof(ick));
++
++ /* Store nonces for (Re)Association Request/Response frame processing */
++ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
++ os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
++
++ return res;
++}
++
++
++static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
++ u8 *buf, size_t buf_len, u16 *_key_data_len)
++{
++ struct ieee802_1x_hdr *hdr;
++ struct wpa_eapol_key *key;
++ u8 *pos;
++ u16 key_data_len;
++ u8 *tmp;
++ const u8 *aad[1];
++ size_t aad_len[1];
++
++ hdr = (struct ieee802_1x_hdr *) buf;
++ key = (struct wpa_eapol_key *) (hdr + 1);
++ pos = (u8 *) (key + 1);
++ key_data_len = WPA_GET_BE16(pos);
++ if (key_data_len < AES_BLOCK_SIZE ||
++ key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
++ "No room for AES-SIV data in the frame");
++ return -1;
++ }
++ pos += 2; /* Pointing at the Encrypted Key Data field */
++
++ tmp = os_malloc(key_data_len);
++ if (!tmp)
++ return -1;
++
++ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
++ * to Key Data (exclusive). */
++ aad[0] = buf;
++ aad_len[0] = pos - buf;
++ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
++ 1, aad, aad_len, tmp) < 0) {
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
++ "Invalid AES-SIV data in the frame");
++ bin_clear_free(tmp, key_data_len);
++ return -1;
++ }
++
++ /* AEAD decryption and validation completed successfully */
++ key_data_len -= AES_BLOCK_SIZE;
++ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
++ tmp, key_data_len);
++
++ /* Replace Key Data field with the decrypted version */
++ os_memcpy(pos, tmp, key_data_len);
++ pos -= 2; /* Key Data Length field */
++ WPA_PUT_BE16(pos, key_data_len);
++ bin_clear_free(tmp, key_data_len);
++ if (_key_data_len)
++ *_key_data_len = key_data_len;
++ return 0;
++}
++
++
++const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
++ const u8 *ies, size_t ies_len,
++ const u8 *fils_session)
++{
++ const u8 *ie, *end;
++ const u8 *session = NULL;
++
++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Not a FILS AKM - reject association");
++ return NULL;
++ }
++
++ /* Verify Session element */
++ ie = ies;
++ end = ((const u8 *) ie) + ies_len;
++ while (ie + 1 < end) {
++ if (ie + 2 + ie[1] > end)
++ break;
++ if (ie[0] == WLAN_EID_EXTENSION &&
++ ie[1] >= 1 + FILS_SESSION_LEN &&
++ ie[2] == WLAN_EID_EXT_FILS_SESSION) {
++ session = ie;
++ break;
++ }
++ ie += 2 + ie[1];
++ }
++
++ if (!session) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
++ __func__);
++ return NULL;
++ }
++
++ if (!fils_session) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: %s: Could not find FILS Session element in STA entry - reject",
++ __func__);
++ return NULL;
++ }
++
++ if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
++ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
++ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
++ fils_session, FILS_SESSION_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
++ session + 3, FILS_SESSION_LEN);
++ return NULL;
++ }
++ return session;
++}
++
++
++int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
++ size_t ies_len)
++{
++ struct ieee802_11_elems elems;
++
++ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Failed to parse decrypted elements");
++ return -1;
++ }
++
++ if (!elems.fils_session) {
++ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
++ return -1;
++ }
++
++ if (!elems.fils_key_confirm) {
++ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
++ return -1;
++ }
++
++ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Unexpected Key-Auth length %d (expected %d)",
++ elems.fils_key_confirm_len,
++ (int) sm->fils_key_auth_len);
++ return -1;
++ }
++
++ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
++ sm->fils_key_auth_len) != 0) {
++ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
++ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
++ elems.fils_key_confirm, elems.fils_key_confirm_len);
++ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
++ sm->fils_key_auth_sta, sm->fils_key_auth_len);
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
++ const struct ieee80211_mgmt *mgmt, size_t frame_len,
++ u8 *pos, size_t left)
++{
++ u16 fc, stype;
++ const u8 *end, *ie_start, *ie, *session, *crypt;
++ const u8 *aad[5];
++ size_t aad_len[5];
++
++ if (!sm || !sm->PTK_valid) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: No KEK to decrypt Assocication Request frame");
++ return -1;
++ }
++
++ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Not a FILS AKM - reject association");
++ return -1;
++ }
++
++ end = ((const u8 *) mgmt) + frame_len;
++ fc = le_to_host16(mgmt->frame_control);
++ stype = WLAN_FC_GET_STYPE(fc);
++ if (stype == WLAN_FC_STYPE_REASSOC_REQ)
++ ie_start = mgmt->u.reassoc_req.variable;
++ else
++ ie_start = mgmt->u.assoc_req.variable;
++ ie = ie_start;
++
++ /*
++ * Find FILS Session element which is the last unencrypted element in
++ * the frame.
++ */
++ session = wpa_fils_validate_fils_session(sm, ie, end - ie,
++ fils_session);
++ if (!session) {
++ wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
++ return -1;
++ }
++
++ crypt = session + 2 + session[1];
++
++ if (end - crypt < AES_BLOCK_SIZE) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Too short frame to include AES-SIV data");
++ return -1;
++ }
++
++ /* AES-SIV AAD vectors */
++
++ /* The STA's MAC address */
++ aad[0] = mgmt->sa;
++ aad_len[0] = ETH_ALEN;
++ /* The AP's BSSID */
++ aad[1] = mgmt->da;
++ aad_len[1] = ETH_ALEN;
++ /* The STA's nonce */
++ aad[2] = sm->SNonce;
++ aad_len[2] = FILS_NONCE_LEN;
++ /* The AP's nonce */
++ aad[3] = sm->ANonce;
++ aad_len[3] = FILS_NONCE_LEN;
++ /*
++ * The (Re)Association Request frame from the Capability Information
++ * field to the FILS Session element (both inclusive).
++ */
++ aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
++ aad_len[4] = crypt - aad[4];
++
++ if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
++ 5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Invalid AES-SIV data in the frame");
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
++ pos, left - AES_BLOCK_SIZE);
++
++ if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
++ wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
++ return -1;
++ }
++
++ return left - AES_BLOCK_SIZE;
++}
++
++
++int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
++ size_t current_len, size_t max_len,
++ const struct wpabuf *hlp)
++{
++ u8 *end = buf + max_len;
++ u8 *pos = buf + current_len;
++ struct ieee80211_mgmt *mgmt;
++ struct wpabuf *plain;
++ const u8 *aad[5];
++ size_t aad_len[5];
++
++ if (!sm || !sm->PTK_valid)
++ return -1;
++
++ wpa_hexdump(MSG_DEBUG,
++ "FILS: Association Response frame before FILS processing",
++ buf, current_len);
++
++ mgmt = (struct ieee80211_mgmt *) buf;
++
++ /* AES-SIV AAD vectors */
++
++ /* The AP's BSSID */
++ aad[0] = mgmt->sa;
++ aad_len[0] = ETH_ALEN;
++ /* The STA's MAC address */
++ aad[1] = mgmt->da;
++ aad_len[1] = ETH_ALEN;
++ /* The AP's nonce */
++ aad[2] = sm->ANonce;
++ aad_len[2] = FILS_NONCE_LEN;
++ /* The STA's nonce */
++ aad[3] = sm->SNonce;
++ aad_len[3] = FILS_NONCE_LEN;
++ /*
++ * The (Re)Association Response frame from the Capability Information
++ * field (the same offset in both Association and Reassociation
++ * Response frames) to the FILS Session element (both inclusive).
++ */
++ aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
++ aad_len[4] = pos - aad[4];
++
++ /* The following elements will be encrypted with AES-SIV */
++ plain = fils_prepare_plainbuf(sm, hlp);
++ if (!plain) {
++ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
++ return -1;
++ }
++
++ if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
++ wpa_printf(MSG_DEBUG,
++ "FILS: Not enough room for FILS elements");
++ wpabuf_free(plain);
++ return -1;
++ }
++
++ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
++ plain);
++
++ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
++ wpabuf_head(plain), wpabuf_len(plain),
++ 5, aad, aad_len, pos) < 0) {
++ wpabuf_free(plain);
++ return -1;
++ }
++
++ wpa_hexdump(MSG_DEBUG,
++ "FILS: Encrypted Association Response elements",
++ pos, AES_BLOCK_SIZE + wpabuf_len(plain));
++ current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
++ wpabuf_free(plain);
++
++ sm->fils_completed = 1;
++
++ return current_len;
++}
++
++
++static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
++ const struct wpabuf *hlp)
++{
++ struct wpabuf *plain;
++ u8 *len, *tmp, *tmp2;
++ u8 hdr[2];
++ u8 *gtk, dummy_gtk[32];
++ size_t gtk_len;
++ struct wpa_group *gsm;
++
++ plain = wpabuf_alloc(1000);
++ if (!plain)
++ return NULL;
++
++ /* TODO: FILS Public Key */
++
++ /* FILS Key Confirmation */
++ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
++ wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
++ /* Element ID Extension */
++ wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
++ wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
++
++ /* FILS HLP Container */
++ if (hlp)
++ wpabuf_put_buf(plain, hlp);
++
++ /* TODO: FILS IP Address Assignment */
++
++ /* Key Delivery */
++ gsm = sm->group;
++ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
++ len = wpabuf_put(plain, 1);
++ wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
++ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
++ wpabuf_put(plain, WPA_KEY_RSC_LEN));
++ /* GTK KDE */
++ gtk = gsm->GTK[gsm->GN - 1];
++ gtk_len = gsm->GTK_len;
++ if (sm->wpa_auth->conf.disable_gtk ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
++ /*
++ * Provide unique random GTK to each STA to prevent use
++ * of GTK in the BSS.
++ */
++ if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
++ wpabuf_free(plain);
++ return NULL;
++ }
++ gtk = dummy_gtk;
++ }
++ hdr[0] = gsm->GN & 0x03;
++ hdr[1] = 0;
++ tmp = wpabuf_put(plain, 0);
++ tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
++ gtk, gtk_len);
++ wpabuf_put(plain, tmp2 - tmp);
++
++ /* IGTK KDE */
++ tmp = wpabuf_put(plain, 0);
++ tmp2 = ieee80211w_kde_add(sm, tmp);
++ wpabuf_put(plain, tmp2 - tmp);
++
++ *len = (u8 *) wpabuf_put(plain, 0) - len - 1;
++
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sm)) {
++ struct wpa_channel_info ci;
++ u8 *pos;
++
++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "FILS: Failed to get channel info for OCI element");
++ wpabuf_free(plain);
++ return NULL;
++ }
++
++ pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
++ if (ocv_insert_extended_oci(&ci, pos) < 0) {
++ wpabuf_free(plain);
++ return NULL;
++ }
++ }
++#endif /* CONFIG_OCV */
++
++ return plain;
++}
++
++
++int fils_set_tk(struct wpa_state_machine *sm)
++{
++ enum wpa_alg alg;
++ int klen;
++
++ if (!sm || !sm->PTK_valid) {
++ wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
++ return -1;
++ }
++ if (sm->tk_already_set) {
++ wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
++ return -1;
++ }
++
++ alg = wpa_cipher_to_alg(sm->pairwise);
++ klen = wpa_cipher_key_len(sm->pairwise);
++
++ wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
++ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
++ sm->PTK.tk, klen)) {
++ wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
++ return -1;
++ }
++ sm->tk_already_set = TRUE;
++
++ return 0;
++}
++
++
++u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
++ const u8 *fils_session, struct wpabuf *hlp)
++{
++ struct wpabuf *plain;
++ u8 *pos = buf;
++
++ /* FILS Session */
++ *pos++ = WLAN_EID_EXTENSION; /* Element ID */
++ *pos++ = 1 + FILS_SESSION_LEN; /* Length */
++ *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
++ os_memcpy(pos, fils_session, FILS_SESSION_LEN);
++ pos += FILS_SESSION_LEN;
++
++ plain = fils_prepare_plainbuf(sm, hlp);
++ if (!plain) {
++ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
++ return NULL;
++ }
++
++ os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
++ pos += wpabuf_len(plain);
++
++ wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
++ (unsigned int) wpabuf_len(plain));
++ wpabuf_free(plain);
++ sm->fils_completed = 1;
++ return pos;
++}
++
++#endif /* CONFIG_FILS */
++
++
++#ifdef CONFIG_OCV
++int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
++ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
++{
++ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
++
++ if (!wpa_auth->cb->get_sta_tx_params)
++ return -1;
++ return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
++ ap_max_chanwidth, ap_seg1_idx,
++ bandwidth, seg1_idx);
++}
++#endif /* CONFIG_OCV */
++
++
+ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+ {
++ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_ptk PTK;
+ int ok = 0, psk_found = 0;
+ const u8 *pmk = NULL;
++ size_t pmk_len;
++ int ft;
++ const u8 *eapol_key_ie, *key_data, *mic;
++ u16 key_data_length;
++ size_t mic_len, eapol_key_ie_len;
++ struct ieee802_1x_hdr *hdr;
++ struct wpa_eapol_key *key;
++ struct wpa_eapol_ie_parse kde;
++ int vlan_id = 0;
+
+ SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+ sm->EAPOLKeyReceived = FALSE;
+ sm->update_snonce = FALSE;
++ os_memset(&PTK, 0, sizeof(PTK));
+
++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
++
+ /* WPA with IEEE 802.1X: use the derived PMK from EAP
+ * WPA-PSK: iterate through possible PSKs and select the one matching
+ * the packet */
+ for (;;) {
+- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
++ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+- sm->p2p_dev_addr, pmk);
++ sm->p2p_dev_addr, pmk, &pmk_len,
++ &vlan_id);
+ if (pmk == NULL)
+ break;
+ psk_found = 1;
+- } else
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
++ os_memcpy(sm->xxkey, pmk, pmk_len);
++ sm->xxkey_len = pmk_len;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++ } else {
+ pmk = sm->PMK;
++ pmk_len = sm->pmk_len;
++ }
+
+- wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
++ if ((!pmk || !pmk_len) && sm->pmksa) {
++ wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
++ pmk = sm->pmksa->pmk;
++ pmk_len = sm->pmksa->pmk_len;
++ }
+
+- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
++ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
++ break;
++
++ if (mic_len &&
++ wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+ sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len) == 0) {
++ if (sm->PMK != pmk) {
++ os_memcpy(sm->PMK, pmk, pmk_len);
++ sm->pmk_len = pmk_len;
++ }
+ ok = 1;
+ break;
+ }
+
+- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
++#ifdef CONFIG_FILS
++ if (!mic_len &&
++ wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
++ sm->last_rx_eapol_key_len, NULL) == 0) {
++ ok = 1;
+ break;
++ }
++#endif /* CONFIG_FILS */
++
++ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
++ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
++ break;
+ }
+
+ if (!ok) {
+@@ -2078,7 +2890,105 @@
+ return;
+ }
+
+-#ifdef CONFIG_IEEE80211R
++ /*
++ * Note: last_rx_eapol_key length fields have already been validated in
++ * wpa_receive().
++ */
++ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
++ key = (struct wpa_eapol_key *) (hdr + 1);
++ mic = (u8 *) (key + 1);
++ key_data = mic + mic_len + 2;
++ key_data_length = WPA_GET_BE16(mic + mic_len);
++ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
++ sizeof(*key) - mic_len - 2)
++ return;
++
++ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
++ "received EAPOL-Key msg 2/4 with invalid Key Data contents");
++ return;
++ }
++ if (kde.rsn_ie) {
++ eapol_key_ie = kde.rsn_ie;
++ eapol_key_ie_len = kde.rsn_ie_len;
++ } else if (kde.osen) {
++ eapol_key_ie = kde.osen;
++ eapol_key_ie_len = kde.osen_len;
++ } else {
++ eapol_key_ie = kde.wpa_ie;
++ eapol_key_ie_len = kde.wpa_ie_len;
++ }
++ ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
++ if (sm->wpa_ie == NULL ||
++ wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
++ eapol_key_ie, eapol_key_ie_len)) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ "WPA IE from (Re)AssocReq did not match with msg 2/4");
++ if (sm->wpa_ie) {
++ wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
++ sm->wpa_ie, sm->wpa_ie_len);
++ }
++ wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
++ eapol_key_ie, eapol_key_ie_len);
++ /* MLME-DEAUTHENTICATE.request */
++ wpa_sta_disconnect(wpa_auth, sm->addr,
++ WLAN_REASON_PREV_AUTH_NOT_VALID);
++ return;
++ }
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sm)) {
++ struct wpa_channel_info ci;
++ int tx_chanwidth;
++ int tx_seg1_idx;
++
++ if (wpa_channel_info(wpa_auth, &ci) != 0) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ "Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
++ return;
++ }
++
++ if (get_sta_tx_parameters(sm,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx, &tx_chanwidth,
++ &tx_seg1_idx) < 0)
++ return;
++
++ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
++ tx_chanwidth, tx_seg1_idx) != 0) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ ocv_errorstr);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++#ifdef CONFIG_IEEE80211R_AP
++ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
++ wpa_sta_disconnect(wpa_auth, sm->addr,
++ WLAN_REASON_PREV_AUTH_NOT_VALID);
++ return;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++#ifdef CONFIG_P2P
++ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
++ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
++ int idx;
++ wpa_printf(MSG_DEBUG,
++ "P2P: IP address requested in EAPOL-Key exchange");
++ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
++ if (idx >= 0) {
++ u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
++ bitfield_set(wpa_auth->ip_pool, idx);
++ WPA_PUT_BE32(sm->ip_addr, start + idx);
++ wpa_printf(MSG_DEBUG,
++ "P2P: Assigned IP address %u.%u.%u.%u to "
++ MACSTR, sm->ip_addr[0], sm->ip_addr[1],
++ sm->ip_addr[2], sm->ip_addr[3],
++ MAC2STR(sm->addr));
++ }
++ }
++#endif /* CONFIG_P2P */
++
++#ifdef CONFIG_IEEE80211R_AP
+ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ /*
+ * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+@@ -2097,8 +3007,15 @@
+ return;
+ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
++ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
++ wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
++ wpa_sta_disconnect(wpa_auth, sm->addr,
++ WLAN_REASON_PREV_AUTH_NOT_VALID);
++ return;
++ }
++
+ sm->pending_1_of_4_timeout = 0;
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+
+@@ -2107,6 +3024,7 @@
+ * state machine data based on whatever PSK was selected here.
+ */
+ os_memcpy(sm->PMK, pmk, PMK_LEN);
++ sm->pmk_len = PMK_LEN;
+ }
+
+ sm->MICVerified = TRUE;
+@@ -2155,7 +3073,8 @@
+ else
+ os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+ os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
+- if (sm->wpa_auth->conf.disable_gtk) {
++ if (sm->wpa_auth->conf.disable_gtk ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use of
+ * IGTK in the BSS.
+@@ -2186,6 +3105,36 @@
+ #endif /* CONFIG_IEEE80211W */
+
+
++static int ocv_oci_len(struct wpa_state_machine *sm)
++{
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sm))
++ return OCV_OCI_KDE_LEN;
++#endif /* CONFIG_OCV */
++ return 0;
++}
++
++static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
++{
++#ifdef CONFIG_OCV
++ struct wpa_channel_info ci;
++
++ if (!wpa_auth_uses_ocv(sm))
++ return 0;
++
++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info for OCI element");
++ return -1;
++ }
++
++ return ocv_insert_oci_kde(&ci, argpos);
++#else /* CONFIG_OCV */
++ return 0;
++#endif /* CONFIG_OCV */
++}
++
++
+ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+ {
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
+@@ -2198,7 +3147,12 @@
+ sm->TimeoutEvt = FALSE;
+
+ sm->TimeoutCtr++;
+- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
++ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
++ sm->TimeoutCtr > 1) {
++ /* Do not allow retransmission of EAPOL-Key msg 3/4 */
++ return;
++ }
++ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+@@ -2228,7 +3182,8 @@
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+- if (sm->wpa_auth->conf.disable_gtk) {
++ if (sm->wpa_auth->conf.disable_gtk ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+@@ -2263,15 +3218,15 @@
+ }
+ }
+
+- kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
++ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0)
+ kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+@@ -2283,9 +3238,13 @@
+ pos = kde;
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+- int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
++ int res;
++ size_t elen;
++
++ elen = pos - kde;
++ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert "
+ "PMKR1Name into RSN IE in EAPOL-Key data");
+@@ -2292,9 +3251,10 @@
+ os_free(kde);
+ return;
+ }
+- pos += res;
++ pos -= wpa_ie_len;
++ pos += elen;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ if (gtk) {
+ u8 hdr[2];
+ hdr[0] = keyidx & 0x03;
+@@ -2303,17 +3263,32 @@
+ gtk, gtk_len);
+ }
+ pos = ieee80211w_kde_add(sm, pos);
++ if (ocv_oci_add(sm, &pos) < 0) {
++ os_free(kde);
++ return;
++ }
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ struct wpa_auth_config *conf;
+
+ conf = &sm->wpa_auth->conf;
+- res = wpa_write_ftie(conf, conf->r0_key_holder,
+- conf->r0_key_holder_len,
+- NULL, NULL, pos, kde + kde_len - pos,
+- NULL, 0);
++ if (sm->assoc_resp_ftie &&
++ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
++ os_memcpy(pos, sm->assoc_resp_ftie,
++ 2 + sm->assoc_resp_ftie[1]);
++ res = 2 + sm->assoc_resp_ftie[1];
++ } else {
++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
++
++ res = wpa_write_ftie(conf, use_sha384,
++ conf->r0_key_holder,
++ conf->r0_key_holder_len,
++ NULL, NULL, pos,
++ kde + kde_len - pos,
++ NULL, 0);
++ }
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+ "into EAPOL-Key Key Data");
+@@ -2333,10 +3308,10 @@
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
++ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+ pos += 4;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0) {
+ u8 addr[3 * 4];
+@@ -2349,7 +3324,9 @@
+ #endif /* CONFIG_P2P */
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+- (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
++ (secure ? WPA_KEY_INFO_SECURE : 0) |
++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
++ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+@@ -2366,20 +3343,18 @@
+ int klen = wpa_cipher_key_len(sm->pairwise);
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk, klen)) {
+- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
++ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
++ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = TRUE;
+
+- if (sm->wpa_auth->conf.wpa_ptk_rekey) {
+- eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+- eloop_register_timeout(sm->wpa_auth->conf.
+- wpa_ptk_rekey, 0, wpa_rekey_ptk,
+- sm->wpa_auth, sm);
+- }
++ wpa_auth_set_ptk_rekey_timer(sm);
+
+- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
++ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 1);
+ }
+@@ -2405,9 +3380,9 @@
+ "pairwise key handshake completed (%s)",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ }
+
+
+@@ -2451,15 +3426,22 @@
+ wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_keyRun) > 0)
+ SM_ENTER(WPA_PTK, INITPMK);
+- else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
++ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
+ /* FIX: && 802.1X::keyRun */)
+ SM_ENTER(WPA_PTK, INITPSK);
++ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
++ SM_ENTER(WPA_PTK, INITPMK);
+ break;
+ case WPA_PTK_INITPMK:
+ if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
+- WPA_EAPOL_keyAvailable) > 0)
++ WPA_EAPOL_keyAvailable) > 0) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+- else {
++#ifdef CONFIG_DPP
++ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
++ SM_ENTER(WPA_PTK, PTKSTART);
++#endif /* CONFIG_DPP */
++ } else {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "INITPMK - keyAvailable = false");
+@@ -2468,9 +3450,13 @@
+ break;
+ case WPA_PTK_INITPSK:
+ if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+- NULL))
++ NULL, NULL, NULL)) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+- else {
++#ifdef CONFIG_SAE
++ } else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
++ SM_ENTER(WPA_PTK, PTKSTART);
++#endif /* CONFIG_SAE */
++ } else {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "no PSK configured for the STA");
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+@@ -2482,11 +3468,12 @@
+ sm->EAPOLKeyPairwise)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->TimeoutCtr >
+- (int) dot11RSNAConfigPairwiseUpdateCount) {
++ sm->wpa_auth->conf.wpa_pairwise_update_count) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+- "PTKSTART: Retry limit %d reached",
+- dot11RSNAConfigPairwiseUpdateCount);
++ wpa_auth_vlogger(
++ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "PTKSTART: Retry limit %u reached",
++ sm->wpa_auth->conf.wpa_pairwise_update_count);
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKSTART);
+@@ -2510,12 +3497,14 @@
+ sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK, PTKINITDONE);
+ else if (sm->TimeoutCtr >
+- (int) dot11RSNAConfigPairwiseUpdateCount) {
++ sm->wpa_auth->conf.wpa_pairwise_update_count ||
++ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
++ sm->TimeoutCtr > 1)) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+- "PTKINITNEGOTIATING: Retry limit %d "
+- "reached",
+- dot11RSNAConfigPairwiseUpdateCount);
++ wpa_auth_vlogger(
++ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "PTKINITNEGOTIATING: Retry limit %u reached",
++ sm->wpa_auth->conf.wpa_pairwise_update_count);
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+@@ -2550,7 +3539,12 @@
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+ sm->GTimeoutCtr++;
+- if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
++ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
++ sm->GTimeoutCtr > 1) {
++ /* Do not allow retransmission of EAPOL-Key group msg 1/2 */
++ return;
++ }
++ if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+@@ -2567,7 +3561,8 @@
+ "sending 1/2 msg of Group Key Handshake");
+
+ gtk = gsm->GTK[gsm->GN - 1];
+- if (sm->wpa_auth->conf.disable_gtk) {
++ if (sm->wpa_auth->conf.disable_gtk ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+@@ -2578,7 +3573,7 @@
+ }
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+- ieee80211w_kde_len(sm);
++ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+ kde_buf = os_malloc(kde_len);
+ if (kde_buf == NULL)
+ return;
+@@ -2589,6 +3584,10 @@
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gsm->GTK_len);
+ pos = ieee80211w_kde_add(sm, pos);
++ if (ocv_oci_add(sm, &pos) < 0) {
++ os_free(kde_buf);
++ return;
++ }
+ kde_len = pos - kde;
+ } else {
+ kde = gtk;
+@@ -2596,10 +3595,12 @@
+ }
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
++ WPA_KEY_INFO_SECURE |
++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
++ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+- rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
++ rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+ os_free(kde_buf);
+ }
+@@ -2607,8 +3608,67 @@
+
+ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+ {
++#ifdef CONFIG_OCV
++ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
++ const u8 *key_data, *mic;
++ struct ieee802_1x_hdr *hdr;
++ struct wpa_eapol_key *key;
++ struct wpa_eapol_ie_parse kde;
++ size_t mic_len;
++ u16 key_data_length;
++#endif /* CONFIG_OCV */
++
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+ sm->EAPOLKeyReceived = FALSE;
++
++#ifdef CONFIG_OCV
++ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
++
++ /*
++ * Note: last_rx_eapol_key length fields have already been validated in
++ * wpa_receive().
++ */
++ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
++ key = (struct wpa_eapol_key *) (hdr + 1);
++ mic = (u8 *) (key + 1);
++ key_data = mic + mic_len + 2;
++ key_data_length = WPA_GET_BE16(mic + mic_len);
++ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
++ sizeof(*key) - mic_len - 2)
++ return;
++
++ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
++ "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
++ return;
++ }
++
++ if (wpa_auth_uses_ocv(sm)) {
++ struct wpa_channel_info ci;
++ int tx_chanwidth;
++ int tx_seg1_idx;
++
++ if (wpa_channel_info(wpa_auth, &ci) != 0) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2");
++ return;
++ }
++
++ if (get_sta_tx_parameters(sm,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx, &tx_chanwidth,
++ &tx_seg1_idx) < 0)
++ return;
++
++ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
++ tx_chanwidth, tx_seg1_idx) != 0) {
++ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
++ ocv_errorstr);
++ return;
++ }
++ }
++#endif /* CONFIG_OCV */
++
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+@@ -2628,6 +3688,10 @@
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ sm->Disconnect = TRUE;
++ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
++ "group key handshake failed (%s) after %u tries",
++ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
++ sm->wpa_auth->conf.wpa_group_update_count);
+ }
+
+
+@@ -2647,7 +3711,9 @@
+ !sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+ else if (sm->GTimeoutCtr >
+- (int) dot11RSNAConfigGroupUpdateCount)
++ sm->wpa_auth->conf.wpa_group_update_count ||
++ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
++ sm->GTimeoutCtr > 1))
+ SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+@@ -2750,7 +3816,7 @@
+ }
+
+
+-#ifdef CONFIG_WNM
++#ifdef CONFIG_WNM_AP
+ /* update GTK when exiting WNM-Sleep Mode */
+ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+ {
+@@ -2829,7 +3895,7 @@
+ return pos - start;
+ }
+ #endif /* CONFIG_IEEE80211W */
+-#endif /* CONFIG_WNM */
++#endif /* CONFIG_WNM_AP */
+
+
+ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+@@ -3107,8 +4173,8 @@
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+ RSN_VERSION,
+ !!wpa_auth->conf.wpa_strict_rekey,
+- dot11RSNAConfigGroupUpdateCount,
+- dot11RSNAConfigPairwiseUpdateCount,
++ wpa_auth->conf.wpa_group_update_count,
++ wpa_auth->conf.wpa_pairwise_update_count,
+ wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
+ dot11RSNAConfigPMKLifetime,
+ dot11RSNAConfigPMKReauthThreshold,
+@@ -3211,6 +4277,15 @@
+ }
+
+
++const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
++{
++ if (!sm)
++ return NULL;
++ *len = sm->pmk_len;
++ return sm->PMK;
++}
++
++
+ int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
+ {
+ if (sm == NULL)
+@@ -3235,6 +4310,14 @@
+ }
+
+
++int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
++{
++ if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
++ return 0;
++ return sm->tk_already_set;
++}
++
++
+ int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+ struct rsn_pmksa_cache_entry *entry)
+ {
+@@ -3269,6 +4352,7 @@
+
+
+ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
++ unsigned int pmk_len,
+ int session_timeout, struct eapol_state_machine *eapol)
+ {
+ if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
+@@ -3275,7 +4359,14 @@
+ sm->wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+- if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
++ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
++ if (pmk_len > PMK_LEN_SUITE_B_192)
++ pmk_len = PMK_LEN_SUITE_B_192;
++ } else if (pmk_len > PMK_LEN) {
++ pmk_len = PMK_LEN;
++ }
++
++ if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
+ sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_auth->addr, sm->addr, session_timeout,
+ eapol, sm->wpa_key_mgmt))
+@@ -3293,7 +4384,7 @@
+ if (wpa_auth == NULL)
+ return -1;
+
+- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
+ NULL, 0,
+ wpa_auth->addr,
+ sta_addr, session_timeout, eapol,
+@@ -3305,12 +4396,12 @@
+
+
+ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+- const u8 *pmk)
++ const u8 *pmk, const u8 *pmkid)
+ {
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
+ NULL, 0,
+ wpa_auth->addr, addr, 0, NULL,
+ WPA_KEY_MGMT_SAE))
+@@ -3320,6 +4411,29 @@
+ }
+
+
++void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
++{
++ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
++ sm->pmkid_set = 1;
++}
++
++
++int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
++ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
++ int session_timeout, int akmp)
++{
++ if (wpa_auth->conf.disable_pmksa_caching)
++ return -1;
++
++ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
++ NULL, 0, wpa_auth->addr, addr, session_timeout,
++ NULL, akmp))
++ return 0;
++
++ return -1;
++}
++
++
+ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+ {
+@@ -3336,6 +4450,99 @@
+ }
+
+
++int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
++ size_t len)
++{
++ if (!wpa_auth || !wpa_auth->pmksa)
++ return 0;
++ return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
++}
++
++
++void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
++{
++ if (wpa_auth && wpa_auth->pmksa)
++ pmksa_cache_auth_flush(wpa_auth->pmksa);
++}
++
++
++#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
++#ifdef CONFIG_MESH
++
++int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
++ char *buf, size_t len)
++{
++ if (!wpa_auth || !wpa_auth->pmksa)
++ return 0;
++
++ return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
++}
++
++
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
++ const u8 *pmkid, int expiration)
++{
++ struct rsn_pmksa_cache_entry *entry;
++ struct os_reltime now;
++
++ entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
++ spa, 0, NULL, WPA_KEY_MGMT_SAE);
++ if (!entry)
++ return NULL;
++
++ os_get_reltime(&now);
++ entry->expiration = now.sec + expiration;
++ return entry;
++}
++
++
++int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
++ struct rsn_pmksa_cache_entry *entry)
++{
++ int ret;
++
++ if (!wpa_auth || !wpa_auth->pmksa)
++ return -1;
++
++ ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
++ if (ret < 0)
++ wpa_printf(MSG_DEBUG,
++ "RSN: Failed to store external PMKSA cache for "
++ MACSTR, MAC2STR(entry->spa));
++
++ return ret;
++}
++
++#endif /* CONFIG_MESH */
++#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
++
++
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 *pmkid)
++{
++ if (!wpa_auth || !wpa_auth->pmksa)
++ return NULL;
++ return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
++}
++
++
++void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
++ struct wpa_state_machine *sm,
++ struct wpa_authenticator *wpa_auth,
++ u8 *pmkid, u8 *pmk)
++{
++ if (!sm)
++ return;
++
++ sm->pmksa = pmksa;
++ os_memcpy(pmk, pmksa->pmk, PMK_LEN);
++ os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
++ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
++}
++
++
+ /*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+@@ -3414,6 +4621,98 @@
+ }
+
+
++/*
++ * Enforce that the group state machine for the VLAN is running, increase
++ * reference counter as interface is up. References might have been increased
++ * even if a negative value is returned.
++ * Returns: -1 on error (group missing, group already failed); otherwise, 0
++ */
++int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
++{
++ struct wpa_group *group;
++
++ if (wpa_auth == NULL)
++ return 0;
++
++ group = wpa_auth->group;
++ while (group) {
++ if (group->vlan_id == vlan_id)
++ break;
++ group = group->next;
++ }
++
++ if (group == NULL) {
++ group = wpa_auth_add_group(wpa_auth, vlan_id);
++ if (group == NULL)
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "WPA: Ensure group state machine running for VLAN ID %d",
++ vlan_id);
++
++ wpa_group_get(wpa_auth, group);
++ group->num_setup_iface++;
++
++ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
++ return -1;
++
++ return 0;
++}
++
++
++/*
++ * Decrease reference counter, expected to be zero afterwards.
++ * returns: -1 on error (group not found, group in fail state)
++ * -2 if wpa_group is still referenced
++ * 0 else
++ */
++int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
++{
++ struct wpa_group *group;
++ int ret = 0;
++
++ if (wpa_auth == NULL)
++ return 0;
++
++ group = wpa_auth->group;
++ while (group) {
++ if (group->vlan_id == vlan_id)
++ break;
++ group = group->next;
++ }
++
++ if (group == NULL)
++ return -1;
++
++ wpa_printf(MSG_DEBUG,
++ "WPA: Try stopping group state machine for VLAN ID %d",
++ vlan_id);
++
++ if (group->num_setup_iface <= 0) {
++ wpa_printf(MSG_ERROR,
++ "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
++ vlan_id);
++ return -1;
++ }
++ group->num_setup_iface--;
++
++ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
++ ret = -1;
++
++ if (group->references > 1) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
++ vlan_id);
++ ret = -2;
++ }
++
++ wpa_group_put(wpa_auth, group);
++
++ return ret;
++}
++
++
+ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+ {
+ struct wpa_group *group;
+@@ -3478,6 +4777,14 @@
+ (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (sm->eapol_status_cb) {
++ sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
++ sm->eapol_status_cb_ctx2);
++ sm->eapol_status_cb = NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
+ }
+
+
+@@ -3524,3 +4831,379 @@
+ for (group = wpa_auth->group; group; group = group->next)
+ wpa_group_config_group_keys(wpa_auth, group);
+ }
++
++
++#ifdef CONFIG_FILS
++
++struct wpa_auth_fils_iter_data {
++ struct wpa_authenticator *auth;
++ const u8 *cache_id;
++ struct rsn_pmksa_cache_entry *pmksa;
++ const u8 *spa;
++ const u8 *pmkid;
++};
++
++
++static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
++{
++ struct wpa_auth_fils_iter_data *data = ctx;
++
++ if (a == data->auth || !a->conf.fils_cache_id_set ||
++ os_memcmp(a->conf.fils_cache_id, data->cache_id,
++ FILS_CACHE_ID_LEN) != 0)
++ return 0;
++ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
++ return data->pmksa != NULL;
++}
++
++
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr, const u8 *pmkid)
++{
++ struct wpa_auth_fils_iter_data idata;
++
++ if (!wpa_auth->conf.fils_cache_id_set)
++ return NULL;
++ idata.auth = wpa_auth;
++ idata.cache_id = wpa_auth->conf.fils_cache_id;
++ idata.pmksa = NULL;
++ idata.spa = sta_addr;
++ idata.pmkid = pmkid;
++ wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
++ return idata.pmksa;
++}
++
++
++#ifdef CONFIG_IEEE80211R_AP
++int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
++ u8 *buf, size_t len)
++{
++ struct wpa_auth_config *conf = &wpa_auth->conf;
++
++ return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
++ conf->r0_key_holder_len,
++ NULL, NULL, buf, len, NULL, 0);
++}
++#endif /* CONFIG_IEEE80211R_AP */
++
++
++void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
++ u8 *fils_anonce, u8 *fils_snonce,
++ u8 *fils_kek, size_t *fils_kek_len)
++{
++ os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
++ os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
++ os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
++ *fils_kek_len = sm->PTK.kek_len;
++}
++
++
++void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
++ size_t pmk_len, const u8 *pmkid)
++{
++ os_memcpy(sm->PMK, pmk, pmk_len);
++ sm->pmk_len = pmk_len;
++ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
++ sm->pmkid_set = 1;
++}
++
++#endif /* CONFIG_FILS */
++
++
++void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
++{
++ if (sm)
++ sm->auth_alg = auth_alg;
++}
++
++
++#ifdef CONFIG_DPP2
++void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
++{
++ if (sm) {
++ wpabuf_clear_free(sm->dpp_z);
++ sm->dpp_z = z ? wpabuf_dup(z) : NULL;
++ }
++}
++#endif /* CONFIG_DPP2 */
++
++
++#ifdef CONFIG_TESTING_OPTIONS
++
++int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2)
++{
++ const u8 *anonce = sm->ANonce;
++ u8 anonce_buf[WPA_NONCE_LEN];
++
++ if (change_anonce) {
++ if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
++ return -1;
++ anonce = anonce_buf;
++ }
++
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "sending 1/4 msg of 4-Way Handshake (TESTING)");
++ wpa_send_eapol(sm->wpa_auth, sm,
++ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
++ anonce, NULL, 0, 0, 0);
++ return 0;
++}
++
++
++int wpa_auth_resend_m3(struct wpa_state_machine *sm,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2)
++{
++ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
++#ifdef CONFIG_IEEE80211W
++ u8 *opos;
++#endif /* CONFIG_IEEE80211W */
++ size_t gtk_len, kde_len;
++ struct wpa_group *gsm = sm->group;
++ u8 *wpa_ie;
++ int wpa_ie_len, secure, keyidx, encr = 0;
++
++ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
++ GTK[GN], IGTK, [FTIE], [TIE * 2])
++ */
++
++ /* Use 0 RSC */
++ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
++ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
++ wpa_ie = sm->wpa_auth->wpa_ie;
++ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
++ if (sm->wpa == WPA_VERSION_WPA &&
++ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
++ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
++ /* WPA-only STA, remove RSN IE and possible MDIE */
++ wpa_ie = wpa_ie + wpa_ie[1] + 2;
++ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
++ wpa_ie = wpa_ie + wpa_ie[1] + 2;
++ wpa_ie_len = wpa_ie[1] + 2;
++ }
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "sending 3/4 msg of 4-Way Handshake (TESTING)");
++ if (sm->wpa == WPA_VERSION_WPA2) {
++ /* WPA2 send GTK in the 4-way handshake */
++ secure = 1;
++ gtk = gsm->GTK[gsm->GN - 1];
++ gtk_len = gsm->GTK_len;
++ keyidx = gsm->GN;
++ _rsc = rsc;
++ encr = 1;
++ } else {
++ /* WPA does not include GTK in msg 3/4 */
++ secure = 0;
++ gtk = NULL;
++ gtk_len = 0;
++ keyidx = 0;
++ _rsc = NULL;
++ if (sm->rx_eapol_key_secure) {
++ /*
++ * It looks like Windows 7 supplicant tries to use
++ * Secure bit in msg 2/4 after having reported Michael
++ * MIC failure and it then rejects the 4-way handshake
++ * if msg 3/4 does not set Secure bit. Work around this
++ * by setting the Secure bit here even in the case of
++ * WPA if the supplicant used it first.
++ */
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "STA used Secure bit in WPA msg 2/4 - "
++ "set Secure for 3/4 as workaround");
++ secure = 1;
++ }
++ }
++
++ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
++ if (gtk)
++ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
++ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
++ kde_len += 300; /* FTIE + 2 * TIE */
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++ kde = os_malloc(kde_len);
++ if (kde == NULL)
++ return -1;
++
++ pos = kde;
++ os_memcpy(pos, wpa_ie, wpa_ie_len);
++ pos += wpa_ie_len;
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
++ int res;
++ size_t elen;
++
++ elen = pos - kde;
++ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR, "FT: Failed to insert "
++ "PMKR1Name into RSN IE in EAPOL-Key data");
++ os_free(kde);
++ return -1;
++ }
++ pos -= wpa_ie_len;
++ pos += elen;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++ if (gtk) {
++ u8 hdr[2];
++ hdr[0] = keyidx & 0x03;
++ hdr[1] = 0;
++ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
++ gtk, gtk_len);
++ }
++#ifdef CONFIG_IEEE80211W
++ opos = pos;
++ pos = ieee80211w_kde_add(sm, pos);
++ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
++ /* skip KDE header and keyid */
++ opos += 2 + RSN_SELECTOR_LEN + 2;
++ os_memset(opos, 0, 6); /* clear PN */
++ }
++#endif /* CONFIG_IEEE80211W */
++ if (ocv_oci_add(sm, &pos) < 0) {
++ os_free(kde);
++ return -1;
++ }
++
++#ifdef CONFIG_IEEE80211R_AP
++ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
++ int res;
++ struct wpa_auth_config *conf;
++
++ conf = &sm->wpa_auth->conf;
++ if (sm->assoc_resp_ftie &&
++ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
++ os_memcpy(pos, sm->assoc_resp_ftie,
++ 2 + sm->assoc_resp_ftie[1]);
++ res = 2 + sm->assoc_resp_ftie[1];
++ } else {
++ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
++
++ res = wpa_write_ftie(conf, use_sha384,
++ conf->r0_key_holder,
++ conf->r0_key_holder_len,
++ NULL, NULL, pos,
++ kde + kde_len - pos,
++ NULL, 0);
++ }
++ if (res < 0) {
++ wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
++ "into EAPOL-Key Key Data");
++ os_free(kde);
++ return -1;
++ }
++ pos += res;
++
++ /* TIE[ReassociationDeadline] (TU) */
++ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
++ *pos++ = 5;
++ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
++ WPA_PUT_LE32(pos, conf->reassociation_deadline);
++ pos += 4;
++
++ /* TIE[KeyLifetime] (seconds) */
++ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
++ *pos++ = 5;
++ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
++ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
++ pos += 4;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++
++ wpa_send_eapol(sm->wpa_auth, sm,
++ (secure ? WPA_KEY_INFO_SECURE : 0) |
++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
++ WPA_KEY_INFO_MIC : 0) |
++ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
++ WPA_KEY_INFO_KEY_TYPE,
++ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
++ os_free(kde);
++ return 0;
++}
++
++
++int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2)
++{
++ u8 rsc[WPA_KEY_RSC_LEN];
++ struct wpa_group *gsm = sm->group;
++ const u8 *kde;
++ u8 *kde_buf = NULL, *pos, hdr[2];
++#ifdef CONFIG_IEEE80211W
++ u8 *opos;
++#endif /* CONFIG_IEEE80211W */
++ size_t kde_len;
++ u8 *gtk;
++
++ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
++ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
++ /* Use 0 RSC */
++ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
++ "sending 1/2 msg of Group Key Handshake (TESTING)");
++
++ gtk = gsm->GTK[gsm->GN - 1];
++ if (sm->wpa == WPA_VERSION_WPA2) {
++ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
++ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
++ kde_buf = os_malloc(kde_len);
++ if (kde_buf == NULL)
++ return -1;
++
++ kde = pos = kde_buf;
++ hdr[0] = gsm->GN & 0x03;
++ hdr[1] = 0;
++ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
++ gtk, gsm->GTK_len);
++#ifdef CONFIG_IEEE80211W
++ opos = pos;
++ pos = ieee80211w_kde_add(sm, pos);
++ if (pos - opos >=
++ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
++ /* skip KDE header and keyid */
++ opos += 2 + RSN_SELECTOR_LEN + 2;
++ os_memset(opos, 0, 6); /* clear PN */
++ }
++#endif /* CONFIG_IEEE80211W */
++ if (ocv_oci_add(sm, &pos) < 0) {
++ os_free(kde_buf);
++ return -1;
++ }
++ kde_len = pos - kde;
++ } else {
++ kde = gtk;
++ kde_len = gsm->GTK_len;
++ }
++
++ sm->eapol_status_cb = cb;
++ sm->eapol_status_cb_ctx1 = ctx1;
++ sm->eapol_status_cb_ctx2 = ctx2;
++
++ wpa_send_eapol(sm->wpa_auth, sm,
++ WPA_KEY_INFO_SECURE |
++ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
++ WPA_KEY_INFO_MIC : 0) |
++ WPA_KEY_INFO_ACK |
++ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
++ rsc, NULL, kde, kde_len, gsm->GN, 1);
++
++ os_free(kde_buf);
++ return 0;
++}
++
++
++int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
++{
++ if (!wpa_auth)
++ return -1;
++ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
++ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
++}
++
++#endif /* CONFIG_TESTING_OPTIONS */
+--- contrib/wpa/src/ap/wpa_auth.h.orig
++++ contrib/wpa/src/ap/wpa_auth.h
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -14,6 +14,8 @@
+ #include "common/wpa_common.h"
+ #include "common/ieee802_11_defs.h"
+
++struct vlan_description;
++
+ #define MAX_OWN_IE_OVERRIDE 256
+
+ #ifdef _MSC_VER
+@@ -37,65 +39,100 @@
+
+ #define FT_PACKET_REQUEST 0
+ #define FT_PACKET_RESPONSE 1
+-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
+-#define FT_PACKET_R0KH_R1KH_PULL 200
+-#define FT_PACKET_R0KH_R1KH_RESP 201
+-#define FT_PACKET_R0KH_R1KH_PUSH 202
+
+-#define FT_R0KH_R1KH_PULL_DATA_LEN 44
+-#define FT_R0KH_R1KH_RESP_DATA_LEN 76
+-#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
++/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
++ * use OUI Extended EtherType as the encapsulating format. */
++#define FT_PACKET_R0KH_R1KH_PULL 0x01
++#define FT_PACKET_R0KH_R1KH_RESP 0x02
++#define FT_PACKET_R0KH_R1KH_PUSH 0x03
++#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
++#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
+
+-struct ft_r0kh_r1kh_pull_frame {
+- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
+- le16 data_length; /* little endian length of data (44) */
+- u8 ap_address[ETH_ALEN];
++/* packet layout
++ * IEEE 802 extended OUI ethertype frame header
++ * u16 authlen (little endian)
++ * multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
++ * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
++ * blocksize length)
++ *
++ * AES-SIV AAD;
++ * source MAC address (6)
++ * authenticated-only TLVs (authlen)
++ * subtype (1; FT_PACKET_*)
++ */
+
+- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+- u8 r1kh_id[FT_R1KH_ID_LEN];
+- u8 s1kh_id[ETH_ALEN];
+- u8 pad[4]; /* 8-octet boundary for AES key wrap */
+- u8 key_wrap_extra[8];
+-} STRUCT_PACKED;
++#define FT_RRB_NONCE_LEN 16
+
+-struct ft_r0kh_r1kh_resp_frame {
+- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+- u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
+- le16 data_length; /* little endian length of data (76) */
+- u8 ap_address[ETH_ALEN];
++#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
+
+- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
+- u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
+- u8 s1kh_id[ETH_ALEN]; /* copied from pull */
+- u8 pmk_r1[PMK_LEN];
+- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+- le16 pairwise;
+- u8 pad[2]; /* 8-octet boundary for AES key wrap */
+- u8 key_wrap_extra[8];
+-} STRUCT_PACKED;
++#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
++#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
++#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
+
+-struct ft_r0kh_r1kh_push_frame {
+- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
+- le16 data_length; /* little endian length of data (88) */
+- u8 ap_address[ETH_ALEN];
++#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */
++#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */
++#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */
+
+- /* Encrypted with AES key-wrap */
+- u8 timestamp[4]; /* current time in seconds since unix epoch, little
+- * endian */
+- u8 r1kh_id[FT_R1KH_ID_LEN];
+- u8 s1kh_id[ETH_ALEN];
+- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+- u8 pmk_r1[PMK_LEN];
+- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+- le16 pairwise;
+- u8 pad[6]; /* 8-octet boundary for AES key wrap */
+- u8 key_wrap_extra[8];
++#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */
++#define FT_RRB_PMK_R0 8 /* PMK_LEN */
++#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */
++#define FT_RRB_PMK_R1 10 /* PMK_LEN */
++
++#define FT_RRB_PAIRWISE 11 /* le16 */
++#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
++
++#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
++#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
++
++#define FT_RRB_IDENTITY 15
++#define FT_RRB_RADIUS_CUI 16
++#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
++
++struct ft_rrb_tlv {
++ le16 type;
++ le16 len;
++ /* followed by data of length len */
+ } STRUCT_PACKED;
+
++struct ft_rrb_seq {
++ le32 dom;
++ le32 seq;
++ le32 ts;
++} STRUCT_PACKED;
++
++/* session TLVs:
++ * required: PMK_R1, PMK_R1_NAME, PAIRWISE
++ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
++ * SESSION_TIMEOUT
++ *
++ * pull frame TLVs:
++ * auth:
++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
++ * encrypted:
++ * required: PMK_R0_NAME, S1KH_ID
++ *
++ * response frame TLVs:
++ * auth:
++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
++ * encrypted:
++ * required: S1KH_ID
++ * optional: session TLVs
++ *
++ * push frame TLVs:
++ * auth:
++ * required: SEQ, R0KH_ID, R1KH_ID
++ * encrypted:
++ * required: S1KH_ID, PMK_R0_NAME, session TLVs
++ *
++ * sequence number request frame TLVs:
++ * auth:
++ * required: R0KH_ID, R1KH_ID, NONCE
++ *
++ * sequence number response frame TLVs:
++ * auth:
++ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
++ */
++
+ #ifdef _MSC_VER
+ #pragma pack(pop)
+ #endif /* _MSC_VER */
+@@ -107,6 +144,8 @@
+ struct wpa_state_machine;
+ struct rsn_pmksa_cache_entry;
+ struct eapol_state_machine;
++struct ft_remote_seq;
++struct wpa_channel_info;
+
+
+ struct ft_remote_r0kh {
+@@ -114,7 +153,8 @@
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R0KH_ID_MAX_LEN];
+ size_t id_len;
+- u8 key[16];
++ u8 key[32];
++ struct ft_remote_seq *seq;
+ };
+
+
+@@ -122,7 +162,8 @@
+ struct ft_remote_r1kh *next;
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R1KH_ID_LEN];
+- u8 key[16];
++ u8 key[32];
++ struct ft_remote_seq *seq;
+ };
+
+
+@@ -135,10 +176,12 @@
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
++ u32 wpa_group_update_count;
++ u32 wpa_pairwise_update_count;
++ int wpa_disable_eapol_key_retries;
+ int rsn_pairwise;
+ int rsn_preauth;
+ int eapol_version;
+- int peerkey;
+ int wmm_enabled;
+ int wmm_uapsd;
+ int disable_pmksa_caching;
+@@ -147,8 +190,12 @@
+ #ifdef CONFIG_IEEE80211W
+ enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
++ int sae_require_mfp;
+ #endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_OCV
++ int ocv; /* Operating Channel Validation */
++#endif /* CONFIG_OCV */
++#ifdef CONFIG_IEEE80211R_AP
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+@@ -155,13 +202,19 @@
+ u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
+ size_t r0_key_holder_len;
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+- u32 r0_key_lifetime;
++ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
++ int rkh_pos_timeout;
++ int rkh_neg_timeout;
++ int rkh_pull_timeout; /* ms */
++ int rkh_pull_retries;
++ int r1_max_key_lifetime;
+ u32 reassociation_deadline;
+- struct ft_remote_r0kh *r0kh_list;
+- struct ft_remote_r1kh *r1kh_list;
++ struct ft_remote_r0kh **r0kh_list;
++ struct ft_remote_r1kh **r1kh_list;
+ int pmk_r1_push;
+ int ft_over_ds;
+-#endif /* CONFIG_IEEE80211R */
++ int ft_psk_generate_local;
++#endif /* CONFIG_IEEE80211R_AP */
+ int disable_gtk;
+ int ap_mlme;
+ #ifdef CONFIG_TESTING_OPTIONS
+@@ -175,6 +228,10 @@
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+ #endif /* CONFIG_P2P */
++#ifdef CONFIG_FILS
++ unsigned int fils_cache_id_set:1;
++ u8 fils_cache_id[FILS_CACHE_ID_LEN];
++#endif /* CONFIG_FILS */
+ };
+
+ typedef enum {
+@@ -188,7 +245,6 @@
+ } wpa_eapol_variable;
+
+ struct wpa_auth_callbacks {
+- void *ctx;
+ void (*logger)(void *ctx, const u8 *addr, logger_level level,
+ const char *txt);
+ void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+@@ -198,7 +254,8 @@
+ int value);
+ int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+ const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+- const u8 *prev_psk);
++ const u8 *prev_psk, size_t *psk_len,
++ int *vlan_id);
+ int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+ int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key, size_t key_len);
+@@ -211,13 +268,34 @@
+ void *ctx), void *cb_ctx);
+ int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+ size_t data_len);
+-#ifdef CONFIG_IEEE80211R
++ int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
++ size_t data_len);
++ int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
++ int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
++ int (*get_sta_tx_params)(void *ctx, const u8 *addr,
++ int ap_max_chanwidth, int ap_seg1_idx,
++ int *bandwidth, int *seg1_idx);
++#ifdef CONFIG_IEEE80211R_AP
+ struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
++ int (*set_vlan)(void *ctx, const u8 *sta_addr,
++ struct vlan_description *vlan);
++ int (*get_vlan)(void *ctx, const u8 *sta_addr,
++ struct vlan_description *vlan);
++ int (*set_identity)(void *ctx, const u8 *sta_addr,
++ const u8 *identity, size_t identity_len);
++ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
++ int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
++ const u8 *radius_cui, size_t radius_cui_len);
++ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
++ void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
++ int session_timeout);
++ int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
++
+ int (*send_ft_action)(void *ctx, const u8 *dst,
+ const u8 *data, size_t data_len);
+ int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_MESH
+ int (*start_ampe)(void *ctx, const u8 *sta_addr);
+ #endif /* CONFIG_MESH */
+@@ -225,7 +303,8 @@
+
+ struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+- struct wpa_auth_callbacks *cb);
++ const struct wpa_auth_callbacks *cb,
++ void *cb_ctx);
+ int wpa_init_keys(struct wpa_authenticator *wpa_auth);
+ void wpa_deinit(struct wpa_authenticator *wpa_auth);
+ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+@@ -235,17 +314,20 @@
+ WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+ WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+ WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+- WPA_INVALID_MDIE, WPA_INVALID_PROTO
++ WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
+ };
+-
++
+ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
++ struct wpa_state_machine *sm, int freq,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+- const u8 *mdie, size_t mdie_len);
++ const u8 *mdie, size_t mdie_len,
++ const u8 *owe_dh, size_t owe_dh_len);
+ int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len);
+ int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
++void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
++int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
+ struct wpa_state_machine *
+ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr);
+@@ -258,7 +340,7 @@
+ u8 *data, size_t data_len);
+ enum wpa_event {
+ WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+- WPA_REAUTH_EAPOL, WPA_ASSOC_FT
++ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
+ };
+ void wpa_remove_ptk(struct wpa_state_machine *sm);
+ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
+@@ -269,9 +351,11 @@
+ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+ int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+ int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
++const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+ int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+ int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
++int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
+ int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+ struct rsn_pmksa_cache_entry *entry);
+ struct rsn_pmksa_cache_entry *
+@@ -280,6 +364,7 @@
+ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+ size_t *len);
+ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
++ unsigned int pmk_len,
+ int session_timeout, struct eapol_state_machine *eapol);
+ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+ const u8 *pmk, size_t len, const u8 *sta_addr,
+@@ -286,14 +371,38 @@
+ int session_timeout,
+ struct eapol_state_machine *eapol);
+ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+- const u8 *pmk);
++ const u8 *pmk, const u8 *pmkid);
++void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
++int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
++ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
++ int session_timeout, int akmp);
+ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr);
++int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
++ size_t len);
++void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
++int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
++ char *buf, size_t len);
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
++ const u8 *pmkid, int expiration);
++int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
++ struct rsn_pmksa_cache_entry *entry);
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 *pmkid);
++struct rsn_pmksa_cache_entry *
++wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr, const u8 *pmkid);
++void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
++ struct wpa_state_machine *sm,
++ struct wpa_authenticator *wpa_auth,
++ u8 *pmkid, u8 *pmk);
+ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack);
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+ size_t max_len, int auth_alg,
+ const u8 *req_ies, size_t req_ies_len);
+@@ -308,8 +417,13 @@
+ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
+ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *data, size_t data_len);
++void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
++ size_t data_len);
+ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+-#endif /* CONFIG_IEEE80211R */
++void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
++void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
++#endif /* CONFIG_IEEE80211R_AP */
+
+ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+ void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+@@ -326,4 +440,54 @@
+ struct radius_das_attrs *attr);
+ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
++int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
++int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
++int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
++ size_t pmk_len, const u8 *snonce, const u8 *anonce,
++ const u8 *dhss, size_t dhss_len,
++ struct wpabuf *g_sta, struct wpabuf *g_ap);
++int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
++ const struct ieee80211_mgmt *mgmt, size_t frame_len,
++ u8 *pos, size_t left);
++int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
++ size_t current_len, size_t max_len,
++ const struct wpabuf *hlp);
++int fils_set_tk(struct wpa_state_machine *sm);
++u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
++ const u8 *fils_session,
++ struct wpabuf *fils_hlp_resp);
++const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
++ const u8 *ies, size_t ies_len,
++ const u8 *fils_session);
++int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
++ size_t ies_len);
++
++int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
++ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
++
++int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
++ u8 *buf, size_t len);
++void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
++ u8 *fils_anonce, u8 *fils_snonce,
++ u8 *fils_kek, size_t *fils_kek_len);
++void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
++ size_t pmk_len, const u8 *pmkid);
++u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
++ u8 *pos, size_t max_len,
++ const u8 *req_ies, size_t req_ies_len);
++void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
++void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
++
++int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2);
++int wpa_auth_resend_m3(struct wpa_state_machine *sm,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2);
++int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
++ void (*cb)(void *ctx1, void *ctx2),
++ void *ctx1, void *ctx2);
++int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
++void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
++
+ #endif /* WPA_AUTH_H */
+--- contrib/wpa/src/ap/wpa_auth_ft.c.orig
++++ contrib/wpa/src/ap/wpa_auth_ft.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -13,7 +13,12 @@
+ #include "utils/list.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/ieee802_11_common.h"
++#include "common/ocv.h"
++#include "drivers/driver.h"
++#include "crypto/aes.h"
++#include "crypto/aes_siv.h"
+ #include "crypto/aes_wrap.h"
++#include "crypto/sha384.h"
+ #include "crypto/random.h"
+ #include "ap_config.h"
+ #include "ieee802_11.h"
+@@ -22,57 +27,762 @@
+ #include "wpa_auth_i.h"
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+
++const unsigned int ftRRBseqTimeout = 10;
++const unsigned int ftRRBmaxQueueLen = 100;
++
++
+ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len);
++static void ft_finish_pull(struct wpa_state_machine *sm);
++static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
++static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
+
++struct tlv_list {
++ u16 type;
++ size_t len;
++ const u8 *data;
++};
+
++
++/**
++ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
++ * @key: AES-SIV key for AEAD
++ * @key_len: Length of key in octets
++ * @enc: Pointer to encrypted TLVs
++ * @enc_len: Length of encrypted TLVs in octets
++ * @auth: Pointer to authenticated TLVs
++ * @auth_len: Length of authenticated TLVs in octets
++ * @src_addr: MAC address of the frame sender
++ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
++ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
++ * needs to be freed by the caller if not NULL;
++ * will only be returned on success
++ * @plain_len: Pointer to return the length of the allocated plaintext buffer
++ * in octets
++ * Returns: 0 on success, -1 on error
++ */
++static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, const size_t auth_len,
++ const u8 *src_addr, u8 type,
++ u8 **plain, size_t *plain_size)
++{
++ const u8 *ad[3] = { src_addr, auth, &type };
++ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
++
++ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
++ MAC2STR(src_addr), type);
++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
++ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
++ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
++
++ if (!key) { /* skip decryption */
++ *plain = os_memdup(enc, enc_len);
++ if (enc_len > 0 && !*plain)
++ goto err;
++
++ *plain_size = enc_len;
++
++ return 0;
++ }
++
++ *plain = NULL;
++
++ /* SIV overhead */
++ if (enc_len < AES_BLOCK_SIZE)
++ goto err;
++
++ *plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
++ if (!*plain)
++ goto err;
++
++ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
++ *plain) < 0) {
++ if (enc_len < AES_BLOCK_SIZE + 2)
++ goto err;
++
++ /* Try to work around Ethernet devices that add extra
++ * two octet padding even if the frame is longer than
++ * the minimum Ethernet frame. */
++ enc_len -= 2;
++ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
++ *plain) < 0)
++ goto err;
++ }
++
++ *plain_size = enc_len - AES_BLOCK_SIZE;
++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
++ *plain, *plain_size);
++ return 0;
++err:
++ os_free(*plain);
++ *plain = NULL;
++ *plain_size = 0;
++
++ wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
++
++ return -1;
++}
++
++
++/* get first tlv record in packet matching type
++ * @data (decrypted) packet
++ * @return 0 on success else -1
++ */
++static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
++ u16 type, size_t *tlv_len, const u8 **tlv_data)
++{
++ const struct ft_rrb_tlv *f;
++ size_t left;
++ le16 type16;
++ size_t len;
++
++ left = plain_len;
++ type16 = host_to_le16(type);
++
++ while (left >= sizeof(*f)) {
++ f = (const struct ft_rrb_tlv *) plain;
++
++ left -= sizeof(*f);
++ plain += sizeof(*f);
++ len = le_to_host16(f->len);
++
++ if (left < len) {
++ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
++ break;
++ }
++
++ if (f->type == type16) {
++ *tlv_len = len;
++ *tlv_data = plain;
++ return 0;
++ }
++
++ left -= len;
++ plain += len;
++ }
++
++ return -1;
++}
++
++
++static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
++{
++ const struct ft_rrb_tlv *f;
++ size_t left;
++ size_t len;
++
++ left = plain_len;
++
++ wpa_printf(MSG_DEBUG, "FT: RRB dump message");
++ while (left >= sizeof(*f)) {
++ f = (const struct ft_rrb_tlv *) plain;
++
++ left -= sizeof(*f);
++ plain += sizeof(*f);
++ len = le_to_host16(f->len);
++
++ wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
++ le_to_host16(f->type), len);
++
++ if (left < len) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB message truncated: left %zu bytes, need %zu",
++ left, len);
++ break;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
++
++ left -= len;
++ plain += len;
++ }
++
++ if (left > 0)
++ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
++
++ wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
++}
++
++
++static int cmp_int(const void *a, const void *b)
++{
++ int x, y;
++
++ x = *((int *) a);
++ y = *((int *) b);
++ return x - y;
++}
++
++
++static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
++ struct vlan_description *vlan)
++{
++ struct ft_rrb_tlv *f;
++ size_t left;
++ size_t len;
++ int taggedidx;
++ int vlan_id;
++ int type;
++
++ left = plain_len;
++ taggedidx = 0;
++ os_memset(vlan, 0, sizeof(*vlan));
++
++ while (left >= sizeof(*f)) {
++ f = (struct ft_rrb_tlv *) plain;
++
++ left -= sizeof(*f);
++ plain += sizeof(*f);
++
++ len = le_to_host16(f->len);
++ type = le_to_host16(f->type);
++
++ if (left < len) {
++ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
++ return -1;
++ }
++
++ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
++ goto skip;
++
++ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB VLAN_UNTAGGED invalid length");
++ return -1;
++ }
++
++ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB VLAN_TAGGED invalid length");
++ return -1;
++ }
++
++ while (len >= sizeof(le16)) {
++ vlan_id = WPA_GET_LE16(plain);
++ plain += sizeof(le16);
++ left -= sizeof(le16);
++ len -= sizeof(le16);
++
++ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB VLAN ID invalid %d",
++ vlan_id);
++ continue;
++ }
++
++ if (type == FT_RRB_VLAN_UNTAGGED)
++ vlan->untagged = vlan_id;
++
++ if (type == FT_RRB_VLAN_TAGGED &&
++ taggedidx < MAX_NUM_TAGGED_VLAN) {
++ vlan->tagged[taggedidx] = vlan_id;
++ taggedidx++;
++ } else if (type == FT_RRB_VLAN_TAGGED) {
++ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
++ }
++ }
++
++ skip:
++ left -= len;
++ plain += len;
++ }
++
++ if (taggedidx)
++ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
++
++ vlan->notempty = vlan->untagged || vlan->tagged[0];
++
++ return 0;
++}
++
++
++static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
++{
++ size_t tlv_len = 0;
++ int i;
++
++ if (!tlvs)
++ return 0;
++
++ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
++ tlv_len += sizeof(struct ft_rrb_tlv);
++ tlv_len += tlvs[i].len;
++ }
++
++ return tlv_len;
++}
++
++
++static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
++ u8 *endpos)
++{
++ int i;
++ size_t tlv_len;
++ struct ft_rrb_tlv *hdr;
++ u8 *pos;
++
++ if (!tlvs)
++ return 0;
++
++ tlv_len = 0;
++ pos = start;
++ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
++ if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
++ return tlv_len;
++ tlv_len += sizeof(*hdr);
++ hdr = (struct ft_rrb_tlv *) pos;
++ hdr->type = host_to_le16(tlvs[i].type);
++ hdr->len = host_to_le16(tlvs[i].len);
++ pos = start + tlv_len;
++
++ if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
++ return tlv_len;
++ if (tlvs[i].len == 0)
++ continue;
++ tlv_len += tlvs[i].len;
++ os_memcpy(pos, tlvs[i].data, tlvs[i].len);
++ pos = start + tlv_len;
++ }
++
++ return tlv_len;
++}
++
++
++static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
++{
++ size_t tlv_len = 0;
++ int i;
++
++ if (!vlan || !vlan->notempty)
++ return 0;
++
++ if (vlan->untagged) {
++ tlv_len += sizeof(struct ft_rrb_tlv);
++ tlv_len += sizeof(le16);
++ }
++ if (vlan->tagged[0])
++ tlv_len += sizeof(struct ft_rrb_tlv);
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
++ tlv_len += sizeof(le16);
++
++ return tlv_len;
++}
++
++
++static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
++ u8 *start, u8 *endpos)
++{
++ size_t tlv_len;
++ int i, len;
++ struct ft_rrb_tlv *hdr;
++ u8 *pos = start;
++
++ if (!vlan || !vlan->notempty)
++ return 0;
++
++ tlv_len = 0;
++ if (vlan->untagged) {
++ tlv_len += sizeof(*hdr);
++ if (start + tlv_len > endpos)
++ return tlv_len;
++ hdr = (struct ft_rrb_tlv *) pos;
++ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
++ hdr->len = host_to_le16(sizeof(le16));
++ pos = start + tlv_len;
++
++ tlv_len += sizeof(le16);
++ if (start + tlv_len > endpos)
++ return tlv_len;
++ WPA_PUT_LE16(pos, vlan->untagged);
++ pos = start + tlv_len;
++ }
++
++ if (!vlan->tagged[0])
++ return tlv_len;
++
++ tlv_len += sizeof(*hdr);
++ if (start + tlv_len > endpos)
++ return tlv_len;
++ hdr = (struct ft_rrb_tlv *) pos;
++ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
++ len = 0; /* len is computed below */
++ pos = start + tlv_len;
++
++ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
++ tlv_len += sizeof(le16);
++ if (start + tlv_len > endpos)
++ break;
++ len += sizeof(le16);
++ WPA_PUT_LE16(pos, vlan->tagged[i]);
++ pos = start + tlv_len;
++ }
++
++ hdr->len = host_to_le16(len);
++
++ return tlv_len;
++}
++
++
++static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
++ const struct tlv_list *tlvs2,
++ const struct vlan_description *vlan,
++ u8 **plain, size_t *plain_len)
++{
++ u8 *pos, *endpos;
++ size_t tlv_len;
++
++ tlv_len = wpa_ft_tlv_len(tlvs1);
++ tlv_len += wpa_ft_tlv_len(tlvs2);
++ tlv_len += wpa_ft_vlan_len(vlan);
++
++ *plain_len = tlv_len;
++ *plain = os_zalloc(tlv_len);
++ if (!*plain) {
++ wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
++ goto err;
++ }
++
++ pos = *plain;
++ endpos = *plain + tlv_len;
++ pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
++ pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
++ pos += wpa_ft_vlan_lin(vlan, pos, endpos);
++
++ /* sanity check */
++ if (pos != endpos) {
++ wpa_printf(MSG_ERROR, "FT: Length error building RRB");
++ goto err;
++ }
++
++ return 0;
++
++err:
++ os_free(*plain);
++ *plain = NULL;
++ *plain_len = 0;
++ return -1;
++}
++
++
++static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
++ const u8 *plain, const size_t plain_len,
++ const u8 *auth, const size_t auth_len,
++ const u8 *src_addr, u8 type, u8 *enc)
++{
++ const u8 *ad[3] = { src_addr, auth, &type };
++ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
++
++ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
++ MAC2STR(src_addr), type);
++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
++ plain, plain_len);
++ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
++ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
++
++ if (!key) {
++ /* encryption not needed, return plaintext as packet */
++ os_memcpy(enc, plain, plain_len);
++ } else if (aes_siv_encrypt(key, key_len, plain, plain_len,
++ 3, ad, ad_len, enc) < 0) {
++ wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
++ enc, plain_len + AES_BLOCK_SIZE);
++
++ return 0;
++}
++
++
++/**
++ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
++ * @key: AES-SIV key for AEAD
++ * @key_len: Length of key in octets
++ * @tlvs_enc0: First set of to-be-encrypted TLVs
++ * @tlvs_enc1: Second set of to-be-encrypted TLVs
++ * @tlvs_auth: Set of to-be-authenticated TLVs
++ * @src_addr: MAC address of the frame sender
++ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
++ * @packet Pointer to return the pointer to the allocated packet buffer;
++ * needs to be freed by the caller if not null;
++ * will only be returned on success
++ * @packet_len: Pointer to return the length of the allocated buffer in octets
++ * Returns: 0 on success, -1 on error
++ */
++static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
++ const struct tlv_list *tlvs_enc0,
++ const struct tlv_list *tlvs_enc1,
++ const struct tlv_list *tlvs_auth,
++ const struct vlan_description *vlan,
++ const u8 *src_addr, u8 type,
++ u8 **packet, size_t *packet_len)
++{
++ u8 *plain = NULL, *auth = NULL, *pos, *tmp;
++ size_t plain_len = 0, auth_len = 0;
++ int ret = -1;
++ size_t pad_len = 0;
++
++ *packet = NULL;
++ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
++ goto out;
++
++ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
++ goto out;
++
++ *packet_len = sizeof(u16) + auth_len + plain_len;
++ if (key)
++ *packet_len += AES_BLOCK_SIZE;
++#define RRB_MIN_MSG_LEN 64
++ if (*packet_len < RRB_MIN_MSG_LEN) {
++ pad_len = RRB_MIN_MSG_LEN - *packet_len;
++ if (pad_len < sizeof(struct ft_rrb_tlv))
++ pad_len = sizeof(struct ft_rrb_tlv);
++ wpa_printf(MSG_DEBUG,
++ "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
++ (int) *packet_len, (int) (*packet_len + pad_len));
++ *packet_len += pad_len;
++ tmp = os_realloc(auth, auth_len + pad_len);
++ if (!tmp)
++ goto out;
++ auth = tmp;
++ pos = auth + auth_len;
++ WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
++ pos += 2;
++ WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
++ pos += 2;
++ os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
++ auth_len += pad_len;
++
++ }
++ *packet = os_zalloc(*packet_len);
++ if (!*packet)
++ goto out;
++
++ pos = *packet;
++ WPA_PUT_LE16(pos, auth_len);
++ pos += 2;
++ os_memcpy(pos, auth, auth_len);
++ pos += auth_len;
++ if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
++ auth_len, src_addr, type, pos) < 0)
++ goto out;
++ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
++
++ ret = 0;
++
++out:
++ bin_clear_free(plain, plain_len);
++ os_free(auth);
++
++ if (ret) {
++ wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
++ os_free(*packet);
++ *packet = NULL;
++ *packet_len = 0;
++ }
++
++ return ret;
++}
++
++
++#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
++ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
++ &f_##field##_len, &f_##field) < 0 || \
++ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
++ wpa_printf(MSG_INFO, "FT: Missing required " #field \
++ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
++ wpa_ft_rrb_dump(srcfield, srcfield##_len); \
++ goto out; \
++ } \
++} while (0)
++
++#define RRB_GET(type, field, txt, checklength) \
++ RRB_GET_SRC(plain, type, field, txt, checklength)
++#define RRB_GET_AUTH(type, field, txt, checklength) \
++ RRB_GET_SRC(auth, type, field, txt, checklength)
++
++#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
++ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
++ &f_##field##_len, &f_##field) < 0 || \
++ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
++ wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
++ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
++ f_##field##_len = 0; \
++ f_##field = NULL; \
++ } \
++} while (0)
++
++#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
++ RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
++#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
++ RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
++
+ static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
+ const u8 *data, size_t data_len)
+ {
+- if (wpa_auth->cb.send_ether == NULL)
++ if (wpa_auth->cb->send_ether == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+- return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
+- data, data_len);
++ return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
++ data, data_len);
+ }
+
+
++static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
++ const u8 *dst, u8 oui_suffix,
++ const u8 *data, size_t data_len)
++{
++ if (!wpa_auth->cb->send_oui)
++ return -1;
++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
++ oui_suffix, MAC2STR(dst), (unsigned int) data_len);
++ return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
++ data_len);
++}
++
++
+ static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, const u8 *data, size_t data_len)
+ {
+- if (wpa_auth->cb.send_ft_action == NULL)
++ if (wpa_auth->cb->send_ft_action == NULL)
+ return -1;
+- return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
+- data, data_len);
++ return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
++ data, data_len);
+ }
+
+
++static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
++ const u8 *addr, const u8 *p2p_dev_addr,
++ const u8 *prev_psk)
++{
++ if (wpa_auth->cb->get_psk == NULL)
++ return NULL;
++ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
++ prev_psk, NULL, NULL);
++}
++
++
+ static struct wpa_state_machine *
+ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+ {
+- if (wpa_auth->cb.add_sta == NULL)
++ if (wpa_auth->cb->add_sta == NULL)
+ return NULL;
+- return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
++ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+ }
+
+
++static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr, struct vlan_description *vlan)
++{
++ if (!wpa_auth->cb->set_vlan)
++ return -1;
++ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
++}
++
++
++static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr, struct vlan_description *vlan)
++{
++ if (!wpa_auth->cb->get_vlan)
++ return -1;
++ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
++}
++
++
++static int
++wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 *identity, size_t identity_len)
++{
++ if (!wpa_auth->cb->set_identity)
++ return -1;
++ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
++ identity_len);
++}
++
++
++static size_t
++wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 **buf)
++{
++ *buf = NULL;
++ if (!wpa_auth->cb->get_identity)
++ return 0;
++ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
++}
++
++
++static int
++wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 *radius_cui, size_t radius_cui_len)
++{
++ if (!wpa_auth->cb->set_radius_cui)
++ return -1;
++ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
++ radius_cui, radius_cui_len);
++}
++
++
++static size_t
++wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
++ const u8 **buf)
++{
++ *buf = NULL;
++ if (!wpa_auth->cb->get_radius_cui)
++ return 0;
++ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
++}
++
++
++static void
++wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr, int session_timeout)
++{
++ if (!wpa_auth->cb->set_session_timeout)
++ return;
++ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
++ session_timeout);
++}
++
++
++static int
++wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
++ const u8 *sta_addr)
++{
++ if (!wpa_auth->cb->get_session_timeout)
++ return 0;
++ return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
++}
++
++
+ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+ {
+- if (wpa_auth->cb.add_tspec == NULL) {
++ if (wpa_auth->cb->add_tspec == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+ return -1;
+ }
+- return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
+- tspec_ielen);
++ return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
++ tspec_ielen);
+ }
+
+
++#ifdef CONFIG_OCV
++static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
++ struct wpa_channel_info *ci)
++{
++ if (!wpa_auth->cb->channel_info)
++ return -1;
++ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
++}
++#endif /* CONFIG_OCV */
++
++
+ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+ {
+ u8 *pos = buf;
+@@ -93,16 +803,17 @@
+ }
+
+
+-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+- size_t r0kh_id_len,
++int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
++ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *anonce, const u8 *snonce,
+ u8 *buf, size_t len, const u8 *subelem,
+ size_t subelem_len)
+ {
+ u8 *pos = buf, *ielen;
+- struct rsn_ftie *hdr;
++ size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
++ sizeof(struct rsn_ftie);
+
+- if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
++ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+ subelem_len)
+ return -1;
+
+@@ -109,15 +820,28 @@
+ *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+ ielen = pos++;
+
+- hdr = (struct rsn_ftie *) pos;
+- os_memset(hdr, 0, sizeof(*hdr));
+- pos += sizeof(*hdr);
+- WPA_PUT_LE16(hdr->mic_control, 0);
+- if (anonce)
+- os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+- if (snonce)
+- os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
++ if (use_sha384) {
++ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
++ os_memset(hdr, 0, sizeof(*hdr));
++ pos += sizeof(*hdr);
++ WPA_PUT_LE16(hdr->mic_control, 0);
++ if (anonce)
++ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
++ if (snonce)
++ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
++ } else {
++ struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
++
++ os_memset(hdr, 0, sizeof(*hdr));
++ pos += sizeof(*hdr);
++ WPA_PUT_LE16(hdr->mic_control, 0);
++ if (anonce)
++ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
++ if (snonce)
++ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
++ }
++
+ /* Optional Parameters */
+ *pos++ = FTIE_SUBELEM_R1KH_ID;
+ *pos++ = FT_R1KH_ID_LEN;
+@@ -142,35 +866,434 @@
+ }
+
+
++/* A packet to be handled after seq response */
++struct ft_remote_item {
++ struct dl_list list;
++
++ u8 nonce[FT_RRB_NONCE_LEN];
++ struct os_reltime nonce_ts;
++
++ u8 src_addr[ETH_ALEN];
++ u8 *enc;
++ size_t enc_len;
++ u8 *auth;
++ size_t auth_len;
++ int (*cb)(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer);
++};
++
++
++static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
++{
++ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
++ dl_list_del(&item->list);
++ bin_clear_free(item->enc, item->enc_len);
++ os_free(item->auth);
++ os_free(item);
++}
++
++
++static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_seq *rkh_seq, int cb)
++{
++ struct ft_remote_item *item, *n;
++
++ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
++ struct ft_remote_item, list) {
++ if (cb && item->cb)
++ item->cb(wpa_auth, item->src_addr, item->enc,
++ item->enc_len, item->auth, item->auth_len, 1);
++ wpa_ft_rrb_seq_free(item);
++ }
++}
++
++
++static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
++{
++ struct ft_remote_item *item = timeout_ctx;
++
++ wpa_ft_rrb_seq_free(item);
++}
++
++
++static int
++wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
++ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
++ const u8 *f_r1kh_id, const u8 *key, size_t key_len,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int (*cb)(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer))
++{
++ struct ft_remote_item *item = NULL;
++ u8 *packet = NULL;
++ size_t packet_len;
++ struct tlv_list seq_req_auth[] = {
++ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
++ .data = NULL /* to be filled: item->nonce */ },
++ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
++ .data = f_r0kh_id },
++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
++ .data = f_r1kh_id },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
++
++ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
++ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
++ goto err;
++ }
++
++ wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR,
++ MAC2STR(src_addr));
++ item = os_zalloc(sizeof(*item));
++ if (!item)
++ goto err;
++
++ os_memcpy(item->src_addr, src_addr, ETH_ALEN);
++ item->cb = cb;
++
++ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
++ goto err;
++ }
++
++ if (os_get_reltime(&item->nonce_ts) < 0)
++ goto err;
++
++ if (enc && enc_len > 0) {
++ item->enc = os_memdup(enc, enc_len);
++ item->enc_len = enc_len;
++ if (!item->enc)
++ goto err;
++ }
++
++ if (auth && auth_len > 0) {
++ item->auth = os_memdup(auth, auth_len);
++ item->auth_len = auth_len;
++ if (!item->auth)
++ goto err;
++ }
++
++ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
++ wpa_auth, item);
++
++ seq_req_auth[0].data = item->nonce;
++
++ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
++ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
++ &packet, &packet_len) < 0) {
++ item = NULL; /* some other seq resp might still accept this */
++ goto err;
++ }
++
++ dl_list_add(&rkh_seq->rx.queue, &item->list);
++
++ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
++ packet, packet_len);
++
++ os_free(packet);
++
++ return 0;
++err:
++ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
++ if (item) {
++ os_free(item->auth);
++ bin_clear_free(item->enc, item->enc_len);
++ os_free(item);
++ }
++
++ return -1;
++}
++
++
++#define FT_RRB_SEQ_OK 0
++#define FT_RRB_SEQ_DROP 1
++#define FT_RRB_SEQ_DEFER 2
++
++static int
++wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ const char *msgtype, int no_defer)
++{
++ const u8 *f_seq;
++ size_t f_seq_len;
++ const struct ft_rrb_seq *msg_both;
++ u32 msg_seq, msg_off, rkh_off;
++ struct os_reltime now;
++ unsigned int i;
++
++ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
++ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
++ msg_both = (const struct ft_rrb_seq *) f_seq;
++
++ if (rkh_seq->rx.num_last == 0) {
++ /* first packet from remote */
++ goto defer;
++ }
++
++ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
++ /* remote might have rebooted */
++ goto defer;
++ }
++
++ if (os_get_reltime(&now) == 0) {
++ u32 msg_ts_now_remote, msg_ts_off;
++ struct os_reltime now_remote;
++
++ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
++ msg_ts_now_remote = now_remote.sec;
++ msg_ts_off = le_to_host32(msg_both->ts) -
++ (msg_ts_now_remote - ftRRBseqTimeout);
++ if (msg_ts_off > 2 * ftRRBseqTimeout)
++ goto defer;
++ }
++
++ msg_seq = le_to_host32(msg_both->seq);
++ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
++ msg_off = msg_seq - rkh_off;
++ if (msg_off > 0xC0000000)
++ goto out; /* too old message, drop it */
++
++ if (msg_off <= 0x40000000) {
++ for (i = 0; i < rkh_seq->rx.num_last; i++) {
++ if (rkh_seq->rx.last[i] == msg_seq)
++ goto out; /* duplicate message, drop it */
++ }
++
++ return FT_RRB_SEQ_OK;
++ }
++
++defer:
++ if (no_defer)
++ goto out;
++
++ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
++ MACSTR, msgtype, MAC2STR(src_addr));
++
++ return FT_RRB_SEQ_DEFER;
++out:
++ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
++ msgtype, MAC2STR(src_addr));
++
++ return FT_RRB_SEQ_DROP;
++}
++
++
++static void
++wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
++ const u8 *auth, size_t auth_len,
++ const char *msgtype)
++{
++ const u8 *f_seq;
++ size_t f_seq_len;
++ const struct ft_rrb_seq *msg_both;
++ u32 msg_seq, msg_off, min_off, rkh_off;
++ int minidx = 0;
++ unsigned int i;
++
++ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
++ msg_both = (const struct ft_rrb_seq *) f_seq;
++
++ msg_seq = le_to_host32(msg_both->seq);
++
++ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
++ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
++ rkh_seq->rx.num_last++;
++ return;
++ }
++
++ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
++ for (i = 0; i < rkh_seq->rx.num_last; i++) {
++ msg_off = rkh_seq->rx.last[i] - rkh_off;
++ min_off = rkh_seq->rx.last[minidx] - rkh_off;
++ if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
++ minidx = i;
++ }
++ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
++ rkh_seq->rx.offsetidx = minidx;
++
++ return;
++out:
++ /* RRB_GET_AUTH should never fail here as
++ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
++ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
++}
++
++
++static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
++ struct ft_rrb_seq *f_seq)
++{
++ struct os_reltime now;
++
++ if (os_get_reltime(&now) < 0)
++ return -1;
++
++ if (!rkh_seq->tx.dom) {
++ if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
++ sizeof(rkh_seq->tx.seq))) {
++ wpa_printf(MSG_ERROR,
++ "FT: Failed to get random data for sequence number initialization");
++ rkh_seq->tx.seq = now.usec;
++ }
++ if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
++ sizeof(rkh_seq->tx.dom))) {
++ wpa_printf(MSG_ERROR,
++ "FT: Failed to get random data for sequence number initialization");
++ rkh_seq->tx.dom = now.usec;
++ }
++ rkh_seq->tx.dom |= 1;
++ }
++
++ f_seq->dom = host_to_le32(rkh_seq->tx.dom);
++ f_seq->seq = host_to_le32(rkh_seq->tx.seq);
++ f_seq->ts = host_to_le32(now.sec);
++
++ rkh_seq->tx.seq++;
++
++ return 0;
++}
++
++
+ struct wpa_ft_pmk_r0_sa {
+- struct wpa_ft_pmk_r0_sa *next;
+- u8 pmk_r0[PMK_LEN];
++ struct dl_list list;
++ u8 pmk_r0[PMK_LEN_MAX];
++ size_t pmk_r0_len;
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 spa[ETH_ALEN];
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
++ struct vlan_description *vlan;
++ os_time_t expiration; /* 0 for no expiration */
++ u8 *identity;
++ size_t identity_len;
++ u8 *radius_cui;
++ size_t radius_cui_len;
++ os_time_t session_timeout; /* 0 for no expiration */
++ /* TODO: radius_class, EAP type */
+ int pmk_r1_pushed;
+ };
+
+ struct wpa_ft_pmk_r1_sa {
+- struct wpa_ft_pmk_r1_sa *next;
+- u8 pmk_r1[PMK_LEN];
++ struct dl_list list;
++ u8 pmk_r1[PMK_LEN_MAX];
++ size_t pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 spa[ETH_ALEN];
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
++ struct vlan_description *vlan;
++ u8 *identity;
++ size_t identity_len;
++ u8 *radius_cui;
++ size_t radius_cui_len;
++ os_time_t session_timeout; /* 0 for no expiration */
++ /* TODO: radius_class, EAP type */
+ };
+
+ struct wpa_ft_pmk_cache {
+- struct wpa_ft_pmk_r0_sa *pmk_r0;
+- struct wpa_ft_pmk_r1_sa *pmk_r1;
++ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
++ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
+ };
+
++
++static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
++static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
++
++
++static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
++{
++ if (!r0)
++ return;
++
++ dl_list_del(&r0->list);
++ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
++
++ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
++ os_free(r0->vlan);
++ os_free(r0->identity);
++ os_free(r0->radius_cui);
++ os_free(r0);
++}
++
++
++static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
++ struct os_reltime now;
++ int expires_in;
++ int session_timeout;
++
++ os_get_reltime(&now);
++
++ if (!r0)
++ return;
++
++ expires_in = r0->expiration - now.sec;
++ session_timeout = r0->session_timeout - now.sec;
++ /* conditions to remove from cache:
++ * a) r0->expiration is set and hit
++ * -or-
++ * b) r0->session_timeout is set and hit
++ */
++ if ((!r0->expiration || expires_in > 0) &&
++ (!r0->session_timeout || session_timeout > 0)) {
++ wpa_printf(MSG_ERROR,
++ "FT: %s() called for non-expired entry %p",
++ __func__, r0);
++ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
++ if (r0->expiration && expires_in > 0)
++ eloop_register_timeout(expires_in + 1, 0,
++ wpa_ft_expire_pmk_r0, r0, NULL);
++ if (r0->session_timeout && session_timeout > 0)
++ eloop_register_timeout(session_timeout + 1, 0,
++ wpa_ft_expire_pmk_r0, r0, NULL);
++ return;
++ }
++
++ wpa_ft_free_pmk_r0(r0);
++}
++
++
++static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
++{
++ if (!r1)
++ return;
++
++ dl_list_del(&r1->list);
++ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
++
++ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
++ os_free(r1->vlan);
++ os_free(r1->identity);
++ os_free(r1->radius_cui);
++ os_free(r1);
++}
++
++
++static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
++
++ wpa_ft_free_pmk_r1(r1);
++}
++
++
+ struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
+ {
+ struct wpa_ft_pmk_cache *cache;
+
+ cache = os_zalloc(sizeof(*cache));
++ if (cache) {
++ dl_list_init(&cache->pmk_r0);
++ dl_list_init(&cache->pmk_r1);
++ }
+
+ return cache;
+ }
+@@ -181,21 +1304,13 @@
+ struct wpa_ft_pmk_r0_sa *r0, *r0prev;
+ struct wpa_ft_pmk_r1_sa *r1, *r1prev;
+
+- r0 = cache->pmk_r0;
+- while (r0) {
+- r0prev = r0;
+- r0 = r0->next;
+- os_memset(r0prev->pmk_r0, 0, PMK_LEN);
+- os_free(r0prev);
+- }
++ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
++ struct wpa_ft_pmk_r0_sa, list)
++ wpa_ft_free_pmk_r0(r0);
+
+- r1 = cache->pmk_r1;
+- while (r1) {
+- r1prev = r1;
+- r1 = r1->next;
+- os_memset(r1prev->pmk_r1, 0, PMK_LEN);
+- os_free(r1prev);
+- }
++ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
++ struct wpa_ft_pmk_r1_sa, list)
++ wpa_ft_free_pmk_r1(r1);
+
+ os_free(cache);
+ }
+@@ -203,24 +1318,63 @@
+
+ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r0,
+- const u8 *pmk_r0_name, int pairwise)
++ size_t pmk_r0_len,
++ const u8 *pmk_r0_name, int pairwise,
++ const struct vlan_description *vlan,
++ int expires_in, int session_timeout,
++ const u8 *identity, size_t identity_len,
++ const u8 *radius_cui, size_t radius_cui_len)
+ {
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0;
++ struct os_reltime now;
+
+- /* TODO: add expiration and limit on number of entries in cache */
++ /* TODO: add limit on number of entries in cache */
++ os_get_reltime(&now);
+
+ r0 = os_zalloc(sizeof(*r0));
+ if (r0 == NULL)
+ return -1;
+
+- os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
++ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
++ r0->pmk_r0_len = pmk_r0_len;
+ os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+ os_memcpy(r0->spa, spa, ETH_ALEN);
+ r0->pairwise = pairwise;
++ if (expires_in > 0)
++ r0->expiration = now.sec + expires_in;
++ if (vlan && vlan->notempty) {
++ r0->vlan = os_zalloc(sizeof(*vlan));
++ if (!r0->vlan) {
++ bin_clear_free(r0, sizeof(*r0));
++ return -1;
++ }
++ *r0->vlan = *vlan;
++ }
++ if (identity) {
++ r0->identity = os_malloc(identity_len);
++ if (r0->identity) {
++ os_memcpy(r0->identity, identity, identity_len);
++ r0->identity_len = identity_len;
++ }
++ }
++ if (radius_cui) {
++ r0->radius_cui = os_malloc(radius_cui_len);
++ if (r0->radius_cui) {
++ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
++ r0->radius_cui_len = radius_cui_len;
++ }
++ }
++ if (session_timeout > 0)
++ r0->session_timeout = now.sec + session_timeout;
+
+- r0->next = cache->pmk_r0;
+- cache->pmk_r0 = r0;
++ dl_list_add(&cache->pmk_r0, &r0->list);
++ if (expires_in > 0)
++ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
++ r0, NULL);
++ if (session_timeout > 0)
++ eloop_register_timeout(session_timeout + 1, 0,
++ wpa_ft_expire_pmk_r0, r0, NULL);
+
+ return 0;
+ }
+@@ -228,25 +1382,23 @@
+
+ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r0_name,
+- u8 *pmk_r0, int *pairwise)
++ const struct wpa_ft_pmk_r0_sa **r0_out)
+ {
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0;
++ struct os_reltime now;
+
+- r0 = cache->pmk_r0;
+- while (r0) {
++ os_get_reltime(&now);
++ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+ os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+ WPA_PMK_NAME_LEN) == 0) {
+- os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
+- if (pairwise)
+- *pairwise = r0->pairwise;
++ *r0_out = r0;
+ return 0;
+ }
+-
+- r0 = r0->next;
+ }
+
++ *r0_out = NULL;
+ return -1;
+ }
+
+@@ -253,25 +1405,67 @@
+
+ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r1,
+- const u8 *pmk_r1_name, int pairwise)
++ size_t pmk_r1_len,
++ const u8 *pmk_r1_name, int pairwise,
++ const struct vlan_description *vlan,
++ int expires_in, int session_timeout,
++ const u8 *identity, size_t identity_len,
++ const u8 *radius_cui, size_t radius_cui_len)
+ {
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
++ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
+ struct wpa_ft_pmk_r1_sa *r1;
++ struct os_reltime now;
+
+- /* TODO: add expiration and limit on number of entries in cache */
++ /* TODO: limit on number of entries in cache */
++ os_get_reltime(&now);
+
++ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
++ expires_in = max_expires_in;
++
+ r1 = os_zalloc(sizeof(*r1));
+ if (r1 == NULL)
+ return -1;
+
+- os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
++ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
++ r1->pmk_r1_len = pmk_r1_len;
+ os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(r1->spa, spa, ETH_ALEN);
+ r1->pairwise = pairwise;
++ if (vlan && vlan->notempty) {
++ r1->vlan = os_zalloc(sizeof(*vlan));
++ if (!r1->vlan) {
++ bin_clear_free(r1, sizeof(*r1));
++ return -1;
++ }
++ *r1->vlan = *vlan;
++ }
++ if (identity) {
++ r1->identity = os_malloc(identity_len);
++ if (r1->identity) {
++ os_memcpy(r1->identity, identity, identity_len);
++ r1->identity_len = identity_len;
++ }
++ }
++ if (radius_cui) {
++ r1->radius_cui = os_malloc(radius_cui_len);
++ if (r1->radius_cui) {
++ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
++ r1->radius_cui_len = radius_cui_len;
++ }
++ }
++ if (session_timeout > 0)
++ r1->session_timeout = now.sec + session_timeout;
+
+- r1->next = cache->pmk_r1;
+- cache->pmk_r1 = r1;
++ dl_list_add(&cache->pmk_r1, &r1->list);
+
++ if (expires_in > 0)
++ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
++ r1, NULL);
++ if (session_timeout > 0)
++ eloop_register_timeout(session_timeout + 1, 0,
++ wpa_ft_expire_pmk_r1, r1, NULL);
++
+ return 0;
+ }
+
+@@ -278,23 +1472,47 @@
+
+ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r1_name,
+- u8 *pmk_r1, int *pairwise)
++ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
++ struct vlan_description *vlan,
++ const u8 **identity, size_t *identity_len,
++ const u8 **radius_cui, size_t *radius_cui_len,
++ int *session_timeout)
+ {
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r1_sa *r1;
++ struct os_reltime now;
+
+- r1 = cache->pmk_r1;
+- while (r1) {
++ os_get_reltime(&now);
++
++ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
+ if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+ os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
+- os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
++ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
++ *pmk_r1_len = r1->pmk_r1_len;
+ if (pairwise)
+ *pairwise = r1->pairwise;
++ if (vlan && r1->vlan)
++ *vlan = *r1->vlan;
++ if (vlan && !r1->vlan)
++ os_memset(vlan, 0, sizeof(*vlan));
++ if (identity && identity_len) {
++ *identity = r1->identity;
++ *identity_len = r1->identity_len;
++ }
++ if (radius_cui && radius_cui_len) {
++ *radius_cui = r1->radius_cui;
++ *radius_cui_len = r1->radius_cui_len;
++ }
++ if (session_timeout && r1->session_timeout > now.sec)
++ *session_timeout = r1->session_timeout -
++ now.sec;
++ else if (session_timeout && r1->session_timeout)
++ *session_timeout = 1;
++ else if (session_timeout)
++ *session_timeout = 0;
+ return 0;
+ }
+-
+- r1 = r1->next;
+ }
+
+ return -1;
+@@ -301,20 +1519,463 @@
+ }
+
+
++static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
++{
++ if (r0kh->seq)
++ return 0;
++
++ r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
++ if (!r0kh->seq) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
++ return -1;
++ }
++
++ dl_list_init(&r0kh->seq->rx.queue);
++
++ return 0;
++}
++
++
++static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
++ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
++ struct ft_remote_r0kh **r0kh_out,
++ struct ft_remote_r0kh **r0kh_wildcard)
++{
++ struct ft_remote_r0kh *r0kh;
++
++ *r0kh_wildcard = NULL;
++ *r0kh_out = NULL;
++
++ if (wpa_auth->conf.r0kh_list)
++ r0kh = *wpa_auth->conf.r0kh_list;
++ else
++ r0kh = NULL;
++ for (; r0kh; r0kh = r0kh->next) {
++ if (r0kh->id_len == 1 && r0kh->id[0] == '*')
++ *r0kh_wildcard = r0kh;
++ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
++ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
++ *r0kh_out = r0kh;
++ }
++
++ if (!*r0kh_out && !*r0kh_wildcard)
++ wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
++
++ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
++ *r0kh_out = NULL;
++}
++
++
++static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
++{
++ if (r1kh->seq)
++ return 0;
++
++ r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
++ if (!r1kh->seq) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
++ return -1;
++ }
++
++ dl_list_init(&r1kh->seq->rx.queue);
++
++ return 0;
++}
++
++
++static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
++ const u8 *f_r1kh_id,
++ struct ft_remote_r1kh **r1kh_out,
++ struct ft_remote_r1kh **r1kh_wildcard)
++{
++ struct ft_remote_r1kh *r1kh;
++
++ *r1kh_wildcard = NULL;
++ *r1kh_out = NULL;
++
++ if (wpa_auth->conf.r1kh_list)
++ r1kh = *wpa_auth->conf.r1kh_list;
++ else
++ r1kh = NULL;
++ for (; r1kh; r1kh = r1kh->next) {
++ if (is_zero_ether_addr(r1kh->addr) &&
++ is_zero_ether_addr(r1kh->id))
++ *r1kh_wildcard = r1kh;
++ if (f_r1kh_id &&
++ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
++ *r1kh_out = r1kh;
++ }
++
++ if (!*r1kh_out && !*r1kh_wildcard)
++ wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
++
++ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
++ *r1kh_out = NULL;
++}
++
++
++static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
++ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
++{
++ if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
++ os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
++ f_r0kh_id_len) != 0)
++ return -1;
++
++ return 0;
++}
++
++
++static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
++ const u8 *f_r1kh_id)
++{
++ if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
++ FT_R1KH_ID_LEN) != 0)
++ return -1;
++
++ return 0;
++}
++
++
++static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_authenticator *wpa_auth = eloop_ctx;
++ struct ft_remote_r0kh *r0kh, *prev = NULL;
++
++ if (!wpa_auth->conf.r0kh_list)
++ return;
++
++ for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
++ if (r0kh == timeout_ctx)
++ break;
++ prev = r0kh;
++ }
++ if (!r0kh)
++ return;
++ if (prev)
++ prev->next = r0kh->next;
++ else
++ *wpa_auth->conf.r0kh_list = r0kh->next;
++ if (r0kh->seq)
++ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
++ os_free(r0kh->seq);
++ os_free(r0kh);
++}
++
++
++static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_r0kh *r0kh, int timeout)
++{
++ if (timeout > 0)
++ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
++ wpa_auth, r0kh);
++}
++
++
++static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_r0kh *r0kh, int timeout)
++{
++ eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
++
++ if (timeout > 0)
++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
++ wpa_auth, r0kh);
++}
++
++
++static struct ft_remote_r0kh *
++wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_r0kh *r0kh_wildcard,
++ const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
++ int timeout)
++{
++ struct ft_remote_r0kh *r0kh;
++
++ if (!wpa_auth->conf.r0kh_list)
++ return NULL;
++
++ r0kh = os_zalloc(sizeof(*r0kh));
++ if (!r0kh)
++ return NULL;
++
++ if (src_addr)
++ os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
++
++ if (id_len > FT_R0KH_ID_MAX_LEN)
++ id_len = FT_R0KH_ID_MAX_LEN;
++ os_memcpy(r0kh->id, r0kh_id, id_len);
++ r0kh->id_len = id_len;
++
++ os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
++
++ r0kh->next = *wpa_auth->conf.r0kh_list;
++ *wpa_auth->conf.r0kh_list = r0kh;
++
++ if (timeout > 0)
++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
++ wpa_auth, r0kh);
++
++ if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
++ return NULL;
++
++ return r0kh;
++}
++
++
++static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_authenticator *wpa_auth = eloop_ctx;
++ struct ft_remote_r1kh *r1kh, *prev = NULL;
++
++ if (!wpa_auth->conf.r1kh_list)
++ return;
++
++ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
++ if (r1kh == timeout_ctx)
++ break;
++ prev = r1kh;
++ }
++ if (!r1kh)
++ return;
++ if (prev)
++ prev->next = r1kh->next;
++ else
++ *wpa_auth->conf.r1kh_list = r1kh->next;
++ if (r1kh->seq)
++ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
++ os_free(r1kh->seq);
++ os_free(r1kh);
++}
++
++
++static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_r1kh *r1kh, int timeout)
++{
++ if (timeout > 0)
++ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
++ wpa_auth, r1kh);
++}
++
++
++static struct ft_remote_r1kh *
++wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
++ struct ft_remote_r1kh *r1kh_wildcard,
++ const u8 *src_addr, const u8 *r1kh_id, int timeout)
++{
++ struct ft_remote_r1kh *r1kh;
++
++ if (!wpa_auth->conf.r1kh_list)
++ return NULL;
++
++ r1kh = os_zalloc(sizeof(*r1kh));
++ if (!r1kh)
++ return NULL;
++
++ os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
++ os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
++ os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
++ r1kh->next = *wpa_auth->conf.r1kh_list;
++ *wpa_auth->conf.r1kh_list = r1kh;
++
++ if (timeout > 0)
++ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
++ wpa_auth, r1kh);
++
++ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
++ return NULL;
++
++ return r1kh;
++}
++
++
++void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
++{
++ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
++}
++
++
++static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
++{
++ struct ft_remote_r0kh *r0kh;
++ struct ft_remote_r1kh *r1kh;
++
++ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
++
++ if (wpa_auth->conf.r0kh_list)
++ r0kh = *wpa_auth->conf.r0kh_list;
++ else
++ r0kh = NULL;
++ for (; r0kh; r0kh = r0kh->next) {
++ if (!r0kh->seq)
++ continue;
++ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
++ os_free(r0kh->seq);
++ r0kh->seq = NULL;
++ }
++
++ if (wpa_auth->conf.r1kh_list)
++ r1kh = *wpa_auth->conf.r1kh_list;
++ else
++ r1kh = NULL;
++ for (; r1kh; r1kh = r1kh->next) {
++ if (!r1kh->seq)
++ continue;
++ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
++ os_free(r1kh->seq);
++ r1kh->seq = NULL;
++ }
++}
++
++
++static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
++{
++ struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
++ struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
++
++ if (wpa_auth->conf.r0kh_list)
++ r0kh = *wpa_auth->conf.r0kh_list;
++ else
++ r0kh = NULL;
++ while (r0kh) {
++ r0kh_next = r0kh->next;
++ if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
++ r0kh) > 0) {
++ if (r0kh_prev)
++ r0kh_prev->next = r0kh_next;
++ else
++ *wpa_auth->conf.r0kh_list = r0kh_next;
++ os_free(r0kh);
++ } else {
++ r0kh_prev = r0kh;
++ }
++ r0kh = r0kh_next;
++ }
++
++ if (wpa_auth->conf.r1kh_list)
++ r1kh = *wpa_auth->conf.r1kh_list;
++ else
++ r1kh = NULL;
++ while (r1kh) {
++ r1kh_next = r1kh->next;
++ if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
++ r1kh) > 0) {
++ if (r1kh_prev)
++ r1kh_prev->next = r1kh_next;
++ else
++ *wpa_auth->conf.r1kh_list = r1kh_next;
++ os_free(r1kh);
++ } else {
++ r1kh_prev = r1kh;
++ }
++ r1kh = r1kh_next;
++ }
++}
++
++
++void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
++{
++ wpa_ft_deinit_seq(wpa_auth);
++ wpa_ft_deinit_rkh_tmp(wpa_auth);
++}
++
++
++static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
++ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
++{
++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
++
++ if (!wpa_auth->conf.rkh_neg_timeout)
++ return;
++
++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
++ &r0kh, &r0kh_wildcard);
++
++ if (!r0kh_wildcard) {
++ /* r0kh removed after neg_timeout and might need re-adding */
++ return;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
++ f_r0kh_id, f_r0kh_id_len);
++
++ if (r0kh) {
++ wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
++ wpa_auth->conf.rkh_neg_timeout);
++ os_memset(r0kh->addr, 0, ETH_ALEN);
++ } else
++ wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
++ f_r0kh_id_len,
++ wpa_auth->conf.rkh_neg_timeout);
++}
++
++
++static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
++{
++ struct wpa_state_machine *sm = eloop_ctx;
++
++ wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
++ MAC2STR(sm->addr));
++ if (sm->ft_pending_pull_left_retries <= 0)
++ wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
++
++ /* cancel multiple timeouts */
++ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
++ ft_finish_pull(sm);
++}
++
++
+ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
+ {
+- struct ft_remote_r0kh *r0kh;
+- struct ft_r0kh_r1kh_pull_frame frame, f;
++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
++ u8 *packet = NULL;
++ const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
++ size_t packet_len, key_len;
++ struct ft_rrb_seq f_seq;
++ int tsecs, tusecs, first;
++ struct wpabuf *ft_pending_req_ies;
++ int r0kh_timeout;
++ struct tlv_list req_enc[] = {
++ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
++ .data = pmk_r0_name },
++ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
++ .data = sm->addr },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
++ struct tlv_list req_auth[] = {
++ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
++ .data = sm->ft_pending_pull_nonce },
++ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
++ .data = (u8 *) &f_seq },
++ { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
++ .data = sm->r0kh_id },
++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
++ .data = f_r1kh_id },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
+
+- r0kh = sm->wpa_auth->conf.r0kh_list;
+- while (r0kh) {
+- if (r0kh->id_len == sm->r0kh_id_len &&
+- os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+- 0)
+- break;
+- r0kh = r0kh->next;
++ if (sm->ft_pending_pull_left_retries <= 0)
++ return -1;
++ first = sm->ft_pending_pull_left_retries ==
++ sm->wpa_auth->conf.rkh_pull_retries;
++ sm->ft_pending_pull_left_retries--;
++
++ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
++ &r0kh, &r0kh_wildcard);
++
++ /* Keep r0kh sufficiently long in the list for seq num check */
++ r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
++ 1 + ftRRBseqTimeout;
++ if (r0kh) {
++ wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
++ } else if (r0kh_wildcard) {
++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
++ /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
++ r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
++ r0kh_wildcard->addr,
++ sm->r0kh_id, sm->r0kh_id_len,
++ r0kh_timeout);
+ }
+ if (r0kh == NULL) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+@@ -321,51 +1982,105 @@
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
++ if (is_zero_ether_addr(r0kh->addr)) {
++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
++ sm->r0kh_id, sm->r0kh_id_len);
++ return -1;
++ }
++ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "FT: R0KH-ID points to self - no matching key available");
++ return -1;
++ }
+
++ key = r0kh->key;
++ key_len = sizeof(r0kh->key);
++
+ wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
+ "address " MACSTR, MAC2STR(r0kh->addr));
+
+- os_memset(&frame, 0, sizeof(frame));
+- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+- frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
+- frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
+- os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
++ if (r0kh->seq->rx.num_last == 0) {
++ /* A sequence request will be sent out anyway when pull
++ * response is received. Send it out now to avoid one RTT. */
++ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
++ r0kh->id, r0kh->id_len, f_r1kh_id, key,
++ key_len, NULL, 0, NULL, 0, NULL);
++ }
+
+- /* aes_wrap() does not support inplace encryption, so use a temporary
+- * buffer for the data. */
+- if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
++ if (first &&
++ random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+ "nonce");
+ return -1;
+ }
+- os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+- FT_R0KH_R1KH_PULL_NONCE_LEN);
+- os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+- os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+- os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
+- os_memset(f.pad, 0, sizeof(f.pad));
+
+- if (aes_wrap(r0kh->key, sizeof(r0kh->key),
+- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+- f.nonce, frame.nonce) < 0)
++ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
++ }
+
++ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
++ sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
++ &packet, &packet_len) < 0)
++ return -1;
++
++ ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+ wpabuf_free(sm->ft_pending_req_ies);
+- sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+- if (sm->ft_pending_req_ies == NULL)
++ sm->ft_pending_req_ies = ft_pending_req_ies;
++ if (!sm->ft_pending_req_ies) {
++ os_free(packet);
+ return -1;
++ }
+
+- wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
++ tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
++ tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
++ eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
++ wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
++ packet, packet_len);
++
++ os_free(packet);
++
+ return 0;
+ }
+
+
+-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+- struct wpa_ptk *ptk)
++int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
++ const u8 *pmk_r0, const u8 *pmk_r0_name)
+ {
+- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+- u8 pmk_r1[PMK_LEN];
++ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
++ struct vlan_description vlan;
++ const u8 *identity, *radius_cui;
++ size_t identity_len, radius_cui_len;
++ int session_timeout;
++ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
++ SHA384_MAC_LEN : PMK_LEN;
++
++ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
++ MAC2STR(sm->addr));
++ return -1;
++ }
++
++ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
++ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
++ &radius_cui);
++ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
++
++ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
++ pmk_r0_name, sm->pairwise, &vlan, expires_in,
++ session_timeout, identity, identity_len,
++ radius_cui, radius_cui_len);
++}
++
++
++int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
++{
++ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
++ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
++ SHA384_MAC_LEN : PMK_LEN;
++ size_t pmk_r1_len = pmk_r0_len;
++ u8 pmk_r1[PMK_LEN_MAX];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
+@@ -373,6 +2088,12 @@
+ const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = sm->wpa_auth->conf.ssid;
+ size_t ssid_len = sm->wpa_auth->conf.ssid_len;
++ int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
++ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
++ struct vlan_description vlan;
++ const u8 *identity, *radius_cui;
++ size_t identity_len, radius_cui_len;
++ int session_timeout;
+
+ if (sm->xxkey_len == 0) {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+@@ -380,23 +2101,45 @@
+ return -1;
+ }
+
+- wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+- r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
++ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
++ MAC2STR(sm->addr));
++ return -1;
++ }
++
++ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
++ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
++ &radius_cui);
++ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
++
++ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
++ r0kh, r0kh_len, sm->addr,
++ pmk_r0, pmk_r0_name,
++ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+- wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+- sm->pairwise);
++ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
++ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
++ pmk_r0_name,
++ sm->pairwise, &vlan, expires_in,
++ session_timeout, identity, identity_len,
++ radius_cui, radius_cui_len);
+
+- wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+- pmk_r1, sm->pmk_r1_name);
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
++ if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
++ pmk_r1, sm->pmk_r1_name) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+- wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
+- sm->pairwise);
++ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
++ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
++ sm->pmk_r1_name, sm->pairwise, &vlan,
++ expires_in, session_timeout, identity,
++ identity_len, radius_cui, radius_cui_len);
+
+- return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+- sm->wpa_auth->addr, sm->pmk_r1_name,
++ return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
++ sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
+ ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
+ }
+
+@@ -404,9 +2147,9 @@
+ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+ {
+- if (wpa_auth->cb.get_seqnum == NULL)
++ if (wpa_auth->cb->get_seqnum == NULL)
+ return -1;
+- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
++ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+ }
+
+
+@@ -418,7 +2161,17 @@
+ const u8 *key;
+ size_t key_len;
+ u8 keybuf[32];
++ const u8 *kek;
++ size_t kek_len;
+
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ kek = sm->PTK.kek2;
++ kek_len = sm->PTK.kek2_len;
++ } else {
++ kek = sm->PTK.kek;
++ kek_len = sm->PTK.kek_len;
++ }
++
+ key_len = gsm->GTK_len;
+ if (key_len > sizeof(keybuf))
+ return NULL;
+@@ -456,8 +2209,10 @@
+ WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+ subelem[4] = gsm->GTK_len;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
+- subelem + 13)) {
++ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: GTK subelem encryption failed: kek_len=%d",
++ (int) kek_len);
+ os_free(subelem);
+ return NULL;
+ }
+@@ -473,10 +2228,23 @@
+ u8 *subelem, *pos;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len;
++ const u8 *kek;
++ size_t kek_len;
++ size_t igtk_len;
+
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ kek = sm->PTK.kek2;
++ kek_len = sm->PTK.kek2_len;
++ } else {
++ kek = sm->PTK.kek;
++ kek_len = sm->PTK.kek_len;
++ }
++
++ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
++
+ /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+ * Key[16+8] */
+- subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
++ subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+@@ -488,9 +2256,12 @@
+ pos += 2;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+ pos += 6;
+- *pos++ = WPA_IGTK_LEN;
+- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
++ *pos++ = igtk_len;
++ if (aes_wrap(kek, kek_len, igtk_len / 8,
+ gsm->IGTK[gsm->GN_igtk - 4], pos)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: IGTK subelem encryption failed: kek_len=%d",
++ (int) kek_len);
+ os_free(subelem);
+ return NULL;
+ }
+@@ -637,17 +2408,21 @@
+ const u8 *req_ies, size_t req_ies_len)
+ {
+ u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
++ u8 *fte_mic, *elem_count;
+ size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+ int res;
+ struct wpa_auth_config *conf;
+- struct rsn_ftie *_ftie;
+ struct wpa_ft_ies parse;
+ u8 *ric_start;
+ u8 *anonce, *snonce;
++ const u8 *kck;
++ size_t kck_len;
++ int use_sha384;
+
+ if (sm == NULL)
+ return pos;
+
++ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+ conf = &sm->wpa_auth->conf;
+
+ if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+@@ -655,14 +2430,28 @@
+
+ end = pos + max_len;
+
+- if (auth_alg == WLAN_AUTH_FT) {
++ if (auth_alg == WLAN_AUTH_FT ||
++ ((auth_alg == WLAN_AUTH_FILS_SK ||
++ auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ auth_alg == WLAN_AUTH_FILS_PK) &&
++ (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
++ WPA_KEY_MGMT_FT_FILS_SHA384)))) {
++ if (!sm->pmk_r1_name_valid) {
++ wpa_printf(MSG_ERROR,
++ "FT: PMKR1Name is not valid for Assoc Resp RSNE");
++ return NULL;
++ }
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
++ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ /*
+ * RSN (only present if this is a Reassociation Response and
+- * part of a fast BSS transition)
++ * part of a fast BSS transition; or if this is a
++ * (Re)Association Response frame during an FT initial mobility
++ * domain association using FILS)
+ */
+ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+ if (res < 0)
+- return pos;
++ return NULL;
+ rsnie = pos;
+ rsnie_len = res;
+ pos += res;
+@@ -671,7 +2460,7 @@
+ /* Mobility Domain Information */
+ res = wpa_write_mdie(conf, pos, end - pos);
+ if (res < 0)
+- return pos;
++ return NULL;
+ mdie = pos;
+ mdie_len = res;
+ pos += res;
+@@ -679,6 +2468,11 @@
+ /* Fast BSS Transition Information */
+ if (auth_alg == WLAN_AUTH_FT) {
+ subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
++ if (!subelem) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Failed to add GTK subelement");
++ return NULL;
++ }
+ r0kh_id = sm->r0kh_id;
+ r0kh_id_len = sm->r0kh_id_len;
+ anonce = sm->ANonce;
+@@ -690,14 +2484,16 @@
+ u8 *nbuf;
+ igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+ if (igtk == NULL) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Failed to add IGTK subelement");
+ os_free(subelem);
+- return pos;
++ return NULL;
+ }
+ nbuf = os_realloc(subelem, subelem_len + igtk_len);
+ if (nbuf == NULL) {
+ os_free(subelem);
+ os_free(igtk);
+- return pos;
++ return NULL;
+ }
+ subelem = nbuf;
+ os_memcpy(subelem + subelem_len, igtk, igtk_len);
+@@ -705,6 +2501,35 @@
+ os_free(igtk);
+ }
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sm)) {
++ struct wpa_channel_info ci;
++ u8 *nbuf, *ocipos;
++
++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info for OCI element");
++ os_free(subelem);
++ return NULL;
++ }
++
++ subelem_len += 2 + OCV_OCI_LEN;
++ nbuf = os_realloc(subelem, subelem_len);
++ if (!nbuf) {
++ os_free(subelem);
++ return NULL;
++ }
++ subelem = nbuf;
++
++ ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
++ *ocipos++ = FTIE_SUBELEM_OCI;
++ *ocipos++ = OCV_OCI_LEN;
++ if (ocv_insert_oci(&ci, &ocipos) < 0) {
++ os_free(subelem);
++ return NULL;
++ }
++ }
++#endif /* CONFIG_OCV */
+ } else {
+ r0kh_id = conf->r0_key_holder;
+ r0kh_id_len = conf->r0_key_holder_len;
+@@ -711,30 +2536,38 @@
+ anonce = NULL;
+ snonce = NULL;
+ }
+- res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
+- end - pos, subelem, subelem_len);
++ res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
++ anonce, snonce, pos, end - pos,
++ subelem, subelem_len);
+ os_free(subelem);
+ if (res < 0)
+- return pos;
++ return NULL;
+ ftie = pos;
+ ftie_len = res;
+ pos += res;
+
+- os_free(sm->assoc_resp_ftie);
+- sm->assoc_resp_ftie = os_malloc(ftie_len);
+- if (sm->assoc_resp_ftie)
+- os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
++ if (use_sha384) {
++ struct rsn_ftie_sha384 *_ftie =
++ (struct rsn_ftie_sha384 *) (ftie + 2);
+
+- _ftie = (struct rsn_ftie *) (ftie + 2);
++ fte_mic = _ftie->mic;
++ elem_count = &_ftie->mic_control[1];
++ } else {
++ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
++
++ fte_mic = _ftie->mic;
++ elem_count = &_ftie->mic_control[1];
++ }
+ if (auth_alg == WLAN_AUTH_FT)
+- _ftie->mic_control[1] = 3; /* Information element count */
++ *elem_count = 3; /* Information element count */
+
+ ric_start = pos;
+- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
++ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
++ && parse.ric) {
+ pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+ parse.ric_len);
+ if (auth_alg == WLAN_AUTH_FT)
+- _ftie->mic_control[1] +=
++ *elem_count +=
+ ieee802_11_ie_count(ric_start,
+ pos - ric_start);
+ }
+@@ -741,15 +2574,29 @@
+ if (ric_start == pos)
+ ric_start = NULL;
+
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ kck = sm->PTK.kck2;
++ kck_len = sm->PTK.kck2_len;
++ } else {
++ kck = sm->PTK.kck;
++ kck_len = sm->PTK.kck_len;
++ }
+ if (auth_alg == WLAN_AUTH_FT &&
+- wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+- sm->wpa_auth->addr, 6,
++ wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
+ mdie, mdie_len, ftie, ftie_len,
+ rsnie, rsnie_len,
+ ric_start, ric_start ? pos - ric_start : 0,
+- _ftie->mic) < 0)
++ fte_mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
++ return NULL;
++ }
+
++ os_free(sm->assoc_resp_ftie);
++ sm->assoc_resp_ftie = os_malloc(ftie_len);
++ if (!sm->assoc_resp_ftie)
++ return NULL;
++ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
++
+ return pos;
+ }
+
+@@ -759,10 +2606,10 @@
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len)
+ {
+- if (wpa_auth->cb.set_key == NULL)
++ if (wpa_auth->cb->set_key == NULL)
+ return -1;
+- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+- key, key_len);
++ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
++ key, key_len);
+ }
+
+
+@@ -804,13 +2651,209 @@
+ }
+
+
++/* Derive PMK-R1 from PSK, check all available PSK */
++static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
++ const u8 *req_pmk_r1_name,
++ u8 *out_pmk_r1, int *out_pairwise,
++ struct vlan_description *out_vlan,
++ const u8 **out_identity, size_t *out_identity_len,
++ const u8 **out_radius_cui,
++ size_t *out_radius_cui_len,
++ int *out_session_timeout)
++{
++ const u8 *pmk = NULL;
++ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
++ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
++ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
++ const u8 *mdid = wpa_auth->conf.mobility_domain;
++ const u8 *r0kh = sm->r0kh_id;
++ size_t r0kh_len = sm->r0kh_id_len;
++ const u8 *r1kh = wpa_auth->conf.r1_key_holder;
++ const u8 *ssid = wpa_auth->conf.ssid;
++ size_t ssid_len = wpa_auth->conf.ssid_len;
++ int pairwise;
++
++ pairwise = sm->pairwise;
++
++ for (;;) {
++ pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
++ pmk);
++ if (pmk == NULL)
++ break;
++
++ if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
++ r0kh_len, sm->addr,
++ pmk_r0, pmk_r0_name, 0) < 0 ||
++ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
++ sm->addr, pmk_r1, pmk_r1_name) < 0 ||
++ os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
++ WPA_PMK_NAME_LEN) != 0)
++ continue;
++
++ /* We found a PSK that matches the requested pmk_r1_name */
++ wpa_printf(MSG_DEBUG,
++ "FT: Found PSK to generate PMK-R1 locally");
++ os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
++ if (out_pairwise)
++ *out_pairwise = pairwise;
++ os_memcpy(sm->PMK, pmk, PMK_LEN);
++ sm->pmk_len = PMK_LEN;
++ if (out_vlan &&
++ wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
++ MACSTR, MAC2STR(sm->addr));
++ return -1;
++ }
++
++ if (out_identity && out_identity_len) {
++ *out_identity_len = wpa_ft_get_identity(
++ sm->wpa_auth, sm->addr, out_identity);
++ }
++
++ if (out_radius_cui && out_radius_cui_len) {
++ *out_radius_cui_len = wpa_ft_get_radius_cui(
++ sm->wpa_auth, sm->addr, out_radius_cui);
++ }
++
++ if (out_session_timeout) {
++ *out_session_timeout = wpa_ft_get_session_timeout(
++ sm->wpa_auth, sm->addr);
++ }
++
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "FT: Did not find PSK to generate PMK-R1 locally");
++ return -1;
++}
++
++
++/* Detect the configuration the station asked for.
++ * Required to detect FT-PSK and pairwise cipher.
++ */
++static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
++ struct wpa_ft_ies *parse)
++{
++ int key_mgmt, ciphers;
++
++ if (sm->wpa_key_mgmt)
++ return 0;
++
++ key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
++ if (!key_mgmt) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
++ MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
++ return -1;
++ }
++ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
++#ifdef CONFIG_SHA384
++ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
++#endif /* CONFIG_SHA384 */
++ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
++#ifdef CONFIG_FILS
++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
++#endif /* CONFIG_FILS */
++ ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
++ if (!ciphers) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
++ MACSTR,
++ parse->pairwise_cipher, MAC2STR(sm->addr));
++ return -1;
++ }
++ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
++
++ return 0;
++}
++
++
++static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
++ struct wpa_state_machine *sm,
++ const u8 *r0kh_id, size_t r0kh_id_len,
++ const u8 *req_pmk_r0_name,
++ const u8 *req_pmk_r1_name,
++ u8 *out_pmk_r1, int *out_pairwise,
++ struct vlan_description *vlan,
++ const u8 **identity, size_t *identity_len,
++ const u8 **radius_cui,
++ size_t *radius_cui_len,
++ int *out_session_timeout)
++{
++ struct wpa_auth_config *conf = &wpa_auth->conf;
++ const struct wpa_ft_pmk_r0_sa *r0;
++ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
++ int expires_in = 0;
++ int session_timeout = 0;
++ struct os_reltime now;
++
++ if (conf->r0_key_holder_len != r0kh_id_len ||
++ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
++ 0)
++ return -1; /* not our R0KH-ID */
++
++ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
++ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
++ 0)
++ return -1; /* no matching PMKR0Name in local cache */
++
++ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
++
++ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
++ conf->r1_key_holder,
++ sm->addr, out_pmk_r1, pmk_r1_name) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
++
++ os_get_reltime(&now);
++ if (r0->expiration)
++ expires_in = r0->expiration - now.sec;
++
++ if (r0->session_timeout)
++ session_timeout = r0->session_timeout - now.sec;
++
++ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
++ pmk_r1_name,
++ sm->pairwise, r0->vlan, expires_in, session_timeout,
++ r0->identity, r0->identity_len,
++ r0->radius_cui, r0->radius_cui_len);
++
++ *out_pairwise = sm->pairwise;
++ if (vlan) {
++ if (r0->vlan)
++ *vlan = *r0->vlan;
++ else
++ os_memset(vlan, 0, sizeof(*vlan));
++ }
++
++ if (identity && identity_len) {
++ *identity = r0->identity;
++ *identity_len = r0->identity_len;
++ }
++
++ if (radius_cui && radius_cui_len) {
++ *radius_cui = r0->radius_cui;
++ *radius_cui_len = r0->radius_cui_len;
++ }
++
++ *out_session_timeout = session_timeout;
++
++ return 0;
++}
++
++
+ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ u8 **resp_ies, size_t *resp_ies_len)
+ {
+ struct rsn_mdie *mdie;
+- struct rsn_ftie *ftie;
+- u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
++ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ struct wpa_auth_config *conf;
+ struct wpa_ft_ies parse;
+@@ -817,7 +2860,12 @@
+ size_t buflen;
+ int ret;
+ u8 *pos, *end;
+- int pairwise;
++ int pairwise, session_timeout = 0;
++ struct vlan_description vlan;
++ const u8 *identity, *radius_cui;
++ size_t identity_len = 0, radius_cui_len = 0;
++ int use_sha384;
++ size_t pmk_r1_len;
+
+ *resp_ies = NULL;
+ *resp_ies_len = 0;
+@@ -828,10 +2876,12 @@
+ wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+ ies, ies_len);
+
+- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
++ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
++ use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
++ pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+@@ -842,14 +2892,28 @@
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+- ftie = (struct rsn_ftie *) parse.ftie;
+- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+- return WLAN_STATUS_INVALID_FTIE;
++ if (use_sha384) {
++ struct rsn_ftie_sha384 *ftie;
++
++ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
++ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
++ return WLAN_STATUS_INVALID_FTIE;
++ }
++
++ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
++ } else {
++ struct rsn_ftie *ftie;
++
++ ftie = (struct rsn_ftie *) parse.ftie;
++ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
++ return WLAN_STATUS_INVALID_FTIE;
++ }
++
++ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
+
+- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+-
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+ return WLAN_STATUS_INVALID_FTIE;
+@@ -865,28 +2929,62 @@
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
++ if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++
+ wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+ parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+- wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+- sm->wpa_auth->conf.r1_key_holder, sm->addr,
+- pmk_r1_name);
++ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
++ sm->wpa_auth->conf.r1_key_holder, sm->addr,
++ pmk_r1_name, use_sha384) < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
+ pmk_r1_name, WPA_PMK_NAME_LEN);
+
+- if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
+- &pairwise) < 0) {
++ if (conf->ft_psk_generate_local &&
++ wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
++ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
++ &vlan, &identity, &identity_len,
++ &radius_cui, &radius_cui_len,
++ &session_timeout) < 0)
++ return WLAN_STATUS_INVALID_PMKID;
++ wpa_printf(MSG_DEBUG,
++ "FT: Generated PMK-R1 for FT-PSK locally");
++ } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
++ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
++ &identity, &identity_len, &radius_cui,
++ &radius_cui_len, &session_timeout) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
++ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
++ parse.r0kh_id, parse.r0kh_id_len,
++ parse.rsn_pmkid,
++ pmk_r1_name, pmk_r1, &pairwise,
++ &vlan, &identity, &identity_len,
++ &radius_cui, &radius_cui_len,
++ &session_timeout) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Generated PMK-R1 based on local PMK-R0");
++ goto pmk_r1_derived;
++ }
++
+ if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+- wpa_printf(MSG_DEBUG, "FT: Did not have matching "
+- "PMK-R1 and unknown R0KH-ID");
++ wpa_printf(MSG_DEBUG,
++ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ return -1; /* Status pending */
++ } else {
++ wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
+ }
+
+- wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
++pmk_r1_derived:
++ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
+ sm->pmk_r1_name_valid = 1;
+ os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
++ os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
++ sm->pmk_r1_len = pmk_r1_len;
+
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+@@ -899,8 +2997,8 @@
+ wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
+ sm->ANonce, WPA_NONCE_LEN);
+
+- if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+- sm->wpa_auth->addr, pmk_r1_name,
++ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
++ sm->addr, sm->wpa_auth->addr, pmk_r1_name,
+ &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+ pairwise) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+@@ -910,44 +3008,51 @@
+ sm->tk_already_set = FALSE;
+ wpa_ft_install_ptk(sm);
+
++ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
++ identity, identity_len) < 0 ||
++ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
++ radius_cui, radius_cui_len) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
++
+ buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + FT_R1KH_ID_LEN + 200;
+ *resp_ies = os_zalloc(buflen);
+- if (*resp_ies == NULL) {
+- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+- }
++ if (*resp_ies == NULL)
++ goto fail;
+
+ pos = *resp_ies;
+ end = *resp_ies + buflen;
+
+ ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+- if (ret < 0) {
+- os_free(*resp_ies);
+- *resp_ies = NULL;
+- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+- }
++ if (ret < 0)
++ goto fail;
+ pos += ret;
+
+ ret = wpa_write_mdie(conf, pos, end - pos);
+- if (ret < 0) {
+- os_free(*resp_ies);
+- *resp_ies = NULL;
+- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+- }
++ if (ret < 0)
++ goto fail;
+ pos += ret;
+
+- ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
++ ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
+ sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
+- if (ret < 0) {
+- os_free(*resp_ies);
+- *resp_ies = NULL;
+- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+- }
++ if (ret < 0)
++ goto fail;
+ pos += ret;
+
+ *resp_ies_len = pos - *resp_ies;
+
+ return WLAN_STATUS_SUCCESS;
++fail:
++ os_free(*resp_ies);
++ *resp_ies = NULL;
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+
+@@ -975,6 +3080,7 @@
+ sm->ft_pending_cb = cb;
+ sm->ft_pending_cb_ctx = ctx;
+ sm->ft_pending_auth_transaction = auth_transaction;
++ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+ res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+@@ -998,17 +3104,23 @@
+ {
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+- struct rsn_ftie *ftie;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = 16;
+ unsigned int count;
++ const u8 *kck;
++ size_t kck_len;
++ int use_sha384;
++ const u8 *anonce, *snonce, *fte_mic;
++ u8 fte_elem_count;
+
+ if (sm == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
++ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
++
+ wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
++ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+@@ -1039,34 +3151,56 @@
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+- ftie = (struct rsn_ftie *) parse.ftie;
+- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+- return WLAN_STATUS_INVALID_FTIE;
++ if (use_sha384) {
++ struct rsn_ftie_sha384 *ftie;
++
++ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
++ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
++ return WLAN_STATUS_INVALID_FTIE;
++ }
++
++ anonce = ftie->anonce;
++ snonce = ftie->snonce;
++ fte_elem_count = ftie->mic_control[1];
++ fte_mic = ftie->mic;
++ } else {
++ struct rsn_ftie *ftie;
++
++ ftie = (struct rsn_ftie *) parse.ftie;
++ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
++ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
++ return WLAN_STATUS_INVALID_FTIE;
++ }
++
++ anonce = ftie->anonce;
++ snonce = ftie->snonce;
++ fte_elem_count = ftie->mic_control[1];
++ fte_mic = ftie->mic;
+ }
+
+- if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
++ if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+- ftie->snonce, WPA_NONCE_LEN);
++ snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->SNonce, WPA_NONCE_LEN);
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+- if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
++ if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+- ftie->anonce, WPA_NONCE_LEN);
++ anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sm->ANonce, WPA_NONCE_LEN);
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+@@ -1078,12 +3212,12 @@
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+@@ -1094,7 +3228,7 @@
+ parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+ sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+- return -1;
++ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+@@ -1102,21 +3236,27 @@
+ {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+ "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+- return -1;
++ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ count = 3;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+- if (ftie->mic_control[1] != count) {
++ if (fte_elem_count != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+ "Control: received %u expected %u",
+- ftie->mic_control[1], count);
+- return -1;
++ fte_elem_count, count);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+- if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+- sm->wpa_auth->addr, 5,
++ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
++ kck = sm->PTK.kck2;
++ kck_len = sm->PTK.kck2_len;
++ } else {
++ kck = sm->PTK.kck;
++ kck_len = sm->PTK.kck_len;
++ }
++ if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+@@ -1126,12 +3266,12 @@
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+- if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
++ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+ MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+- ftie->mic, mic_len);
++ fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+ parse.mdie - 2, parse.mdie_len + 2);
+@@ -1142,6 +3282,32 @@
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
++#ifdef CONFIG_OCV
++ if (wpa_auth_uses_ocv(sm)) {
++ struct wpa_channel_info ci;
++ int tx_chanwidth;
++ int tx_seg1_idx;
++
++ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
++ wpa_printf(MSG_WARNING,
++ "Failed to get channel info to validate received OCI in (Re)Assoc Request");
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++
++ if (get_sta_tx_parameters(sm,
++ channel_width_to_int(ci.chanwidth),
++ ci.seg1_idx, &tx_chanwidth,
++ &tx_seg1_idx) < 0)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++
++ if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
++ tx_chanwidth, tx_seg1_idx) != 0) {
++ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ }
++ }
++#endif /* CONFIG_OCV */
++
+ return WLAN_STATUS_SUCCESS;
+ }
+
+@@ -1199,6 +3365,11 @@
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
++ if (!sm->wpa_auth->conf.ft_over_ds) {
++ wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
++ return -1;
++ }
++
+ /* RRB - Forward action frame to the target AP */
+ frame = os_malloc(sizeof(*frame) + len);
+ if (frame == NULL)
+@@ -1251,6 +3422,7 @@
+ sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+ sm->ft_pending_cb_ctx = sm;
+ os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
++ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+ res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+@@ -1316,112 +3488,431 @@
+ }
+
+
++static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
++ const struct tlv_list *tlvs,
++ const struct wpa_ft_pmk_r0_sa *pmk_r0,
++ const u8 *r1kh_id, const u8 *s1kh_id,
++ const struct tlv_list *tlv_auth,
++ const u8 *src_addr, u8 type,
++ u8 **packet, size_t *packet_len)
++{
++ u8 pmk_r1[PMK_LEN_MAX];
++ size_t pmk_r1_len = pmk_r0->pmk_r0_len;
++ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
++ u8 f_pairwise[sizeof(le16)];
++ u8 f_expires_in[sizeof(le16)];
++ u8 f_session_timeout[sizeof(le32)];
++ int expires_in;
++ int session_timeout;
++ struct os_reltime now;
++ int ret;
++ struct tlv_list sess_tlv[] = {
++ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
++ .data = pmk_r1 },
++ { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
++ .data = pmk_r1_name },
++ { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
++ .data = f_pairwise },
++ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
++ .data = f_expires_in },
++ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
++ .data = pmk_r0->identity },
++ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
++ .data = pmk_r0->radius_cui },
++ { .type = FT_RRB_SESSION_TIMEOUT,
++ .len = sizeof(f_session_timeout),
++ .data = f_session_timeout },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
++
++ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
++ pmk_r0->pmk_r0_name, r1kh_id,
++ s1kh_id, pmk_r1, pmk_r1_name) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
++ pmk_r1, pmk_r1_len);
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
++ pmk_r1_name, WPA_PMK_NAME_LEN);
++ WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
++
++ os_get_reltime(&now);
++ if (pmk_r0->expiration > now.sec)
++ expires_in = pmk_r0->expiration - now.sec;
++ else if (pmk_r0->expiration)
++ expires_in = 1;
++ else
++ expires_in = 0;
++ WPA_PUT_LE16(f_expires_in, expires_in);
++
++ if (pmk_r0->session_timeout > now.sec)
++ session_timeout = pmk_r0->session_timeout - now.sec;
++ else if (pmk_r0->session_timeout)
++ session_timeout = 1;
++ else
++ session_timeout = 0;
++ WPA_PUT_LE32(f_session_timeout, session_timeout);
++
++ ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
++ pmk_r0->vlan, src_addr, type,
++ packet, packet_len);
++
++ os_memset(pmk_r1, 0, sizeof(pmk_r1));
++
++ return ret;
++}
++
++
+ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+- const u8 *data, size_t data_len)
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer)
+ {
+- struct ft_r0kh_r1kh_pull_frame f;
+- const u8 *crypt;
+- u8 *plain;
+- struct ft_remote_r1kh *r1kh;
+- struct ft_r0kh_r1kh_resp_frame resp, r;
+- u8 pmk_r0[PMK_LEN];
+- int pairwise;
++ const char *msgtype = "pull request";
++ u8 *plain = NULL, *packet = NULL;
++ size_t plain_len = 0, packet_len = 0;
++ struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
++ const u8 *key;
++ size_t key_len;
++ int seq_ret;
++ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
++ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
++ size_t f_pmk_r0_name_len;
++ const struct wpa_ft_pmk_r0_sa *r0;
++ int ret;
++ struct tlv_list resp[2];
++ struct tlv_list resp_auth[5];
++ struct ft_rrb_seq f_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+- if (data_len < sizeof(f))
+- return -1;
++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+- r1kh = wpa_auth->conf.r1kh_list;
+- while (r1kh) {
+- if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
+- break;
+- r1kh = r1kh->next;
++ if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
++ wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
++ goto out;
+ }
+- if (r1kh == NULL) {
+- wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
+- "PMK-R1 pull source address " MACSTR,
+- MAC2STR(src_addr));
+- return -1;
++
++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
++
++ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
++ if (r1kh) {
++ key = r1kh->key;
++ key_len = sizeof(r1kh->key);
++ } else if (r1kh_wildcard) {
++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
++ key = r1kh_wildcard->key;
++ key_len = sizeof(r1kh_wildcard->key);
++ } else {
++ goto out;
+ }
+
+- crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+- os_memset(&f, 0, sizeof(f));
+- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+- /* aes_unwrap() does not support inplace decryption, so use a temporary
+- * buffer for the data. */
+- if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+- crypt, plain) < 0) {
+- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+- "request from " MACSTR, MAC2STR(src_addr));
+- return -1;
++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
++
++ seq_ret = FT_RRB_SEQ_DROP;
++ if (r1kh)
++ seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
++ auth, auth_len, msgtype, no_defer);
++ if (!no_defer && r1kh_wildcard &&
++ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
++ /* wildcard: r1kh-id unknown or changed addr -> do a seq req */
++ seq_ret = FT_RRB_SEQ_DEFER;
+ }
+
+- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+- f.nonce, sizeof(f.nonce));
+- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
+- f.pmk_r0_name, WPA_PMK_NAME_LEN);
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
+- MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
++ if (seq_ret == FT_RRB_SEQ_DROP)
++ goto out;
+
+- os_memset(&resp, 0, sizeof(resp));
+- resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+- resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
+- resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
+- os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
++ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
++ src_addr, FT_PACKET_R0KH_R1KH_PULL,
++ &plain, &plain_len) < 0)
++ goto out;
+
+- /* aes_wrap() does not support inplace encryption, so use a temporary
+- * buffer for the data. */
+- os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
+- os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
+- os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
+- if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
+- &pairwise) < 0) {
+- wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
+- "PMK-R1 pull");
+- return -1;
++ if (!r1kh)
++ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
++ f_r1kh_id,
++ wpa_auth->conf.rkh_pos_timeout);
++ if (!r1kh)
++ goto out;
++
++ if (seq_ret == FT_RRB_SEQ_DEFER) {
++ wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
++ f_r0kh_id_len, f_r1kh_id, key, key_len,
++ enc, enc_len, auth, auth_len,
++ &wpa_ft_rrb_rx_pull);
++ goto out;
+ }
+
+- wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
+- r.pmk_r1, r.pmk_r1_name);
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
+- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
+- WPA_PMK_NAME_LEN);
+- r.pairwise = host_to_le16(pairwise);
+- os_memset(r.pad, 0, sizeof(r.pad));
++ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
++ msgtype);
++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
++ wpa_auth->conf.rkh_pos_timeout);
+
+- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+- r.nonce, resp.nonce) < 0) {
+- os_memset(pmk_r0, 0, PMK_LEN);
+- return -1;
++ RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
++ f_pmk_r0_name_len);
++
++ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
++ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
++
++ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
++ goto out;
+ }
+
+- os_memset(pmk_r0, 0, PMK_LEN);
++ resp[0].type = FT_RRB_S1KH_ID;
++ resp[0].len = f_s1kh_id_len;
++ resp[0].data = f_s1kh_id;
++ resp[1].type = FT_RRB_LAST_EMPTY;
++ resp[1].len = 0;
++ resp[1].data = NULL;
+
+- wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
++ resp_auth[0].type = FT_RRB_NONCE;
++ resp_auth[0].len = f_nonce_len;
++ resp_auth[0].data = f_nonce;
++ resp_auth[1].type = FT_RRB_SEQ;
++ resp_auth[1].len = sizeof(f_seq);
++ resp_auth[1].data = (u8 *) &f_seq;
++ resp_auth[2].type = FT_RRB_R0KH_ID;
++ resp_auth[2].len = f_r0kh_id_len;
++ resp_auth[2].data = f_r0kh_id;
++ resp_auth[3].type = FT_RRB_R1KH_ID;
++ resp_auth[3].len = f_r1kh_id_len;
++ resp_auth[3].data = f_r1kh_id;
++ resp_auth[4].type = FT_RRB_LAST_EMPTY;
++ resp_auth[4].len = 0;
++ resp_auth[4].data = NULL;
+
++ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
++ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
++ NULL, wpa_auth->addr,
++ FT_PACKET_R0KH_R1KH_RESP,
++ &packet, &packet_len);
++ } else {
++ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
++ f_s1kh_id, resp_auth, wpa_auth->addr,
++ FT_PACKET_R0KH_R1KH_RESP,
++ &packet, &packet_len);
++ }
++
++ if (!ret)
++ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
++ FT_PACKET_R0KH_R1KH_RESP, packet,
++ packet_len);
++
++out:
++ os_free(plain);
++ os_free(packet);
++
+ return 0;
+ }
+
+
+-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
++/* @returns 0 on success
++ * -1 on error
++ * -2 if FR_RRB_PAIRWISE is missing
++ */
++static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr, u8 type,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ const char *msgtype, u8 *s1kh_id_out,
++ int (*cb)(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer))
+ {
+- struct wpa_state_machine *sm = eloop_ctx;
++ u8 *plain = NULL;
++ size_t plain_len = 0;
++ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
++ const u8 *key;
++ size_t key_len;
++ int seq_ret;
++ const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
++ const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
++ const u8 *f_expires_in;
++ size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
++ const u8 *f_identity, *f_radius_cui;
++ const u8 *f_session_timeout;
++ size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
++ size_t f_expires_in_len;
++ size_t f_identity_len, f_radius_cui_len;
++ size_t f_session_timeout_len;
++ int pairwise;
++ int ret = -1;
++ int expires_in;
++ int session_timeout;
++ struct vlan_description vlan;
++ size_t pmk_r1_len;
++
++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
++ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
++
++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
++
++ if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
++ wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
++ goto out;
++ }
++
++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
++ &r0kh_wildcard);
++ if (r0kh) {
++ key = r0kh->key;
++ key_len = sizeof(r0kh->key);
++ } else if (r0kh_wildcard) {
++ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
++ key = r0kh_wildcard->key;
++ key_len = sizeof(r0kh_wildcard->key);
++ } else {
++ goto out;
++ }
++
++ seq_ret = FT_RRB_SEQ_DROP;
++ if (r0kh) {
++ seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
++ auth, auth_len, msgtype,
++ cb ? 0 : 1);
++ }
++ if (cb && r0kh_wildcard &&
++ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
++ /* wildcard: r0kh-id unknown or changed addr -> do a seq req */
++ seq_ret = FT_RRB_SEQ_DEFER;
++ }
++
++ if (seq_ret == FT_RRB_SEQ_DROP)
++ goto out;
++
++ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
++ src_addr, type, &plain, &plain_len) < 0)
++ goto out;
++
++ if (!r0kh)
++ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
++ f_r0kh_id, f_r0kh_id_len,
++ wpa_auth->conf.rkh_pos_timeout);
++ if (!r0kh)
++ goto out;
++
++ if (seq_ret == FT_RRB_SEQ_DEFER) {
++ wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
++ f_r0kh_id_len, f_r1kh_id, key, key_len,
++ enc, enc_len, auth, auth_len, cb);
++ goto out;
++ }
++
++ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
++ msgtype);
++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
++ wpa_auth->conf.rkh_pos_timeout);
++
++ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
++ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
++
++ if (s1kh_id_out)
++ os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
++
++ ret = -2;
++ RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
++ wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
++
++ ret = -1;
++ RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
++ f_pmk_r1_name, WPA_PMK_NAME_LEN);
++
++ pmk_r1_len = PMK_LEN;
++ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
++ &f_pmk_r1) == 0 &&
++ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
++ pmk_r1_len = f_pmk_r1_len;
++ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
++
++ pairwise = WPA_GET_LE16(f_pairwise);
++
++ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
++ sizeof(le16));
++ if (f_expires_in)
++ expires_in = WPA_GET_LE16(f_expires_in);
++ else
++ expires_in = 0;
++
++ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
++ expires_in);
++
++ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
++ wpa_ft_rrb_dump(plain, plain_len);
++ goto out;
++ }
++
++ wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
++ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
++
++ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
++ if (f_identity)
++ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
++ f_identity_len);
++
++ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
++ if (f_radius_cui)
++ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
++ f_radius_cui_len);
++
++ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
++ sizeof(le32));
++ if (f_session_timeout)
++ session_timeout = WPA_GET_LE32(f_session_timeout);
++ else
++ session_timeout = 0;
++ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
++
++ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
++ f_pmk_r1_name,
++ pairwise, &vlan, expires_in, session_timeout,
++ f_identity, f_identity_len, f_radius_cui,
++ f_radius_cui_len) < 0)
++ goto out;
++
++ ret = 0;
++out:
++ if (plain) {
++ os_memset(plain, 0, plain_len);
++ os_free(plain);
++ }
++
++ return ret;
++
++}
++
++
++static void ft_finish_pull(struct wpa_state_machine *sm)
++{
+ int res;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ u16 status;
+
++ if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
++ return;
++
+ res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+ wpabuf_len(sm->ft_pending_req_ies),
+ &resp_ies, &resp_ies_len);
++ if (res < 0) {
++ /* this loop is broken by ft_pending_pull_left_retries */
++ wpa_printf(MSG_DEBUG,
++ "FT: Callback postponed until response is available");
++ return;
++ }
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = NULL;
+- if (res < 0)
+- res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ status = res;
+ wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+ " - status %u", MAC2STR(sm->addr), status);
+@@ -1433,21 +3924,26 @@
+ }
+
+
+-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
++struct ft_get_sta_ctx {
++ const u8 *nonce;
++ const u8 *s1kh_id;
++ struct wpa_state_machine *sm;
++};
++
++
++static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
+ {
+- struct ft_r0kh_r1kh_resp_frame *frame = ctx;
++ struct ft_get_sta_ctx *info = ctx;
+
+- if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
++ if ((info->s1kh_id &&
++ os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
++ os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
++ FT_RRB_NONCE_LEN) != 0 ||
++ sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ return 0;
+- if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+- FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+- return 0;
+- if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+- return 0;
+
+- wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+- MACSTR " - process from timeout", MAC2STR(sm->addr));
+- eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
++ info->sm = sm;
++
+ return 1;
+ }
+
+@@ -1454,153 +3950,360 @@
+
+ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+- const u8 *data, size_t data_len)
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer)
+ {
+- struct ft_r0kh_r1kh_resp_frame f;
+- const u8 *crypt;
+- u8 *plain;
+- struct ft_remote_r0kh *r0kh;
+- int pairwise, res;
++ const char *msgtype = "pull response";
++ int nak, ret = -1;
++ struct ft_get_sta_ctx ctx;
++ u8 s1kh_id[ETH_ALEN];
++ const u8 *f_nonce;
++ size_t f_nonce_len;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+- if (data_len < sizeof(f))
+- return -1;
++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+- r0kh = wpa_auth->conf.r0kh_list;
+- while (r0kh) {
+- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+- break;
+- r0kh = r0kh->next;
+- }
+- if (r0kh == NULL) {
+- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+- "PMK-R0 pull response source address " MACSTR,
+- MAC2STR(src_addr));
++ os_memset(&ctx, 0, sizeof(ctx));
++ ctx.nonce = f_nonce;
++ if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
++ /* nonce not found */
++ wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
+ return -1;
+ }
+
+- crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+- os_memset(&f, 0, sizeof(f));
+- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+- /* aes_unwrap() does not support inplace decryption, so use a temporary
+- * buffer for the data. */
+- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+- crypt, plain) < 0) {
+- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+- "response from " MACSTR, MAC2STR(src_addr));
++ ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
++ enc, enc_len, auth, auth_len, msgtype, s1kh_id,
++ no_defer ? NULL : &wpa_ft_rrb_rx_resp);
++ if (ret == -2) {
++ ret = 0;
++ nak = 1;
++ } else {
++ nak = 0;
++ }
++ if (ret < 0)
+ return -1;
+- }
+
+- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+- FT_R1KH_ID_LEN) != 0) {
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
+- "matching R1KH-ID");
+- return -1;
++ ctx.s1kh_id = s1kh_id;
++ if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Response to a pending pull request for " MACSTR,
++ MAC2STR(ctx.sm->addr));
++ eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
++ if (nak)
++ ctx.sm->ft_pending_pull_left_retries = 0;
++ ft_finish_pull(ctx.sm);
+ }
+
+- pairwise = le_to_host16(f.pairwise);
+- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+- f.nonce, sizeof(f.nonce));
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
+- MACSTR " pairwise=0x%x",
+- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
+- f.pmk_r1, PMK_LEN);
+- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
+- f.pmk_r1_name, WPA_PMK_NAME_LEN);
+-
+- res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+- pairwise);
+- wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+- wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
+- os_memset(f.pmk_r1, 0, PMK_LEN);
+-
+- return res ? 0 : -1;
++out:
++ return ret;
+ }
+
+
+ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+- const u8 *data, size_t data_len)
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len, int no_defer)
+ {
+- struct ft_r0kh_r1kh_push_frame f;
+- const u8 *crypt;
+- u8 *plain;
+- struct ft_remote_r0kh *r0kh;
+- struct os_time now;
+- os_time_t tsend;
+- int pairwise;
++ const char *msgtype = "push";
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+- if (data_len < sizeof(f))
++ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
++ enc, enc_len, auth, auth_len, msgtype, NULL,
++ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
+ return -1;
+
+- r0kh = wpa_auth->conf.r0kh_list;
+- while (r0kh) {
+- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+- break;
+- r0kh = r0kh->next;
++ return 0;
++}
++
++
++static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr, int type,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ struct ft_remote_seq **rkh_seq,
++ u8 **key, size_t *key_len,
++ struct ft_remote_r0kh **r0kh_out,
++ struct ft_remote_r1kh **r1kh_out,
++ struct ft_remote_r0kh **r0kh_wildcard_out,
++ struct ft_remote_r1kh **r1kh_wildcard_out)
++{
++ struct ft_remote_r0kh *r0kh = NULL;
++ struct ft_remote_r1kh *r1kh = NULL;
++ const u8 *f_r0kh_id, *f_r1kh_id;
++ size_t f_r0kh_id_len, f_r1kh_id_len;
++ int to_r0kh, to_r1kh;
++ u8 *plain = NULL;
++ size_t plain_len = 0;
++ struct ft_remote_r0kh *r0kh_wildcard;
++ struct ft_remote_r1kh *r1kh_wildcard;
++
++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
++
++ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
++ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
++
++ if (to_r0kh && to_r1kh) {
++ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
++ goto out;
+ }
+- if (r0kh == NULL) {
+- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+- "PMK-R0 push source address " MACSTR,
+- MAC2STR(src_addr));
+- return -1;
++
++ if (!to_r0kh && !to_r1kh) {
++ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
++ goto out;
+ }
+
+- crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+- os_memset(&f, 0, sizeof(f));
+- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+- timestamp);
+- /* aes_unwrap() does not support inplace decryption, so use a temporary
+- * buffer for the data. */
+- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+- crypt, plain) < 0) {
+- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
+- MACSTR, MAC2STR(src_addr));
+- return -1;
++ if (!to_r0kh) {
++ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
++ &r0kh, &r0kh_wildcard);
++ if (!r0kh_wildcard &&
++ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
++ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
++ f_r0kh_id, f_r0kh_id_len);
++ goto out;
++ }
++ if (r0kh) {
++ *key = r0kh->key;
++ *key_len = sizeof(r0kh->key);
++ } else {
++ *key = r0kh_wildcard->key;
++ *key_len = sizeof(r0kh_wildcard->key);
++ }
+ }
+
+- os_get_time(&now);
+- tsend = WPA_GET_LE32(f.timestamp);
+- if ((now.sec > tsend && now.sec - tsend > 60) ||
+- (now.sec < tsend && tsend - now.sec > 60)) {
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
+- "timestamp: sender time %d own time %d\n",
+- (int) tsend, (int) now.sec);
+- return -1;
++ if (!to_r1kh) {
++ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
++ &r1kh_wildcard);
++ if (!r1kh_wildcard &&
++ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
++ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
++ f_r1kh_id, FT_R1KH_ID_LEN);
++ goto out;
++ }
++ if (r1kh) {
++ *key = r1kh->key;
++ *key_len = sizeof(r1kh->key);
++ } else {
++ *key = r1kh_wildcard->key;
++ *key_len = sizeof(r1kh_wildcard->key);
++ }
+ }
+
+- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+- FT_R1KH_ID_LEN) != 0) {
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
+- "R1KH-ID (received " MACSTR " own " MACSTR ")",
+- MAC2STR(f.r1kh_id),
+- MAC2STR(wpa_auth->conf.r1_key_holder));
+- return -1;
++ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
++ src_addr, type, &plain, &plain_len) < 0)
++ goto out;
++
++ os_free(plain);
++
++ if (!to_r0kh) {
++ if (!r0kh)
++ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
++ src_addr, f_r0kh_id,
++ f_r0kh_id_len,
++ ftRRBseqTimeout);
++ if (!r0kh)
++ goto out;
++
++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
++ *rkh_seq = r0kh->seq;
++ if (r0kh_out)
++ *r0kh_out = r0kh;
++ if (r0kh_wildcard_out)
++ *r0kh_wildcard_out = r0kh_wildcard;
+ }
+
+- pairwise = le_to_host16(f.pairwise);
+- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
+- MACSTR " pairwise=0x%x",
+- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
+- f.pmk_r1, PMK_LEN);
+- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
+- f.pmk_r1_name, WPA_PMK_NAME_LEN);
++ if (!to_r1kh) {
++ if (!r1kh)
++ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
++ src_addr, f_r1kh_id,
++ ftRRBseqTimeout);
++ if (!r1kh)
++ goto out;
+
+- wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+- pairwise);
+- os_memset(f.pmk_r1, 0, PMK_LEN);
++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
++ *rkh_seq = r1kh->seq;
++ if (r1kh_out)
++ *r1kh_out = r1kh;
++ if (r1kh_wildcard_out)
++ *r1kh_wildcard_out = r1kh_wildcard;
++ }
+
+ return 0;
++out:
++ return -1;
+ }
+
+
++static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer)
++{
++ int ret = -1;
++ struct ft_rrb_seq f_seq;
++ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
++ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
++ struct ft_remote_seq *rkh_seq = NULL;
++ u8 *packet = NULL, *key = NULL;
++ size_t packet_len = 0, key_len = 0;
++ struct tlv_list seq_resp_auth[5];
++
++ wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
++
++ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
++ enc, enc_len, auth, auth_len, &rkh_seq, &key,
++ &key_len, NULL, NULL, NULL, NULL) < 0)
++ goto out;
++
++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
++
++ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
++ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
++
++ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
++ goto out;
++ }
++
++ seq_resp_auth[0].type = FT_RRB_NONCE;
++ seq_resp_auth[0].len = f_nonce_len;
++ seq_resp_auth[0].data = f_nonce;
++ seq_resp_auth[1].type = FT_RRB_SEQ;
++ seq_resp_auth[1].len = sizeof(f_seq);
++ seq_resp_auth[1].data = (u8 *) &f_seq;
++ seq_resp_auth[2].type = FT_RRB_R0KH_ID;
++ seq_resp_auth[2].len = f_r0kh_id_len;
++ seq_resp_auth[2].data = f_r0kh_id;
++ seq_resp_auth[3].type = FT_RRB_R1KH_ID;
++ seq_resp_auth[3].len = FT_R1KH_ID_LEN;
++ seq_resp_auth[3].data = f_r1kh_id;
++ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
++ seq_resp_auth[4].len = 0;
++ seq_resp_auth[4].data = NULL;
++
++ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
++ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
++ &packet, &packet_len) < 0)
++ goto out;
++
++ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
++ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
++ packet_len);
++
++out:
++ os_free(packet);
++
++ return ret;
++}
++
++
++static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
++ const u8 *src_addr,
++ const u8 *enc, size_t enc_len,
++ const u8 *auth, size_t auth_len,
++ int no_defer)
++{
++ u8 *key = NULL;
++ size_t key_len = 0;
++ struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
++ struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
++ const u8 *f_nonce, *f_seq;
++ size_t f_nonce_len, f_seq_len;
++ struct ft_remote_seq *rkh_seq = NULL;
++ struct ft_remote_item *item;
++ struct os_reltime now, now_remote;
++ int seq_ret, found;
++ const struct ft_rrb_seq *msg_both;
++ u32 msg_dom, msg_seq;
++
++ wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
++
++ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
++ enc, enc_len, auth, auth_len, &rkh_seq, &key,
++ &key_len, &r0kh, &r1kh, &r0kh_wildcard,
++ &r1kh_wildcard) < 0)
++ goto out;
++
++ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
++ f_nonce_len);
++
++ found = 0;
++ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
++ list) {
++ if (os_memcmp_const(f_nonce, item->nonce,
++ FT_RRB_NONCE_LEN) != 0 ||
++ os_get_reltime(&now) < 0 ||
++ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
++ continue;
++
++ found = 1;
++ break;
++ }
++ if (!found) {
++ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
++ goto out;
++ }
++
++ if (r0kh) {
++ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
++ wpa_auth->conf.rkh_pos_timeout);
++ if (r0kh_wildcard)
++ os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
++ }
++
++ if (r1kh) {
++ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
++ wpa_auth->conf.rkh_pos_timeout);
++ if (r1kh_wildcard)
++ os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
++ }
++
++ seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
++ auth_len, "seq response", 1);
++ if (seq_ret == FT_RRB_SEQ_OK) {
++ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
++ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
++ auth_len, "seq response");
++ } else {
++ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
++
++ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
++ sizeof(*msg_both));
++ msg_both = (const struct ft_rrb_seq *) f_seq;
++
++ msg_dom = le_to_host32(msg_both->dom);
++ msg_seq = le_to_host32(msg_both->seq);
++ now_remote.sec = le_to_host32(msg_both->ts);
++ now_remote.usec = 0;
++
++ rkh_seq->rx.num_last = 2;
++ rkh_seq->rx.dom = msg_dom;
++ rkh_seq->rx.offsetidx = 0;
++ /* Accept some older, possibly cached packets as well */
++ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
++ dl_list_len(&rkh_seq->rx.queue);
++ rkh_seq->rx.last[1] = msg_seq;
++
++ /* local time - offset = remote time
++ * <=> local time - remote time = offset */
++ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
++ }
++
++ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
++
++ return 0;
++out:
++ return -1;
++}
++
++
+ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *data, size_t data_len)
+ {
+@@ -1642,13 +4345,6 @@
+ return -1;
+ }
+
+- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
+- return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
+- if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
+- return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
+- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
+- return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
+-
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+ if (alen < 1 + 1 + 2 * ETH_ALEN) {
+@@ -1726,65 +4422,140 @@
+ }
+
+
+-static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+- struct wpa_ft_pmk_r0_sa *pmk_r0,
+- struct ft_remote_r1kh *r1kh,
+- const u8 *s1kh_id, int pairwise)
++void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
++ size_t data_len)
+ {
+- struct ft_r0kh_r1kh_push_frame frame, f;
+- struct os_time now;
+- const u8 *plain;
+- u8 *crypt;
++ const u8 *auth, *enc;
++ size_t alen, elen;
++ int no_defer = 0;
+
+- os_memset(&frame, 0, sizeof(frame));
+- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+- frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
+- frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
+- os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
++ MACSTR, MAC2STR(src_addr));
++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
++ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
+
+- /* aes_wrap() does not support inplace encryption, so use a temporary
+- * buffer for the data. */
+- os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
+- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+- os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
+- wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
+- s1kh_id, f.pmk_r1, f.pmk_r1_name);
+- wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
+- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
+- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
+- WPA_PMK_NAME_LEN);
+- os_get_time(&now);
+- WPA_PUT_LE32(f.timestamp, now.sec);
+- f.pairwise = host_to_le16(pairwise);
+- os_memset(f.pad, 0, sizeof(f.pad));
+- plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+- timestamp);
+- crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+- timestamp);
+- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+- plain, crypt) < 0)
++ if (is_multicast_ether_addr(src_addr)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB-OUI received frame from multicast address "
++ MACSTR, MAC2STR(src_addr));
+ return;
++ }
+
+- wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
++ if (is_multicast_ether_addr(dst_addr)) {
++ wpa_printf(MSG_DEBUG,
++ "FT: RRB-OUI received frame from remote AP " MACSTR
++ " to multicast address " MACSTR,
++ MAC2STR(src_addr), MAC2STR(dst_addr));
++ no_defer = 1;
++ }
++
++ if (data_len < sizeof(u16)) {
++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
++ return;
++ }
++
++ alen = WPA_GET_LE16(data);
++ if (data_len < sizeof(u16) + alen) {
++ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
++ return;
++ }
++
++ auth = data + sizeof(u16);
++ wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
++ enc = data + sizeof(u16) + alen;
++ elen = data_len - sizeof(u16) - alen;
++ wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
++
++ switch (oui_suffix) {
++ case FT_PACKET_R0KH_R1KH_PULL:
++ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
++ no_defer);
++ break;
++ case FT_PACKET_R0KH_R1KH_RESP:
++ wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
++ no_defer);
++ break;
++ case FT_PACKET_R0KH_R1KH_PUSH:
++ wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
++ no_defer);
++ break;
++ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
++ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
++ no_defer);
++ break;
++ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
++ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
++ alen, no_defer);
++ break;
++ }
+ }
+
+
++static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
++ struct wpa_ft_pmk_r0_sa *pmk_r0,
++ struct ft_remote_r1kh *r1kh,
++ const u8 *s1kh_id)
++{
++ u8 *packet;
++ size_t packet_len;
++ struct ft_rrb_seq f_seq;
++ struct tlv_list push[] = {
++ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
++ .data = s1kh_id },
++ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
++ .data = pmk_r0->pmk_r0_name },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
++ struct tlv_list push_auth[] = {
++ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
++ .data = (u8 *) &f_seq },
++ { .type = FT_RRB_R0KH_ID,
++ .len = wpa_auth->conf.r0_key_holder_len,
++ .data = wpa_auth->conf.r0_key_holder },
++ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
++ .data = r1kh->id },
++ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
++ };
++
++ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
++ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
++ return -1;
++ }
++
++ if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
++ r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
++ FT_PACKET_R0KH_R1KH_PUSH,
++ &packet, &packet_len) < 0)
++ return -1;
++
++ wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
++ packet, packet_len);
++
++ os_free(packet);
++ return 0;
++}
++
++
+ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+ {
+- struct wpa_ft_pmk_r0_sa *r0;
++ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
++ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.pmk_r1_push)
+ return;
++ if (!wpa_auth->conf.r1kh_list)
++ return;
+
+- r0 = wpa_auth->ft_pmk_cache->pmk_r0;
+- while (r0) {
+- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
++ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
++ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
++ r0found = r0;
+ break;
+- r0 = r0->next;
++ }
+ }
+
++ r0 = r0found;
+ if (r0 == NULL || r0->pmk_r1_pushed)
+ return;
+ r0->pmk_r1_pushed = 1;
+@@ -1792,11 +4563,14 @@
+ wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+ "for STA " MACSTR, MAC2STR(addr));
+
+- r1kh = wpa_auth->conf.r1kh_list;
+- while (r1kh) {
+- wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
+- r1kh = r1kh->next;
++ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
++ if (is_zero_ether_addr(r1kh->addr) ||
++ is_zero_ether_addr(r1kh->id))
++ continue;
++ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
++ continue;
++ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
+ }
+ }
+
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+--- contrib/wpa/src/ap/wpa_auth_glue.c.orig
++++ contrib/wpa/src/ap/wpa_auth_glue.c
+@@ -9,13 +9,17 @@
+ #include "utils/includes.h"
+
+ #include "utils/common.h"
++#include "utils/eloop.h"
++#include "utils/list.h"
+ #include "common/ieee802_11_defs.h"
+ #include "common/sae.h"
+ #include "common/wpa_ctrl.h"
++#include "crypto/sha1.h"
+ #include "eapol_auth/eapol_auth_sm.h"
+ #include "eapol_auth/eapol_auth_sm_i.h"
+ #include "eap_server/eap.h"
+ #include "l2_packet/l2_packet.h"
++#include "eth_p_oui.h"
+ #include "hostapd.h"
+ #include "ieee802_1x.h"
+ #include "preauth_auth.h"
+@@ -23,6 +27,8 @@
+ #include "tkip_countermeasures.h"
+ #include "ap_drv_ops.h"
+ #include "ap_config.h"
++#include "ieee802_11.h"
++#include "pmksa_cache_auth.h"
+ #include "wpa_auth.h"
+ #include "wpa_auth_glue.h"
+
+@@ -40,19 +46,26 @@
+ wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+ wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+ wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
++ wconf->wpa_group_update_count = conf->wpa_group_update_count;
++ wconf->wpa_disable_eapol_key_retries =
++ conf->wpa_disable_eapol_key_retries;
++ wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
+ wconf->rsn_pairwise = conf->rsn_pairwise;
+ wconf->rsn_preauth = conf->rsn_preauth;
+ wconf->eapol_version = conf->eapol_version;
+- wconf->peerkey = conf->peerkey;
+ wconf->wmm_enabled = conf->wmm_enabled;
+ wconf->wmm_uapsd = conf->wmm_uapsd;
+ wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
++#ifdef CONFIG_OCV
++ wconf->ocv = conf->ocv;
++#endif /* CONFIG_OCV */
+ wconf->okc = conf->okc;
+ #ifdef CONFIG_IEEE80211W
+ wconf->ieee80211w = conf->ieee80211w;
+ wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
++ wconf->sae_require_mfp = conf->sae_require_mfp;
+ #endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ wconf->ssid_len = conf->ssid.ssid_len;
+ if (wconf->ssid_len > SSID_MAX_LEN)
+ wconf->ssid_len = SSID_MAX_LEN;
+@@ -67,12 +80,18 @@
+ }
+ os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+ wconf->r0_key_lifetime = conf->r0_key_lifetime;
++ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
+ wconf->reassociation_deadline = conf->reassociation_deadline;
+- wconf->r0kh_list = conf->r0kh_list;
+- wconf->r1kh_list = conf->r1kh_list;
++ wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
++ wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
++ wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
++ wconf->rkh_pull_retries = conf->rkh_pull_retries;
++ wconf->r0kh_list = &conf->r0kh_list;
++ wconf->r1kh_list = &conf->r1kh_list;
+ wconf->pmk_r1_push = conf->pmk_r1_push;
+ wconf->ft_over_ds = conf->ft_over_ds;
+-#endif /* CONFIG_IEEE80211R */
++ wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_HS20
+ wconf->disable_gtk = conf->disable_dgaf;
+ if (conf->osen) {
+@@ -106,6 +125,11 @@
+ os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+ os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+ #endif /* CONFIG_P2P */
++#ifdef CONFIG_FILS
++ wconf->fils_cache_id_set = conf->fils_cache_id_set;
++ os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
++ FILS_CACHE_ID_LEN);
++#endif /* CONFIG_FILS */
+ }
+
+
+@@ -222,12 +246,18 @@
+
+ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr,
+- const u8 *prev_psk)
++ const u8 *prev_psk, size_t *psk_len,
++ int *vlan_id)
+ {
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const u8 *psk;
+
++ if (vlan_id)
++ *vlan_id = 0;
++ if (psk_len)
++ *psk_len = PMK_LEN;
++
+ #ifdef CONFIG_SAE
+ if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+ if (!sta->sae || prev_psk)
+@@ -234,9 +264,34 @@
+ return NULL;
+ return sta->sae->pmk;
+ }
++ if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
++ wpa_printf(MSG_DEBUG,
++ "No PSK for STA trying to use SAE with PMKSA caching");
++ return NULL;
++ }
+ #endif /* CONFIG_SAE */
+
+- psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
++#ifdef CONFIG_OWE
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
++ sta && sta->owe_pmk) {
++ if (psk_len)
++ *psk_len = sta->owe_pmk_len;
++ return sta->owe_pmk;
++ }
++ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
++ struct rsn_pmksa_cache_entry *sa;
++
++ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
++ if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
++ if (psk_len)
++ *psk_len = sa->pmk_len;
++ return sa->pmk;
++ }
++ }
++#endif /* CONFIG_OWE */
++
++ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
++ vlan_id);
+ /*
+ * This is about to iterate over all psks, prev_psk gives the last
+ * returned psk which should not be returned again.
+@@ -244,8 +299,18 @@
+ */
+ if (sta && sta->psk && !psk) {
+ struct hostapd_sta_wpa_psk_short *pos;
++
++ if (vlan_id)
++ *vlan_id = 0;
+ psk = sta->psk->psk;
+ for (pos = sta->psk; pos; pos = pos->next) {
++ if (pos->is_passphrase) {
++ pbkdf2_sha1(pos->passphrase,
++ hapd->conf->ssid.ssid,
++ hapd->conf->ssid.ssid_len, 4096,
++ pos->psk, PMK_LEN);
++ pos->is_passphrase = 0;
++ }
+ if (pos->psk == prev_psk) {
+ psk = pos->next ? pos->next->psk : NULL;
+ break;
+@@ -299,6 +364,37 @@
+ return -1;
+ }
+
++#ifdef CONFIG_TESTING_OPTIONS
++ if (addr && !is_broadcast_ether_addr(addr)) {
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, addr);
++ if (sta) {
++ sta->last_tk_alg = alg;
++ sta->last_tk_key_idx = idx;
++ if (key)
++ os_memcpy(sta->last_tk, key, key_len);
++ sta->last_tk_len = key_len;
++ }
++#ifdef CONFIG_IEEE80211W
++ } else if (alg == WPA_ALG_IGTK ||
++ alg == WPA_ALG_BIP_GMAC_128 ||
++ alg == WPA_ALG_BIP_GMAC_256 ||
++ alg == WPA_ALG_BIP_CMAC_256) {
++ hapd->last_igtk_alg = alg;
++ hapd->last_igtk_key_idx = idx;
++ if (key)
++ os_memcpy(hapd->last_igtk, key, key_len);
++ hapd->last_igtk_len = key_len;
++#endif /* CONFIG_IEEE80211W */
++ } else {
++ hapd->last_gtk_alg = alg;
++ hapd->last_gtk_key_idx = idx;
++ if (key)
++ os_memcpy(hapd->last_gtk, key, key_len);
++ hapd->last_gtk_len = key_len;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
+ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+ key, key_len);
+ }
+@@ -393,8 +489,33 @@
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+
++struct wpa_ft_rrb_rx_later_data {
++ struct dl_list list;
++ u8 addr[ETH_ALEN];
++ size_t data_len;
++ /* followed by data_len octets of data */
++};
++
++static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct wpa_ft_rrb_rx_later_data *data, *n;
++
++ dl_list_for_each_safe(data, n, &hapd->l2_queue,
++ struct wpa_ft_rrb_rx_later_data, list) {
++ if (hapd->wpa_auth) {
++ wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
++ (const u8 *) (data + 1),
++ data->data_len);
++ }
++ dl_list_del(&data->list);
++ os_free(data);
++ }
++}
++
++
+ struct wpa_auth_ft_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst;
+@@ -406,31 +527,54 @@
+ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+ {
+ struct wpa_auth_ft_iface_iter_data *idata = ctx;
++ struct wpa_ft_rrb_rx_later_data *data;
+ struct hostapd_data *hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+- if (hapd == idata->src_hapd)
++ if (hapd == idata->src_hapd ||
++ !hapd->wpa_auth ||
++ os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+ continue;
+- if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
+- wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
+- "locally managed BSS " MACSTR "@%s -> "
+- MACSTR "@%s",
+- MAC2STR(idata->src_hapd->own_addr),
+- idata->src_hapd->conf->iface,
+- MAC2STR(hapd->own_addr), hapd->conf->iface);
+- wpa_ft_rrb_rx(hapd->wpa_auth,
+- idata->src_hapd->own_addr,
+- idata->data, idata->data_len);
++
++ wpa_printf(MSG_DEBUG,
++ "FT: Send RRB data directly to locally managed BSS "
++ MACSTR "@%s -> " MACSTR "@%s",
++ MAC2STR(idata->src_hapd->own_addr),
++ idata->src_hapd->conf->iface,
++ MAC2STR(hapd->own_addr), hapd->conf->iface);
++
++ /* Defer wpa_ft_rrb_rx() until next eloop step as this is
++ * when it would be triggered when reading from a socket.
++ * This avoids
++ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
++ * that is calling hapd0:recv handler from within
++ * hapd0:send directly.
++ */
++ data = os_zalloc(sizeof(*data) + idata->data_len);
++ if (!data)
+ return 1;
+- }
++
++ os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
++ os_memcpy(data + 1, idata->data, idata->data_len);
++ data->data_len = idata->data_len;
++
++ dl_list_add(&hapd->l2_queue, &data->list);
++
++ if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
++ hapd, NULL))
++ eloop_register_timeout(0, 0,
++ hostapd_wpa_ft_rrb_rx_later,
++ hapd, NULL);
++
++ return 1;
+ }
+
+ return 0;
+ }
+
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+
+ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
+@@ -455,7 +599,7 @@
+ }
+ #endif /* CONFIG_TESTING_OPTIONS */
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
+ int res;
+@@ -470,7 +614,7 @@
+ if (res == 1)
+ return data_len;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ if (hapd->driver && hapd->driver->send_ether)
+ return hapd->driver->send_ether(hapd->drv_priv, dst,
+@@ -493,8 +637,226 @@
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_ETH_P_OUI
++static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
++ u8 oui_suffix)
++{
++ switch (oui_suffix) {
++#ifdef CONFIG_IEEE80211R_AP
++ case FT_PACKET_R0KH_R1KH_PULL:
++ return hapd->oui_pull;
++ case FT_PACKET_R0KH_R1KH_RESP:
++ return hapd->oui_resp;
++ case FT_PACKET_R0KH_R1KH_PUSH:
++ return hapd->oui_push;
++ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
++ return hapd->oui_sreq;
++ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
++ return hapd->oui_sresp;
++#endif /* CONFIG_IEEE80211R_AP */
++ default:
++ return NULL;
++ }
++}
++#endif /* CONFIG_ETH_P_OUI */
+
++
++#ifdef CONFIG_IEEE80211R_AP
++
++struct oui_deliver_later_data {
++ struct dl_list list;
++ u8 src_addr[ETH_ALEN];
++ u8 dst_addr[ETH_ALEN];
++ size_t data_len;
++ u8 oui_suffix;
++ /* followed by data_len octets of data */
++};
++
++static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
++{
++ struct hostapd_data *hapd = eloop_ctx;
++ struct oui_deliver_later_data *data, *n;
++ struct eth_p_oui_ctx *oui_ctx;
++
++ dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
++ struct oui_deliver_later_data, list) {
++ oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
++ if (hapd->wpa_auth && oui_ctx) {
++ eth_p_oui_deliver(oui_ctx, data->src_addr,
++ data->dst_addr,
++ (const u8 *) (data + 1),
++ data->data_len);
++ }
++ dl_list_del(&data->list);
++ os_free(data);
++ }
++}
++
++
++struct wpa_auth_oui_iface_iter_data {
++ struct hostapd_data *src_hapd;
++ const u8 *dst_addr;
++ const u8 *data;
++ size_t data_len;
++ u8 oui_suffix;
++};
++
++static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
++{
++ struct wpa_auth_oui_iface_iter_data *idata = ctx;
++ struct oui_deliver_later_data *data;
++ struct hostapd_data *hapd;
++ size_t j;
++
++ for (j = 0; j < iface->num_bss; j++) {
++ hapd = iface->bss[j];
++ if (hapd == idata->src_hapd)
++ continue;
++ if (!is_multicast_ether_addr(idata->dst_addr) &&
++ os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
++ continue;
++
++ /* defer eth_p_oui_deliver until next eloop step as this is
++ * when it would be triggerd from reading from sock
++ * This avoids
++ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
++ * that is calling hapd0:recv handler from within
++ * hapd0:send directly.
++ */
++ data = os_zalloc(sizeof(*data) + idata->data_len);
++ if (!data)
++ return 1;
++
++ os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN);
++ os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
++ os_memcpy(data + 1, idata->data, idata->data_len);
++ data->data_len = idata->data_len;
++ data->oui_suffix = idata->oui_suffix;
++
++ dl_list_add(&hapd->l2_oui_queue, &data->list);
++
++ if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
++ hapd, NULL))
++ eloop_register_timeout(0, 0,
++ hostapd_oui_deliver_later,
++ hapd, NULL);
++
++ return 1;
++ }
++
++ return 0;
++}
++
++#endif /* CONFIG_IEEE80211R_AP */
++
++
++static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
++ const u8 *data, size_t data_len)
++{
++#ifdef CONFIG_ETH_P_OUI
++ struct hostapd_data *hapd = ctx;
++ struct eth_p_oui_ctx *oui_ctx;
++
++#ifdef CONFIG_IEEE80211R_AP
++ if (hapd->iface->interfaces &&
++ hapd->iface->interfaces->for_each_interface) {
++ struct wpa_auth_oui_iface_iter_data idata;
++ int res;
++
++ idata.src_hapd = hapd;
++ idata.dst_addr = dst;
++ idata.data = data;
++ idata.data_len = data_len;
++ idata.oui_suffix = oui_suffix;
++ res = hapd->iface->interfaces->for_each_interface(
++ hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
++ &idata);
++ if (res == 1)
++ return data_len;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++
++ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
++ if (!oui_ctx)
++ return -1;
++
++ return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
++#else /* CONFIG_ETH_P_OUI */
++ return -1;
++#endif /* CONFIG_ETH_P_OUI */
++}
++
++
++static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
++{
++ struct hostapd_data *hapd = ctx;
++
++ return hostapd_drv_channel_info(hapd, ci);
++}
++
++
++static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
++{
++#ifndef CONFIG_NO_VLAN
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ struct vlan_description vlan_desc;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta)
++ return -1;
++
++ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
++ vlan_desc.notempty = 1;
++ vlan_desc.untagged = vlan_id;
++ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
++ wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file",
++ vlan_id);
++ return -1;
++ }
++
++ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
++ wpa_printf(MSG_INFO,
++ "Failed to assign VLAN ID %d from wpa_psk_file to "
++ MACSTR, vlan_id, MAC2STR(sta->addr));
++ return -1;
++ }
++
++ wpa_printf(MSG_INFO,
++ "Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
++ vlan_id, MAC2STR(sta->addr));
++ if ((sta->flags & WLAN_STA_ASSOC) &&
++ ap_sta_bind_vlan(hapd, sta) < 0)
++ return -1;
++#endif /* CONFIG_NO_VLAN */
++
++ return 0;
++}
++
++
++#ifdef CONFIG_OCV
++static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
++ int ap_max_chanwidth, int ap_seg1_idx,
++ int *bandwidth, int *seg1_idx)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, addr);
++ if (!sta) {
++ hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO,
++ "Failed to get STA info to validate received OCI");
++ return -1;
++ }
++
++ return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth,
++ seg1_idx);
++}
++#endif /* CONFIG_OCV */
++
++
++#ifdef CONFIG_IEEE80211R_AP
++
+ static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
+ const u8 *data, size_t data_len)
+ {
+@@ -531,6 +893,9 @@
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
++ wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
++ " based on WPA authenticator callback",
++ MAC2STR(sta_addr));
+ if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+ return NULL;
+
+@@ -537,6 +902,9 @@
+ sta = ap_sta_add(hapd, sta_addr);
+ if (sta == NULL)
+ return NULL;
++ if (hapd->driver && hapd->driver->add_sta_node)
++ sta->added_unassoc = 1;
++ sta->ft_over_ds = 1;
+ if (sta->wpa_sm) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ return sta->wpa_sm;
+@@ -553,6 +921,244 @@
+ }
+
+
++static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
++ struct vlan_description *vlan)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta || !sta->wpa_sm)
++ return -1;
++
++ if (vlan->notempty &&
++ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO,
++ "Invalid VLAN %d%s received from FT",
++ vlan->untagged, vlan->tagged[0] ? "+" : "");
++ return -1;
++ }
++
++ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
++ return -1;
++ /* Configure wpa_group for GTK but ignore error due to driver not
++ * knowing this STA. */
++ ap_sta_bind_vlan(hapd, sta);
++
++ if (sta->vlan_id)
++ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
++ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
++
++ return 0;
++}
++
++
++static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
++ struct vlan_description *vlan)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return -1;
++
++ if (sta->vlan_desc)
++ *vlan = *sta->vlan_desc;
++ else
++ os_memset(vlan, 0, sizeof(*vlan));
++
++ return 0;
++}
++
++
++static int
++hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
++ const u8 *identity, size_t identity_len)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return -1;
++
++ os_free(sta->identity);
++ sta->identity = NULL;
++
++ if (sta->eapol_sm) {
++ os_free(sta->eapol_sm->identity);
++ sta->eapol_sm->identity = NULL;
++ sta->eapol_sm->identity_len = 0;
++ }
++
++ if (!identity_len)
++ return 0;
++
++ /* sta->identity is NULL terminated */
++ sta->identity = os_zalloc(identity_len + 1);
++ if (!sta->identity)
++ return -1;
++ os_memcpy(sta->identity, identity, identity_len);
++
++ if (sta->eapol_sm) {
++ sta->eapol_sm->identity = os_zalloc(identity_len);
++ if (!sta->eapol_sm->identity)
++ return -1;
++ os_memcpy(sta->eapol_sm->identity, identity, identity_len);
++ sta->eapol_sm->identity_len = identity_len;
++ }
++
++ return 0;
++}
++
++
++static size_t
++hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ size_t len;
++ char *identity;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return 0;
++
++ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
++ if (*buf && len)
++ return len;
++
++ if (!sta->identity) {
++ *buf = NULL;
++ return 0;
++ }
++
++ identity = sta->identity;
++ len = os_strlen(identity);
++ *buf = (u8 *) identity;
++
++ return len;
++}
++
++
++static int
++hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
++ const u8 *radius_cui, size_t radius_cui_len)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return -1;
++
++ os_free(sta->radius_cui);
++ sta->radius_cui = NULL;
++
++ if (sta->eapol_sm) {
++ wpabuf_free(sta->eapol_sm->radius_cui);
++ sta->eapol_sm->radius_cui = NULL;
++ }
++
++ if (!radius_cui)
++ return 0;
++
++ /* sta->radius_cui is NULL terminated */
++ sta->radius_cui = os_zalloc(radius_cui_len + 1);
++ if (!sta->radius_cui)
++ return -1;
++ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
++
++ if (sta->eapol_sm) {
++ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
++ radius_cui_len);
++ if (!sta->eapol_sm->radius_cui)
++ return -1;
++ }
++
++ return 0;
++}
++
++
++static size_t
++hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ struct wpabuf *b;
++ size_t len;
++ char *radius_cui;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return 0;
++
++ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
++ if (b) {
++ len = wpabuf_len(b);
++ *buf = wpabuf_head(b);
++ return len;
++ }
++
++ if (!sta->radius_cui) {
++ *buf = NULL;
++ return 0;
++ }
++
++ radius_cui = sta->radius_cui;
++ len = os_strlen(radius_cui);
++ *buf = (u8 *) radius_cui;
++
++ return len;
++}
++
++
++static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
++ int session_timeout)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta)
++ return;
++
++ if (session_timeout) {
++ os_get_reltime(&sta->session_timeout);
++ sta->session_timeout.sec += session_timeout;
++ sta->session_timeout_set = 1;
++ ap_sta_session_timeout(hapd, sta, session_timeout);
++ } else {
++ sta->session_timeout_set = 0;
++ ap_sta_no_session_timeout(hapd, sta);
++ }
++}
++
++
++static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
++{
++ struct hostapd_data *hapd = ctx;
++ struct sta_info *sta;
++ struct os_reltime now, remaining;
++
++ sta = ap_get_sta(hapd, sta_addr);
++ if (!sta || !sta->session_timeout_set)
++ return 0;
++
++ os_get_reltime(&now);
++ if (os_reltime_before(&sta->session_timeout, &now)) {
++ /* already expired, return >0 as timeout was set */
++ return 1;
++ }
++
++ os_reltime_sub(&sta->session_timeout, &now, &remaining);
++
++ return (remaining.sec > 0) ? remaining.sec : 1;
++}
++
++
+ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+ {
+@@ -563,11 +1169,30 @@
+ ethhdr = (struct l2_ethhdr *) buf;
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
++ if (!is_multicast_ether_addr(ethhdr->h_dest) &&
++ os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
++ return;
+ wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+ len - sizeof(*ethhdr));
+ }
+
+
++static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
++ const u8 *dst_addr, u8 oui_suffix,
++ const u8 *buf, size_t len)
++{
++ struct hostapd_data *hapd = ctx;
++
++ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
++ MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
++ if (!is_multicast_ether_addr(dst_addr) &&
++ os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
++ return;
++ wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
++ len);
++}
++
++
+ static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+ {
+@@ -575,13 +1200,99 @@
+ return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+ }
+
+-#endif /* CONFIG_IEEE80211R */
+
+
++static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
++ const char *ft_iface)
++{
++ hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
++ FT_PACKET_R0KH_R1KH_PULL,
++ hostapd_rrb_oui_receive, hapd);
++ if (!hapd->oui_pull)
++ return -1;
++
++ hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
++ FT_PACKET_R0KH_R1KH_RESP,
++ hostapd_rrb_oui_receive, hapd);
++ if (!hapd->oui_resp)
++ return -1;
++
++ hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
++ FT_PACKET_R0KH_R1KH_PUSH,
++ hostapd_rrb_oui_receive, hapd);
++ if (!hapd->oui_push)
++ return -1;
++
++ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
++ FT_PACKET_R0KH_R1KH_SEQ_REQ,
++ hostapd_rrb_oui_receive, hapd);
++ if (!hapd->oui_sreq)
++ return -1;
++
++ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
++ FT_PACKET_R0KH_R1KH_SEQ_RESP,
++ hostapd_rrb_oui_receive, hapd);
++ if (!hapd->oui_sresp)
++ return -1;
++
++ return 0;
++}
++
++
++static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
++{
++ eth_p_oui_unregister(hapd->oui_pull);
++ hapd->oui_pull = NULL;
++ eth_p_oui_unregister(hapd->oui_resp);
++ hapd->oui_resp = NULL;
++ eth_p_oui_unregister(hapd->oui_push);
++ hapd->oui_push = NULL;
++ eth_p_oui_unregister(hapd->oui_sreq);
++ hapd->oui_sreq = NULL;
++ eth_p_oui_unregister(hapd->oui_sresp);
++ hapd->oui_sresp = NULL;
++}
++#endif /* CONFIG_IEEE80211R_AP */
++
++
+ int hostapd_setup_wpa(struct hostapd_data *hapd)
+ {
+ struct wpa_auth_config _conf;
+- struct wpa_auth_callbacks cb;
++ static const struct wpa_auth_callbacks cb = {
++ .logger = hostapd_wpa_auth_logger,
++ .disconnect = hostapd_wpa_auth_disconnect,
++ .mic_failure_report = hostapd_wpa_auth_mic_failure_report,
++ .psk_failure_report = hostapd_wpa_auth_psk_failure_report,
++ .set_eapol = hostapd_wpa_auth_set_eapol,
++ .get_eapol = hostapd_wpa_auth_get_eapol,
++ .get_psk = hostapd_wpa_auth_get_psk,
++ .get_msk = hostapd_wpa_auth_get_msk,
++ .set_key = hostapd_wpa_auth_set_key,
++ .get_seqnum = hostapd_wpa_auth_get_seqnum,
++ .send_eapol = hostapd_wpa_auth_send_eapol,
++ .for_each_sta = hostapd_wpa_auth_for_each_sta,
++ .for_each_auth = hostapd_wpa_auth_for_each_auth,
++ .send_ether = hostapd_wpa_auth_send_ether,
++ .send_oui = hostapd_wpa_auth_send_oui,
++ .channel_info = hostapd_channel_info,
++ .update_vlan = hostapd_wpa_auth_update_vlan,
++#ifdef CONFIG_OCV
++ .get_sta_tx_params = hostapd_get_sta_tx_params,
++#endif /* CONFIG_OCV */
++#ifdef CONFIG_IEEE80211R_AP
++ .send_ft_action = hostapd_wpa_auth_send_ft_action,
++ .add_sta = hostapd_wpa_auth_add_sta,
++ .add_tspec = hostapd_wpa_auth_add_tspec,
++ .set_vlan = hostapd_wpa_auth_set_vlan,
++ .get_vlan = hostapd_wpa_auth_get_vlan,
++ .set_identity = hostapd_wpa_auth_set_identity,
++ .get_identity = hostapd_wpa_auth_get_identity,
++ .set_radius_cui = hostapd_wpa_auth_set_radius_cui,
++ .get_radius_cui = hostapd_wpa_auth_get_radius_cui,
++ .set_session_timeout = hostapd_wpa_auth_set_session_timeout,
++ .get_session_timeout = hostapd_wpa_auth_get_session_timeout,
++#endif /* CONFIG_IEEE80211R_AP */
++ };
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+@@ -590,28 +1301,7 @@
+ _conf.tx_status = 1;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+ _conf.ap_mlme = 1;
+- os_memset(&cb, 0, sizeof(cb));
+- cb.ctx = hapd;
+- cb.logger = hostapd_wpa_auth_logger;
+- cb.disconnect = hostapd_wpa_auth_disconnect;
+- cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
+- cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
+- cb.set_eapol = hostapd_wpa_auth_set_eapol;
+- cb.get_eapol = hostapd_wpa_auth_get_eapol;
+- cb.get_psk = hostapd_wpa_auth_get_psk;
+- cb.get_msk = hostapd_wpa_auth_get_msk;
+- cb.set_key = hostapd_wpa_auth_set_key;
+- cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
+- cb.send_eapol = hostapd_wpa_auth_send_eapol;
+- cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
+- cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
+- cb.send_ether = hostapd_wpa_auth_send_ether;
+-#ifdef CONFIG_IEEE80211R
+- cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
+- cb.add_sta = hostapd_wpa_auth_add_sta;
+- cb.add_tspec = hostapd_wpa_auth_add_tspec;
+-#endif /* CONFIG_IEEE80211R */
+- hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
++ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
+ if (hapd->wpa_auth == NULL) {
+ wpa_printf(MSG_ERROR, "WPA initialization failed.");
+ return -1;
+@@ -636,12 +1326,14 @@
+ return -1;
+ }
+
+-#ifdef CONFIG_IEEE80211R
+- if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
++#ifdef CONFIG_IEEE80211R_AP
++ if (!hostapd_drv_none(hapd) &&
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+- hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
+- hapd->conf->bridge :
+- hapd->conf->iface, NULL, ETH_P_RRB,
++ const char *ft_iface;
++
++ ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
++ hapd->conf->iface;
++ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ hostapd_rrb_receive, hapd, 1);
+ if (hapd->l2 == NULL &&
+ (hapd->driver == NULL ||
+@@ -650,8 +1342,14 @@
+ "interface");
+ return -1;
+ }
++
++ if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
++ wpa_printf(MSG_ERROR,
++ "Failed to open ETH_P_OUI interface");
++ return -1;
++ }
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
+ return 0;
+
+@@ -674,13 +1372,14 @@
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+
+- if (hostapd_set_privacy(hapd, 0)) {
++ if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) {
+ wpa_printf(MSG_DEBUG, "Could not disable "
+ "PrivacyInvoked for interface %s",
+ hapd->conf->iface);
+ }
+
+- if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
++ if (hapd->drv_priv &&
++ hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+ wpa_printf(MSG_DEBUG, "Could not remove generic "
+ "information element from interface %s",
+ hapd->conf->iface);
+@@ -688,8 +1387,13 @@
+ }
+ ieee802_1x_deinit(hapd);
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
++ eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
++ hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
++ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
++ hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
+ l2_packet_deinit(hapd->l2);
+ hapd->l2 = NULL;
+-#endif /* CONFIG_IEEE80211R */
++ hostapd_wpa_unregister_ft_oui(hapd);
++#endif /* CONFIG_IEEE80211R_AP */
+ }
+--- contrib/wpa/src/ap/wpa_auth_i.h.orig
++++ contrib/wpa/src/ap/wpa_auth_i.h
+@@ -9,18 +9,13 @@
+ #ifndef WPA_AUTH_I_H
+ #define WPA_AUTH_I_H
+
++#include "utils/list.h"
++
+ /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+ #define RSNA_MAX_EAPOL_RETRIES 4
+
+ struct wpa_group;
+
+-struct wpa_stsl_negotiation {
+- struct wpa_stsl_negotiation *next;
+- u8 initiator[ETH_ALEN];
+- u8 peer[ETH_ALEN];
+-};
+-
+-
+ struct wpa_state_machine {
+ struct wpa_authenticator *wpa_auth;
+ struct wpa_group *group;
+@@ -27,6 +22,7 @@
+
+ u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
++ u16 auth_alg;
+
+ enum {
+ WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+@@ -48,8 +44,9 @@
+ Boolean AuthenticationRequest;
+ Boolean ReAuthenticationRequest;
+ Boolean Disconnect;
+- int TimeoutCtr;
+- int GTimeoutCtr;
++ u16 disconnect_reason; /* specific reason code to use with Disconnect */
++ u32 TimeoutCtr;
++ u32 GTimeoutCtr;
+ Boolean TimeoutEvt;
+ Boolean EAPOLKeyReceived;
+ Boolean EAPOLKeyPairwise;
+@@ -60,7 +57,9 @@
+ u8 SNonce[WPA_NONCE_LEN];
+ u8 alt_SNonce[WPA_NONCE_LEN];
+ u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
+- u8 PMK[PMK_LEN];
++ u8 PMK[PMK_LEN_MAX];
++ unsigned int pmk_len;
++ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
+ struct wpa_ptk PTK;
+ Boolean PTK_valid;
+ Boolean pairwise_set;
+@@ -88,11 +87,15 @@
+ unsigned int rx_eapol_key_secure:1;
+ unsigned int update_snonce:1;
+ unsigned int alt_snonce_valid:1;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ unsigned int ft_completed:1;
+ unsigned int pmk_r1_name_valid:1;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ unsigned int is_wnmsleep:1;
++ unsigned int pmkid_set:1;
++#ifdef CONFIG_OCV
++ unsigned int ocv_enabled:1;
++#endif /* CONFIG_OCV */
+
+ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int req_replay_counter_used;
+@@ -112,9 +115,12 @@
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+ u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+-#ifdef CONFIG_IEEE80211R
+- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
++#ifdef CONFIG_IEEE80211R_AP
++ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
++ * first 384 bits of MSK */
+ size_t xxkey_len;
++ u8 pmk_r1[PMK_LEN_MAX];
++ unsigned int pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+ * Request */
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+@@ -128,10 +134,11 @@
+ const u8 *ies, size_t ies_len);
+ void *ft_pending_cb_ctx;
+ struct wpabuf *ft_pending_req_ies;
+- u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
++ u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
+ u8 ft_pending_auth_transaction;
+ u8 ft_pending_current_ap[ETH_ALEN];
+-#endif /* CONFIG_IEEE80211R */
++ int ft_pending_pull_left_retries;
++#endif /* CONFIG_IEEE80211R_AP */
+
+ int pending_1_of_4_timeout;
+
+@@ -138,6 +145,23 @@
+ #ifdef CONFIG_P2P
+ u8 ip_addr[4];
+ #endif /* CONFIG_P2P */
++
++#ifdef CONFIG_FILS
++ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
++ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
++ size_t fils_key_auth_len;
++ unsigned int fils_completed:1;
++#endif /* CONFIG_FILS */
++
++#ifdef CONFIG_DPP2
++ struct wpabuf *dpp_z;
++#endif /* CONFIG_DPP2 */
++
++#ifdef CONFIG_TESTING_OPTIONS
++ void (*eapol_status_cb)(void *ctx1, void *ctx2);
++ void *eapol_status_cb_ctx1;
++ void *eapol_status_cb_ctx2;
++#endif /* CONFIG_TESTING_OPTIONS */
+ };
+
+
+@@ -172,6 +196,7 @@
+ #endif /* CONFIG_IEEE80211W */
+ /* Number of references except those in struct wpa_group->next */
+ unsigned int references;
++ unsigned int num_setup_iface;
+ };
+
+
+@@ -192,10 +217,9 @@
+ unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+ unsigned int dot11RSNA4WayHandshakeFailures;
+
+- struct wpa_stsl_negotiation *stsl_negotiations;
+-
+ struct wpa_auth_config conf;
+- struct wpa_auth_callbacks cb;
++ const struct wpa_auth_callbacks *cb;
++ void *cb_ctx;
+
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+@@ -211,6 +235,38 @@
+ };
+
+
++#ifdef CONFIG_IEEE80211R_AP
++
++#define FT_REMOTE_SEQ_BACKLOG 16
++struct ft_remote_seq_rx {
++ u32 dom;
++ struct os_reltime time_offset; /* local time - offset = remote time */
++
++ /* accepted sequence numbers: (offset ... offset + 0x40000000]
++ * (except those in last)
++ * dropped sequence numbers: (offset - 0x40000000 ... offset]
++ * all others trigger SEQ_REQ message (except first message)
++ */
++ u32 last[FT_REMOTE_SEQ_BACKLOG];
++ unsigned int num_last;
++ u32 offsetidx;
++
++ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
++};
++
++struct ft_remote_seq_tx {
++ u32 dom; /* non zero if initialized */
++ u32 seq;
++};
++
++struct ft_remote_seq {
++ struct ft_remote_seq_rx rx;
++ struct ft_remote_seq_tx tx;
++};
++
++#endif /* CONFIG_IEEE80211R_AP */
++
++
+ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid);
+ void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+@@ -229,32 +285,19 @@
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx);
+
+-#ifdef CONFIG_PEERKEY
+-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
+- struct wpa_stsl_negotiation *neg);
+-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
+- const u8 *key_data, size_t key_data_len);
+-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+- const u8 *key_data, size_t key_data_len);
+-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+- const u8 *key_data, size_t key_data_len);
+-#endif /* CONFIG_PEERKEY */
+-
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
+-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
+- size_t r0kh_id_len,
++int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
++ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *anonce, const u8 *snonce,
+ u8 *buf, size_t len, const u8 *subelem,
+ size_t subelem_len);
+-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+- struct wpa_ptk *ptk);
++int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
+ struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
+ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
+ void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+-#endif /* CONFIG_IEEE80211R */
++int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
++ const u8 *pmk_r0_name);
++#endif /* CONFIG_IEEE80211R_AP */
+
+ #endif /* WPA_AUTH_I_H */
+--- contrib/wpa/src/ap/wpa_auth_ie.c.orig
++++ contrib/wpa/src/ap/wpa_auth_ie.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd - WPA/RSN IE and KDE definitions
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -164,18 +164,25 @@
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
++#ifdef CONFIG_SHA384
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#endif /* CONFIG_SHA384 */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+@@ -210,6 +217,51 @@
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
++#ifdef CONFIG_FILS
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#ifdef CONFIG_IEEE80211R_AP
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#endif /* CONFIG_IEEE80211R_AP */
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_OWE
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#endif /* CONFIG_DPP */
++#ifdef CONFIG_HS20
++ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
++ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
++ pos += RSN_SELECTOR_LEN;
++ num_suites++;
++ }
++#endif /* CONFIG_HS20 */
+
+ #ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+@@ -230,8 +282,6 @@
+ capab = 0;
+ if (conf->rsn_preauth)
+ capab |= WPA_CAPABILITY_PREAUTH;
+- if (conf->peerkey)
+- capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
+ if (conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+@@ -243,15 +293,19 @@
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ if (conf->ocv)
++ capab |= WPA_CAPABILITY_OCVC;
++#endif /* CONFIG_OCV */
+ #ifdef CONFIG_RSN_TESTING
+ if (rsn_testing)
+- capab |= BIT(8) | BIT(14) | BIT(15);
++ capab |= BIT(8) | BIT(15);
+ #endif /* CONFIG_RSN_TESTING */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ if (pmkid) {
+- if (pos + 2 + PMKID_LEN > buf + len)
++ if (2 + PMKID_LEN > buf + len - pos)
+ return -1;
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 1);
+@@ -263,7 +317,7 @@
+ #ifdef CONFIG_IEEE80211W
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+- if (pos + 2 + 4 > buf + len)
++ if (2 + 4 > buf + len - pos)
+ return -1;
+ if (pmkid == NULL) {
+ /* PMKID Count */
+@@ -364,6 +418,10 @@
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ if (conf->ocv)
++ capab |= WPA_CAPABILITY_OCVC;
++#endif /* CONFIG_OCV */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+@@ -407,7 +465,7 @@
+ return res;
+ pos += res;
+ }
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
+ res = wpa_write_mdie(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+@@ -415,7 +473,7 @@
+ return res;
+ pos += res;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+ res = wpa_write_wpa_ie(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+@@ -472,9 +530,10 @@
+
+
+ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+- struct wpa_state_machine *sm,
++ struct wpa_state_machine *sm, int freq,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+- const u8 *mdie, size_t mdie_len)
++ const u8 *mdie, size_t mdie_len,
++ const u8 *owe_dh, size_t owe_dh_len)
+ {
+ struct wpa_ie_data data;
+ int ciphers, key_mgmt, res, version;
+@@ -501,7 +560,24 @@
+
+ if (version == WPA_PROTO_RSN) {
+ res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
++ if (!data.has_pairwise)
++ data.pairwise_cipher = wpa_default_rsn_cipher(freq);
++ if (!data.has_group)
++ data.group_cipher = wpa_default_rsn_cipher(freq);
+
++ if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
++ !wpa_key_mgmt_only_ft(data.key_mgmt)) {
++ /* Workaround for some HP and Epson printers that seem
++ * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP
++ * advertised RSNE to Association Request frame. */
++ wpa_printf(MSG_DEBUG,
++ "RSN: FT set in RSNE AKM but MDE is missing from "
++ MACSTR
++ " - ignore FT AKM(s) because there's also a non-FT AKM",
++ MAC2STR(sm->addr));
++ data.key_mgmt &= ~WPA_KEY_MGMT_FT;
++ }
++
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (0) {
+ }
+@@ -509,12 +585,28 @@
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_FILS
++#ifdef CONFIG_IEEE80211R_AP
++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
++ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
++ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
++#endif /* CONFIG_IEEE80211R_AP */
++ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
++ selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
++ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
++ selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_IEEE80211R_AP
++#ifdef CONFIG_SHA384
++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
++ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
++#endif /* CONFIG_SHA384 */
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+@@ -531,6 +623,18 @@
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
++#ifdef CONFIG_OWE
++ else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
++ selector = RSN_AUTH_KEY_MGMT_OWE;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
++ selector = RSN_AUTH_KEY_MGMT_DPP;
++#endif /* CONFIG_DPP */
++#ifdef CONFIG_HS20
++ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
++ selector = RSN_AUTH_KEY_MGMT_OSEN;
++#endif /* CONFIG_HS20 */
+ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+ selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+@@ -591,12 +695,28 @@
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_FILS
++#ifdef CONFIG_IEEE80211R_AP
++ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
++ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
++#endif /* CONFIG_IEEE80211R_AP */
++ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
++ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
++#endif /* CONFIG_FILS */
++#ifdef CONFIG_IEEE80211R_AP
++#ifdef CONFIG_SHA384
++ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
++#endif /* CONFIG_SHA384 */
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_IEEE80211W
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+@@ -611,6 +731,18 @@
+ #endif /* CONFIG_SAE */
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
++#ifdef CONFIG_OWE
++ else if (key_mgmt & WPA_KEY_MGMT_OWE)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ else if (key_mgmt & WPA_KEY_MGMT_DPP)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
++#endif /* CONFIG_DPP */
++#ifdef CONFIG_HS20
++ else if (key_mgmt & WPA_KEY_MGMT_OSEN)
++ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
++#endif /* CONFIG_HS20 */
+ else
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+@@ -634,12 +766,6 @@
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+
+- if (ciphers & WPA_CIPHER_TKIP) {
+- wpa_printf(MSG_DEBUG, "Management frame protection "
+- "cannot use TKIP");
+- return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+- }
+-
+ if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+ {
+ wpa_printf(MSG_DEBUG, "Unsupported management group "
+@@ -648,14 +774,42 @@
+ }
+ }
+
++#ifdef CONFIG_SAE
++ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
++ wpa_auth->conf.sae_require_mfp &&
++ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
++ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
++ wpa_printf(MSG_DEBUG,
++ "Management frame protection required with SAE, but client did not enable it");
++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
++ }
++#endif /* CONFIG_SAE */
++
++#ifdef CONFIG_OCV
++ if ((data.capabilities & WPA_CAPABILITY_OCVC) &&
++ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
++ wpa_printf(MSG_DEBUG,
++ "Management frame protection required with OCV, but client did not enable it");
++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
++ }
++ wpa_auth_set_ocv(sm, wpa_auth->conf.ocv &&
++ (data.capabilities & WPA_CAPABILITY_OCVC));
++#endif /* CONFIG_OCV */
++
+ if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+ !(data.capabilities & WPA_CAPABILITY_MFPC))
+ sm->mgmt_frame_prot = 0;
+ else
+ sm->mgmt_frame_prot = 1;
++
++ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
++ wpa_printf(MSG_DEBUG,
++ "Management frame protection cannot use TKIP");
++ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
++ }
+ #endif /* CONFIG_IEEE80211W */
+
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+@@ -668,9 +822,32 @@
+ "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+ return WPA_INVALID_MDIE;
+ }
++ } else if (mdie != NULL) {
++ wpa_printf(MSG_DEBUG,
++ "RSN: Trying to use non-FT AKM suite, but MDIE included");
++ return WPA_INVALID_AKMP;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+
++#ifdef CONFIG_OWE
++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
++ wpa_printf(MSG_DEBUG,
++ "OWE: No Diffie-Hellman Parameter element");
++ return WPA_INVALID_AKMP;
++ }
++#ifdef CONFIG_DPP
++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) {
++ /* Diffie-Hellman Parameter element can be used with DPP as
++ * well, so allow this to proceed. */
++ } else
++#endif /* CONFIG_DPP */
++ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
++ wpa_printf(MSG_DEBUG,
++ "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
++ return WPA_INVALID_AKMP;
++ }
++#endif /* CONFIG_OWE */
++
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+ if (sm->pairwise < 0)
+ return WPA_INVALID_PAIRWISE;
+@@ -681,6 +858,21 @@
+ else
+ sm->wpa = WPA_VERSION_WPA;
+
++#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
++ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
++ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
++ (sm->auth_alg == WLAN_AUTH_FILS_SK ||
++ sm->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
++ sm->auth_alg == WLAN_AUTH_FILS_PK) &&
++ (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid ||
++ os_memcmp_const(data.pmkid, sm->pmk_r1_name,
++ WPA_PMK_NAME_LEN) != 0)) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
++ "No PMKR1Name match for FILS+FT");
++ return WPA_INVALID_PMKID;
++ }
++#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */
++
+ sm->pmksa = NULL;
+ for (i = 0; i < data.num_pmkid; i++) {
+ wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+@@ -712,14 +904,34 @@
+ }
+ }
+ if (sm->pmksa && pmkid) {
++ struct vlan_description *vlan;
++
++ vlan = sm->pmksa->vlan_desc;
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+- "PMKID found from PMKSA cache "
+- "eap_type=%d vlan_id=%d",
++ "PMKID found from PMKSA cache eap_type=%d vlan=%d%s",
+ sm->pmksa->eap_type_authsrv,
+- sm->pmksa->vlan_id);
++ vlan ? vlan->untagged : 0,
++ (vlan && vlan->tagged[0]) ? "+" : "");
+ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+ }
+
++#ifdef CONFIG_SAE
++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
++ !sm->pmksa) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
++ "No PMKSA cache entry found for SAE");
++ return WPA_INVALID_PMKID;
++ }
++#endif /* CONFIG_SAE */
++
++#ifdef CONFIG_DPP
++ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
++ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
++ "No PMKSA cache entry found for DPP");
++ return WPA_INVALID_PMKID;
++ }
++#endif /* CONFIG_DPP */
++
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(wpa_ie_len);
+@@ -791,7 +1003,7 @@
+ return 0;
+ }
+
+- if (pos + 1 + RSN_SELECTOR_LEN < end &&
++ if (1 + RSN_SELECTOR_LEN < end - pos &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+@@ -812,36 +1024,6 @@
+ return 0;
+ }
+
+-#ifdef CONFIG_PEERKEY
+- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+- ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+- ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+- return 0;
+- }
+-
+- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+- ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+- ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+- return 0;
+- }
+-
+- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+- ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+- ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+- return 0;
+- }
+-
+- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+- ie->error = pos + 2 + RSN_SELECTOR_LEN;
+- ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+- return 0;
+- }
+-#endif /* CONFIG_PEERKEY */
+-
+ #ifdef CONFIG_IEEE80211W
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+@@ -870,6 +1052,15 @@
+ }
+ #endif /* CONFIG_P2P */
+
++#ifdef CONFIG_OCV
++ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
++ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
++ ie->oci = pos + 2 + RSN_SELECTOR_LEN;
++ ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
++ return 0;
++ }
++#endif /* CONFIG_OCV */
++
+ return 0;
+ }
+
+@@ -887,13 +1078,13 @@
+ int ret = 0;
+
+ os_memset(ie, 0, sizeof(*ie));
+- for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
++ for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
+ if (pos[0] == 0xdd &&
+ ((pos == buf + len - 1) || pos[1] == 0)) {
+ /* Ignore padding */
+ break;
+ }
+- if (pos + 2 + pos[1] > end) {
++ if (2 + pos[1] > end - pos) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d pos=%d)",
+ pos[0], pos[1], (int) (pos - buf));
+@@ -905,7 +1096,7 @@
+ if (*pos == WLAN_EID_RSN) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+ ie->mdie = pos;
+ ie->mdie_len = pos[1] + 2;
+@@ -912,7 +1103,7 @@
+ } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+ ie->ftie = pos;
+ ie->ftie_len = pos[1] + 2;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+ ret = wpa_parse_generic(pos, end, ie);
+ if (ret < 0)
+@@ -935,3 +1126,53 @@
+ {
+ return sm ? sm->mgmt_frame_prot : 0;
+ }
++
++
++#ifdef CONFIG_OCV
++
++void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
++{
++ if (sm)
++ sm->ocv_enabled = ocv;
++}
++
++
++int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
++{
++ return sm ? sm->ocv_enabled : 0;
++}
++
++#endif /* CONFIG_OCV */
++
++
++#ifdef CONFIG_OWE
++u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
++ u8 *pos, size_t max_len,
++ const u8 *req_ies, size_t req_ies_len)
++{
++ int res;
++ struct wpa_auth_config *conf;
++
++ if (!sm)
++ return pos;
++ conf = &sm->wpa_auth->conf;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (conf->own_ie_override_len) {
++ if (max_len < conf->own_ie_override_len)
++ return NULL;
++ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
++ conf->own_ie_override, conf->own_ie_override_len);
++ os_memcpy(pos, conf->own_ie_override,
++ conf->own_ie_override_len);
++ return pos + conf->own_ie_override_len;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ res = wpa_write_rsn_ie(conf, pos, max_len,
++ sm->pmksa ? sm->pmksa->pmkid : NULL);
++ if (res < 0)
++ return pos;
++ return pos + res;
++}
++#endif /* CONFIG_OWE */
+--- contrib/wpa/src/ap/wpa_auth_ie.h.orig
++++ contrib/wpa/src/ap/wpa_auth_ie.h
+@@ -19,30 +19,24 @@
+ size_t gtk_len;
+ const u8 *mac_addr;
+ size_t mac_addr_len;
+-#ifdef CONFIG_PEERKEY
+- const u8 *smk;
+- size_t smk_len;
+- const u8 *nonce;
+- size_t nonce_len;
+- const u8 *lifetime;
+- size_t lifetime_len;
+- const u8 *error;
+- size_t error_len;
+-#endif /* CONFIG_PEERKEY */
+ #ifdef CONFIG_IEEE80211W
+ const u8 *igtk;
+ size_t igtk_len;
+ #endif /* CONFIG_IEEE80211W */
+-#ifdef CONFIG_IEEE80211R
++#ifdef CONFIG_IEEE80211R_AP
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R_AP */
+ #ifdef CONFIG_P2P
+ const u8 *ip_addr_req;
+ const u8 *ip_addr_alloc;
+ #endif /* CONFIG_P2P */
++#ifdef CONFIG_OCV
++ const u8 *oci;
++ size_t oci_len;
++#endif /* CONFIG_OCV */
+
+ const u8 *osen;
+ size_t osen_len;
+--- contrib/wpa/src/ap/wps_hostapd.c.orig
++++ contrib/wpa/src/ap/wps_hostapd.c
+@@ -1,6 +1,6 @@
+ /*
+ * hostapd / WPS integration
+- * Copyright (c) 2008-2012, Jouni Malinen
++ * Copyright (c) 2008-2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -269,12 +269,6 @@
+ }
+
+
+-static int str_starts(const char *str, const char *start)
+-{
+- return os_strncmp(str, start, os_strlen(start)) == 0;
+-}
+-
+-
+ static void wps_reload_config(void *eloop_data, void *user_ctx)
+ {
+ struct hostapd_iface *iface = eloop_data;
+@@ -360,6 +354,18 @@
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
+
++ if (hapd->conf->wps_cred_add_sae &&
++ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
++ cred->key_len != 2 * PMK_LEN) {
++ bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
++#ifdef CONFIG_IEEE80211W
++ if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
++ bss->ieee80211w =
++ MGMT_FRAME_PROTECTION_OPTIONAL;
++ bss->sae_require_mfp = 1;
++#endif /* CONFIG_IEEE80211W */
++ }
++
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
+@@ -407,6 +413,7 @@
+ char buf[1024];
+ int multi_bss;
+ int wpa;
++ int pmf_changed = 0;
+
+ if (hapd->wps == NULL)
+ return 0;
+@@ -445,6 +452,8 @@
+ os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+ hapd->wps->ssid_len = cred->ssid_len;
+ hapd->wps->encr_types = cred->encr_type;
++ hapd->wps->encr_types_rsn = cred->encr_type;
++ hapd->wps->encr_types_wpa = cred->encr_type;
+ hapd->wps->auth_types = cred->auth_type;
+ hapd->wps->ap_encr_type = cred->encr_type;
+ hapd->wps->ap_auth_type = cred->auth_type;
+@@ -524,6 +533,10 @@
+
+ if (wpa) {
+ char *prefix;
++#ifdef CONFIG_IEEE80211W
++ int sae = 0;
++#endif /* CONFIG_IEEE80211W */
++
+ fprintf(nconf, "wpa=%d\n", wpa);
+
+ fprintf(nconf, "wpa_key_mgmt=");
+@@ -532,10 +545,30 @@
+ fprintf(nconf, "WPA-EAP");
+ prefix = " ";
+ }
+- if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
++ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+ fprintf(nconf, "%sWPA-PSK", prefix);
++ prefix = " ";
++ }
++ if (hapd->conf->wps_cred_add_sae &&
++ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
++ cred->key_len != 2 * PMK_LEN) {
++ fprintf(nconf, "%sSAE", prefix);
++#ifdef CONFIG_IEEE80211W
++ sae = 1;
++#endif /* CONFIG_IEEE80211W */
++ }
+ fprintf(nconf, "\n");
+
++#ifdef CONFIG_IEEE80211W
++ if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
++ fprintf(nconf, "ieee80211w=%d\n",
++ MGMT_FRAME_PROTECTION_OPTIONAL);
++ pmf_changed = 1;
++ }
++ if (sae)
++ fprintf(nconf, "sae_require_mfp=1\n");
++#endif /* CONFIG_IEEE80211W */
++
+ fprintf(nconf, "wpa_pairwise=");
+ prefix = "";
+ if (cred->encr_type & WPS_ENCR_AES) {
+@@ -589,6 +622,7 @@
+ str_starts(buf, "wep_default_key=") ||
+ str_starts(buf, "wep_key") ||
+ str_starts(buf, "wps_state=") ||
++ (pmf_changed && str_starts(buf, "ieee80211w=")) ||
+ str_starts(buf, "wpa=") ||
+ str_starts(buf, "wpa_psk=") ||
+ str_starts(buf, "wpa_pairwise=") ||
+@@ -872,7 +906,8 @@
+ hapd->wps_probe_resp_ie = NULL;
+
+ if (deinit_only) {
+- hostapd_reset_ap_wps_ie(hapd);
++ if (hapd->drv_priv)
++ hostapd_reset_ap_wps_ie(hapd);
+ return;
+ }
+
+@@ -978,6 +1013,7 @@
+ {
+ struct wps_context *wps;
+ struct wps_registrar_config cfg;
++ u8 *multi_ap_netw_key = NULL;
+
+ if (conf->wps_state == 0) {
+ hostapd_wps_clear_ies(hapd, 0);
+@@ -1067,10 +1103,16 @@
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA2;
+
+- if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
++ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
++ WPA_CIPHER_CCMP_256 |
++ WPA_CIPHER_GCMP_256)) {
+ wps->encr_types |= WPS_ENCR_AES;
+- if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
++ wps->encr_types_rsn |= WPS_ENCR_AES;
++ }
++ if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ wps->encr_types |= WPS_ENCR_TKIP;
++ wps->encr_types_rsn |= WPS_ENCR_TKIP;
++ }
+ }
+
+ if (conf->wpa & WPA_PROTO_WPA) {
+@@ -1079,10 +1121,14 @@
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA;
+
+- if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
++ if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ wps->encr_types |= WPS_ENCR_AES;
+- if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
++ wps->encr_types_wpa |= WPS_ENCR_AES;
++ }
++ if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ wps->encr_types |= WPS_ENCR_TKIP;
++ wps->encr_types_wpa |= WPS_ENCR_TKIP;
++ }
+ }
+
+ if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+@@ -1122,8 +1168,35 @@
+ /* Override parameters to enable security by default */
+ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+ wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
++ wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP;
++ wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
+ }
+
++ if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
++ hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
++ cfg.multi_ap_backhaul_ssid_len =
++ hapd->conf->multi_ap_backhaul_ssid.ssid_len;
++ cfg.multi_ap_backhaul_ssid =
++ hapd->conf->multi_ap_backhaul_ssid.ssid;
++
++ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
++ cfg.multi_ap_backhaul_network_key = (const u8 *)
++ conf->multi_ap_backhaul_ssid.wpa_passphrase;
++ cfg.multi_ap_backhaul_network_key_len =
++ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
++ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
++ multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
++ if (!multi_ap_netw_key)
++ goto fail;
++ wpa_snprintf_hex((char *) multi_ap_netw_key,
++ 2 * PMK_LEN + 1,
++ conf->multi_ap_backhaul_ssid.wpa_psk->psk,
++ PMK_LEN);
++ cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
++ cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
++ }
++ }
++
+ wps->ap_settings = conf->ap_settings;
+ wps->ap_settings_len = conf->ap_settings_len;
+
+@@ -1165,10 +1238,12 @@
+ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+ hapd->wps = wps;
++ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+
+ return 0;
+
+ fail:
++ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+ hostapd_free_wps(wps);
+ return -1;
+ }
+@@ -1614,7 +1689,8 @@
+ unsigned int pin;
+ struct wps_ap_pin_data data;
+
+- pin = wps_generate_pin();
++ if (wps_generate_pin(&pin) < 0)
++ return NULL;
+ os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+ data.timeout = timeout;
+ hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+--- contrib/wpa/src/common/cli.c.orig
++++ contrib/wpa/src/common/cli.c
+@@ -0,0 +1,267 @@
++/*
++ * Common hostapd/wpa_supplicant command line interface functions
++ * Copyright (c) 2004-2016, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "utils/common.h"
++#include "common/cli.h"
++
++
++const char *const cli_license =
++"This software may be distributed under the terms of the BSD license.\n"
++"See README for more details.\n";
++
++const char *const cli_full_license =
++"This software may be distributed under the terms of the BSD license.\n"
++"\n"
++"Redistribution and use in source and binary forms, with or without\n"
++"modification, are permitted provided that the following conditions are\n"
++"met:\n"
++"\n"
++"1. Redistributions of source code must retain the above copyright\n"
++" notice, this list of conditions and the following disclaimer.\n"
++"\n"
++"2. Redistributions in binary form must reproduce the above copyright\n"
++" notice, this list of conditions and the following disclaimer in the\n"
++" documentation and/or other materials provided with the distribution.\n"
++"\n"
++"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
++" names of its contributors may be used to endorse or promote products\n"
++" derived from this software without specific prior written permission.\n"
++"\n"
++"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
++"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
++"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
++"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
++"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
++"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
++"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
++"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
++"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
++"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
++"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
++"\n";
++
++
++void cli_txt_list_free(struct cli_txt_entry *e)
++{
++ dl_list_del(&e->list);
++ os_free(e->txt);
++ os_free(e);
++}
++
++
++void cli_txt_list_flush(struct dl_list *list)
++{
++ struct cli_txt_entry *e;
++
++ while ((e = dl_list_first(list, struct cli_txt_entry, list)))
++ cli_txt_list_free(e);
++}
++
++
++struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
++ const char *txt)
++{
++ struct cli_txt_entry *e;
++
++ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
++ if (os_strcmp(e->txt, txt) == 0)
++ return e;
++ }
++ return NULL;
++}
++
++
++void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
++{
++ struct cli_txt_entry *e;
++
++ e = cli_txt_list_get(txt_list, txt);
++ if (e)
++ cli_txt_list_free(e);
++}
++
++
++void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
++{
++ u8 addr[ETH_ALEN];
++ char buf[18];
++
++ if (hwaddr_aton(txt, addr) < 0)
++ return;
++ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
++ cli_txt_list_del(txt_list, buf);
++}
++
++
++void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
++ int separator)
++{
++ const char *end;
++ char *buf;
++
++ end = os_strchr(txt, separator);
++ if (end == NULL)
++ end = txt + os_strlen(txt);
++ buf = dup_binstr(txt, end - txt);
++ if (buf == NULL)
++ return;
++ cli_txt_list_del(txt_list, buf);
++ os_free(buf);
++}
++
++
++int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
++{
++ struct cli_txt_entry *e;
++
++ e = cli_txt_list_get(txt_list, txt);
++ if (e)
++ return 0;
++ e = os_zalloc(sizeof(*e));
++ if (e == NULL)
++ return -1;
++ e->txt = os_strdup(txt);
++ if (e->txt == NULL) {
++ os_free(e);
++ return -1;
++ }
++ dl_list_add(txt_list, &e->list);
++ return 0;
++}
++
++
++int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
++{
++ u8 addr[ETH_ALEN];
++ char buf[18];
++
++ if (hwaddr_aton(txt, addr) < 0)
++ return -1;
++ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
++ return cli_txt_list_add(txt_list, buf);
++}
++
++
++int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
++ int separator)
++{
++ const char *end;
++ char *buf;
++ int ret;
++
++ end = os_strchr(txt, separator);
++ if (end == NULL)
++ end = txt + os_strlen(txt);
++ buf = dup_binstr(txt, end - txt);
++ if (buf == NULL)
++ return -1;
++ ret = cli_txt_list_add(txt_list, buf);
++ os_free(buf);
++ return ret;
++}
++
++
++char ** cli_txt_list_array(struct dl_list *txt_list)
++{
++ unsigned int i, count = dl_list_len(txt_list);
++ char **res;
++ struct cli_txt_entry *e;
++
++ res = os_calloc(count + 1, sizeof(char *));
++ if (res == NULL)
++ return NULL;
++
++ i = 0;
++ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
++ res[i] = os_strdup(e->txt);
++ if (res[i] == NULL)
++ break;
++ i++;
++ }
++
++ return res;
++}
++
++
++int get_cmd_arg_num(const char *str, int pos)
++{
++ int arg = 0, i;
++
++ for (i = 0; i <= pos; i++) {
++ if (str[i] != ' ') {
++ arg++;
++ while (i <= pos && str[i] != ' ')
++ i++;
++ }
++ }
++
++ if (arg > 0)
++ arg--;
++ return arg;
++}
++
++
++int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[])
++{
++ int i, res;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ res = os_snprintf(pos, end - pos, "%s", cmd);
++ if (os_snprintf_error(end - pos, res))
++ goto fail;
++ pos += res;
++
++ for (i = 0; i < argc; i++) {
++ res = os_snprintf(pos, end - pos, " %s", argv[i]);
++ if (os_snprintf_error(end - pos, res))
++ goto fail;
++ pos += res;
++ }
++
++ buf[buflen - 1] = '\0';
++ return 0;
++
++fail:
++ printf("Too long command\n");
++ return -1;
++}
++
++
++int tokenize_cmd(char *cmd, char *argv[])
++{
++ char *pos;
++ int argc = 0;
++
++ pos = cmd;
++ for (;;) {
++ while (*pos == ' ')
++ pos++;
++ if (*pos == '\0')
++ break;
++ argv[argc] = pos;
++ argc++;
++ if (argc == max_args)
++ break;
++ if (*pos == '"') {
++ char *pos2 = os_strrchr(pos, '"');
++ if (pos2)
++ pos = pos2 + 1;
++ }
++ while (*pos != '\0' && *pos != ' ')
++ pos++;
++ if (*pos == ' ')
++ *pos++ = '\0';
++ }
++
++ return argc;
++}
+--- contrib/wpa/src/common/cli.h.orig
++++ contrib/wpa/src/common/cli.h
+@@ -0,0 +1,47 @@
++/*
++ * Common hostapd/wpa_supplicant command line interface functionality
++ * Copyright (c) 2004-2016, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef CLI_H
++#define CLI_H
++
++#include "utils/list.h"
++
++extern const char *const cli_license;
++extern const char *const cli_full_license;
++
++struct cli_txt_entry {
++ struct dl_list list;
++ char *txt;
++};
++
++void cli_txt_list_free(struct cli_txt_entry *e);
++void cli_txt_list_flush(struct dl_list *list);
++
++struct cli_txt_entry *
++cli_txt_list_get(struct dl_list *txt_list, const char *txt);
++
++void cli_txt_list_del(struct dl_list *txt_list, const char *txt);
++void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt);
++void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
++ int separator);
++
++int cli_txt_list_add(struct dl_list *txt_list, const char *txt);
++int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt);
++int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
++ int separator);
++
++char ** cli_txt_list_array(struct dl_list *txt_list);
++
++int get_cmd_arg_num(const char *str, int pos);
++int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
++ char *argv[]);
++
++#define max_args 10
++int tokenize_cmd(char *cmd, char *argv[]);
++
++#endif /* CLI_H */
+--- contrib/wpa/src/common/common_module_tests.c.orig
++++ contrib/wpa/src/common/common_module_tests.c
+@@ -1,6 +1,6 @@
+ /*
+ * common module tests
+- * Copyright (c) 2014-2015, Jouni Malinen
++ * Copyright (c) 2014-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -9,10 +9,13 @@
+ #include "utils/includes.h"
+
+ #include "utils/common.h"
++#include "utils/module_tests.h"
++#include "crypto/crypto.h"
+ #include "ieee802_11_common.h"
+ #include "ieee802_11_defs.h"
+ #include "gas.h"
+ #include "wpa_common.h"
++#include "sae.h"
+
+
+ struct ieee802_11_parse_test_data {
+@@ -52,6 +55,31 @@
+ 18, ParseOK, 9 },
+ { (u8 *) "\x8b\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
++ { (u8 *) "\xed\x00", 2, ParseOK, 1 },
++ { (u8 *) "\xef\x00", 2, ParseOK, 1 },
++ { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 },
++ { (u8 *) "\xf0\x00", 2, ParseOK, 1 },
++ { (u8 *) "\xf1\x00", 2, ParseOK, 1 },
++ { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 },
++ { (u8 *) "\xf2\x00", 2, ParseOK, 1 },
++ { (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
++ { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 },
++ { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55",
++ 15, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11,
++ ParseOK, 1 },
++ { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 },
++ { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 },
++ { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 },
+ { NULL, 0, ParseOK, 0 }
+ };
+
+@@ -58,6 +86,7 @@
+ static int ieee802_11_parse_tests(void)
+ {
+ int i, ret = 0;
++ struct wpabuf *buf;
+
+ wpa_printf(MSG_INFO, "ieee802_11_parse tests");
+
+@@ -83,6 +112,35 @@
+ ret = -1;
+ }
+
++ buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01",
++ 16, 0x11223344);
++ do {
++ const u8 *pos;
++
++ if (!buf) {
++ wpa_printf(MSG_ERROR,
++ "ieee802_11_vendor_ie_concat test 2 failed");
++ ret = -1;
++ break;
++ }
++
++ if (wpabuf_len(buf) != 2) {
++ wpa_printf(MSG_ERROR,
++ "ieee802_11_vendor_ie_concat test 3 failed");
++ ret = -1;
++ break;
++ }
++
++ pos = wpabuf_head(buf);
++ if (pos[0] != 0x01 || pos[1] != 0x02) {
++ wpa_printf(MSG_ERROR,
++ "ieee802_11_vendor_ie_concat test 3 failed");
++ ret = -1;
++ break;
++ }
++ } while (0);
++ wpabuf_free(buf);
++
+ return ret;
+ }
+
+@@ -192,6 +250,179 @@
+ }
+
+
++static int sae_tests(void)
++{
++#ifdef CONFIG_SAE
++ struct sae_data sae;
++ int ret = -1;
++ /* IEEE P802.11-REVmd/D2.1, Annex J.10 */
++ const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
++ const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
++ const char *pw = "mekmitasdigoat";
++ const char *pwid = "psk4internet";
++ const u8 local_rand[] = {
++ 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e,
++ 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd,
++ 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5,
++ 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf
++ };
++ const u8 local_mask[] = {
++ 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c,
++ 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4,
++ 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e,
++ 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15
++ };
++ const u8 local_commit[] = {
++ 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4,
++ 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39,
++ 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0,
++ 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4,
++ 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58,
++ 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25,
++ 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5,
++ 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61,
++ 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4,
++ 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98,
++ 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88,
++ 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52,
++ 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
++ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
++ 0x74
++ };
++ const u8 peer_commit[] = {
++ 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea,
++ 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70,
++ 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55,
++ 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2,
++ 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9,
++ 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d,
++ 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38,
++ 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f,
++ 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc,
++ 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf,
++ 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf,
++ 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62,
++ 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
++ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
++ 0x74
++ };
++ const u8 kck[] = {
++ 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8,
++ 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94,
++ 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3,
++ 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed
++ };
++ const u8 pmk[] = {
++ 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21,
++ 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85,
++ 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16,
++ 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5
++ };
++ const u8 pmkid[] = {
++ 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00,
++ 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f
++ };
++ const u8 local_confirm[] = {
++ 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50,
++ 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a,
++ 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca,
++ 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9,
++ 0xfc, 0x77
++ };
++ const u8 peer_confirm[] = {
++ 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89,
++ 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe,
++ 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e,
++ 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa,
++ 0xc2, 0xfd
++ };
++ struct wpabuf *buf = NULL;
++ struct crypto_bignum *mask = NULL;
++
++ os_memset(&sae, 0, sizeof(sae));
++ buf = wpabuf_alloc(1000);
++ if (!buf ||
++ sae_set_group(&sae, 19) < 0 ||
++ sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw),
++ pwid, &sae) < 0)
++ goto fail;
++
++ /* Override local values based on SAE test vector */
++ crypto_bignum_deinit(sae.tmp->sae_rand, 1);
++ sae.tmp->sae_rand = crypto_bignum_init_set(local_rand,
++ sizeof(local_rand));
++ mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
++ if (!sae.tmp->sae_rand || !mask)
++ goto fail;
++
++ if (crypto_bignum_add(sae.tmp->sae_rand, mask,
++ sae.tmp->own_commit_scalar) < 0 ||
++ crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order,
++ sae.tmp->own_commit_scalar) < 0 ||
++ crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask,
++ sae.tmp->own_commit_element_ecc) < 0 ||
++ crypto_ec_point_invert(sae.tmp->ec,
++ sae.tmp->own_commit_element_ecc) < 0)
++ goto fail;
++
++ /* Check that output matches the test vector */
++ sae_write_commit(&sae, buf, NULL, pwid);
++ wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
++
++ if (wpabuf_len(buf) != sizeof(local_commit) ||
++ os_memcmp(wpabuf_head(buf), local_commit,
++ sizeof(local_commit)) != 0) {
++ wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit");
++ goto fail;
++ }
++
++ if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
++ NULL) != 0 ||
++ sae_process_commit(&sae) < 0)
++ goto fail;
++
++ if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) {
++ wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK");
++ goto fail;
++ }
++
++ if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) {
++ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK");
++ goto fail;
++ }
++
++ if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) {
++ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID");
++ goto fail;
++ }
++
++ buf->used = 0;
++ sae.send_confirm = 1;
++ sae_write_confirm(&sae, buf);
++ wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf);
++
++ if (wpabuf_len(buf) != sizeof(local_confirm) ||
++ os_memcmp(wpabuf_head(buf), local_confirm,
++ sizeof(local_confirm)) != 0) {
++ wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm");
++ goto fail;
++ }
++
++ if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
++ goto fail;
++
++ ret = 0;
++fail:
++ sae_clear_data(&sae);
++ wpabuf_free(buf);
++ crypto_bignum_deinit(mask, 1);
++ return ret;
++#else /* CONFIG_SAE */
++ return 0;
++#endif /* CONFIG_SAE */
++}
++
++
+ int common_module_tests(void)
+ {
+ int ret = 0;
+@@ -200,6 +431,7 @@
+
+ if (ieee802_11_parse_tests() < 0 ||
+ gas_tests() < 0 ||
++ sae_tests() < 0 ||
+ rsn_ie_parse_tests() < 0)
+ ret = -1;
+
+--- contrib/wpa/src/common/ctrl_iface_common.c.orig
++++ contrib/wpa/src/common/ctrl_iface_common.c
+@@ -0,0 +1,209 @@
++/*
++ * Common hostapd/wpa_supplicant ctrl iface code.
++ * Copyright (c) 2002-2013, Jouni Malinen
++ * Copyright (c) 2015, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include
++#include
++
++#include "utils/common.h"
++#include "ctrl_iface_common.h"
++
++static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len,
++ struct sockaddr_storage *b, socklen_t b_len)
++{
++ if (a->ss_family != b->ss_family)
++ return 1;
++
++ switch (a->ss_family) {
++#ifdef CONFIG_CTRL_IFACE_UDP
++ case AF_INET:
++ {
++ struct sockaddr_in *in_a, *in_b;
++
++ in_a = (struct sockaddr_in *) a;
++ in_b = (struct sockaddr_in *) b;
++
++ if (in_a->sin_port != in_b->sin_port)
++ return 1;
++ if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr)
++ return 1;
++ break;
++ }
++ case AF_INET6:
++ {
++ struct sockaddr_in6 *in6_a, *in6_b;
++
++ in6_a = (struct sockaddr_in6 *) a;
++ in6_b = (struct sockaddr_in6 *) b;
++
++ if (in6_a->sin6_port != in6_b->sin6_port)
++ return 1;
++ if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr,
++ sizeof(in6_a->sin6_addr)) != 0)
++ return 1;
++ break;
++ }
++#endif /* CONFIG_CTRL_IFACE_UDP */
++#ifdef CONFIG_CTRL_IFACE_UNIX
++ case AF_UNIX:
++ {
++ struct sockaddr_un *u_a, *u_b;
++
++ u_a = (struct sockaddr_un *) a;
++ u_b = (struct sockaddr_un *) b;
++
++ if (a_len != b_len ||
++ os_memcmp(u_a->sun_path, u_b->sun_path,
++ a_len - offsetof(struct sockaddr_un, sun_path))
++ != 0)
++ return 1;
++ break;
++ }
++#endif /* CONFIG_CTRL_IFACE_UNIX */
++ default:
++ return 1;
++ }
++
++ return 0;
++}
++
++
++void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
++ socklen_t socklen)
++{
++ switch (sock->ss_family) {
++#ifdef CONFIG_CTRL_IFACE_UDP
++ case AF_INET:
++ case AF_INET6:
++ {
++ char host[NI_MAXHOST] = { 0 };
++ char service[NI_MAXSERV] = { 0 };
++
++ getnameinfo((struct sockaddr *) sock, socklen,
++ host, sizeof(host),
++ service, sizeof(service),
++ NI_NUMERICHOST);
++
++ wpa_printf(level, "%s %s:%s", msg, host, service);
++ break;
++ }
++#endif /* CONFIG_CTRL_IFACE_UDP */
++#ifdef CONFIG_CTRL_IFACE_UNIX
++ case AF_UNIX:
++ {
++ char addr_txt[200];
++
++ printf_encode(addr_txt, sizeof(addr_txt),
++ (u8 *) ((struct sockaddr_un *) sock)->sun_path,
++ socklen - offsetof(struct sockaddr_un, sun_path));
++ wpa_printf(level, "%s %s", msg, addr_txt);
++ break;
++ }
++#endif /* CONFIG_CTRL_IFACE_UNIX */
++ default:
++ wpa_printf(level, "%s", msg);
++ break;
++ }
++}
++
++
++static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input)
++{
++ const char *value;
++ int val;
++
++ if (!input)
++ return 0;
++
++ value = os_strchr(input, '=');
++ if (!value)
++ return -1;
++ value++;
++ val = atoi(value);
++ if (val < 0 || val > 1)
++ return -1;
++
++ if (str_starts(input, "probe_rx_events=")) {
++ if (val)
++ dst->events |= WPA_EVENT_RX_PROBE_REQUEST;
++ else
++ dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST;
++ }
++
++ return 0;
++}
++
++
++int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen, const char *input)
++{
++ struct wpa_ctrl_dst *dst;
++
++ /* Update event registration if already attached */
++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
++ if (!sockaddr_compare(from, fromlen,
++ &dst->addr, dst->addrlen))
++ return ctrl_set_events(dst, input);
++ }
++
++ /* New attachment */
++ dst = os_zalloc(sizeof(*dst));
++ if (dst == NULL)
++ return -1;
++ os_memcpy(&dst->addr, from, fromlen);
++ dst->addrlen = fromlen;
++ dst->debug_level = MSG_INFO;
++ ctrl_set_events(dst, input);
++ dl_list_add(ctrl_dst, &dst->list);
++
++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
++ return 0;
++}
++
++
++int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen)
++{
++ struct wpa_ctrl_dst *dst;
++
++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
++ if (!sockaddr_compare(from, fromlen,
++ &dst->addr, dst->addrlen)) {
++ sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached",
++ from, fromlen);
++ dl_list_del(&dst->list);
++ os_free(dst);
++ return 0;
++ }
++ }
++
++ return -1;
++}
++
++
++int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen, const char *level)
++{
++ struct wpa_ctrl_dst *dst;
++
++ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
++
++ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
++ if (!sockaddr_compare(from, fromlen,
++ &dst->addr, dst->addrlen)) {
++ sockaddr_print(MSG_DEBUG,
++ "CTRL_IFACE changed monitor level",
++ from, fromlen);
++ dst->debug_level = atoi(level);
++ return 0;
++ }
++ }
++
++ return -1;
++}
+--- contrib/wpa/src/common/ctrl_iface_common.h.orig
++++ contrib/wpa/src/common/ctrl_iface_common.h
+@@ -0,0 +1,42 @@
++/*
++ * Common hostapd/wpa_supplicant ctrl iface code.
++ * Copyright (c) 2002-2013, Jouni Malinen
++ * Copyright (c) 2015, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++#ifndef CONTROL_IFACE_COMMON_H
++#define CONTROL_IFACE_COMMON_H
++
++#include "utils/list.h"
++
++/* Events enable bits (wpa_ctrl_dst::events) */
++#define WPA_EVENT_RX_PROBE_REQUEST BIT(0)
++
++/**
++ * struct wpa_ctrl_dst - Data structure of control interface monitors
++ *
++ * This structure is used to store information about registered control
++ * interface monitors into struct wpa_supplicant.
++ */
++struct wpa_ctrl_dst {
++ struct dl_list list;
++ struct sockaddr_storage addr;
++ socklen_t addrlen;
++ int debug_level;
++ int errors;
++ u32 events; /* WPA_EVENT_* bitmap */
++};
++
++void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
++ socklen_t socklen);
++
++int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen, const char *input);
++int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen);
++int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
++ socklen_t fromlen, const char *level);
++
++#endif /* CONTROL_IFACE_COMMON_H */
+--- contrib/wpa/src/common/defs.h.orig
++++ contrib/wpa/src/common/defs.h
+@@ -1,6 +1,6 @@
+ /*
+ * WPA Supplicant - Common definitions
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -51,16 +51,35 @@
+ #define WPA_KEY_MGMT_OSEN BIT(15)
+ #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
+ #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
++#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
++#define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
++#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
++#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
++#define WPA_KEY_MGMT_OWE BIT(22)
++#define WPA_KEY_MGMT_DPP BIT(23)
++#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
+
++#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
++ WPA_KEY_MGMT_FT_IEEE8021X | \
++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \
++ WPA_KEY_MGMT_FT_SAE | \
++ WPA_KEY_MGMT_FT_FILS_SHA256 | \
++ WPA_KEY_MGMT_FT_FILS_SHA384)
++
+ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+ {
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X |
++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
+ WPA_KEY_MGMT_CCKM |
+ WPA_KEY_MGMT_OSEN |
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
++ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
++ WPA_KEY_MGMT_FILS_SHA256 |
++ WPA_KEY_MGMT_FILS_SHA384 |
++ WPA_KEY_MGMT_FT_FILS_SHA256 |
++ WPA_KEY_MGMT_FT_FILS_SHA384));
+ }
+
+ static inline int wpa_key_mgmt_wpa_psk(int akm)
+@@ -74,11 +93,21 @@
+
+ static inline int wpa_key_mgmt_ft(int akm)
+ {
+- return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+- WPA_KEY_MGMT_FT_IEEE8021X |
+- WPA_KEY_MGMT_FT_SAE));
++ return !!(akm & WPA_KEY_MGMT_FT);
+ }
+
++static inline int wpa_key_mgmt_only_ft(int akm)
++{
++ int ft = wpa_key_mgmt_ft(akm);
++ akm &= ~WPA_KEY_MGMT_FT;
++ return ft && !akm;
++}
++
++static inline int wpa_key_mgmt_ft_psk(int akm)
++{
++ return !!(akm & WPA_KEY_MGMT_FT_PSK);
++}
++
+ static inline int wpa_key_mgmt_sae(int akm)
+ {
+ return !!(akm & (WPA_KEY_MGMT_SAE |
+@@ -85,17 +114,32 @@
+ WPA_KEY_MGMT_FT_SAE));
+ }
+
++static inline int wpa_key_mgmt_fils(int akm)
++{
++ return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
++ WPA_KEY_MGMT_FILS_SHA384 |
++ WPA_KEY_MGMT_FT_FILS_SHA256 |
++ WPA_KEY_MGMT_FT_FILS_SHA384));
++}
++
+ static inline int wpa_key_mgmt_sha256(int akm)
+ {
+ return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
++ WPA_KEY_MGMT_SAE |
++ WPA_KEY_MGMT_FT_SAE |
+ WPA_KEY_MGMT_OSEN |
+- WPA_KEY_MGMT_IEEE8021X_SUITE_B));
++ WPA_KEY_MGMT_IEEE8021X_SUITE_B |
++ WPA_KEY_MGMT_FILS_SHA256 |
++ WPA_KEY_MGMT_FT_FILS_SHA256));
+ }
+
+ static inline int wpa_key_mgmt_sha384(int akm)
+ {
+- return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
++ return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
++ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
++ WPA_KEY_MGMT_FILS_SHA384 |
++ WPA_KEY_MGMT_FT_FILS_SHA384));
+ }
+
+ static inline int wpa_key_mgmt_suite_b(int akm)
+@@ -108,7 +152,10 @@
+ {
+ return wpa_key_mgmt_wpa_ieee8021x(akm) ||
+ wpa_key_mgmt_wpa_psk(akm) ||
+- wpa_key_mgmt_sae(akm);
++ wpa_key_mgmt_fils(akm) ||
++ wpa_key_mgmt_sae(akm) ||
++ akm == WPA_KEY_MGMT_OWE ||
++ akm == WPA_KEY_MGMT_DPP;
+ }
+
+ static inline int wpa_key_mgmt_wpa_any(int akm)
+@@ -132,7 +179,13 @@
+ #define WPA_AUTH_ALG_LEAP BIT(2)
+ #define WPA_AUTH_ALG_FT BIT(3)
+ #define WPA_AUTH_ALG_SAE BIT(4)
++#define WPA_AUTH_ALG_FILS BIT(5)
++#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
+
++static inline int wpa_auth_alg_fils(int alg)
++{
++ return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
++}
+
+ enum wpa_alg {
+ WPA_ALG_NONE,
+@@ -312,6 +365,7 @@
+ WPA_CTRL_REQ_EAP_PASSPHRASE,
+ WPA_CTRL_REQ_SIM,
+ WPA_CTRL_REQ_PSK_PASSPHRASE,
++ WPA_CTRL_REQ_EXT_CERT_CHECK,
+ NUM_WPA_CTRL_REQS
+ };
+
+@@ -319,13 +373,13 @@
+ #define EAP_MAX_METHODS 8
+
+ enum mesh_plink_state {
+- PLINK_LISTEN = 1,
+- PLINK_OPEN_SENT,
+- PLINK_OPEN_RCVD,
++ PLINK_IDLE = 1,
++ PLINK_OPN_SNT,
++ PLINK_OPN_RCVD,
+ PLINK_CNF_RCVD,
+ PLINK_ESTAB,
+ PLINK_HOLDING,
+- PLINK_BLOCKED,
++ PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */
+ };
+
+ enum set_band {
+@@ -334,4 +388,35 @@
+ WPA_SETBAND_2G
+ };
+
++enum wpa_radio_work_band {
++ BAND_2_4_GHZ = BIT(0),
++ BAND_5_GHZ = BIT(1),
++ BAND_60_GHZ = BIT(2),
++};
++
++enum beacon_rate_type {
++ BEACON_RATE_LEGACY,
++ BEACON_RATE_HT,
++ BEACON_RATE_VHT
++};
++
++enum eap_proxy_sim_state {
++ SIM_STATE_ERROR,
++};
++
++#define OCE_STA BIT(0)
++#define OCE_STA_CFON BIT(1)
++#define OCE_AP BIT(2)
++
++/* enum chan_width - Channel width definitions */
++enum chan_width {
++ CHAN_WIDTH_20_NOHT,
++ CHAN_WIDTH_20,
++ CHAN_WIDTH_40,
++ CHAN_WIDTH_80,
++ CHAN_WIDTH_80P80,
++ CHAN_WIDTH_160,
++ CHAN_WIDTH_UNKNOWN
++};
++
+ #endif /* DEFS_H */
+--- contrib/wpa/src/common/dhcp.h.orig
++++ contrib/wpa/src/common/dhcp.h
+@@ -0,0 +1,279 @@
++/*
++ * DHCP definitions
++ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef DHCP_H
++#define DHCP_H
++
++/*
++ * Translate Linux to FreeBSD
++ */
++#define iphdr ip
++#define ihl ip_hl
++#define verson ip_v
++#define tos ip_tos
++#define tot_len ip_len
++#define id ip_id
++#define frag_off ip_off
++#define ttl ip_ttl
++#define protocol ip_p
++#define check ip_sum
++#define saddr ip_src
++#define daddr ip_dst
++
++#include
++#if __FAVOR_BSD
++#include
++#else
++#define __FAVOR_BSD 1
++#include
++#undef __FAVOR_BSD
++#endif
++
++#define DHCP_SERVER_PORT 67
++#define DHCP_CLIENT_PORT 68
++
++struct dhcp_data {
++ u8 op;
++ u8 htype;
++ u8 hlen;
++ u8 hops;
++ be32 xid;
++ be16 secs;
++ be16 flags;
++ be32 client_ip;
++ be32 your_ip;
++ be32 server_ip;
++ be32 relay_ip;
++ u8 hw_addr[16];
++ u8 serv_name[64];
++ u8 boot_file[128];
++} STRUCT_PACKED;
++
++struct bootp_pkt {
++ struct iphdr iph;
++ struct udphdr udph;
++ u8 op;
++ u8 htype;
++ u8 hlen;
++ u8 hops;
++ be32 xid;
++ be16 secs;
++ be16 flags;
++ be32 client_ip;
++ be32 your_ip;
++ be32 server_ip;
++ be32 relay_ip;
++ u8 hw_addr[16];
++ u8 serv_name[64];
++ u8 boot_file[128];
++ u8 exten[312];
++} STRUCT_PACKED;
++
++#define DHCP_MAGIC 0x63825363
++
++/*
++ * IANA DHCP/BOOTP registry
++ * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
++*/
++enum dhcp_options {
++ DHCP_OPT_PAD = 0,
++ DHCP_OPT_SUBNET_MASK = 1,
++ DHCP_OPT_TIME_OFFSET = 2,
++ DHCP_OPT_ROUTER = 3,
++ DHCP_OPT_TIME_SERVER = 4,
++ DHCP_OPT_NAME_SERVER = 5,
++ DHCP_OPT_DOMAIN_NAME_SERVER = 6,
++ DHCP_OPT_LOG_SERVER = 7,
++ DHCP_OPT_QUOTES_SERVER = 8,
++ DHCP_OPT_LPR_SERVER = 9,
++ DHCP_OPT_IMPRESS_SERVER = 10,
++ DHCP_OPT_RLP_SERVER = 11,
++ DHCP_OPT_HOSTNAME = 12,
++ DHCP_OPT_BOOT_FILE_SIZE = 13,
++ DHCP_OPT_MERIT_DUMP_FILE = 14,
++ DHCP_OPT_DOMAIN_NAME = 15,
++ DHCP_OPT_SWAP_SERVER = 16,
++ DHCP_OPT_ROOT_PATH = 17,
++ DHCP_OPT_EXTENSION_PATH = 18,
++ DHCP_OPT_FORWARD = 19,
++ DHCP_OPT_SRC_RTE = 20,
++ DHCP_OPT_POLICY_FILTER = 21,
++ DHCP_OPT_MAX_DG_ASSEMBLY = 22,
++ DHCP_OPT_DEFAULT_IP_TTL = 23,
++ DHCP_OPT_MTU_TIMEOUT = 24,
++ DHCP_OPT_MTU_PLATEAU = 25,
++ DHCP_OPT_MTU_INTERFACE = 26,
++ DHCP_OPT_ALL_SUBNETS_LOCAL = 27,
++ DHCP_OPT_BROADCAST_ADDRESS = 28,
++ DHCP_OPT_MASK_DISCOVERY = 29,
++ DHCP_OPT_MASK_SUPPLIER = 30,
++ DHCP_OPT_ROUTER_DISCOVERY = 31,
++ DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32,
++ DHCP_OPT_STATIC_ROUTE = 33,
++ DHCP_OPT_TRAILERS = 34,
++ DHCP_OPT_ARP_TIMEOUT = 35,
++ DHCP_OPT_ETHERNET = 36,
++ DHCP_OPT_TCP_DEFAULT_TTL = 37,
++ DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38,
++ DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39,
++ DHCP_OPT_NIS_DOMAIN = 40,
++ DHCP_OPT_NIS_SERVERS = 41,
++ DHCP_OPT_NTP_SERVERS = 42,
++ DHCP_OPT_VENDOR_SPECIFIC = 43,
++ DHCP_OPT_NETBIOS_NAME_SERVER = 44,
++ DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45,
++ DHCP_OPT_NETBIOS_NODE_TYPE = 46,
++ DHCP_OPT_NETBIOS_SCOPE = 47,
++ DHCP_OPT_FONT_SERVER = 48,
++ DHCP_OPT_DISPLAY_MANAGER = 49,
++ DHCP_OPT_REQUESTED_IP_ADDRESS = 50,
++ DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51,
++ DHCP_OPT_OVERLOAD = 52,
++ DHCP_OPT_MSG_TYPE = 53,
++ DHCP_OPT_SERVER_ID = 54,
++ DHCP_OPT_PARAMETER_REQ_LIST = 55,
++ DHCP_OPT_MESSAGE = 56,
++ DHCP_OPT_MAX_MESSAGE_SIZE = 57,
++ DHCP_OPT_RENEWAL_TIME = 58,
++ DHCP_OPT_REBINDING_TIME = 59,
++ DHCP_OPT_VENDOR_CLASS_ID = 60,
++ DHCP_OPT_CLIENT_ID = 61,
++ DHCP_OPT_NETWARE_IP_DOMAIN = 62,
++ DHCP_OPT_NETWARE_IP_OPTION = 63,
++ DHCP_OPT_NIS_V3_DOMAIN = 64,
++ DHCP_OPT_NIS_V3_SERVERS = 65,
++ DHCP_OPT_TFTP_SERVER_NAME = 66,
++ DHCP_OPT_BOOT_FILE_NAME = 67,
++ DHCP_OPT_HOME_AGENT_ADDRESSES = 68,
++ DHCP_OPT_SMTP_SERVER = 69,
++ DHCP_OPT_POP3_SERVER = 70,
++ DHCP_OPT_NNTP_SERVER = 71,
++ DHCP_OPT_WWW_SERVER = 72,
++ DHCP_OPT_FINGER_SERVER = 73,
++ DHCP_OPT_IRC_SERVER = 74,
++ DHCP_OPT_STREETTALK_SERVER = 75,
++ DHCP_OPT_STDA_SERVER = 76,
++ DHCP_OPT_USER_CLASS = 77,
++ DHCP_OPT_DIRECTORY_AGENT = 78,
++ DHCP_OPT_SERVICE_SCOPE = 79,
++ DHCP_OPT_RAPID_COMMIT = 80,
++ DHCP_OPT_CLIENT_FQDN = 81,
++ DHCP_OPT_RELAY_AGENT_INFO = 82,
++ DHCP_OPT_ISNS = 83,
++ DHCP_OPT_NDS_SERVERS = 85,
++ DHCP_OPT_NDS_TREE_NAME = 86,
++ DHCP_OPT_NDS_CONTEXT = 87,
++ DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88,
++ DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89,
++ DHCP_OPT_AUTHENTICATION = 90,
++ DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91,
++ DHCP_OPT_ASSOCIATED_IP = 92,
++ DHCP_OPT_CLIENT_SYSYEM = 93,
++ DHCP_OPT_CLIENT_NDI = 94,
++ DHCP_OPT_LDAP = 95,
++ DHCP_OPT_UUID_GUID = 97,
++ DHCP_OPT_USER_AUTH = 98,
++ DHCP_OPT_GEOCONF_CIVIC = 99,
++ DHCP_OPT_PCODE = 100,
++ DHCP_OPT_TCODE = 101,
++ DHCP_OPT_NETINFO_ADDRESS = 112,
++ DHCP_OPT_NETINFO_TAG = 113,
++ DHCP_OPT_URL = 114,
++ DHCP_OPT_AUTO_CONFIG = 116,
++ DHCP_OPT_NAME_SERVICE_SEARCH = 117,
++ DHCP_OPT_SUBNET_SELECTION = 118,
++ DHCP_OPT_DOMAIN_SEARCH = 119,
++ DHCP_OPT_SIP_SERVERS_DCP = 120,
++ DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121,
++ DHCP_OPT_CCC = 122,
++ DHCP_OPT_GEOCONF = 123,
++ DHCP_OPT_V_I_VENDOR_CLASS = 124,
++ DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125,
++ DHCP_OPT_PANA_AGENT = 136,
++ DHCP_OPT_V4_LOST = 137,
++ DHCP_OPT_CAPWAP_AC_V4 = 138,
++ DHCP_OPT_IPV4_ADDRESS_MOS = 139,
++ DHCP_OPT_IPV4_FQDN_MOS = 140,
++ DHCP_OPT_SIP_UA_CONF = 141,
++ DHCP_OPT_IPV4_ADDRESS_ANDSF = 142,
++ DHCP_OPT_GEOLOC = 144,
++ DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145,
++ DHCP_OPT_RDNSS_SELECTION = 146,
++ DHCP_OPT_TFTP_SERVER_ADDRESS = 150,
++ DHCP_OPT_STATUS_CODE = 151,
++ DHCP_OPT_BASE_TIME = 152,
++ DHCP_OPT_START_TIME_OF_STATE = 153,
++ DHCP_OPT_QUERY_START_TIME = 154,
++ DHCP_OPT_QUERY_END_TIME = 155,
++ DHCP_OPT_STATE = 156,
++ DHCP_OPT_DATA_SOURCE = 157,
++ DHCP_OPT_V4_PCP_SERVER = 158,
++ DHCP_OPT_V4_PORTPARAMS = 159,
++ DHCP_OPT_CAPTIVE_PORTAL = 160,
++ DHCP_OPT_CONF_FILE = 209,
++ DHCP_OPT_PATH_PREFIX = 210,
++ DHCP_OPT_REBOOT_TIME = 211,
++ DHCP_OPT_6RD = 212,
++ DHCP_OPT_V4_ACCESS_DOMAIN = 213,
++ DHCP_OPT_SUBNET_ALLOCATION = 220,
++ DHCP_OPT_VSS = 221,
++ DHCP_OPT_END = 255
++};
++
++enum dhcp_message_types {
++ DHCPDISCOVER = 1,
++ DHCPOFFER = 2,
++ DHCPREQUEST = 3,
++ DHCPDECLINE = 4,
++ DHCPACK = 5,
++ DHCPNAK = 6,
++ DHCPRELEASE = 7,
++ DHCPINFORM = 8,
++ DHCPFORCERENEW = 9,
++ DHCPLEASEQUERY = 10,
++ DHCPLEASEUNASSIGNED = 11,
++ DHCPLEASEUNKNOWN = 12,
++ DHCPLEASEACTIVE = 13,
++ DHCPBULKLEASEQUERY = 14,
++ DHCPLEASEQUERYDONE = 15,
++ DHCPACTIVELEASEQUERY = 16,
++ DHCPLEASEQUERYSTATUS = 17,
++ DHCPTLS = 18,
++};
++
++enum dhcp_relay_agent_suboptions {
++ DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1,
++ DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2,
++ DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4,
++ DHCP_RELAY_OPT_LINK_SELECTION = 5,
++ DHCP_RELAY_OPT_SUBSCRIBE_ID = 6,
++ DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7,
++ DHCP_RELAY_OPT_AUTHENTICATION = 8,
++ DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9,
++ DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10,
++ DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11,
++ DHCP_RELAY_OPT_RELAY_AGENT_ID = 12,
++ DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13,
++ DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14,
++ DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15,
++ DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16,
++ DHCP_RELAY_OPT_OPERATOR_ID = 17,
++ DHCP_RELAY_OPT_OPERATOR_REALM = 18,
++ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151,
++ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152,
++};
++
++enum access_technology_types {
++ ACCESS_TECHNOLOGY_VIRTUAL = 1,
++ ACCESS_TECHNOLOGY_PPP = 2,
++ ACCESS_TECHNOLOGY_ETHERNET = 3,
++ ACCESS_TECHNOLOGY_WLAN = 4,
++ ACCESS_TECHNOLOGY_WIMAX = 5,
++};
++
++#endif /* DHCP_H */
+--- contrib/wpa/src/common/dpp.c.orig
++++ contrib/wpa/src/common/dpp.c
+@@ -0,0 +1,8721 @@
++/*
++ * DPP functionality shared between hostapd and wpa_supplicant
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ * Copyright (c) 2018-2019, The Linux Foundation
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include
++#include
++#include
++#include
++
++#include "utils/common.h"
++#include "utils/base64.h"
++#include "utils/json.h"
++#include "common/ieee802_11_common.h"
++#include "common/ieee802_11_defs.h"
++#include "common/wpa_ctrl.h"
++#include "common/gas.h"
++#include "crypto/crypto.h"
++#include "crypto/random.h"
++#include "crypto/aes.h"
++#include "crypto/aes_siv.h"
++#include "crypto/sha384.h"
++#include "crypto/sha512.h"
++#include "drivers/driver.h"
++#include "dpp.h"
++
++
++#ifdef CONFIG_TESTING_OPTIONS
++enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
++u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
++u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
++u8 dpp_pkex_ephemeral_key_override[600];
++size_t dpp_pkex_ephemeral_key_override_len = 0;
++u8 dpp_protocol_key_override[600];
++size_t dpp_protocol_key_override_len = 0;
++u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
++size_t dpp_nonce_override_len = 0;
++
++static int dpp_test_gen_invalid_key(struct wpabuf *msg,
++ const struct dpp_curve_params *curve);
++#endif /* CONFIG_TESTING_OPTIONS */
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
++/* Compatibility wrappers for older versions. */
++
++static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
++{
++ sig->r = r;
++ sig->s = s;
++ return 1;
++}
++
++
++static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
++ const BIGNUM **ps)
++{
++ if (pr)
++ *pr = sig->r;
++ if (ps)
++ *ps = sig->s;
++}
++
++#endif
++
++
++struct dpp_global {
++ struct dl_list bootstrap; /* struct dpp_bootstrap_info */
++ struct dl_list configurator; /* struct dpp_configurator */
++};
++
++static const struct dpp_curve_params dpp_curves[] = {
++ /* The mandatory to support and the default NIST P-256 curve needs to
++ * be the first entry on this list. */
++ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
++ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
++ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
++ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
++ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
++ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
++ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
++};
++
++
++/* Role-specific elements for PKEX */
++
++/* NIST P-256 */
++static const u8 pkex_init_x_p256[32] = {
++ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
++ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
++ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
++ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
++ };
++static const u8 pkex_init_y_p256[32] = {
++ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
++ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
++ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
++ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
++ };
++static const u8 pkex_resp_x_p256[32] = {
++ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
++ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
++ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
++ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
++};
++static const u8 pkex_resp_y_p256[32] = {
++ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
++ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
++ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
++ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
++};
++
++/* NIST P-384 */
++static const u8 pkex_init_x_p384[48] = {
++ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
++ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
++ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
++ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
++ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
++ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
++};
++static const u8 pkex_init_y_p384[48] = {
++ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
++ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
++ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
++ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
++ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
++ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
++};
++static const u8 pkex_resp_x_p384[48] = {
++ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
++ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
++ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
++ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
++ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
++ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
++};
++static const u8 pkex_resp_y_p384[48] = {
++ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
++ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
++ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
++ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
++ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
++ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
++};
++
++/* NIST P-521 */
++static const u8 pkex_init_x_p521[66] = {
++ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
++ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
++ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
++ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
++ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
++ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
++ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
++ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
++ 0x97, 0x76
++};
++static const u8 pkex_init_y_p521[66] = {
++ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
++ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
++ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
++ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
++ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
++ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
++ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
++ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
++ 0x03, 0xa8
++};
++static const u8 pkex_resp_x_p521[66] = {
++ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
++ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
++ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
++ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
++ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
++ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
++ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
++ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
++ 0x84, 0xb4
++};
++static const u8 pkex_resp_y_p521[66] = {
++ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
++ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
++ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
++ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
++ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
++ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
++ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
++ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
++ 0xce, 0xe1
++};
++
++/* Brainpool P-256r1 */
++static const u8 pkex_init_x_bp_p256r1[32] = {
++ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
++ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
++ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
++ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
++};
++static const u8 pkex_init_y_bp_p256r1[32] = {
++ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
++ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
++ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
++ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
++};
++static const u8 pkex_resp_x_bp_p256r1[32] = {
++ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
++ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
++ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
++ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
++};
++static const u8 pkex_resp_y_bp_p256r1[32] = {
++ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
++ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
++ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
++ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
++};
++
++/* Brainpool P-384r1 */
++static const u8 pkex_init_x_bp_p384r1[48] = {
++ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
++ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
++ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
++ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
++ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
++ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
++};
++static const u8 pkex_init_y_bp_p384r1[48] = {
++ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
++ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
++ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
++ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
++ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
++ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
++};
++static const u8 pkex_resp_x_bp_p384r1[48] = {
++ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
++ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
++ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
++ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
++ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
++ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
++};
++static const u8 pkex_resp_y_bp_p384r1[48] = {
++ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
++ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
++ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
++ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
++ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
++ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
++};
++
++/* Brainpool P-512r1 */
++static const u8 pkex_init_x_bp_p512r1[64] = {
++ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
++ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
++ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
++ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
++ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
++ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
++ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
++ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
++};
++static const u8 pkex_init_y_bp_p512r1[64] = {
++ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
++ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
++ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
++ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
++ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
++ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
++ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
++ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
++};
++static const u8 pkex_resp_x_bp_p512r1[64] = {
++ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
++ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
++ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
++ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
++ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
++ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
++ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
++ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
++};
++static const u8 pkex_resp_y_bp_p512r1[64] = {
++ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
++ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
++ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
++ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
++ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
++ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
++ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
++ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
++};
++
++
++static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
++ const EC_POINT *point)
++{
++ BIGNUM *x, *y;
++ BN_CTX *ctx;
++ char *x_str = NULL, *y_str = NULL;
++
++ if (!wpa_debug_show_keys)
++ return;
++
++ ctx = BN_CTX_new();
++ x = BN_new();
++ y = BN_new();
++ if (!ctx || !x || !y ||
++ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
++ goto fail;
++
++ x_str = BN_bn2hex(x);
++ y_str = BN_bn2hex(y);
++ if (!x_str || !y_str)
++ goto fail;
++
++ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
++
++fail:
++ OPENSSL_free(x_str);
++ OPENSSL_free(y_str);
++ BN_free(x);
++ BN_free(y);
++ BN_CTX_free(ctx);
++}
++
++
++static int dpp_hash_vector(const struct dpp_curve_params *curve,
++ size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ if (curve->hash_len == 32)
++ return sha256_vector(num_elem, addr, len, mac);
++ if (curve->hash_len == 48)
++ return sha384_vector(num_elem, addr, len, mac);
++ if (curve->hash_len == 64)
++ return sha512_vector(num_elem, addr, len, mac);
++ return -1;
++}
++
++
++static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
++ const char *label, u8 *out, size_t outlen)
++{
++ if (hash_len == 32)
++ return hmac_sha256_kdf(secret, secret_len, NULL,
++ (const u8 *) label, os_strlen(label),
++ out, outlen);
++ if (hash_len == 48)
++ return hmac_sha384_kdf(secret, secret_len, NULL,
++ (const u8 *) label, os_strlen(label),
++ out, outlen);
++ if (hash_len == 64)
++ return hmac_sha512_kdf(secret, secret_len, NULL,
++ (const u8 *) label, os_strlen(label),
++ out, outlen);
++ return -1;
++}
++
++
++static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
++ size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac)
++{
++ if (hash_len == 32)
++ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
++ mac);
++ if (hash_len == 48)
++ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
++ mac);
++ if (hash_len == 64)
++ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
++ mac);
++ return -1;
++}
++
++
++static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
++ const u8 *data, size_t data_len, u8 *mac)
++{
++ if (hash_len == 32)
++ return hmac_sha256(key, key_len, data, data_len, mac);
++ if (hash_len == 48)
++ return hmac_sha384(key, key_len, data, data_len, mac);
++ if (hash_len == 64)
++ return hmac_sha512(key, key_len, data, data_len, mac);
++ return -1;
++}
++
++
++static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
++{
++ int num_bytes, offset;
++
++ num_bytes = BN_num_bytes(bn);
++ if ((size_t) num_bytes > len)
++ return -1;
++ offset = len - num_bytes;
++ os_memset(pos, 0, offset);
++ BN_bn2bin(bn, pos + offset);
++ return 0;
++}
++
++
++static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
++{
++ int len, res;
++ EC_KEY *eckey;
++ struct wpabuf *buf;
++ unsigned char *pos;
++
++ eckey = EVP_PKEY_get1_EC_KEY(pkey);
++ if (!eckey)
++ return NULL;
++ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
++ len = i2o_ECPublicKey(eckey, NULL);
++ if (len <= 0) {
++ wpa_printf(MSG_ERROR,
++ "DDP: Failed to determine public key encoding length");
++ EC_KEY_free(eckey);
++ return NULL;
++ }
++
++ buf = wpabuf_alloc(len);
++ if (!buf) {
++ EC_KEY_free(eckey);
++ return NULL;
++ }
++
++ pos = wpabuf_put(buf, len);
++ res = i2o_ECPublicKey(eckey, &pos);
++ EC_KEY_free(eckey);
++ if (res != len) {
++ wpa_printf(MSG_ERROR,
++ "DDP: Failed to encode public key (res=%d/%d)",
++ res, len);
++ wpabuf_free(buf);
++ return NULL;
++ }
++
++ if (!prefix) {
++ /* Remove 0x04 prefix to match DPP definition */
++ pos = wpabuf_mhead(buf);
++ os_memmove(pos, pos + 1, len - 1);
++ buf->used--;
++ }
++
++ return buf;
++}
++
++
++static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
++ const u8 *buf_x, const u8 *buf_y,
++ size_t len)
++{
++ EC_KEY *eckey = NULL;
++ BN_CTX *ctx;
++ EC_POINT *point = NULL;
++ BIGNUM *x = NULL, *y = NULL;
++ EVP_PKEY *pkey = NULL;
++
++ ctx = BN_CTX_new();
++ if (!ctx) {
++ wpa_printf(MSG_ERROR, "DPP: Out of memory");
++ return NULL;
++ }
++
++ point = EC_POINT_new(group);
++ x = BN_bin2bn(buf_x, len, NULL);
++ y = BN_bin2bn(buf_y, len, NULL);
++ if (!point || !x || !y) {
++ wpa_printf(MSG_ERROR, "DPP: Out of memory");
++ goto fail;
++ }
++
++ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
++ wpa_printf(MSG_ERROR,
++ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ if (!EC_POINT_is_on_curve(group, point, ctx) ||
++ EC_POINT_is_at_infinity(group, point)) {
++ wpa_printf(MSG_ERROR, "DPP: Invalid point");
++ goto fail;
++ }
++ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
++
++ eckey = EC_KEY_new();
++ if (!eckey ||
++ EC_KEY_set_group(eckey, group) != 1 ||
++ EC_KEY_set_public_key(eckey, point) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to set EC_KEY: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
++
++ pkey = EVP_PKEY_new();
++ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
++ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
++ goto fail;
++ }
++
++out:
++ BN_free(x);
++ BN_free(y);
++ EC_KEY_free(eckey);
++ EC_POINT_free(point);
++ BN_CTX_free(ctx);
++ return pkey;
++fail:
++ EVP_PKEY_free(pkey);
++ pkey = NULL;
++ goto out;
++}
++
++
++static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
++ const u8 *buf, size_t len)
++{
++ EC_KEY *eckey;
++ const EC_GROUP *group;
++ EVP_PKEY *pkey = NULL;
++
++ if (len & 1)
++ return NULL;
++
++ eckey = EVP_PKEY_get1_EC_KEY(group_key);
++ if (!eckey) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Could not get EC_KEY from group_key");
++ return NULL;
++ }
++
++ group = EC_KEY_get0_group(eckey);
++ if (group)
++ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
++ len / 2);
++ else
++ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
++
++ EC_KEY_free(eckey);
++ return pkey;
++}
++
++
++static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
++{
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
++}
++
++
++struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
++ size_t len)
++{
++ struct wpabuf *msg;
++
++ msg = wpabuf_alloc(8 + len);
++ if (!msg)
++ return NULL;
++ wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
++ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
++ wpabuf_put_be24(msg, OUI_WFA);
++ wpabuf_put_u8(msg, DPP_OUI_TYPE);
++ wpabuf_put_u8(msg, 1); /* Crypto Suite */
++ wpabuf_put_u8(msg, type);
++ return msg;
++}
++
++
++const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
++{
++ u16 id, alen;
++ const u8 *pos = buf, *end = buf + len;
++
++ while (end - pos >= 4) {
++ id = WPA_GET_LE16(pos);
++ pos += 2;
++ alen = WPA_GET_LE16(pos);
++ pos += 2;
++ if (alen > end - pos)
++ return NULL;
++ if (id == req_id) {
++ *ret_len = alen;
++ return pos;
++ }
++ pos += alen;
++ }
++
++ return NULL;
++}
++
++
++int dpp_check_attrs(const u8 *buf, size_t len)
++{
++ const u8 *pos, *end;
++ int wrapped_data = 0;
++
++ pos = buf;
++ end = buf + len;
++ while (end - pos >= 4) {
++ u16 id, alen;
++
++ id = WPA_GET_LE16(pos);
++ pos += 2;
++ alen = WPA_GET_LE16(pos);
++ pos += 2;
++ wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
++ id, alen);
++ if (alen > end - pos) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Truncated message - not enough room for the attribute - dropped");
++ return -1;
++ }
++ if (wrapped_data) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: An unexpected attribute included after the Wrapped Data attribute");
++ return -1;
++ }
++ if (id == DPP_ATTR_WRAPPED_DATA)
++ wrapped_data = 1;
++ pos += alen;
++ }
++
++ if (end != pos) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected octets (%d) after the last attribute",
++ (int) (end - pos));
++ return -1;
++ }
++
++ return 0;
++}
++
++
++void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
++{
++ if (!info)
++ return;
++ os_free(info->uri);
++ os_free(info->info);
++ EVP_PKEY_free(info->pubkey);
++ os_free(info);
++}
++
++
++const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
++{
++ switch (type) {
++ case DPP_BOOTSTRAP_QR_CODE:
++ return "QRCODE";
++ case DPP_BOOTSTRAP_PKEX:
++ return "PKEX";
++ }
++ return "??";
++}
++
++
++static int dpp_uri_valid_info(const char *info)
++{
++ while (*info) {
++ unsigned char val = *info++;
++
++ if (val < 0x20 || val > 0x7e || val == 0x3b)
++ return 0;
++ }
++
++ return 1;
++}
++
++
++static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
++{
++ bi->uri = os_strdup(uri);
++ return bi->uri ? 0 : -1;
++}
++
++
++int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
++ const char *chan_list)
++{
++ const char *pos = chan_list;
++ int opclass, channel, freq;
++
++ while (pos && *pos && *pos != ';') {
++ opclass = atoi(pos);
++ if (opclass <= 0)
++ goto fail;
++ pos = os_strchr(pos, '/');
++ if (!pos)
++ goto fail;
++ pos++;
++ channel = atoi(pos);
++ if (channel <= 0)
++ goto fail;
++ while (*pos >= '0' && *pos <= '9')
++ pos++;
++ freq = ieee80211_chan_to_freq(NULL, opclass, channel);
++ wpa_printf(MSG_DEBUG,
++ "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
++ opclass, channel, freq);
++ if (freq < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
++ opclass, channel);
++ } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Too many channels in URI channel-list - ignore list");
++ bi->num_freq = 0;
++ break;
++ } else {
++ bi->freq[bi->num_freq++] = freq;
++ }
++
++ if (*pos == ';' || *pos == '\0')
++ break;
++ if (*pos != ',')
++ goto fail;
++ pos++;
++ }
++
++ return 0;
++fail:
++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
++ return -1;
++}
++
++
++int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
++{
++ if (!mac)
++ return 0;
++
++ if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
++
++ return 0;
++}
++
++
++int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
++{
++ const char *end;
++
++ if (!info)
++ return 0;
++
++ end = os_strchr(info, ';');
++ if (!end)
++ end = info + os_strlen(info);
++ bi->info = os_malloc(end - info + 1);
++ if (!bi->info)
++ return -1;
++ os_memcpy(bi->info, info, end - info);
++ bi->info[end - info] = '\0';
++ wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
++ if (!dpp_uri_valid_info(bi->info)) {
++ wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
++ return -1;
++ }
++
++ return 0;
++}
++
++
++static const struct dpp_curve_params *
++dpp_get_curve_oid(const ASN1_OBJECT *poid)
++{
++ ASN1_OBJECT *oid;
++ int i;
++
++ for (i = 0; dpp_curves[i].name; i++) {
++ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
++ if (oid && OBJ_cmp(poid, oid) == 0)
++ return &dpp_curves[i];
++ }
++ return NULL;
++}
++
++
++static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
++{
++ int i, tmp;
++
++ if (!nid)
++ return NULL;
++ for (i = 0; dpp_curves[i].name; i++) {
++ tmp = OBJ_txt2nid(dpp_curves[i].name);
++ if (tmp == nid)
++ return &dpp_curves[i];
++ }
++ return NULL;
++}
++
++
++static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
++{
++ const char *end;
++ u8 *data;
++ size_t data_len;
++ EVP_PKEY *pkey;
++ const unsigned char *p;
++ int res;
++ X509_PUBKEY *pub = NULL;
++ ASN1_OBJECT *ppkalg;
++ const unsigned char *pk;
++ int ppklen;
++ X509_ALGOR *pa;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20800000L)
++ ASN1_OBJECT *pa_oid;
++#else
++ const ASN1_OBJECT *pa_oid;
++#endif
++ const void *pval;
++ int ptype;
++ const ASN1_OBJECT *poid;
++ char buf[100];
++
++ end = os_strchr(info, ';');
++ if (!end)
++ return -1;
++
++ data = base64_decode((const unsigned char *) info, end - info,
++ &data_len);
++ if (!data) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Invalid base64 encoding on URI public-key");
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
++ data, data_len);
++
++ if (sha256_vector(1, (const u8 **) &data, &data_len,
++ bi->pubkey_hash) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
++ os_free(data);
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
++ bi->pubkey_hash, SHA256_MAC_LEN);
++
++ /* DER encoded ASN.1 SubjectPublicKeyInfo
++ *
++ * SubjectPublicKeyInfo ::= SEQUENCE {
++ * algorithm AlgorithmIdentifier,
++ * subjectPublicKey BIT STRING }
++ *
++ * AlgorithmIdentifier ::= SEQUENCE {
++ * algorithm OBJECT IDENTIFIER,
++ * parameters ANY DEFINED BY algorithm OPTIONAL }
++ *
++ * subjectPublicKey = compressed format public key per ANSI X9.63
++ * algorithm = ecPublicKey (1.2.840.10045.2.1)
++ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
++ * prime256v1 (1.2.840.10045.3.1.7)
++ */
++
++ p = data;
++ pkey = d2i_PUBKEY(NULL, &p, data_len);
++ os_free(data);
++
++ if (!pkey) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
++ return -1;
++ }
++
++ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: SubjectPublicKeyInfo does not describe an EC key");
++ EVP_PKEY_free(pkey);
++ return -1;
++ }
++
++ res = X509_PUBKEY_set(&pub, pkey);
++ if (res != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
++ goto fail;
++ }
++
++ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
++ if (res != 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not extract SubjectPublicKeyInfo parameters");
++ goto fail;
++ }
++ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
++ if (res < 0 || (size_t) res >= sizeof(buf)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
++ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
++ goto fail;
++ }
++
++ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
++ if (ptype != V_ASN1_OBJECT) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
++ goto fail;
++ }
++ poid = pval;
++ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
++ if (res < 0 || (size_t) res >= sizeof(buf)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
++ bi->curve = dpp_get_curve_oid(poid);
++ if (!bi->curve) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
++ buf);
++ goto fail;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
++
++ X509_PUBKEY_free(pub);
++ bi->pubkey = pkey;
++ return 0;
++fail:
++ X509_PUBKEY_free(pub);
++ EVP_PKEY_free(pkey);
++ return -1;
++}
++
++
++static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
++{
++ const char *pos = uri;
++ const char *end;
++ const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
++ struct dpp_bootstrap_info *bi;
++
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
++
++ if (os_strncmp(pos, "DPP:", 4) != 0) {
++ wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
++ return NULL;
++ }
++ pos += 4;
++
++ for (;;) {
++ end = os_strchr(pos, ';');
++ if (!end)
++ break;
++
++ if (end == pos) {
++ /* Handle terminating ";;" and ignore unexpected ";"
++ * for parsing robustness. */
++ pos++;
++ continue;
++ }
++
++ if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
++ chan_list = pos + 2;
++ else if (pos[0] == 'M' && pos[1] == ':' && !mac)
++ mac = pos + 2;
++ else if (pos[0] == 'I' && pos[1] == ':' && !info)
++ info = pos + 2;
++ else if (pos[0] == 'K' && pos[1] == ':' && !pk)
++ pk = pos + 2;
++ else
++ wpa_hexdump_ascii(MSG_DEBUG,
++ "DPP: Ignore unrecognized URI parameter",
++ pos, end - pos);
++ pos = end + 1;
++ }
++
++ if (!pk) {
++ wpa_printf(MSG_INFO, "DPP: URI missing public-key");
++ return NULL;
++ }
++
++ bi = os_zalloc(sizeof(*bi));
++ if (!bi)
++ return NULL;
++
++ if (dpp_clone_uri(bi, uri) < 0 ||
++ dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
++ dpp_parse_uri_mac(bi, mac) < 0 ||
++ dpp_parse_uri_info(bi, info) < 0 ||
++ dpp_parse_uri_pk(bi, pk) < 0) {
++ dpp_bootstrap_info_free(bi);
++ bi = NULL;
++ }
++
++ return bi;
++}
++
++
++struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
++{
++ struct dpp_bootstrap_info *bi;
++
++ bi = dpp_parse_uri(uri);
++ if (bi)
++ bi->type = DPP_BOOTSTRAP_QR_CODE;
++ return bi;
++}
++
++
++static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
++{
++ EC_KEY *eckey;
++ BIO *out;
++ size_t rlen;
++ char *txt;
++ int res;
++ unsigned char *der = NULL;
++ int der_len;
++ const EC_GROUP *group;
++ const EC_POINT *point;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return;
++
++ EVP_PKEY_print_private(out, key, 0, NULL);
++ rlen = BIO_ctrl_pending(out);
++ txt = os_malloc(rlen + 1);
++ if (txt) {
++ res = BIO_read(out, txt, rlen);
++ if (res > 0) {
++ txt[res] = '\0';
++ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
++ }
++ os_free(txt);
++ }
++ BIO_free(out);
++
++ eckey = EVP_PKEY_get1_EC_KEY(key);
++ if (!eckey)
++ return;
++
++ group = EC_KEY_get0_group(eckey);
++ point = EC_KEY_get0_public_key(eckey);
++ if (group && point)
++ dpp_debug_print_point(title, group, point);
++
++ der_len = i2d_ECPrivateKey(eckey, &der);
++ if (der_len > 0)
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
++ OPENSSL_free(der);
++ if (der_len <= 0) {
++ der = NULL;
++ der_len = i2d_EC_PUBKEY(eckey, &der);
++ if (der_len > 0)
++ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
++ OPENSSL_free(der);
++ }
++
++ EC_KEY_free(eckey);
++}
++
++
++static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
++{
++ EVP_PKEY_CTX *kctx = NULL;
++ EC_KEY *ec_params;
++ EVP_PKEY *params = NULL, *key = NULL;
++ int nid;
++
++ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
++
++ nid = OBJ_txt2nid(curve->name);
++ if (nid == NID_undef) {
++ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
++ return NULL;
++ }
++
++ ec_params = EC_KEY_new_by_curve_name(nid);
++ if (!ec_params) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to generate EC_KEY parameters");
++ goto fail;
++ }
++ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
++ params = EVP_PKEY_new();
++ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to generate EVP_PKEY parameters");
++ goto fail;
++ }
++
++ kctx = EVP_PKEY_CTX_new(params, NULL);
++ if (!kctx ||
++ EVP_PKEY_keygen_init(kctx) != 1 ||
++ EVP_PKEY_keygen(kctx, &key) != 1) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
++ goto fail;
++ }
++
++ if (wpa_debug_show_keys)
++ dpp_debug_print_key("Own generated key", key);
++
++ EVP_PKEY_free(params);
++ EVP_PKEY_CTX_free(kctx);
++ return key;
++fail:
++ EVP_PKEY_CTX_free(kctx);
++ EVP_PKEY_free(params);
++ return NULL;
++}
++
++
++static const struct dpp_curve_params *
++dpp_get_curve_name(const char *name)
++{
++ int i;
++
++ for (i = 0; dpp_curves[i].name; i++) {
++ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
++ (dpp_curves[i].jwk_crv &&
++ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
++ return &dpp_curves[i];
++ }
++ return NULL;
++}
++
++
++static const struct dpp_curve_params *
++dpp_get_curve_jwk_crv(const char *name)
++{
++ int i;
++
++ for (i = 0; dpp_curves[i].name; i++) {
++ if (dpp_curves[i].jwk_crv &&
++ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
++ return &dpp_curves[i];
++ }
++ return NULL;
++}
++
++
++static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
++ const u8 *privkey, size_t privkey_len)
++{
++ EVP_PKEY *pkey;
++ EC_KEY *eckey;
++ const EC_GROUP *group;
++ int nid;
++
++ pkey = EVP_PKEY_new();
++ if (!pkey)
++ return NULL;
++ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
++ if (!eckey) {
++ wpa_printf(MSG_INFO,
++ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ EVP_PKEY_free(pkey);
++ return NULL;
++ }
++ group = EC_KEY_get0_group(eckey);
++ if (!group) {
++ EC_KEY_free(eckey);
++ EVP_PKEY_free(pkey);
++ return NULL;
++ }
++ nid = EC_GROUP_get_curve_name(group);
++ *curve = dpp_get_curve_nid(nid);
++ if (!*curve) {
++ wpa_printf(MSG_INFO,
++ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
++ nid);
++ EC_KEY_free(eckey);
++ EVP_PKEY_free(pkey);
++ return NULL;
++ }
++
++ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
++ EC_KEY_free(eckey);
++ EVP_PKEY_free(pkey);
++ return NULL;
++ }
++ return pkey;
++}
++
++
++typedef struct {
++ /* AlgorithmIdentifier ecPublicKey with optional parameters present
++ * as an OID identifying the curve */
++ X509_ALGOR *alg;
++ /* Compressed format public key per ANSI X9.63 */
++ ASN1_BIT_STRING *pub_key;
++} DPP_BOOTSTRAPPING_KEY;
++
++ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
++ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
++ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
++} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
++
++IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
++
++
++static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
++{
++ unsigned char *der = NULL;
++ int der_len;
++ EC_KEY *eckey;
++ struct wpabuf *ret = NULL;
++ size_t len;
++ const EC_GROUP *group;
++ const EC_POINT *point;
++ BN_CTX *ctx;
++ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
++ int nid;
++
++ ctx = BN_CTX_new();
++ eckey = EVP_PKEY_get1_EC_KEY(key);
++ if (!ctx || !eckey)
++ goto fail;
++
++ group = EC_KEY_get0_group(eckey);
++ point = EC_KEY_get0_public_key(eckey);
++ if (!group || !point)
++ goto fail;
++ dpp_debug_print_point("DPP: bootstrap public key", group, point);
++ nid = EC_GROUP_get_curve_name(group);
++
++ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
++ if (!bootstrap ||
++ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
++ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
++ goto fail;
++
++ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
++ NULL, 0, ctx);
++ if (len == 0)
++ goto fail;
++
++ der = OPENSSL_malloc(len);
++ if (!der)
++ goto fail;
++ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
++ der, len, ctx);
++
++ OPENSSL_free(bootstrap->pub_key->data);
++ bootstrap->pub_key->data = der;
++ der = NULL;
++ bootstrap->pub_key->length = len;
++ /* No unused bits */
++ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
++ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
++
++ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
++ if (der_len <= 0) {
++ wpa_printf(MSG_ERROR,
++ "DDP: Failed to build DER encoded public key");
++ goto fail;
++ }
++
++ ret = wpabuf_alloc_copy(der, der_len);
++fail:
++ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
++ OPENSSL_free(der);
++ EC_KEY_free(eckey);
++ BN_CTX_free(ctx);
++ return ret;
++}
++
++
++int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
++{
++ struct wpabuf *der;
++ int res;
++ const u8 *addr[1];
++ size_t len[1];
++
++ der = dpp_bootstrap_key_der(bi->pubkey);
++ if (!der)
++ return -1;
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
++ der);
++
++ addr[0] = wpabuf_head(der);
++ len[0] = wpabuf_len(der);
++ res = sha256_vector(1, addr, len, bi->pubkey_hash);
++ if (res < 0)
++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
++ else
++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
++ SHA256_MAC_LEN);
++ wpabuf_free(der);
++ return res;
++}
++
++
++char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
++ const u8 *privkey, size_t privkey_len)
++{
++ unsigned char *base64 = NULL;
++ char *pos, *end;
++ size_t len;
++ struct wpabuf *der = NULL;
++ const u8 *addr[1];
++ int res;
++
++ if (!curve) {
++ bi->curve = &dpp_curves[0];
++ } else {
++ bi->curve = dpp_get_curve_name(curve);
++ if (!bi->curve) {
++ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
++ curve);
++ return NULL;
++ }
++ }
++ if (privkey)
++ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
++ else
++ bi->pubkey = dpp_gen_keypair(bi->curve);
++ if (!bi->pubkey)
++ goto fail;
++ bi->own = 1;
++
++ der = dpp_bootstrap_key_der(bi->pubkey);
++ if (!der)
++ goto fail;
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
++ der);
++
++ addr[0] = wpabuf_head(der);
++ len = wpabuf_len(der);
++ res = sha256_vector(1, addr, &len, bi->pubkey_hash);
++ if (res < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
++ SHA256_MAC_LEN);
++
++ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
++ wpabuf_free(der);
++ der = NULL;
++ if (!base64)
++ goto fail;
++ pos = (char *) base64;
++ end = pos + len;
++ for (;;) {
++ pos = os_strchr(pos, '\n');
++ if (!pos)
++ break;
++ os_memmove(pos, pos + 1, end - pos);
++ }
++ return (char *) base64;
++fail:
++ os_free(base64);
++ wpabuf_free(der);
++ return NULL;
++}
++
++
++static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
++ unsigned int hash_len)
++{
++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
++ const char *info = "first intermediate key";
++ int res;
++
++ /* k1 = HKDF(<>, "first intermediate key", M.x) */
++
++ /* HKDF-Extract(<>, M.x) */
++ os_memset(salt, 0, hash_len);
++ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
++ prk, hash_len);
++
++ /* HKDF-Expand(PRK, info, L) */
++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
++ os_memset(prk, 0, hash_len);
++ if (res < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
++ k1, hash_len);
++ return 0;
++}
++
++
++static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
++ unsigned int hash_len)
++{
++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
++ const char *info = "second intermediate key";
++ int res;
++
++ /* k2 = HKDF(<>, "second intermediate key", N.x) */
++
++ /* HKDF-Extract(<>, N.x) */
++ os_memset(salt, 0, hash_len);
++ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
++ if (res < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
++ prk, hash_len);
++
++ /* HKDF-Expand(PRK, info, L) */
++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
++ os_memset(prk, 0, hash_len);
++ if (res < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
++ k2, hash_len);
++ return 0;
++}
++
++
++static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
++ unsigned int hash_len)
++{
++ size_t nonce_len;
++ u8 nonces[2 * DPP_MAX_NONCE_LEN];
++ const char *info_ke = "DPP Key";
++ u8 prk[DPP_MAX_HASH_LEN];
++ int res;
++ const u8 *addr[3];
++ size_t len[3];
++ size_t num_elem = 0;
++
++ if (!auth->Mx_len || !auth->Nx_len) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Mx/Nx not available - cannot derive ke");
++ return -1;
++ }
++
++ /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
++
++ /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
++ nonce_len = auth->curve->nonce_len;
++ os_memcpy(nonces, auth->i_nonce, nonce_len);
++ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
++ addr[num_elem] = auth->Mx;
++ len[num_elem] = auth->Mx_len;
++ num_elem++;
++ addr[num_elem] = auth->Nx;
++ len[num_elem] = auth->Nx_len;
++ num_elem++;
++ if (auth->peer_bi && auth->own_bi) {
++ if (!auth->Lx_len) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Lx not available - cannot derive ke");
++ return -1;
++ }
++ addr[num_elem] = auth->Lx;
++ len[num_elem] = auth->secret_len;
++ num_elem++;
++ }
++ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
++ num_elem, addr, len, prk);
++ if (res < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
++ prk, hash_len);
++
++ /* HKDF-Expand(PRK, info, L) */
++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
++ os_memset(prk, 0, hash_len);
++ if (res < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
++ ke, hash_len);
++ return 0;
++}
++
++
++static void dpp_build_attr_status(struct wpabuf *msg,
++ enum dpp_status_error status)
++{
++ wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
++ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
++ wpabuf_put_le16(msg, 1);
++ wpabuf_put_u8(msg, status);
++}
++
++
++static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
++ const u8 *hash)
++{
++ if (hash) {
++ wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
++ wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
++ wpabuf_put_le16(msg, SHA256_MAC_LEN);
++ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
++ }
++}
++
++
++static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
++ const u8 *hash)
++{
++ if (hash) {
++ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
++ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
++ wpabuf_put_le16(msg, SHA256_MAC_LEN);
++ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
++ }
++}
++
++
++static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
++ const struct wpabuf *pi,
++ size_t nonce_len,
++ const u8 *r_pubkey_hash,
++ const u8 *i_pubkey_hash,
++ unsigned int neg_freq)
++{
++ struct wpabuf *msg;
++ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
++ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
++ u8 *pos;
++ const u8 *addr[2];
++ size_t len[2], siv_len, attr_len;
++ u8 *attr_start, *attr_end;
++
++ /* Build DPP Authentication Request frame attributes */
++ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
++ 4 + sizeof(wrapped_data);
++ if (neg_freq > 0)
++ attr_len += 4 + 2;
++#ifdef CONFIG_DPP2
++ attr_len += 5;
++#endif /* CONFIG_DPP2 */
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
++ if (!msg)
++ return NULL;
++
++ attr_start = wpabuf_put(msg, 0);
++
++ /* Responder Bootstrapping Key Hash */
++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
++
++ /* Initiator Bootstrapping Key Hash */
++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
++
++ /* Initiator Protocol Key */
++ if (pi) {
++ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
++ wpabuf_put_le16(msg, wpabuf_len(pi));
++ wpabuf_put_buf(msg, pi);
++ }
++
++ /* Channel */
++ if (neg_freq > 0) {
++ u8 op_class, channel;
++
++ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
++ &channel) ==
++ NUM_HOSTAPD_MODES) {
++ wpa_printf(MSG_INFO,
++ "DPP: Unsupported negotiation frequency request: %d",
++ neg_freq);
++ wpabuf_free(msg);
++ return NULL;
++ }
++ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
++ wpabuf_put_le16(msg, 2);
++ wpabuf_put_u8(msg, op_class);
++ wpabuf_put_u8(msg, channel);
++ }
++
++#ifdef CONFIG_DPP2
++ /* Protocol Version */
++ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
++ wpabuf_put_le16(msg, 1);
++ wpabuf_put_u8(msg, 2);
++#endif /* CONFIG_DPP2 */
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Wrapped data ({I-nonce, I-capabilities}k1) */
++ pos = clear;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
++ goto skip_i_nonce;
++ }
++ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
++ pos += 2;
++ WPA_PUT_LE16(pos, nonce_len - 1);
++ pos += 2;
++ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
++ pos += nonce_len - 1;
++ goto skip_i_nonce;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* I-nonce */
++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
++ pos += 2;
++ WPA_PUT_LE16(pos, nonce_len);
++ pos += 2;
++ os_memcpy(pos, auth->i_nonce, nonce_len);
++ pos += nonce_len;
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_i_nonce:
++ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
++ goto skip_i_capab;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* I-capabilities */
++ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
++ pos += 2;
++ WPA_PUT_LE16(pos, 1);
++ pos += 2;
++ auth->i_capab = auth->allowed_roles;
++ *pos++ = auth->i_capab;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
++ pos[-1] = 0;
++ }
++skip_i_capab:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ attr_end = wpabuf_put(msg, 0);
++
++ /* OUI, OUI type, Crypto Suite, DPP frame type */
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = 3 + 1 + 1 + 1;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++
++ /* Attributes before Wrapped Data */
++ addr[1] = attr_start;
++ len[1] = attr_end - attr_start;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ siv_len = pos - clear;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
++ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
++ 2, addr, len, wrapped_data) < 0) {
++ wpabuf_free(msg);
++ return NULL;
++ }
++ siv_len += AES_BLOCK_SIZE;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, siv_len);
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, siv_len);
++ wpabuf_put_data(msg, wrapped_data, siv_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_hexdump_buf(MSG_DEBUG,
++ "DPP: Authentication Request frame attributes", msg);
++
++ return msg;
++}
++
++
++static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
++ enum dpp_status_error status,
++ const struct wpabuf *pr,
++ size_t nonce_len,
++ const u8 *r_pubkey_hash,
++ const u8 *i_pubkey_hash,
++ const u8 *r_nonce, const u8 *i_nonce,
++ const u8 *wrapped_r_auth,
++ size_t wrapped_r_auth_len,
++ const u8 *siv_key)
++{
++ struct wpabuf *msg;
++#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
++ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
++ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
++ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
++ const u8 *addr[2];
++ size_t len[2], siv_len, attr_len;
++ u8 *attr_start, *attr_end, *pos;
++
++ auth->waiting_auth_conf = 1;
++ auth->auth_resp_tries = 0;
++
++ /* Build DPP Authentication Response frame attributes */
++ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
++ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
++#ifdef CONFIG_DPP2
++ attr_len += 5;
++#endif /* CONFIG_DPP2 */
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
++ if (!msg)
++ return NULL;
++
++ attr_start = wpabuf_put(msg, 0);
++
++ /* DPP Status */
++ if (status != 255)
++ dpp_build_attr_status(msg, status);
++
++ /* Responder Bootstrapping Key Hash */
++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
++
++ /* Initiator Bootstrapping Key Hash (mutual authentication) */
++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
++
++ /* Responder Protocol Key */
++ if (pr) {
++ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
++ wpabuf_put_le16(msg, wpabuf_len(pr));
++ wpabuf_put_buf(msg, pr);
++ }
++
++#ifdef CONFIG_DPP2
++ /* Protocol Version */
++ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
++ wpabuf_put_le16(msg, 1);
++ wpabuf_put_u8(msg, 2);
++#endif /* CONFIG_DPP2 */
++
++ attr_end = wpabuf_put(msg, 0);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
++ pos = clear;
++
++ if (r_nonce) {
++ /* R-nonce */
++ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
++ pos += 2;
++ WPA_PUT_LE16(pos, nonce_len);
++ pos += 2;
++ os_memcpy(pos, r_nonce, nonce_len);
++ pos += nonce_len;
++ }
++
++ if (i_nonce) {
++ /* I-nonce */
++ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
++ pos += 2;
++ WPA_PUT_LE16(pos, nonce_len);
++ pos += 2;
++ os_memcpy(pos, i_nonce, nonce_len);
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
++ pos[nonce_len / 2] ^= 0x01;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ pos += nonce_len;
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
++ goto skip_r_capab;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* R-capabilities */
++ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
++ pos += 2;
++ WPA_PUT_LE16(pos, 1);
++ pos += 2;
++ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
++ DPP_CAPAB_ENROLLEE;
++ *pos++ = auth->r_capab;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
++ pos[-1] = 0;
++ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - incompatible R-capabilities");
++ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
++ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
++ pos[-1] = 0;
++ else
++ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
++ DPP_CAPAB_CONFIGURATOR;
++ }
++skip_r_capab:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (wrapped_r_auth) {
++ /* {R-auth}ke */
++ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
++ pos += 2;
++ WPA_PUT_LE16(pos, wrapped_r_auth_len);
++ pos += 2;
++ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
++ pos += wrapped_r_auth_len;
++ }
++
++ /* OUI, OUI type, Crypto Suite, DPP frame type */
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = 3 + 1 + 1 + 1;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++
++ /* Attributes before Wrapped Data */
++ addr[1] = attr_start;
++ len[1] = attr_end - attr_start;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ siv_len = pos - clear;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
++ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
++ 2, addr, len, wrapped_data) < 0) {
++ wpabuf_free(msg);
++ return NULL;
++ }
++ siv_len += AES_BLOCK_SIZE;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, siv_len);
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, siv_len);
++ wpabuf_put_data(msg, wrapped_data, siv_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_hexdump_buf(MSG_DEBUG,
++ "DPP: Authentication Response frame attributes", msg);
++ return msg;
++}
++
++
++static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
++ u16 num_modes, unsigned int freq)
++{
++ u16 m;
++ int c, flag;
++
++ if (!own_modes || !num_modes)
++ return 1;
++
++ for (m = 0; m < num_modes; m++) {
++ for (c = 0; c < own_modes[m].num_channels; c++) {
++ if ((unsigned int) own_modes[m].channels[c].freq !=
++ freq)
++ continue;
++ flag = own_modes[m].channels[c].flag;
++ if (!(flag & (HOSTAPD_CHAN_DISABLED |
++ HOSTAPD_CHAN_NO_IR |
++ HOSTAPD_CHAN_RADAR)))
++ return 1;
++ }
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
++ return 0;
++}
++
++
++static int freq_included(const unsigned int freqs[], unsigned int num,
++ unsigned int freq)
++{
++ while (num > 0) {
++ if (freqs[--num] == freq)
++ return 1;
++ }
++ return 0;
++}
++
++
++static void freq_to_start(unsigned int freqs[], unsigned int num,
++ unsigned int freq)
++{
++ unsigned int i;
++
++ for (i = 0; i < num; i++) {
++ if (freqs[i] == freq)
++ break;
++ }
++ if (i == 0 || i >= num)
++ return;
++ os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
++ freqs[0] = freq;
++}
++
++
++static int dpp_channel_intersect(struct dpp_authentication *auth,
++ struct hostapd_hw_modes *own_modes,
++ u16 num_modes)
++{
++ struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
++ unsigned int i, freq;
++
++ for (i = 0; i < peer_bi->num_freq; i++) {
++ freq = peer_bi->freq[i];
++ if (freq_included(auth->freq, auth->num_freq, freq))
++ continue;
++ if (dpp_channel_ok_init(own_modes, num_modes, freq))
++ auth->freq[auth->num_freq++] = freq;
++ }
++ if (!auth->num_freq) {
++ wpa_printf(MSG_INFO,
++ "DPP: No available channels for initiating DPP Authentication");
++ return -1;
++ }
++ auth->curr_freq = auth->freq[0];
++ return 0;
++}
++
++
++static int dpp_channel_local_list(struct dpp_authentication *auth,
++ struct hostapd_hw_modes *own_modes,
++ u16 num_modes)
++{
++ u16 m;
++ int c, flag;
++ unsigned int freq;
++
++ auth->num_freq = 0;
++
++ if (!own_modes || !num_modes) {
++ auth->freq[0] = 2412;
++ auth->freq[1] = 2437;
++ auth->freq[2] = 2462;
++ auth->num_freq = 3;
++ return 0;
++ }
++
++ for (m = 0; m < num_modes; m++) {
++ for (c = 0; c < own_modes[m].num_channels; c++) {
++ freq = own_modes[m].channels[c].freq;
++ flag = own_modes[m].channels[c].flag;
++ if (flag & (HOSTAPD_CHAN_DISABLED |
++ HOSTAPD_CHAN_NO_IR |
++ HOSTAPD_CHAN_RADAR))
++ continue;
++ if (freq_included(auth->freq, auth->num_freq, freq))
++ continue;
++ auth->freq[auth->num_freq++] = freq;
++ if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
++ m = num_modes;
++ break;
++ }
++ }
++ }
++
++ return auth->num_freq == 0 ? -1 : 0;
++}
++
++
++static int dpp_prepare_channel_list(struct dpp_authentication *auth,
++ struct hostapd_hw_modes *own_modes,
++ u16 num_modes)
++{
++ int res;
++ char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
++ unsigned int i;
++
++ if (auth->peer_bi->num_freq > 0)
++ res = dpp_channel_intersect(auth, own_modes, num_modes);
++ else
++ res = dpp_channel_local_list(auth, own_modes, num_modes);
++ if (res < 0)
++ return res;
++
++ /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
++ * likely channels first. */
++ freq_to_start(auth->freq, auth->num_freq, 2462);
++ freq_to_start(auth->freq, auth->num_freq, 2412);
++ freq_to_start(auth->freq, auth->num_freq, 2437);
++
++ auth->freq_idx = 0;
++ auth->curr_freq = auth->freq[0];
++
++ pos = freqs;
++ end = pos + sizeof(freqs);
++ for (i = 0; i < auth->num_freq; i++) {
++ res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
++ if (os_snprintf_error(end - pos, res))
++ break;
++ pos += res;
++ }
++ *pos = '\0';
++ wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
++ freqs);
++
++ return 0;
++}
++
++
++static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
++{
++ struct dpp_bootstrap_info *bi;
++ char *pk = NULL;
++ size_t len;
++
++ if (auth->own_bi)
++ return 0; /* already generated */
++
++ bi = os_zalloc(sizeof(*bi));
++ if (!bi)
++ return -1;
++ bi->type = DPP_BOOTSTRAP_QR_CODE;
++ pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
++ if (!pk)
++ goto fail;
++
++ len = 4; /* "DPP:" */
++ len += 4 + os_strlen(pk);
++ bi->uri = os_malloc(len + 1);
++ if (!bi->uri)
++ goto fail;
++ os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
++ wpa_printf(MSG_DEBUG,
++ "DPP: Auto-generated own bootstrapping key info: URI %s",
++ bi->uri);
++
++ auth->tmp_own_bi = auth->own_bi = bi;
++
++ os_free(pk);
++
++ return 0;
++fail:
++ os_free(pk);
++ dpp_bootstrap_info_free(bi);
++ return -1;
++}
++
++
++struct dpp_authentication * dpp_auth_init(void *msg_ctx,
++ struct dpp_bootstrap_info *peer_bi,
++ struct dpp_bootstrap_info *own_bi,
++ u8 dpp_allowed_roles,
++ unsigned int neg_freq,
++ struct hostapd_hw_modes *own_modes,
++ u16 num_modes)
++{
++ struct dpp_authentication *auth;
++ size_t nonce_len;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t secret_len;
++ struct wpabuf *pi = NULL;
++ const u8 *r_pubkey_hash, *i_pubkey_hash;
++#ifdef CONFIG_TESTING_OPTIONS
++ u8 test_hash[SHA256_MAC_LEN];
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ auth = os_zalloc(sizeof(*auth));
++ if (!auth)
++ return NULL;
++ auth->msg_ctx = msg_ctx;
++ auth->initiator = 1;
++ auth->waiting_auth_resp = 1;
++ auth->allowed_roles = dpp_allowed_roles;
++ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
++ auth->peer_bi = peer_bi;
++ auth->own_bi = own_bi;
++ auth->curve = peer_bi->curve;
++
++ if (dpp_autogen_bootstrap_key(auth) < 0 ||
++ dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_nonce_override_len > 0) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
++ nonce_len = dpp_nonce_override_len;
++ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
++ } else {
++ nonce_len = auth->curve->nonce_len;
++ if (random_get_bytes(auth->i_nonce, nonce_len)) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to generate I-nonce");
++ goto fail;
++ }
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ nonce_len = auth->curve->nonce_len;
++ if (random_get_bytes(auth->i_nonce, nonce_len)) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
++ goto fail;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_protocol_key_override_len) {
++ const struct dpp_curve_params *tmp_curve;
++
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - override protocol key");
++ auth->own_protocol_key = dpp_set_keypair(
++ &tmp_curve, dpp_protocol_key_override,
++ dpp_protocol_key_override_len);
++ } else {
++ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (!auth->own_protocol_key)
++ goto fail;
++
++ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ if (!pi)
++ goto fail;
++
++ /* ECDH: M = pI * BR */
++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
++ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ auth->secret_len = secret_len;
++ EVP_PKEY_CTX_free(ctx);
++ ctx = NULL;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
++ auth->Mx, auth->secret_len);
++ auth->Mx_len = auth->secret_len;
++
++ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
++ auth->curve->hash_len) < 0)
++ goto fail;
++
++ r_pubkey_hash = auth->peer_bi->pubkey_hash;
++ i_pubkey_hash = auth->own_bi->pubkey_hash;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
++ r_pubkey_hash = NULL;
++ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid R-Bootstrap Key Hash");
++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ r_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
++ i_pubkey_hash = NULL;
++ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid I-Bootstrap Key Hash");
++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ i_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
++ wpabuf_free(pi);
++ pi = NULL;
++ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
++ wpabuf_free(pi);
++ pi = wpabuf_alloc(2 * auth->curve->prime_len);
++ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
++ goto fail;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
++ i_pubkey_hash, neg_freq);
++ if (!auth->req_msg)
++ goto fail;
++
++out:
++ wpabuf_free(pi);
++ EVP_PKEY_CTX_free(ctx);
++ return auth;
++fail:
++ dpp_auth_deinit(auth);
++ auth = NULL;
++ goto out;
++}
++
++
++static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
++ const char *json)
++{
++ size_t nonce_len;
++ size_t json_len, clear_len;
++ struct wpabuf *clear = NULL, *msg = NULL;
++ u8 *wrapped;
++ size_t attr_len;
++
++ wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
++
++ nonce_len = auth->curve->nonce_len;
++ if (random_get_bytes(auth->e_nonce, nonce_len)) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
++ json_len = os_strlen(json);
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
++
++ /* { E-nonce, configAttrib }ke */
++ clear_len = 4 + nonce_len + 4 + json_len;
++ clear = wpabuf_alloc(clear_len);
++ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = wpabuf_alloc(attr_len);
++ if (!clear || !msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
++ goto skip_e_nonce;
++ }
++ if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
++ wpabuf_put_le16(clear, nonce_len - 1);
++ wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
++ goto skip_e_nonce;
++ }
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* E-nonce */
++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
++ wpabuf_put_le16(clear, nonce_len);
++ wpabuf_put_data(clear, auth->e_nonce, nonce_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_e_nonce:
++ if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
++ goto skip_conf_attr_obj;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* configAttrib */
++ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
++ wpabuf_put_le16(clear, json_len);
++ wpabuf_put_data(clear, json, json_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_conf_attr_obj:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++ /* No AES-SIV AD */
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
++ wpabuf_head(clear), wpabuf_len(clear),
++ 0, NULL, NULL, wrapped) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_hexdump_buf(MSG_DEBUG,
++ "DPP: Configuration Request frame attributes", msg);
++ wpabuf_free(clear);
++ return msg;
++
++fail:
++ wpabuf_free(clear);
++ wpabuf_free(msg);
++ return NULL;
++}
++
++
++static void dpp_write_adv_proto(struct wpabuf *buf)
++{
++ /* Advertisement Protocol IE */
++ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
++ wpabuf_put_u8(buf, 8); /* Length */
++ wpabuf_put_u8(buf, 0x7f);
++ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
++ wpabuf_put_u8(buf, 5);
++ wpabuf_put_be24(buf, OUI_WFA);
++ wpabuf_put_u8(buf, DPP_OUI_TYPE);
++ wpabuf_put_u8(buf, 0x01);
++}
++
++
++static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
++{
++ /* GAS Query */
++ wpabuf_put_le16(buf, wpabuf_len(query));
++ wpabuf_put_buf(buf, query);
++}
++
++
++struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
++ const char *json)
++{
++ struct wpabuf *buf, *conf_req;
++
++ conf_req = dpp_build_conf_req_attr(auth, json);
++ if (!conf_req) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No configuration request data available");
++ return NULL;
++ }
++
++ buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
++ if (!buf) {
++ wpabuf_free(conf_req);
++ return NULL;
++ }
++
++ dpp_write_adv_proto(buf);
++ dpp_write_gas_query(buf, conf_req);
++ wpabuf_free(conf_req);
++ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf);
++
++ return buf;
++}
++
++
++static void dpp_auth_success(struct dpp_authentication *auth)
++{
++ wpa_printf(MSG_DEBUG,
++ "DPP: Authentication success - clear temporary keys");
++ os_memset(auth->Mx, 0, sizeof(auth->Mx));
++ auth->Mx_len = 0;
++ os_memset(auth->Nx, 0, sizeof(auth->Nx));
++ auth->Nx_len = 0;
++ os_memset(auth->Lx, 0, sizeof(auth->Lx));
++ auth->Lx_len = 0;
++ os_memset(auth->k1, 0, sizeof(auth->k1));
++ os_memset(auth->k2, 0, sizeof(auth->k2));
++
++ auth->auth_success = 1;
++}
++
++
++static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
++{
++ struct wpabuf *pix, *prx, *bix, *brx;
++ const u8 *addr[7];
++ size_t len[7];
++ size_t i, num_elem = 0;
++ size_t nonce_len;
++ u8 zero = 0;
++ int res = -1;
++
++ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
++ nonce_len = auth->curve->nonce_len;
++
++ if (auth->initiator) {
++ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
++ if (auth->own_bi)
++ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
++ else
++ bix = NULL;
++ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
++ } else {
++ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
++ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ if (auth->peer_bi)
++ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
++ else
++ bix = NULL;
++ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
++ }
++ if (!pix || !prx || !brx)
++ goto fail;
++
++ addr[num_elem] = auth->i_nonce;
++ len[num_elem] = nonce_len;
++ num_elem++;
++
++ addr[num_elem] = auth->r_nonce;
++ len[num_elem] = nonce_len;
++ num_elem++;
++
++ addr[num_elem] = wpabuf_head(pix);
++ len[num_elem] = wpabuf_len(pix) / 2;
++ num_elem++;
++
++ addr[num_elem] = wpabuf_head(prx);
++ len[num_elem] = wpabuf_len(prx) / 2;
++ num_elem++;
++
++ if (bix) {
++ addr[num_elem] = wpabuf_head(bix);
++ len[num_elem] = wpabuf_len(bix) / 2;
++ num_elem++;
++ }
++
++ addr[num_elem] = wpabuf_head(brx);
++ len[num_elem] = wpabuf_len(brx) / 2;
++ num_elem++;
++
++ addr[num_elem] = &zero;
++ len[num_elem] = 1;
++ num_elem++;
++
++ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
++ for (i = 0; i < num_elem; i++)
++ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
++ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
++ if (res == 0)
++ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
++ auth->curve->hash_len);
++fail:
++ wpabuf_free(pix);
++ wpabuf_free(prx);
++ wpabuf_free(bix);
++ wpabuf_free(brx);
++ return res;
++}
++
++
++static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
++{
++ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
++ const u8 *addr[7];
++ size_t len[7];
++ size_t i, num_elem = 0;
++ size_t nonce_len;
++ u8 one = 1;
++ int res = -1;
++
++ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
++ nonce_len = auth->curve->nonce_len;
++
++ if (auth->initiator) {
++ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
++ if (auth->own_bi)
++ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
++ else
++ bix = NULL;
++ if (!auth->peer_bi)
++ goto fail;
++ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
++ } else {
++ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
++ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ if (auth->peer_bi)
++ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
++ else
++ bix = NULL;
++ if (!auth->own_bi)
++ goto fail;
++ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
++ }
++ if (!pix || !prx || !brx)
++ goto fail;
++
++ addr[num_elem] = auth->r_nonce;
++ len[num_elem] = nonce_len;
++ num_elem++;
++
++ addr[num_elem] = auth->i_nonce;
++ len[num_elem] = nonce_len;
++ num_elem++;
++
++ addr[num_elem] = wpabuf_head(prx);
++ len[num_elem] = wpabuf_len(prx) / 2;
++ num_elem++;
++
++ addr[num_elem] = wpabuf_head(pix);
++ len[num_elem] = wpabuf_len(pix) / 2;
++ num_elem++;
++
++ addr[num_elem] = wpabuf_head(brx);
++ len[num_elem] = wpabuf_len(brx) / 2;
++ num_elem++;
++
++ if (bix) {
++ addr[num_elem] = wpabuf_head(bix);
++ len[num_elem] = wpabuf_len(bix) / 2;
++ num_elem++;
++ }
++
++ addr[num_elem] = &one;
++ len[num_elem] = 1;
++ num_elem++;
++
++ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
++ for (i = 0; i < num_elem; i++)
++ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
++ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
++ if (res == 0)
++ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
++ auth->curve->hash_len);
++fail:
++ wpabuf_free(pix);
++ wpabuf_free(prx);
++ wpabuf_free(bix);
++ wpabuf_free(brx);
++ return res;
++}
++
++
++static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
++{
++ const EC_GROUP *group;
++ EC_POINT *l = NULL;
++ EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
++ const EC_POINT *BI_point;
++ BN_CTX *bnctx;
++ BIGNUM *lx, *sum, *q;
++ const BIGNUM *bR_bn, *pR_bn;
++ int ret = -1;
++
++ /* L = ((bR + pR) modulo q) * BI */
++
++ bnctx = BN_CTX_new();
++ sum = BN_new();
++ q = BN_new();
++ lx = BN_new();
++ if (!bnctx || !sum || !q || !lx)
++ goto fail;
++ BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
++ if (!BI)
++ goto fail;
++ BI_point = EC_KEY_get0_public_key(BI);
++ group = EC_KEY_get0_group(BI);
++ if (!group)
++ goto fail;
++
++ bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
++ pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
++ if (!bR || !pR)
++ goto fail;
++ bR_bn = EC_KEY_get0_private_key(bR);
++ pR_bn = EC_KEY_get0_private_key(pR);
++ if (!bR_bn || !pR_bn)
++ goto fail;
++ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
++ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
++ goto fail;
++ l = EC_POINT_new(group);
++ if (!l ||
++ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
++ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
++ bnctx) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
++ goto fail;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
++ auth->Lx_len = auth->secret_len;
++ ret = 0;
++fail:
++ EC_POINT_clear_free(l);
++ EC_KEY_free(BI);
++ EC_KEY_free(bR);
++ EC_KEY_free(pR);
++ BN_clear_free(lx);
++ BN_clear_free(sum);
++ BN_free(q);
++ BN_CTX_free(bnctx);
++ return ret;
++}
++
++
++static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
++{
++ const EC_GROUP *group;
++ EC_POINT *l = NULL, *sum = NULL;
++ EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
++ const EC_POINT *BR_point, *PR_point;
++ BN_CTX *bnctx;
++ BIGNUM *lx;
++ const BIGNUM *bI_bn;
++ int ret = -1;
++
++ /* L = bI * (BR + PR) */
++
++ bnctx = BN_CTX_new();
++ lx = BN_new();
++ if (!bnctx || !lx)
++ goto fail;
++ BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
++ PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
++ if (!BR || !PR)
++ goto fail;
++ BR_point = EC_KEY_get0_public_key(BR);
++ PR_point = EC_KEY_get0_public_key(PR);
++
++ bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
++ if (!bI)
++ goto fail;
++ group = EC_KEY_get0_group(bI);
++ bI_bn = EC_KEY_get0_private_key(bI);
++ if (!group || !bI_bn)
++ goto fail;
++ sum = EC_POINT_new(group);
++ l = EC_POINT_new(group);
++ if (!sum || !l ||
++ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
++ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
++ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
++ bnctx) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
++ goto fail;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
++ auth->Lx_len = auth->secret_len;
++ ret = 0;
++fail:
++ EC_POINT_clear_free(l);
++ EC_POINT_clear_free(sum);
++ EC_KEY_free(bI);
++ EC_KEY_free(BR);
++ EC_KEY_free(PR);
++ BN_clear_free(lx);
++ BN_CTX_free(bnctx);
++ return ret;
++}
++
++
++static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
++{
++ size_t nonce_len;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t secret_len;
++ struct wpabuf *msg, *pr = NULL;
++ u8 r_auth[4 + DPP_MAX_HASH_LEN];
++ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
++ size_t wrapped_r_auth_len;
++ int ret = -1;
++ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
++ enum dpp_status_error status = DPP_STATUS_OK;
++#ifdef CONFIG_TESTING_OPTIONS
++ u8 test_hash[SHA256_MAC_LEN];
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
++ if (!auth->own_bi)
++ return -1;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_nonce_override_len > 0) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
++ nonce_len = dpp_nonce_override_len;
++ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
++ } else {
++ nonce_len = auth->curve->nonce_len;
++ if (random_get_bytes(auth->r_nonce, nonce_len)) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to generate R-nonce");
++ goto fail;
++ }
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ nonce_len = auth->curve->nonce_len;
++ if (random_get_bytes(auth->r_nonce, nonce_len)) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
++ goto fail;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_protocol_key_override_len) {
++ const struct dpp_curve_params *tmp_curve;
++
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - override protocol key");
++ auth->own_protocol_key = dpp_set_keypair(
++ &tmp_curve, dpp_protocol_key_override,
++ dpp_protocol_key_override_len);
++ } else {
++ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (!auth->own_protocol_key)
++ goto fail;
++
++ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
++ if (!pr)
++ goto fail;
++
++ /* ECDH: N = pR * PI */
++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
++ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ EVP_PKEY_CTX_free(ctx);
++ ctx = NULL;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
++ auth->Nx, auth->secret_len);
++ auth->Nx_len = auth->secret_len;
++
++ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
++ auth->curve->hash_len) < 0)
++ goto fail;
++
++ if (auth->own_bi && auth->peer_bi) {
++ /* Mutual authentication */
++ if (dpp_auth_derive_l_responder(auth) < 0)
++ goto fail;
++ }
++
++ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
++ goto fail;
++
++ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
++ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
++ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
++ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
++ goto fail;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
++ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
++ r_auth, 4 + auth->curve->hash_len,
++ 0, NULL, NULL, wrapped_r_auth) < 0)
++ goto fail;
++ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
++ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
++ wrapped_r_auth, wrapped_r_auth_len);
++ w_r_auth = wrapped_r_auth;
++
++ r_pubkey_hash = auth->own_bi->pubkey_hash;
++ if (auth->peer_bi)
++ i_pubkey_hash = auth->peer_bi->pubkey_hash;
++ else
++ i_pubkey_hash = NULL;
++
++ i_nonce = auth->i_nonce;
++ r_nonce = auth->r_nonce;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
++ r_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid R-Bootstrap Key Hash");
++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ r_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
++ i_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid I-Bootstrap Key Hash");
++ if (i_pubkey_hash)
++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
++ else
++ os_memset(test_hash, 0, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ i_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
++ wpabuf_free(pr);
++ pr = NULL;
++ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
++ wpabuf_free(pr);
++ pr = wpabuf_alloc(2 * auth->curve->prime_len);
++ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
++ goto fail;
++ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
++ w_r_auth = NULL;
++ wrapped_r_auth_len = 0;
++ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
++ status = 255;
++ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
++ status = 254;
++ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
++ r_nonce = NULL;
++ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
++ i_nonce = NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
++ r_pubkey_hash, i_pubkey_hash,
++ r_nonce, i_nonce,
++ w_r_auth, wrapped_r_auth_len,
++ auth->k2);
++ if (!msg)
++ goto fail;
++ wpabuf_free(auth->resp_msg);
++ auth->resp_msg = msg;
++ ret = 0;
++fail:
++ wpabuf_free(pr);
++ return ret;
++}
++
++
++static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
++ enum dpp_status_error status)
++{
++ struct wpabuf *msg;
++ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
++#ifdef CONFIG_TESTING_OPTIONS
++ u8 test_hash[SHA256_MAC_LEN];
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (!auth->own_bi)
++ return -1;
++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
++
++ r_pubkey_hash = auth->own_bi->pubkey_hash;
++ if (auth->peer_bi)
++ i_pubkey_hash = auth->peer_bi->pubkey_hash;
++ else
++ i_pubkey_hash = NULL;
++
++ i_nonce = auth->i_nonce;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
++ r_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid R-Bootstrap Key Hash");
++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ r_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
++ i_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid I-Bootstrap Key Hash");
++ if (i_pubkey_hash)
++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
++ else
++ os_memset(test_hash, 0, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ i_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
++ status = 255;
++ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
++ i_nonce = NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
++ r_pubkey_hash, i_pubkey_hash,
++ NULL, i_nonce, NULL, 0, auth->k1);
++ if (!msg)
++ return -1;
++ wpabuf_free(auth->resp_msg);
++ auth->resp_msg = msg;
++ return 0;
++}
++
++
++struct dpp_authentication *
++dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
++ struct dpp_bootstrap_info *peer_bi,
++ struct dpp_bootstrap_info *own_bi,
++ unsigned int freq, const u8 *hdr, const u8 *attr_start,
++ size_t attr_len)
++{
++ EVP_PKEY *pi = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t secret_len;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
++ *channel;
++ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
++ i_bootstrap_len, channel_len;
++ struct dpp_authentication *auth = NULL;
++#ifdef CONFIG_DPP2
++ const u8 *version;
++ u16 version_len;
++#endif /* CONFIG_DPP2 */
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at Authentication Request");
++ return NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Missing or invalid required Wrapped Data attribute");
++ return NULL;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
++ wrapped_data, wrapped_data_len);
++ attr_len = wrapped_data - 4 - attr_start;
++
++ auth = os_zalloc(sizeof(*auth));
++ if (!auth)
++ goto fail;
++ auth->msg_ctx = msg_ctx;
++ auth->peer_bi = peer_bi;
++ auth->own_bi = own_bi;
++ auth->curve = own_bi->curve;
++ auth->curr_freq = freq;
++
++ auth->peer_version = 1; /* default to the first version */
++#ifdef CONFIG_DPP2
++ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
++ &version_len);
++ if (version) {
++ if (version_len < 1 || version[0] == 0) {
++ dpp_auth_fail(auth,
++ "Invalid Protocol Version attribute");
++ goto fail;
++ }
++ auth->peer_version = version[0];
++ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
++ auth->peer_version);
++ }
++#endif /* CONFIG_DPP2 */
++
++ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
++ &channel_len);
++ if (channel) {
++ int neg_freq;
++
++ if (channel_len < 2) {
++ dpp_auth_fail(auth, "Too short Channel attribute");
++ goto fail;
++ }
++
++ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
++ wpa_printf(MSG_DEBUG,
++ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
++ channel[0], channel[1], neg_freq);
++ if (neg_freq < 0) {
++ dpp_auth_fail(auth,
++ "Unsupported Channel attribute value");
++ goto fail;
++ }
++
++ if (auth->curr_freq != (unsigned int) neg_freq) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Changing negotiation channel from %u MHz to %u MHz",
++ freq, neg_freq);
++ auth->curr_freq = neg_freq;
++ }
++ }
++
++ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
++ &i_proto_len);
++ if (!i_proto) {
++ dpp_auth_fail(auth,
++ "Missing required Initiator Protocol Key attribute");
++ goto fail;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
++ i_proto, i_proto_len);
++
++ /* M = bR * PI */
++ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
++ if (!pi) {
++ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
++ goto fail;
++ }
++ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
++
++ ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
++ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
++ goto fail;
++ }
++ auth->secret_len = secret_len;
++ EVP_PKEY_CTX_free(ctx);
++ ctx = NULL;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
++ auth->Mx, auth->secret_len);
++ auth->Mx_len = auth->secret_len;
++
++ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
++ auth->curve->hash_len) < 0)
++ goto fail;
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
++ &i_nonce_len);
++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth, "Missing or invalid I-nonce");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
++ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
++
++ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_I_CAPABILITIES,
++ &i_capab_len);
++ if (!i_capab || i_capab_len < 1) {
++ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
++ goto fail;
++ }
++ auth->i_capab = i_capab[0];
++ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
++
++ bin_clear_free(unwrapped, unwrapped_len);
++ unwrapped = NULL;
++
++ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
++ case DPP_CAPAB_ENROLLEE:
++ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Local policy does not allow Configurator role");
++ goto not_compatible;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
++ auth->configurator = 1;
++ break;
++ case DPP_CAPAB_CONFIGURATOR:
++ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Local policy does not allow Enrollee role");
++ goto not_compatible;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
++ auth->configurator = 0;
++ break;
++ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
++ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
++ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
++ auth->configurator = 0;
++ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
++ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
++ auth->configurator = 1;
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Local policy does not allow Configurator/Enrollee role");
++ goto not_compatible;
++ }
++ break;
++ default:
++ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
++ wpa_msg(auth->msg_ctx, MSG_INFO,
++ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
++ auth->i_capab & DPP_CAPAB_ROLE_MASK);
++ goto fail;
++ }
++
++ auth->peer_protocol_key = pi;
++ pi = NULL;
++ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
++ char hex[SHA256_MAC_LEN * 2 + 1];
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
++ if (dpp_auth_build_resp_status(auth,
++ DPP_STATUS_RESPONSE_PENDING) < 0)
++ goto fail;
++ i_bootstrap = dpp_get_attr(attr_start, attr_len,
++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
++ &i_bootstrap_len);
++ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
++ auth->response_pending = 1;
++ os_memcpy(auth->waiting_pubkey_hash,
++ i_bootstrap, i_bootstrap_len);
++ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
++ i_bootstrap_len);
++ } else {
++ hex[0] = '\0';
++ }
++
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
++ "%s", hex);
++ return auth;
++ }
++ if (dpp_auth_build_resp_ok(auth) < 0)
++ goto fail;
++
++ return auth;
++
++not_compatible:
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
++ "i-capab=0x%02x", auth->i_capab);
++ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
++ auth->configurator = 1;
++ else
++ auth->configurator = 0;
++ auth->peer_protocol_key = pi;
++ pi = NULL;
++ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
++ goto fail;
++
++ auth->remove_on_tx_status = 1;
++ return auth;
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++ EVP_PKEY_free(pi);
++ EVP_PKEY_CTX_free(ctx);
++ dpp_auth_deinit(auth);
++ return NULL;
++}
++
++
++int dpp_notify_new_qr_code(struct dpp_authentication *auth,
++ struct dpp_bootstrap_info *peer_bi)
++{
++ if (!auth || !auth->response_pending ||
++ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
++ SHA256_MAC_LEN) != 0)
++ return 0;
++
++ wpa_printf(MSG_DEBUG,
++ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
++ MACSTR, MAC2STR(auth->peer_mac_addr));
++ auth->peer_bi = peer_bi;
++
++ if (dpp_auth_build_resp_ok(auth) < 0)
++ return -1;
++
++ return 1;
++}
++
++
++static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
++ enum dpp_status_error status)
++{
++ struct wpabuf *msg;
++ u8 i_auth[4 + DPP_MAX_HASH_LEN];
++ size_t i_auth_len;
++ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
++ size_t r_nonce_len;
++ const u8 *addr[2];
++ size_t len[2], attr_len;
++ u8 *wrapped_i_auth;
++ u8 *wrapped_r_nonce;
++ u8 *attr_start, *attr_end;
++ const u8 *r_pubkey_hash, *i_pubkey_hash;
++#ifdef CONFIG_TESTING_OPTIONS
++ u8 test_hash[SHA256_MAC_LEN];
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
++
++ i_auth_len = 4 + auth->curve->hash_len;
++ r_nonce_len = 4 + auth->curve->nonce_len;
++ /* Build DPP Authentication Confirmation frame attributes */
++ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
++ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
++ if (!msg)
++ goto fail;
++
++ attr_start = wpabuf_put(msg, 0);
++
++ r_pubkey_hash = auth->peer_bi->pubkey_hash;
++ if (auth->own_bi)
++ i_pubkey_hash = auth->own_bi->pubkey_hash;
++ else
++ i_pubkey_hash = NULL;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
++ goto skip_status;
++ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
++ status = 254;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* DPP Status */
++ dpp_build_attr_status(msg, status);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_status:
++ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
++ r_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid R-Bootstrap Key Hash");
++ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ r_pubkey_hash = test_hash;
++ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
++ i_pubkey_hash = NULL;
++ } else if (dpp_test ==
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - invalid I-Bootstrap Key Hash");
++ if (i_pubkey_hash)
++ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
++ else
++ os_memset(test_hash, 0, SHA256_MAC_LEN);
++ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
++ i_pubkey_hash = test_hash;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Responder Bootstrapping Key Hash */
++ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
++
++ /* Initiator Bootstrapping Key Hash (mutual authentication) */
++ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
++ goto skip_wrapped_data;
++ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
++ i_auth_len = 0;
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ attr_end = wpabuf_put(msg, 0);
++
++ /* OUI, OUI type, Crypto Suite, DPP frame type */
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = 3 + 1 + 1 + 1;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++
++ /* Attributes before Wrapped Data */
++ addr[1] = attr_start;
++ len[1] = attr_end - attr_start;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ if (status == DPP_STATUS_OK) {
++ /* I-auth wrapped with ke */
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
++ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
++ goto skip_i_auth;
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
++ * 1) */
++ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
++ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
++ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
++ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
++ }
++skip_i_auth:
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
++ i_auth, i_auth_len,
++ 2, addr, len, wrapped_i_auth) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
++ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
++ } else {
++ /* R-nonce wrapped with k2 */
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
++ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
++
++ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
++ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
++ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
++
++ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
++ r_nonce, r_nonce_len,
++ 2, addr, len, wrapped_r_nonce) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
++ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_hexdump_buf(MSG_DEBUG,
++ "DPP: Authentication Confirmation frame attributes",
++ msg);
++ if (status == DPP_STATUS_OK)
++ dpp_auth_success(auth);
++
++ return msg;
++
++fail:
++ wpabuf_free(msg);
++ return NULL;
++}
++
++
++static void
++dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
++ const u8 *attr_start, size_t attr_len,
++ const u8 *wrapped_data, u16 wrapped_data_len,
++ enum dpp_status_error status)
++{
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ const u8 *i_nonce, *r_capab;
++ u16 i_nonce_len, r_capab_len;
++
++ if (status == DPP_STATUS_NOT_COMPATIBLE) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Responder reported incompatible roles");
++ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Responder reported more time needed");
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Responder reported failure (status %d)",
++ status);
++ dpp_auth_fail(auth, "Responder reported failure");
++ return;
++ }
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
++ &i_nonce_len);
++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth, "Missing or invalid I-nonce");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
++ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
++ dpp_auth_fail(auth, "I-nonce mismatch");
++ goto fail;
++ }
++
++ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_R_CAPABILITIES,
++ &r_capab_len);
++ if (!r_capab || r_capab_len < 1) {
++ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
++ goto fail;
++ }
++ auth->r_capab = r_capab[0];
++ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
++ if (status == DPP_STATUS_NOT_COMPATIBLE) {
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
++ "r-capab=0x%02x", auth->r_capab);
++ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
++ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
++
++ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
++ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
++ wpa_msg(auth->msg_ctx, MSG_INFO,
++ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
++ role);
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Continue waiting for full DPP Authentication Response");
++ wpa_msg(auth->msg_ctx, MSG_INFO,
++ DPP_EVENT_RESPONSE_PENDING "%s",
++ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
++ }
++ }
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++}
++
++
++struct wpabuf *
++dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
++ const u8 *attr_start, size_t attr_len)
++{
++ EVP_PKEY *pr;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t secret_len;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL, *unwrapped2 = NULL;
++ size_t unwrapped_len = 0, unwrapped2_len = 0;
++ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
++ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
++ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
++ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
++ wrapped2_len, r_auth_len;
++ u8 r_auth2[DPP_MAX_HASH_LEN];
++ u8 role;
++#ifdef CONFIG_DPP2
++ const u8 *version;
++ u16 version_len;
++#endif /* CONFIG_DPP2 */
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at Authentication Response");
++ return NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (!auth->initiator || !auth->peer_bi) {
++ dpp_auth_fail(auth, "Unexpected Authentication Response");
++ return NULL;
++ }
++
++ auth->waiting_auth_resp = 0;
++
++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Wrapped Data attribute");
++ return NULL;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
++ wrapped_data, wrapped_data_len);
++
++ attr_len = wrapped_data - 4 - attr_start;
++
++ r_bootstrap = dpp_get_attr(attr_start, attr_len,
++ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
++ &r_bootstrap_len);
++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
++ return NULL;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
++ r_bootstrap, r_bootstrap_len);
++ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
++ SHA256_MAC_LEN) != 0) {
++ dpp_auth_fail(auth,
++ "Unexpected Responder Bootstrapping Key Hash value");
++ wpa_hexdump(MSG_DEBUG,
++ "DPP: Expected Responder Bootstrapping Key Hash",
++ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
++ return NULL;
++ }
++
++ i_bootstrap = dpp_get_attr(attr_start, attr_len,
++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
++ &i_bootstrap_len);
++ if (i_bootstrap) {
++ if (i_bootstrap_len != SHA256_MAC_LEN) {
++ dpp_auth_fail(auth,
++ "Invalid Initiator Bootstrapping Key Hash attribute");
++ return NULL;
++ }
++ wpa_hexdump(MSG_MSGDUMP,
++ "DPP: Initiator Bootstrapping Key Hash",
++ i_bootstrap, i_bootstrap_len);
++ if (!auth->own_bi ||
++ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
++ SHA256_MAC_LEN) != 0) {
++ dpp_auth_fail(auth,
++ "Initiator Bootstrapping Key Hash attribute did not match");
++ return NULL;
++ }
++ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
++ /* PKEX bootstrapping mandates use of mutual authentication */
++ dpp_auth_fail(auth,
++ "Missing Initiator Bootstrapping Key Hash attribute");
++ return NULL;
++ }
++
++ auth->peer_version = 1; /* default to the first version */
++#ifdef CONFIG_DPP2
++ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
++ &version_len);
++ if (version) {
++ if (version_len < 1 || version[0] == 0) {
++ dpp_auth_fail(auth,
++ "Invalid Protocol Version attribute");
++ return NULL;
++ }
++ auth->peer_version = version[0];
++ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
++ auth->peer_version);
++ }
++#endif /* CONFIG_DPP2 */
++
++ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
++ &status_len);
++ if (!status || status_len < 1) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required DPP Status attribute");
++ return NULL;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
++ auth->auth_resp_status = status[0];
++ if (status[0] != DPP_STATUS_OK) {
++ dpp_auth_resp_rx_status(auth, hdr, attr_start,
++ attr_len, wrapped_data,
++ wrapped_data_len, status[0]);
++ return NULL;
++ }
++
++ if (!i_bootstrap && auth->own_bi) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Responder decided not to use mutual authentication");
++ auth->own_bi = NULL;
++ }
++
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
++ auth->own_bi != NULL);
++
++ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
++ &r_proto_len);
++ if (!r_proto) {
++ dpp_auth_fail(auth,
++ "Missing required Responder Protocol Key attribute");
++ return NULL;
++ }
++ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
++ r_proto, r_proto_len);
++
++ /* N = pI * PR */
++ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
++ if (!pr) {
++ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
++ return NULL;
++ }
++ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
++
++ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
++ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
++ goto fail;
++ }
++ EVP_PKEY_CTX_free(ctx);
++ ctx = NULL;
++ auth->peer_protocol_key = pr;
++ pr = NULL;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
++ auth->Nx, auth->secret_len);
++ auth->Nx_len = auth->secret_len;
++
++ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
++ auth->curve->hash_len) < 0)
++ goto fail;
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
++ &r_nonce_len);
++ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
++ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
++
++ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
++ &i_nonce_len);
++ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth, "Missing or invalid I-nonce");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
++ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
++ dpp_auth_fail(auth, "I-nonce mismatch");
++ goto fail;
++ }
++
++ if (auth->own_bi) {
++ /* Mutual authentication */
++ if (dpp_auth_derive_l_initiator(auth) < 0)
++ goto fail;
++ }
++
++ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_R_CAPABILITIES,
++ &r_capab_len);
++ if (!r_capab || r_capab_len < 1) {
++ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
++ goto fail;
++ }
++ auth->r_capab = r_capab[0];
++ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
++ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
++ if ((auth->allowed_roles ==
++ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
++ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
++ /* Peer selected its role, so move from "either role" to the
++ * role that is compatible with peer's selection. */
++ auth->configurator = role == DPP_CAPAB_ENROLLEE;
++ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
++ auth->configurator ? "Configurator" : "Enrollee");
++ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
++ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
++ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
++ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Unexpected role in R-capabilities 0x%02x",
++ role);
++ if (role != DPP_CAPAB_ENROLLEE &&
++ role != DPP_CAPAB_CONFIGURATOR)
++ goto fail;
++ bin_clear_free(unwrapped, unwrapped_len);
++ auth->remove_on_tx_status = 1;
++ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
++ }
++
++ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
++ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Secondary Wrapped Data");
++ goto fail;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped2, wrapped2_len);
++
++ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
++ goto fail;
++
++ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
++ unwrapped2 = os_malloc(unwrapped2_len);
++ if (!unwrapped2)
++ goto fail;
++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
++ wrapped2, wrapped2_len,
++ 0, NULL, NULL, unwrapped2) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped2, unwrapped2_len);
++
++ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
++ dpp_auth_fail(auth,
++ "Invalid attribute in secondary unwrapped data");
++ goto fail;
++ }
++
++ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
++ &r_auth_len);
++ if (!r_auth || r_auth_len != auth->curve->hash_len) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Responder Authenticating Tag");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
++ r_auth, r_auth_len);
++ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
++ if (dpp_gen_r_auth(auth, r_auth2) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
++ r_auth2, r_auth_len);
++ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
++ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
++ bin_clear_free(unwrapped, unwrapped_len);
++ bin_clear_free(unwrapped2, unwrapped2_len);
++ auth->remove_on_tx_status = 1;
++ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
++ }
++
++ bin_clear_free(unwrapped, unwrapped_len);
++ bin_clear_free(unwrapped2, unwrapped2_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - Authentication Response in place of Confirm");
++ if (dpp_auth_build_resp_ok(auth) < 0)
++ return NULL;
++ return wpabuf_dup(auth->resp_msg);
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
++
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++ bin_clear_free(unwrapped2, unwrapped2_len);
++ EVP_PKEY_free(pr);
++ EVP_PKEY_CTX_free(ctx);
++ return NULL;
++}
++
++
++static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
++ const u8 *hdr,
++ const u8 *attr_start, size_t attr_len,
++ const u8 *wrapped_data,
++ u16 wrapped_data_len,
++ enum dpp_status_error status)
++{
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ const u8 *r_nonce;
++ u16 r_nonce_len;
++
++ /* Authentication Confirm failure cases are expected to include
++ * {R-nonce}k2 in the Wrapped Data attribute. */
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped) {
++ dpp_auth_fail(auth, "Authentication failed");
++ goto fail;
++ }
++ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
++ &r_nonce_len);
++ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
++ goto fail;
++ }
++ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
++ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
++ r_nonce, r_nonce_len);
++ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
++ auth->r_nonce, r_nonce_len);
++ dpp_auth_fail(auth, "R-nonce mismatch");
++ goto fail;
++ }
++
++ if (status == DPP_STATUS_NOT_COMPATIBLE)
++ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
++ else if (status == DPP_STATUS_AUTH_FAILURE)
++ dpp_auth_fail(auth, "Peer reported authentication failure)");
++
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++ return -1;
++}
++
++
++int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
++ const u8 *attr_start, size_t attr_len)
++{
++ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
++ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
++ i_auth_len;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ u8 i_auth2[DPP_MAX_HASH_LEN];
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at Authentication Confirm");
++ return -1;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (auth->initiator || !auth->own_bi) {
++ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
++ return -1;
++ }
++
++ auth->waiting_auth_conf = 0;
++
++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Wrapped Data attribute");
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
++ wrapped_data, wrapped_data_len);
++
++ attr_len = wrapped_data - 4 - attr_start;
++
++ r_bootstrap = dpp_get_attr(attr_start, attr_len,
++ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
++ &r_bootstrap_len);
++ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
++ return -1;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
++ r_bootstrap, r_bootstrap_len);
++ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
++ SHA256_MAC_LEN) != 0) {
++ wpa_hexdump(MSG_DEBUG,
++ "DPP: Expected Responder Bootstrapping Key Hash",
++ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
++ dpp_auth_fail(auth,
++ "Responder Bootstrapping Key Hash mismatch");
++ return -1;
++ }
++
++ i_bootstrap = dpp_get_attr(attr_start, attr_len,
++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
++ &i_bootstrap_len);
++ if (i_bootstrap) {
++ if (i_bootstrap_len != SHA256_MAC_LEN) {
++ dpp_auth_fail(auth,
++ "Invalid Initiator Bootstrapping Key Hash attribute");
++ return -1;
++ }
++ wpa_hexdump(MSG_MSGDUMP,
++ "DPP: Initiator Bootstrapping Key Hash",
++ i_bootstrap, i_bootstrap_len);
++ if (!auth->peer_bi ||
++ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
++ SHA256_MAC_LEN) != 0) {
++ dpp_auth_fail(auth,
++ "Initiator Bootstrapping Key Hash mismatch");
++ return -1;
++ }
++ } else if (auth->peer_bi) {
++ /* Mutual authentication and peer did not include its
++ * Bootstrapping Key Hash attribute. */
++ dpp_auth_fail(auth,
++ "Missing Initiator Bootstrapping Key Hash attribute");
++ return -1;
++ }
++
++ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
++ &status_len);
++ if (!status || status_len < 1) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required DPP Status attribute");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
++ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
++ status[0] == DPP_STATUS_AUTH_FAILURE)
++ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
++ attr_len, wrapped_data,
++ wrapped_data_len, status[0]);
++
++ if (status[0] != DPP_STATUS_OK) {
++ dpp_auth_fail(auth, "Authentication failed");
++ return -1;
++ }
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ return -1;
++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
++ &i_auth_len);
++ if (!i_auth || i_auth_len != auth->curve->hash_len) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Initiator Authenticating Tag");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
++ i_auth, i_auth_len);
++ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
++ if (dpp_gen_i_auth(auth, i_auth2) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
++ i_auth2, i_auth_len);
++ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
++ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
++ goto fail;
++ }
++
++ bin_clear_free(unwrapped, unwrapped_len);
++ dpp_auth_success(auth);
++ return 0;
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++ return -1;
++}
++
++
++static int bin_str_eq(const char *val, size_t len, const char *cmp)
++{
++ return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
++}
++
++
++struct dpp_configuration * dpp_configuration_alloc(const char *type)
++{
++ struct dpp_configuration *conf;
++ const char *end;
++ size_t len;
++
++ conf = os_zalloc(sizeof(*conf));
++ if (!conf)
++ goto fail;
++
++ end = os_strchr(type, ' ');
++ if (end)
++ len = end - type;
++ else
++ len = os_strlen(type);
++
++ if (bin_str_eq(type, len, "psk"))
++ conf->akm = DPP_AKM_PSK;
++ else if (bin_str_eq(type, len, "sae"))
++ conf->akm = DPP_AKM_SAE;
++ else if (bin_str_eq(type, len, "psk-sae") ||
++ bin_str_eq(type, len, "psk+sae"))
++ conf->akm = DPP_AKM_PSK_SAE;
++ else if (bin_str_eq(type, len, "sae-dpp") ||
++ bin_str_eq(type, len, "dpp+sae"))
++ conf->akm = DPP_AKM_SAE_DPP;
++ else if (bin_str_eq(type, len, "psk-sae-dpp") ||
++ bin_str_eq(type, len, "dpp+psk+sae"))
++ conf->akm = DPP_AKM_PSK_SAE_DPP;
++ else if (bin_str_eq(type, len, "dpp"))
++ conf->akm = DPP_AKM_DPP;
++ else
++ goto fail;
++
++ return conf;
++fail:
++ dpp_configuration_free(conf);
++ return NULL;
++}
++
++
++int dpp_akm_psk(enum dpp_akm akm)
++{
++ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
++ akm == DPP_AKM_PSK_SAE_DPP;
++}
++
++
++int dpp_akm_sae(enum dpp_akm akm)
++{
++ return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE ||
++ akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
++}
++
++
++int dpp_akm_legacy(enum dpp_akm akm)
++{
++ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
++ akm == DPP_AKM_SAE;
++}
++
++
++int dpp_akm_dpp(enum dpp_akm akm)
++{
++ return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP ||
++ akm == DPP_AKM_PSK_SAE_DPP;
++}
++
++
++int dpp_akm_ver2(enum dpp_akm akm)
++{
++ return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
++}
++
++
++int dpp_configuration_valid(const struct dpp_configuration *conf)
++{
++ if (conf->ssid_len == 0)
++ return 0;
++ if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
++ return 0;
++ if (dpp_akm_sae(conf->akm) && !conf->passphrase)
++ return 0;
++ return 1;
++}
++
++
++void dpp_configuration_free(struct dpp_configuration *conf)
++{
++ if (!conf)
++ return;
++ str_clear_free(conf->passphrase);
++ os_free(conf->group_id);
++ bin_clear_free(conf, sizeof(*conf));
++}
++
++
++static int dpp_configuration_parse(struct dpp_authentication *auth,
++ const char *cmd)
++{
++ const char *pos, *end;
++ struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
++ struct dpp_configuration *conf = NULL;
++
++ pos = os_strstr(cmd, " conf=sta-");
++ if (pos) {
++ conf_sta = dpp_configuration_alloc(pos + 10);
++ if (!conf_sta)
++ goto fail;
++ conf = conf_sta;
++ }
++
++ pos = os_strstr(cmd, " conf=ap-");
++ if (pos) {
++ conf_ap = dpp_configuration_alloc(pos + 9);
++ if (!conf_ap)
++ goto fail;
++ conf = conf_ap;
++ }
++
++ if (!conf)
++ return 0;
++
++ pos = os_strstr(cmd, " ssid=");
++ if (pos) {
++ pos += 6;
++ end = os_strchr(pos, ' ');
++ conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
++ conf->ssid_len /= 2;
++ if (conf->ssid_len > sizeof(conf->ssid) ||
++ hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0)
++ goto fail;
++ } else {
++#ifdef CONFIG_TESTING_OPTIONS
++ /* use a default SSID for legacy testing reasons */
++ os_memcpy(conf->ssid, "test", 4);
++ conf->ssid_len = 4;
++#else /* CONFIG_TESTING_OPTIONS */
++ goto fail;
++#endif /* CONFIG_TESTING_OPTIONS */
++ }
++
++ pos = os_strstr(cmd, " pass=");
++ if (pos) {
++ size_t pass_len;
++
++ pos += 6;
++ end = os_strchr(pos, ' ');
++ pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
++ pass_len /= 2;
++ if (pass_len > 63 || pass_len < 8)
++ goto fail;
++ conf->passphrase = os_zalloc(pass_len + 1);
++ if (!conf->passphrase ||
++ hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
++ goto fail;
++ }
++
++ pos = os_strstr(cmd, " psk=");
++ if (pos) {
++ pos += 5;
++ if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0)
++ goto fail;
++ conf->psk_set = 1;
++ }
++
++ pos = os_strstr(cmd, " group_id=");
++ if (pos) {
++ size_t group_id_len;
++
++ pos += 10;
++ end = os_strchr(pos, ' ');
++ group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
++ conf->group_id = os_malloc(group_id_len + 1);
++ if (!conf->group_id)
++ goto fail;
++ os_memcpy(conf->group_id, pos, group_id_len);
++ conf->group_id[group_id_len] = '\0';
++ }
++
++ pos = os_strstr(cmd, " expiry=");
++ if (pos) {
++ long int val;
++
++ pos += 8;
++ val = strtol(pos, NULL, 0);
++ if (val <= 0)
++ goto fail;
++ conf->netaccesskey_expiry = val;
++ }
++
++ if (!dpp_configuration_valid(conf))
++ goto fail;
++
++ auth->conf_sta = conf_sta;
++ auth->conf_ap = conf_ap;
++ return 0;
++
++fail:
++ dpp_configuration_free(conf_sta);
++ dpp_configuration_free(conf_ap);
++ return -1;
++}
++
++
++static struct dpp_configurator *
++dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
++{
++ struct dpp_configurator *conf;
++
++ if (!dpp)
++ return NULL;
++
++ dl_list_for_each(conf, &dpp->configurator,
++ struct dpp_configurator, list) {
++ if (conf->id == id)
++ return conf;
++ }
++ return NULL;
++}
++
++
++int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
++ struct dpp_authentication *auth,
++ const char *cmd)
++{
++ const char *pos;
++
++ if (!cmd)
++ return 0;
++
++ wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
++
++ pos = os_strstr(cmd, " configurator=");
++ if (pos) {
++ pos += 14;
++ auth->conf = dpp_configurator_get_id(dpp, atoi(pos));
++ if (!auth->conf) {
++ wpa_printf(MSG_INFO,
++ "DPP: Could not find the specified configurator");
++ return -1;
++ }
++ }
++
++ if (dpp_configuration_parse(auth, cmd) < 0) {
++ wpa_msg(msg_ctx, MSG_INFO,
++ "DPP: Failed to set configurator parameters");
++ return -1;
++ }
++ return 0;
++}
++
++
++void dpp_auth_deinit(struct dpp_authentication *auth)
++{
++ if (!auth)
++ return;
++ dpp_configuration_free(auth->conf_ap);
++ dpp_configuration_free(auth->conf_sta);
++ EVP_PKEY_free(auth->own_protocol_key);
++ EVP_PKEY_free(auth->peer_protocol_key);
++ wpabuf_free(auth->req_msg);
++ wpabuf_free(auth->resp_msg);
++ wpabuf_free(auth->conf_req);
++ os_free(auth->connector);
++ wpabuf_free(auth->net_access_key);
++ wpabuf_free(auth->c_sign_key);
++ dpp_bootstrap_info_free(auth->tmp_own_bi);
++#ifdef CONFIG_TESTING_OPTIONS
++ os_free(auth->config_obj_override);
++ os_free(auth->discovery_override);
++ os_free(auth->groups_override);
++#endif /* CONFIG_TESTING_OPTIONS */
++ bin_clear_free(auth, sizeof(*auth));
++}
++
++
++static struct wpabuf *
++dpp_build_conf_start(struct dpp_authentication *auth,
++ struct dpp_configuration *conf, size_t tailroom)
++{
++ struct wpabuf *buf;
++ char ssid[6 * sizeof(conf->ssid) + 1];
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->discovery_override)
++ tailroom += os_strlen(auth->discovery_override);
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ buf = wpabuf_alloc(200 + tailroom);
++ if (!buf)
++ return NULL;
++ wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->discovery_override) {
++ wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
++ auth->discovery_override);
++ wpabuf_put_str(buf, auth->discovery_override);
++ wpabuf_put_u8(buf, ',');
++ return buf;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ wpabuf_put_str(buf, "{\"ssid\":\"");
++ json_escape_string(ssid, sizeof(ssid),
++ (const char *) conf->ssid, conf->ssid_len);
++ wpabuf_put_str(buf, ssid);
++ wpabuf_put_str(buf, "\"},");
++
++ return buf;
++}
++
++
++static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
++ const char *kid, const struct dpp_curve_params *curve)
++{
++ struct wpabuf *pub;
++ const u8 *pos;
++ char *x = NULL, *y = NULL;
++ int ret = -1;
++
++ pub = dpp_get_pubkey_point(key, 0);
++ if (!pub)
++ goto fail;
++ pos = wpabuf_head(pub);
++ x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
++ pos += curve->prime_len;
++ y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
++ if (!x || !y)
++ goto fail;
++
++ wpabuf_put_str(buf, "\"");
++ wpabuf_put_str(buf, name);
++ wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
++ wpabuf_put_str(buf, curve->jwk_crv);
++ wpabuf_put_str(buf, "\",\"x\":\"");
++ wpabuf_put_str(buf, x);
++ wpabuf_put_str(buf, "\",\"y\":\"");
++ wpabuf_put_str(buf, y);
++ if (kid) {
++ wpabuf_put_str(buf, "\",\"kid\":\"");
++ wpabuf_put_str(buf, kid);
++ }
++ wpabuf_put_str(buf, "\"}");
++ ret = 0;
++fail:
++ wpabuf_free(pub);
++ os_free(x);
++ os_free(y);
++ return ret;
++}
++
++
++static void dpp_build_legacy_cred_params(struct wpabuf *buf,
++ struct dpp_configuration *conf)
++{
++ if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
++ char pass[63 * 6 + 1];
++
++ json_escape_string(pass, sizeof(pass), conf->passphrase,
++ os_strlen(conf->passphrase));
++ wpabuf_put_str(buf, "\"pass\":\"");
++ wpabuf_put_str(buf, pass);
++ wpabuf_put_str(buf, "\"");
++ os_memset(pass, 0, sizeof(pass));
++ } else if (conf->psk_set) {
++ char psk[2 * sizeof(conf->psk) + 1];
++
++ wpa_snprintf_hex(psk, sizeof(psk),
++ conf->psk, sizeof(conf->psk));
++ wpabuf_put_str(buf, "\"psk_hex\":\"");
++ wpabuf_put_str(buf, psk);
++ wpabuf_put_str(buf, "\"");
++ os_memset(psk, 0, sizeof(psk));
++ }
++}
++
++
++static struct wpabuf *
++dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
++ struct dpp_configuration *conf)
++{
++ struct wpabuf *buf = NULL;
++ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
++ size_t tailroom;
++ const struct dpp_curve_params *curve;
++ char jws_prot_hdr[100];
++ size_t signed1_len, signed2_len, signed3_len;
++ struct wpabuf *dppcon = NULL;
++ unsigned char *signature = NULL;
++ const unsigned char *p;
++ size_t signature_len;
++ EVP_MD_CTX *md_ctx = NULL;
++ ECDSA_SIG *sig = NULL;
++ char *dot = ".";
++ const EVP_MD *sign_md;
++ const BIGNUM *r, *s;
++ size_t extra_len = 1000;
++ int incl_legacy;
++ enum dpp_akm akm;
++
++ if (!auth->conf) {
++ wpa_printf(MSG_INFO,
++ "DPP: No configurator specified - cannot generate DPP config object");
++ goto fail;
++ }
++ curve = auth->conf->curve;
++ if (curve->hash_len == SHA256_MAC_LEN) {
++ sign_md = EVP_sha256();
++ } else if (curve->hash_len == SHA384_MAC_LEN) {
++ sign_md = EVP_sha384();
++ } else if (curve->hash_len == SHA512_MAC_LEN) {
++ sign_md = EVP_sha512();
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
++ goto fail;
++ }
++
++ akm = conf->akm;
++ if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2");
++ akm = DPP_AKM_DPP;
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->groups_override)
++ extra_len += os_strlen(auth->groups_override);
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (conf->group_id)
++ extra_len += os_strlen(conf->group_id);
++
++ /* Connector (JSON dppCon object) */
++ dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
++ if (!dppcon)
++ goto fail;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->groups_override) {
++ wpabuf_put_u8(dppcon, '{');
++ if (auth->groups_override) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: TESTING - groups override: '%s'",
++ auth->groups_override);
++ wpabuf_put_str(dppcon, "\"groups\":");
++ wpabuf_put_str(dppcon, auth->groups_override);
++ wpabuf_put_u8(dppcon, ',');
++ }
++ goto skip_groups;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++ wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
++ conf->group_id ? conf->group_id : "*");
++ wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
++#ifdef CONFIG_TESTING_OPTIONS
++skip_groups:
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
++ auth->curve) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
++ goto fail;
++ }
++ if (conf->netaccesskey_expiry) {
++ struct os_tm tm;
++
++ if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to generate expiry string");
++ goto fail;
++ }
++ wpabuf_printf(dppcon,
++ ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
++ tm.year, tm.month, tm.day,
++ tm.hour, tm.min, tm.sec);
++ }
++ wpabuf_put_u8(dppcon, '}');
++ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
++ (const char *) wpabuf_head(dppcon));
++
++ os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
++ "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
++ auth->conf->kid, curve->jws_alg);
++ signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
++ os_strlen(jws_prot_hdr),
++ &signed1_len, 0);
++ signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
++ wpabuf_len(dppcon),
++ &signed2_len, 0);
++ if (!signed1 || !signed2)
++ goto fail;
++
++ md_ctx = EVP_MD_CTX_create();
++ if (!md_ctx)
++ goto fail;
++
++ ERR_clear_error();
++ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
++ auth->conf->csign) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
++ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
++ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ signature = os_malloc(signature_len);
++ if (!signature)
++ goto fail;
++ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
++ signature, signature_len);
++ /* Convert to raw coordinates r,s */
++ p = signature;
++ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
++ if (!sig)
++ goto fail;
++ ECDSA_SIG_get0(sig, &r, &s);
++ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(s, signature + curve->prime_len,
++ curve->prime_len) < 0)
++ goto fail;
++ signature_len = 2 * curve->prime_len;
++ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
++ signature, signature_len);
++ signed3 = (char *) base64_url_encode(signature, signature_len,
++ &signed3_len, 0);
++ if (!signed3)
++ goto fail;
++
++ incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
++ tailroom = 1000;
++ tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
++ tailroom += signed1_len + signed2_len + signed3_len;
++ if (incl_legacy)
++ tailroom += 1000;
++ buf = dpp_build_conf_start(auth, conf, tailroom);
++ if (!buf)
++ goto fail;
++
++ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm));
++ if (incl_legacy) {
++ dpp_build_legacy_cred_params(buf, conf);
++ wpabuf_put_str(buf, ",");
++ }
++ wpabuf_put_str(buf, "\"signedConnector\":\"");
++ wpabuf_put_str(buf, signed1);
++ wpabuf_put_u8(buf, '.');
++ wpabuf_put_str(buf, signed2);
++ wpabuf_put_u8(buf, '.');
++ wpabuf_put_str(buf, signed3);
++ wpabuf_put_str(buf, "\",");
++ if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
++ curve) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
++ goto fail;
++ }
++
++ wpabuf_put_str(buf, "}}");
++
++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
++ wpabuf_head(buf), wpabuf_len(buf));
++
++out:
++ EVP_MD_CTX_destroy(md_ctx);
++ ECDSA_SIG_free(sig);
++ os_free(signed1);
++ os_free(signed2);
++ os_free(signed3);
++ os_free(signature);
++ wpabuf_free(dppcon);
++ return buf;
++fail:
++ wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
++ wpabuf_free(buf);
++ buf = NULL;
++ goto out;
++}
++
++
++static struct wpabuf *
++dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
++ struct dpp_configuration *conf)
++{
++ struct wpabuf *buf;
++
++ buf = dpp_build_conf_start(auth, conf, 1000);
++ if (!buf)
++ return NULL;
++
++ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
++ dpp_build_legacy_cred_params(buf, conf);
++ wpabuf_put_str(buf, "}}");
++
++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
++ wpabuf_head(buf), wpabuf_len(buf));
++
++ return buf;
++}
++
++
++static struct wpabuf *
++dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
++{
++ struct dpp_configuration *conf;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->config_obj_override) {
++ wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
++ return wpabuf_alloc_copy(auth->config_obj_override,
++ os_strlen(auth->config_obj_override));
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ conf = ap ? auth->conf_ap : auth->conf_sta;
++ if (!conf) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No configuration available for Enrollee(%s) - reject configuration request",
++ ap ? "ap" : "sta");
++ return NULL;
++ }
++
++ if (dpp_akm_dpp(conf->akm))
++ return dpp_build_conf_obj_dpp(auth, ap, conf);
++ return dpp_build_conf_obj_legacy(auth, ap, conf);
++}
++
++
++static struct wpabuf *
++dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
++ u16 e_nonce_len, int ap)
++{
++ struct wpabuf *conf;
++ size_t clear_len, attr_len;
++ struct wpabuf *clear = NULL, *msg = NULL;
++ u8 *wrapped;
++ const u8 *addr[1];
++ size_t len[1];
++ enum dpp_status_error status;
++
++ conf = dpp_build_conf_obj(auth, ap);
++ if (conf) {
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
++ wpabuf_head(conf), wpabuf_len(conf));
++ }
++ status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
++ auth->conf_resp_status = status;
++
++ /* { E-nonce, configurationObject}ke */
++ clear_len = 4 + e_nonce_len;
++ if (conf)
++ clear_len += 4 + wpabuf_len(conf);
++ clear = wpabuf_alloc(clear_len);
++ attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = wpabuf_alloc(attr_len);
++ if (!clear || !msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
++ goto skip_e_nonce;
++ }
++ if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
++ wpabuf_put_le16(clear, e_nonce_len);
++ wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
++ wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
++ goto skip_e_nonce;
++ }
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* E-nonce */
++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
++ wpabuf_put_le16(clear, e_nonce_len);
++ wpabuf_put_data(clear, e_nonce, e_nonce_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_e_nonce:
++ if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
++ goto skip_config_obj;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (conf) {
++ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
++ wpabuf_put_le16(clear, wpabuf_len(conf));
++ wpabuf_put_buf(clear, conf);
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_config_obj:
++ if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - Status");
++ goto skip_status;
++ }
++ if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
++ status = 255;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* DPP Status */
++ dpp_build_attr_status(msg, status);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_status:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ addr[0] = wpabuf_head(msg);
++ len[0] = wpabuf_len(msg);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
++ wpabuf_head(clear), wpabuf_len(clear),
++ 1, addr, len, wrapped) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ wpa_hexdump_buf(MSG_DEBUG,
++ "DPP: Configuration Response attributes", msg);
++out:
++ wpabuf_free(conf);
++ wpabuf_free(clear);
++
++ return msg;
++fail:
++ wpabuf_free(msg);
++ msg = NULL;
++ goto out;
++}
++
++
++struct wpabuf *
++dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
++ size_t attr_len)
++{
++ const u8 *wrapped_data, *e_nonce, *config_attr;
++ u16 wrapped_data_len, e_nonce_len, config_attr_len;
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ struct wpabuf *resp = NULL;
++ struct json_token *root = NULL, *token;
++ int ap;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at Config Request");
++ return NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (dpp_check_attrs(attr_start, attr_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in config request");
++ return NULL;
++ }
++
++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Wrapped Data attribute");
++ return NULL;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ return NULL;
++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 0, NULL, NULL, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_ENROLLEE_NONCE,
++ &e_nonce_len);
++ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Enrollee Nonce attribute");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
++ os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
++
++ config_attr = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_CONFIG_ATTR_OBJ,
++ &config_attr_len);
++ if (!config_attr) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Config Attributes attribute");
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
++ config_attr, config_attr_len);
++
++ root = json_parse((const char *) config_attr, config_attr_len);
++ if (!root) {
++ dpp_auth_fail(auth, "Could not parse Config Attributes");
++ goto fail;
++ }
++
++ token = json_get_member(root, "name");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No Config Attributes - name");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
++
++ token = json_get_member(root, "wi-fi_tech");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
++ if (os_strcmp(token->string, "infra") != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
++ token->string);
++ dpp_auth_fail(auth, "Unsupported wi-fi_tech");
++ goto fail;
++ }
++
++ token = json_get_member(root, "netRole");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No Config Attributes - netRole");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
++ if (os_strcmp(token->string, "sta") == 0) {
++ ap = 0;
++ } else if (os_strcmp(token->string, "ap") == 0) {
++ ap = 1;
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
++ token->string);
++ dpp_auth_fail(auth, "Unsupported netRole");
++ goto fail;
++ }
++
++ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
++
++fail:
++ json_free(root);
++ os_free(unwrapped);
++ return resp;
++}
++
++
++static struct wpabuf *
++dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
++ const u8 *prot_hdr, u16 prot_hdr_len,
++ const EVP_MD **ret_md)
++{
++ struct json_token *root, *token;
++ struct wpabuf *kid = NULL;
++
++ root = json_parse((const char *) prot_hdr, prot_hdr_len);
++ if (!root) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: JSON parsing failed for JWS Protected Header");
++ goto fail;
++ }
++
++ if (root->type != JSON_OBJECT) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: JWS Protected Header root is not an object");
++ goto fail;
++ }
++
++ token = json_get_member(root, "typ");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
++ token->string);
++ if (os_strcmp(token->string, "dppCon") != 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unsupported JWS Protected Header typ=%s",
++ token->string);
++ goto fail;
++ }
++
++ token = json_get_member(root, "alg");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
++ token->string);
++ if (os_strcmp(token->string, curve->jws_alg) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
++ token->string, curve->jws_alg);
++ goto fail;
++ }
++ if (os_strcmp(token->string, "ES256") == 0 ||
++ os_strcmp(token->string, "BS256") == 0)
++ *ret_md = EVP_sha256();
++ else if (os_strcmp(token->string, "ES384") == 0 ||
++ os_strcmp(token->string, "BS384") == 0)
++ *ret_md = EVP_sha384();
++ else if (os_strcmp(token->string, "ES512") == 0 ||
++ os_strcmp(token->string, "BS512") == 0)
++ *ret_md = EVP_sha512();
++ else
++ *ret_md = NULL;
++ if (!*ret_md) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unsupported JWS Protected Header alg=%s",
++ token->string);
++ goto fail;
++ }
++
++ kid = json_get_member_base64url(root, "kid");
++ if (!kid) {
++ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
++ goto fail;
++ }
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
++ kid);
++
++fail:
++ json_free(root);
++ return kid;
++}
++
++
++static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
++ struct json_token *cred)
++{
++ struct json_token *pass, *psk_hex;
++
++ wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
++
++ pass = json_get_member(cred, "pass");
++ psk_hex = json_get_member(cred, "psk_hex");
++
++ if (pass && pass->type == JSON_STRING) {
++ size_t len = os_strlen(pass->string);
++
++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
++ pass->string, len);
++ if (len < 8 || len > 63)
++ return -1;
++ os_strlcpy(auth->passphrase, pass->string,
++ sizeof(auth->passphrase));
++ } else if (psk_hex && psk_hex->type == JSON_STRING) {
++ if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected psk_hex with akm=sae");
++ return -1;
++ }
++ if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
++ hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
++ return -1;
++ }
++ wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
++ auth->psk, PMK_LEN);
++ auth->psk_set = 1;
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
++ return -1;
++ }
++
++ if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
++ wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
++ return -1;
++ }
++
++ return 0;
++}
++
++
++static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
++ const struct dpp_curve_params **key_curve)
++{
++ struct json_token *token;
++ const struct dpp_curve_params *curve;
++ struct wpabuf *x = NULL, *y = NULL;
++ EC_GROUP *group;
++ EVP_PKEY *pkey = NULL;
++
++ token = json_get_member(jwk, "kty");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
++ goto fail;
++ }
++ if (os_strcmp(token->string, "EC") != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'",
++ token->string);
++ goto fail;
++ }
++
++ token = json_get_member(jwk, "crv");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
++ goto fail;
++ }
++ curve = dpp_get_curve_jwk_crv(token->string);
++ if (!curve) {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
++ token->string);
++ goto fail;
++ }
++
++ x = json_get_member_base64url(jwk, "x");
++ if (!x) {
++ wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
++ goto fail;
++ }
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
++ if (wpabuf_len(x) != curve->prime_len) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
++ (unsigned int) wpabuf_len(x),
++ (unsigned int) curve->prime_len, curve->name);
++ goto fail;
++ }
++
++ y = json_get_member_base64url(jwk, "y");
++ if (!y) {
++ wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
++ goto fail;
++ }
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
++ if (wpabuf_len(y) != curve->prime_len) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
++ (unsigned int) wpabuf_len(y),
++ (unsigned int) curve->prime_len, curve->name);
++ goto fail;
++ }
++
++ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
++ if (!group) {
++ wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
++ goto fail;
++ }
++
++ pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
++ wpabuf_len(x));
++ *key_curve = curve;
++
++fail:
++ wpabuf_free(x);
++ wpabuf_free(y);
++
++ return pkey;
++}
++
++
++int dpp_key_expired(const char *timestamp, os_time_t *expiry)
++{
++ struct os_time now;
++ unsigned int year, month, day, hour, min, sec;
++ os_time_t utime;
++ const char *pos;
++
++ /* ISO 8601 date and time:
++ * T
++ * YYYY-MM-DDTHH:MM:SSZ
++ * YYYY-MM-DDTHH:MM:SS+03:00
++ */
++ if (os_strlen(timestamp) < 19) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Too short timestamp - assume expired key");
++ return 1;
++ }
++ if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
++ &year, &month, &day, &hour, &min, &sec) != 6) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to parse expiration day - assume expired key");
++ return 1;
++ }
++
++ if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Invalid date/time information - assume expired key");
++ return 1;
++ }
++
++ pos = timestamp + 19;
++ if (*pos == 'Z' || *pos == '\0') {
++ /* In UTC - no need to adjust */
++ } else if (*pos == '-' || *pos == '+') {
++ int items;
++
++ /* Adjust local time to UTC */
++ items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
++ if (items < 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Invalid time zone designator (%s) - assume expired key",
++ pos);
++ return 1;
++ }
++ if (*pos == '-')
++ utime += 3600 * hour;
++ if (*pos == '+')
++ utime -= 3600 * hour;
++ if (items > 1) {
++ if (*pos == '-')
++ utime += 60 * min;
++ if (*pos == '+')
++ utime -= 60 * min;
++ }
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Invalid time zone designator (%s) - assume expired key",
++ pos);
++ return 1;
++ }
++ if (expiry)
++ *expiry = utime;
++
++ if (os_get_time(&now) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Cannot get current time - assume expired key");
++ return 1;
++ }
++
++ if (now.sec > utime) {
++ wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
++ utime, now.sec);
++ return 1;
++ }
++
++ return 0;
++}
++
++
++static int dpp_parse_connector(struct dpp_authentication *auth,
++ const unsigned char *payload,
++ u16 payload_len)
++{
++ struct json_token *root, *groups, *netkey, *token;
++ int ret = -1;
++ EVP_PKEY *key = NULL;
++ const struct dpp_curve_params *curve;
++ unsigned int rules = 0;
++
++ root = json_parse((const char *) payload, payload_len);
++ if (!root) {
++ wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
++ goto fail;
++ }
++
++ groups = json_get_member(root, "groups");
++ if (!groups || groups->type != JSON_ARRAY) {
++ wpa_printf(MSG_DEBUG, "DPP: No groups array found");
++ goto skip_groups;
++ }
++ for (token = groups->child; token; token = token->sibling) {
++ struct json_token *id, *role;
++
++ id = json_get_member(token, "groupId");
++ if (!id || id->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
++ goto fail;
++ }
++
++ role = json_get_member(token, "netRole");
++ if (!role || role->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG,
++ "DPP: connector group: groupId='%s' netRole='%s'",
++ id->string, role->string);
++ rules++;
++ }
++skip_groups:
++
++ if (!rules) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Connector includes no groups");
++ goto fail;
++ }
++
++ token = json_get_member(root, "expiry");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No expiry string found - connector does not expire");
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
++ if (dpp_key_expired(token->string,
++ &auth->net_access_key_expiry)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Connector (netAccessKey) has expired");
++ goto fail;
++ }
++ }
++
++ netkey = json_get_member(root, "netAccessKey");
++ if (!netkey || netkey->type != JSON_OBJECT) {
++ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
++ goto fail;
++ }
++
++ key = dpp_parse_jwk(netkey, &curve);
++ if (!key)
++ goto fail;
++ dpp_debug_print_key("DPP: Received netAccessKey", key);
++
++ if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: netAccessKey in connector does not match own protocol key");
++#ifdef CONFIG_TESTING_OPTIONS
++ if (auth->ignore_netaccesskey_mismatch) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: TESTING - skip netAccessKey mismatch");
++ } else {
++ goto fail;
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ goto fail;
++#endif /* CONFIG_TESTING_OPTIONS */
++ }
++
++ ret = 0;
++fail:
++ EVP_PKEY_free(key);
++ json_free(root);
++ return ret;
++}
++
++
++static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
++{
++ struct wpabuf *uncomp;
++ int res;
++ u8 hash[SHA256_MAC_LEN];
++ const u8 *addr[1];
++ size_t len[1];
++
++ if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
++ return -1;
++ uncomp = dpp_get_pubkey_point(pub, 1);
++ if (!uncomp)
++ return -1;
++ addr[0] = wpabuf_head(uncomp);
++ len[0] = wpabuf_len(uncomp);
++ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
++ addr[0], len[0]);
++ res = sha256_vector(1, addr, len, hash);
++ wpabuf_free(uncomp);
++ if (res < 0)
++ return -1;
++ if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Received hash value does not match calculated public key hash value");
++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
++ hash, SHA256_MAC_LEN);
++ return -1;
++ }
++ return 0;
++}
++
++
++static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
++{
++ unsigned char *der = NULL;
++ int der_len;
++
++ der_len = i2d_PUBKEY(csign, &der);
++ if (der_len <= 0)
++ return;
++ wpabuf_free(auth->c_sign_key);
++ auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
++ OPENSSL_free(der);
++}
++
++
++static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
++{
++ unsigned char *der = NULL;
++ int der_len;
++ EC_KEY *eckey;
++
++ eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
++ if (!eckey)
++ return;
++
++ der_len = i2d_ECPrivateKey(eckey, &der);
++ if (der_len <= 0) {
++ EC_KEY_free(eckey);
++ return;
++ }
++ wpabuf_free(auth->net_access_key);
++ auth->net_access_key = wpabuf_alloc_copy(der, der_len);
++ OPENSSL_free(der);
++ EC_KEY_free(eckey);
++}
++
++
++struct dpp_signed_connector_info {
++ unsigned char *payload;
++ size_t payload_len;
++};
++
++static enum dpp_status_error
++dpp_process_signed_connector(struct dpp_signed_connector_info *info,
++ EVP_PKEY *csign_pub, const char *connector)
++{
++ enum dpp_status_error ret = 255;
++ const char *pos, *end, *signed_start, *signed_end;
++ struct wpabuf *kid = NULL;
++ unsigned char *prot_hdr = NULL, *signature = NULL;
++ size_t prot_hdr_len = 0, signature_len = 0;
++ const EVP_MD *sign_md = NULL;
++ unsigned char *der = NULL;
++ int der_len;
++ int res;
++ EVP_MD_CTX *md_ctx = NULL;
++ ECDSA_SIG *sig = NULL;
++ BIGNUM *r = NULL, *s = NULL;
++ const struct dpp_curve_params *curve;
++ EC_KEY *eckey;
++ const EC_GROUP *group;
++ int nid;
++
++ eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
++ if (!eckey)
++ goto fail;
++ group = EC_KEY_get0_group(eckey);
++ if (!group)
++ goto fail;
++ nid = EC_GROUP_get_curve_name(group);
++ curve = dpp_get_curve_nid(nid);
++ if (!curve)
++ goto fail;
++ wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
++ os_memset(info, 0, sizeof(*info));
++
++ signed_start = pos = connector;
++ end = os_strchr(pos, '.');
++ if (!end) {
++ wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ prot_hdr = base64_url_decode((const unsigned char *) pos,
++ end - pos, &prot_hdr_len);
++ if (!prot_hdr) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to base64url decode signedConnector JWS Protected Header");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG,
++ "DPP: signedConnector - JWS Protected Header",
++ prot_hdr, prot_hdr_len);
++ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
++ if (!kid) {
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ if (wpabuf_len(kid) != SHA256_MAC_LEN) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
++ (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ pos = end + 1;
++ end = os_strchr(pos, '.');
++ if (!end) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Missing dot(2) in signedConnector");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ signed_end = end - 1;
++ info->payload = base64_url_decode((const unsigned char *) pos,
++ end - pos, &info->payload_len);
++ if (!info->payload) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to base64url decode signedConnector JWS Payload");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG,
++ "DPP: signedConnector - JWS Payload",
++ info->payload, info->payload_len);
++ pos = end + 1;
++ signature = base64_url_decode((const unsigned char *) pos,
++ os_strlen(pos), &signature_len);
++ if (!signature) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to base64url decode signedConnector signature");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
++ signature, signature_len);
++
++ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
++ ret = DPP_STATUS_NO_MATCH;
++ goto fail;
++ }
++
++ if (signature_len & 0x01) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected signedConnector signature length (%d)",
++ (int) signature_len);
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ /* JWS Signature encodes the signature (r,s) as two octet strings. Need
++ * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
++ r = BN_bin2bn(signature, signature_len / 2, NULL);
++ s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
++ sig = ECDSA_SIG_new();
++ if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
++ goto fail;
++ r = NULL;
++ s = NULL;
++
++ der_len = i2d_ECDSA_SIG(sig, &der);
++ if (der_len <= 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
++ md_ctx = EVP_MD_CTX_create();
++ if (!md_ctx)
++ goto fail;
++
++ ERR_clear_error();
++ if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
++ signed_end - signed_start + 1) != 1) {
++ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
++ if (res != 1) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
++ res, ERR_error_string(ERR_get_error(), NULL));
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ ret = DPP_STATUS_OK;
++fail:
++ EC_KEY_free(eckey);
++ EVP_MD_CTX_destroy(md_ctx);
++ os_free(prot_hdr);
++ wpabuf_free(kid);
++ os_free(signature);
++ ECDSA_SIG_free(sig);
++ BN_free(r);
++ BN_free(s);
++ OPENSSL_free(der);
++ return ret;
++}
++
++
++static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
++ struct json_token *cred)
++{
++ struct dpp_signed_connector_info info;
++ struct json_token *token, *csign;
++ int ret = -1;
++ EVP_PKEY *csign_pub = NULL;
++ const struct dpp_curve_params *key_curve = NULL;
++ const char *signed_connector;
++
++ os_memset(&info, 0, sizeof(info));
++
++ if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Legacy credential included in Connector credential");
++ if (dpp_parse_cred_legacy(auth, cred) < 0)
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: Connector credential");
++
++ csign = json_get_member(cred, "csign");
++ if (!csign || csign->type != JSON_OBJECT) {
++ wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
++ goto fail;
++ }
++
++ csign_pub = dpp_parse_jwk(csign, &key_curve);
++ if (!csign_pub) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
++ goto fail;
++ }
++ dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
++
++ token = json_get_member(cred, "signedConnector");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
++ token->string, os_strlen(token->string));
++ signed_connector = token->string;
++
++ if (os_strchr(signed_connector, '"') ||
++ os_strchr(signed_connector, '\n')) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Unexpected character in signedConnector");
++ goto fail;
++ }
++
++ if (dpp_process_signed_connector(&info, csign_pub,
++ signed_connector) != DPP_STATUS_OK)
++ goto fail;
++
++ if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
++ goto fail;
++ }
++
++ os_free(auth->connector);
++ auth->connector = os_strdup(signed_connector);
++
++ dpp_copy_csign(auth, csign_pub);
++ dpp_copy_netaccesskey(auth);
++
++ ret = 0;
++fail:
++ EVP_PKEY_free(csign_pub);
++ os_free(info.payload);
++ return ret;
++}
++
++
++const char * dpp_akm_str(enum dpp_akm akm)
++{
++ switch (akm) {
++ case DPP_AKM_DPP:
++ return "dpp";
++ case DPP_AKM_PSK:
++ return "psk";
++ case DPP_AKM_SAE:
++ return "sae";
++ case DPP_AKM_PSK_SAE:
++ return "psk+sae";
++ case DPP_AKM_SAE_DPP:
++ return "dpp+sae";
++ case DPP_AKM_PSK_SAE_DPP:
++ return "dpp+psk+sae";
++ default:
++ return "??";
++ }
++}
++
++
++static enum dpp_akm dpp_akm_from_str(const char *akm)
++{
++ if (os_strcmp(akm, "psk") == 0)
++ return DPP_AKM_PSK;
++ if (os_strcmp(akm, "sae") == 0)
++ return DPP_AKM_SAE;
++ if (os_strcmp(akm, "psk+sae") == 0)
++ return DPP_AKM_PSK_SAE;
++ if (os_strcmp(akm, "dpp") == 0)
++ return DPP_AKM_DPP;
++ if (os_strcmp(akm, "dpp+sae") == 0)
++ return DPP_AKM_SAE_DPP;
++ if (os_strcmp(akm, "dpp+psk+sae") == 0)
++ return DPP_AKM_PSK_SAE_DPP;
++ return DPP_AKM_UNKNOWN;
++}
++
++
++static int dpp_parse_conf_obj(struct dpp_authentication *auth,
++ const u8 *conf_obj, u16 conf_obj_len)
++{
++ int ret = -1;
++ struct json_token *root, *token, *discovery, *cred;
++
++ root = json_parse((const char *) conf_obj, conf_obj_len);
++ if (!root)
++ return -1;
++ if (root->type != JSON_OBJECT) {
++ dpp_auth_fail(auth, "JSON root is not an object");
++ goto fail;
++ }
++
++ token = json_get_member(root, "wi-fi_tech");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No wi-fi_tech string value found");
++ goto fail;
++ }
++ if (os_strcmp(token->string, "infra") != 0) {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
++ token->string);
++ dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
++ goto fail;
++ }
++
++ discovery = json_get_member(root, "discovery");
++ if (!discovery || discovery->type != JSON_OBJECT) {
++ dpp_auth_fail(auth, "No discovery object in JSON");
++ goto fail;
++ }
++
++ token = json_get_member(discovery, "ssid");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No discovery::ssid string value found");
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
++ token->string, os_strlen(token->string));
++ if (os_strlen(token->string) > SSID_MAX_LEN) {
++ dpp_auth_fail(auth, "Too long discovery::ssid string value");
++ goto fail;
++ }
++ auth->ssid_len = os_strlen(token->string);
++ os_memcpy(auth->ssid, token->string, auth->ssid_len);
++
++ cred = json_get_member(root, "cred");
++ if (!cred || cred->type != JSON_OBJECT) {
++ dpp_auth_fail(auth, "No cred object in JSON");
++ goto fail;
++ }
++
++ token = json_get_member(cred, "akm");
++ if (!token || token->type != JSON_STRING) {
++ dpp_auth_fail(auth, "No cred::akm string value found");
++ goto fail;
++ }
++ auth->akm = dpp_akm_from_str(token->string);
++
++ if (dpp_akm_legacy(auth->akm)) {
++ if (dpp_parse_cred_legacy(auth, cred) < 0)
++ goto fail;
++ } else if (dpp_akm_dpp(auth->akm)) {
++ if (dpp_parse_cred_dpp(auth, cred) < 0)
++ goto fail;
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
++ token->string);
++ dpp_auth_fail(auth, "Unsupported akm");
++ goto fail;
++ }
++
++ wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
++ ret = 0;
++fail:
++ json_free(root);
++ return ret;
++}
++
++
++int dpp_conf_resp_rx(struct dpp_authentication *auth,
++ const struct wpabuf *resp)
++{
++ const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
++ u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
++ const u8 *addr[1];
++ size_t len[1];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ int ret = -1;
++
++ auth->conf_resp_status = 255;
++
++ if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in config response");
++ return -1;
++ }
++
++ wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
++ DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Wrapped Data attribute");
++ return -1;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ return -1;
++
++ addr[0] = wpabuf_head(resp);
++ len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
++
++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 1, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_ENROLLEE_NONCE,
++ &e_nonce_len);
++ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Enrollee Nonce attribute");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
++ if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
++ dpp_auth_fail(auth, "Enrollee Nonce mismatch");
++ goto fail;
++ }
++
++ status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
++ DPP_ATTR_STATUS, &status_len);
++ if (!status || status_len < 1) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required DPP Status attribute");
++ goto fail;
++ }
++ auth->conf_resp_status = status[0];
++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
++ if (status[0] != DPP_STATUS_OK) {
++ dpp_auth_fail(auth, "Configurator rejected configuration");
++ goto fail;
++ }
++
++ conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
++ if (!conf_obj) {
++ dpp_auth_fail(auth,
++ "Missing required Configuration Object attribute");
++ goto fail;
++ }
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
++ conf_obj, conf_obj_len);
++ if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
++ goto fail;
++
++ ret = 0;
++
++fail:
++ os_free(unwrapped);
++ return ret;
++}
++
++
++#ifdef CONFIG_DPP2
++enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
++ const u8 *hdr,
++ const u8 *attr_start, size_t attr_len)
++{
++ const u8 *wrapped_data, *status, *e_nonce;
++ u16 wrapped_data_len, status_len, e_nonce_len;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ enum dpp_status_error ret = 256;
++
++ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required Wrapped Data attribute");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
++ wrapped_data, wrapped_data_len);
++
++ attr_len = wrapped_data - 4 - attr_start;
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ addr[1] = attr_start;
++ len[1] = attr_len;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_auth_fail(auth, "AES-SIV decryption failed");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
++ DPP_ATTR_ENROLLEE_NONCE,
++ &e_nonce_len);
++ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
++ dpp_auth_fail(auth,
++ "Missing or invalid Enrollee Nonce attribute");
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
++ if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
++ dpp_auth_fail(auth, "Enrollee Nonce mismatch");
++ wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
++ auth->e_nonce, e_nonce_len);
++ goto fail;
++ }
++
++ status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS,
++ &status_len);
++ if (!status || status_len < 1) {
++ dpp_auth_fail(auth,
++ "Missing or invalid required DPP Status attribute");
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
++ ret = status[0];
++
++fail:
++ bin_clear_free(unwrapped, unwrapped_len);
++ return ret;
++}
++#endif /* CONFIG_DPP2 */
++
++
++struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
++ enum dpp_status_error status)
++{
++ struct wpabuf *msg, *clear;
++ size_t nonce_len, clear_len, attr_len;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 *wrapped;
++
++ nonce_len = auth->curve->nonce_len;
++ clear_len = 5 + 4 + nonce_len;
++ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
++ clear = wpabuf_alloc(clear_len);
++ msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
++ if (!clear || !msg)
++ return NULL;
++
++ /* DPP Status */
++ dpp_build_attr_status(clear, status);
++
++ /* E-nonce */
++ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
++ wpabuf_put_le16(clear, nonce_len);
++ wpabuf_put_data(clear, auth->e_nonce, nonce_len);
++
++ /* OUI, OUI type, Crypto Suite, DPP frame type */
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = 3 + 1 + 1 + 1;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++
++ /* Attributes before Wrapped Data (none) */
++ addr[1] = wpabuf_put(msg, 0);
++ len[1] = 0;
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ /* Wrapped Data */
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
++ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
++ wpabuf_head(clear), wpabuf_len(clear),
++ 2, addr, len, wrapped) < 0)
++ goto fail;
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg);
++ wpabuf_free(clear);
++ return msg;
++fail:
++ wpabuf_free(clear);
++ wpabuf_free(msg);
++ return NULL;
++}
++
++
++void dpp_configurator_free(struct dpp_configurator *conf)
++{
++ if (!conf)
++ return;
++ EVP_PKEY_free(conf->csign);
++ os_free(conf->kid);
++ os_free(conf);
++}
++
++
++int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
++ size_t buflen)
++{
++ EC_KEY *eckey;
++ int key_len, ret = -1;
++ unsigned char *key = NULL;
++
++ if (!conf->csign)
++ return -1;
++
++ eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
++ if (!eckey)
++ return -1;
++
++ key_len = i2d_ECPrivateKey(eckey, &key);
++ if (key_len > 0)
++ ret = wpa_snprintf_hex(buf, buflen, key, key_len);
++
++ EC_KEY_free(eckey);
++ OPENSSL_free(key);
++ return ret;
++}
++
++
++struct dpp_configurator *
++dpp_keygen_configurator(const char *curve, const u8 *privkey,
++ size_t privkey_len)
++{
++ struct dpp_configurator *conf;
++ struct wpabuf *csign_pub = NULL;
++ u8 kid_hash[SHA256_MAC_LEN];
++ const u8 *addr[1];
++ size_t len[1];
++
++ conf = os_zalloc(sizeof(*conf));
++ if (!conf)
++ return NULL;
++
++ if (!curve) {
++ conf->curve = &dpp_curves[0];
++ } else {
++ conf->curve = dpp_get_curve_name(curve);
++ if (!conf->curve) {
++ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
++ curve);
++ os_free(conf);
++ return NULL;
++ }
++ }
++ if (privkey)
++ conf->csign = dpp_set_keypair(&conf->curve, privkey,
++ privkey_len);
++ else
++ conf->csign = dpp_gen_keypair(conf->curve);
++ if (!conf->csign)
++ goto fail;
++ conf->own = 1;
++
++ csign_pub = dpp_get_pubkey_point(conf->csign, 1);
++ if (!csign_pub) {
++ wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
++ goto fail;
++ }
++
++ /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
++ addr[0] = wpabuf_head(csign_pub);
++ len[0] = wpabuf_len(csign_pub);
++ if (sha256_vector(1, addr, len, kid_hash) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to derive kid for C-sign-key");
++ goto fail;
++ }
++
++ conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
++ NULL, 0);
++ if (!conf->kid)
++ goto fail;
++out:
++ wpabuf_free(csign_pub);
++ return conf;
++fail:
++ dpp_configurator_free(conf);
++ conf = NULL;
++ goto out;
++}
++
++
++int dpp_configurator_own_config(struct dpp_authentication *auth,
++ const char *curve, int ap)
++{
++ struct wpabuf *conf_obj;
++ int ret = -1;
++
++ if (!auth->conf) {
++ wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
++ return -1;
++ }
++
++ if (!curve) {
++ auth->curve = &dpp_curves[0];
++ } else {
++ auth->curve = dpp_get_curve_name(curve);
++ if (!auth->curve) {
++ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
++ curve);
++ return -1;
++ }
++ }
++ wpa_printf(MSG_DEBUG,
++ "DPP: Building own configuration/connector with curve %s",
++ auth->curve->name);
++
++ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
++ if (!auth->own_protocol_key)
++ return -1;
++ dpp_copy_netaccesskey(auth);
++ auth->peer_protocol_key = auth->own_protocol_key;
++ dpp_copy_csign(auth, auth->conf->csign);
++
++ conf_obj = dpp_build_conf_obj(auth, ap);
++ if (!conf_obj)
++ goto fail;
++ ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
++ wpabuf_len(conf_obj));
++fail:
++ wpabuf_free(conf_obj);
++ auth->peer_protocol_key = NULL;
++ return ret;
++}
++
++
++static int dpp_compatible_netrole(const char *role1, const char *role2)
++{
++ return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
++ (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
++}
++
++
++static int dpp_connector_compatible_group(struct json_token *root,
++ const char *group_id,
++ const char *net_role)
++{
++ struct json_token *groups, *token;
++
++ groups = json_get_member(root, "groups");
++ if (!groups || groups->type != JSON_ARRAY)
++ return 0;
++
++ for (token = groups->child; token; token = token->sibling) {
++ struct json_token *id, *role;
++
++ id = json_get_member(token, "groupId");
++ if (!id || id->type != JSON_STRING)
++ continue;
++
++ role = json_get_member(token, "netRole");
++ if (!role || role->type != JSON_STRING)
++ continue;
++
++ if (os_strcmp(id->string, "*") != 0 &&
++ os_strcmp(group_id, "*") != 0 &&
++ os_strcmp(id->string, group_id) != 0)
++ continue;
++
++ if (dpp_compatible_netrole(role->string, net_role))
++ return 1;
++ }
++
++ return 0;
++}
++
++
++static int dpp_connector_match_groups(struct json_token *own_root,
++ struct json_token *peer_root)
++{
++ struct json_token *groups, *token;
++
++ groups = json_get_member(peer_root, "groups");
++ if (!groups || groups->type != JSON_ARRAY) {
++ wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
++ return 0;
++ }
++
++ for (token = groups->child; token; token = token->sibling) {
++ struct json_token *id, *role;
++
++ id = json_get_member(token, "groupId");
++ if (!id || id->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Missing peer groupId string");
++ continue;
++ }
++
++ role = json_get_member(token, "netRole");
++ if (!role || role->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Missing peer groups::netRole string");
++ continue;
++ }
++ wpa_printf(MSG_DEBUG,
++ "DPP: peer connector group: groupId='%s' netRole='%s'",
++ id->string, role->string);
++ if (dpp_connector_compatible_group(own_root, id->string,
++ role->string)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Compatible group/netRole in own connector");
++ return 1;
++ }
++ }
++
++ return 0;
++}
++
++
++static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
++ unsigned int hash_len)
++{
++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
++ const char *info = "DPP PMK";
++ int res;
++
++ /* PMK = HKDF(<>, "DPP PMK", N.x) */
++
++ /* HKDF-Extract(<>, N.x) */
++ os_memset(salt, 0, hash_len);
++ if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
++ prk, hash_len);
++
++ /* HKDF-Expand(PRK, info, L) */
++ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
++ os_memset(prk, 0, hash_len);
++ if (res < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
++ pmk, hash_len);
++ return 0;
++}
++
++
++static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
++ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
++{
++ struct wpabuf *nkx, *pkx;
++ int ret = -1, res;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 hash[SHA256_MAC_LEN];
++
++ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
++ nkx = dpp_get_pubkey_point(own_key, 0);
++ pkx = dpp_get_pubkey_point(peer_key, 0);
++ if (!nkx || !pkx)
++ goto fail;
++ addr[0] = wpabuf_head(nkx);
++ len[0] = wpabuf_len(nkx) / 2;
++ addr[1] = wpabuf_head(pkx);
++ len[1] = wpabuf_len(pkx) / 2;
++ if (len[0] != len[1])
++ goto fail;
++ if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
++ addr[0] = wpabuf_head(pkx);
++ addr[1] = wpabuf_head(nkx);
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
++ res = sha256_vector(2, addr, len, hash);
++ if (res < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
++ os_memcpy(pmkid, hash, PMKID_LEN);
++ wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
++ ret = 0;
++fail:
++ wpabuf_free(nkx);
++ wpabuf_free(pkx);
++ return ret;
++}
++
++
++enum dpp_status_error
++dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
++ const u8 *net_access_key, size_t net_access_key_len,
++ const u8 *csign_key, size_t csign_key_len,
++ const u8 *peer_connector, size_t peer_connector_len,
++ os_time_t *expiry)
++{
++ struct json_token *root = NULL, *netkey, *token;
++ struct json_token *own_root = NULL;
++ enum dpp_status_error ret = 255, res;
++ EVP_PKEY *own_key = NULL, *peer_key = NULL;
++ struct wpabuf *own_key_pub = NULL;
++ const struct dpp_curve_params *curve, *own_curve;
++ struct dpp_signed_connector_info info;
++ const unsigned char *p;
++ EVP_PKEY *csign = NULL;
++ char *signed_connector = NULL;
++ const char *pos, *end;
++ unsigned char *own_conn = NULL;
++ size_t own_conn_len;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t Nx_len;
++ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
++
++ os_memset(intro, 0, sizeof(*intro));
++ os_memset(&info, 0, sizeof(info));
++ if (expiry)
++ *expiry = 0;
++
++ p = csign_key;
++ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
++ if (!csign) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to parse local C-sign-key information");
++ goto fail;
++ }
++
++ own_key = dpp_set_keypair(&own_curve, net_access_key,
++ net_access_key_len);
++ if (!own_key) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
++ goto fail;
++ }
++
++ pos = os_strchr(own_connector, '.');
++ if (!pos) {
++ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
++ goto fail;
++ }
++ pos++;
++ end = os_strchr(pos, '.');
++ if (!end) {
++ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
++ goto fail;
++ }
++ own_conn = base64_url_decode((const unsigned char *) pos,
++ end - pos, &own_conn_len);
++ if (!own_conn) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Failed to base64url decode own signedConnector JWS Payload");
++ goto fail;
++ }
++
++ own_root = json_parse((const char *) own_conn, own_conn_len);
++ if (!own_root) {
++ wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
++ goto fail;
++ }
++
++ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
++ peer_connector, peer_connector_len);
++ signed_connector = os_malloc(peer_connector_len + 1);
++ if (!signed_connector)
++ goto fail;
++ os_memcpy(signed_connector, peer_connector, peer_connector_len);
++ signed_connector[peer_connector_len] = '\0';
++
++ res = dpp_process_signed_connector(&info, csign, signed_connector);
++ if (res != DPP_STATUS_OK) {
++ ret = res;
++ goto fail;
++ }
++
++ root = json_parse((const char *) info.payload, info.payload_len);
++ if (!root) {
++ wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ if (!dpp_connector_match_groups(own_root, root)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Peer connector does not include compatible group netrole with own connector");
++ ret = DPP_STATUS_NO_MATCH;
++ goto fail;
++ }
++
++ token = json_get_member(root, "expiry");
++ if (!token || token->type != JSON_STRING) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No expiry string found - connector does not expire");
++ } else {
++ wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
++ if (dpp_key_expired(token->string, expiry)) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Connector (netAccessKey) has expired");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ }
++
++ netkey = json_get_member(root, "netAccessKey");
++ if (!netkey || netkey->type != JSON_OBJECT) {
++ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ peer_key = dpp_parse_jwk(netkey, &curve);
++ if (!peer_key) {
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
++
++ if (own_curve != curve) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Mismatching netAccessKey curves (%s != %s)",
++ own_curve->name, curve->name);
++ ret = DPP_STATUS_INVALID_CONNECTOR;
++ goto fail;
++ }
++
++ /* ECDH: N = nk * PK */
++ ctx = EVP_PKEY_CTX_new(own_key, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
++ Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
++ Nx, Nx_len);
++
++ /* PMK = HKDF(<>, "DPP PMK", N.x) */
++ if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
++ goto fail;
++ }
++ intro->pmk_len = curve->hash_len;
++
++ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
++ if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
++ goto fail;
++ }
++
++ ret = DPP_STATUS_OK;
++fail:
++ if (ret != DPP_STATUS_OK)
++ os_memset(intro, 0, sizeof(*intro));
++ os_memset(Nx, 0, sizeof(Nx));
++ EVP_PKEY_CTX_free(ctx);
++ os_free(own_conn);
++ os_free(signed_connector);
++ os_free(info.payload);
++ EVP_PKEY_free(own_key);
++ wpabuf_free(own_key_pub);
++ EVP_PKEY_free(peer_key);
++ EVP_PKEY_free(csign);
++ json_free(root);
++ json_free(own_root);
++ return ret;
++}
++
++
++static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
++ int init)
++{
++ EC_GROUP *group;
++ size_t len = curve->prime_len;
++ const u8 *x, *y;
++
++ switch (curve->ike_group) {
++ case 19:
++ x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
++ y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
++ break;
++ case 20:
++ x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
++ y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
++ break;
++ case 21:
++ x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
++ y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
++ break;
++ case 28:
++ x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
++ y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
++ break;
++ case 29:
++ x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
++ y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
++ break;
++ case 30:
++ x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
++ y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
++ break;
++ default:
++ return NULL;
++ }
++
++ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
++ if (!group)
++ return NULL;
++ return dpp_set_pubkey_point_group(group, x, y, len);
++}
++
++
++static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
++ const u8 *mac_init, const char *code,
++ const char *identifier, BN_CTX *bnctx,
++ const EC_GROUP **ret_group)
++{
++ u8 hash[DPP_MAX_HASH_LEN];
++ const u8 *addr[3];
++ size_t len[3];
++ unsigned int num_elem = 0;
++ EC_POINT *Qi = NULL;
++ EVP_PKEY *Pi = NULL;
++ EC_KEY *Pi_ec = NULL;
++ const EC_POINT *Pi_point;
++ BIGNUM *hash_bn = NULL;
++ const EC_GROUP *group = NULL;
++ EC_GROUP *group2 = NULL;
++
++ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
++
++ wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
++ addr[num_elem] = mac_init;
++ len[num_elem] = ETH_ALEN;
++ num_elem++;
++ if (identifier) {
++ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
++ identifier);
++ addr[num_elem] = (const u8 *) identifier;
++ len[num_elem] = os_strlen(identifier);
++ num_elem++;
++ }
++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
++ addr[num_elem] = (const u8 *) code;
++ len[num_elem] = os_strlen(code);
++ num_elem++;
++ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
++ goto fail;
++ wpa_hexdump_key(MSG_DEBUG,
++ "DPP: H(MAC-Initiator | [identifier |] code)",
++ hash, curve->hash_len);
++ Pi = dpp_pkex_get_role_elem(curve, 1);
++ if (!Pi)
++ goto fail;
++ dpp_debug_print_key("DPP: Pi", Pi);
++ Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
++ if (!Pi_ec)
++ goto fail;
++ Pi_point = EC_KEY_get0_public_key(Pi_ec);
++
++ group = EC_KEY_get0_group(Pi_ec);
++ if (!group)
++ goto fail;
++ group2 = EC_GROUP_dup(group);
++ if (!group2)
++ goto fail;
++ Qi = EC_POINT_new(group2);
++ if (!Qi) {
++ EC_GROUP_free(group2);
++ goto fail;
++ }
++ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
++ if (!hash_bn ||
++ EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
++ goto fail;
++ if (EC_POINT_is_at_infinity(group, Qi)) {
++ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
++ goto fail;
++ }
++ dpp_debug_print_point("DPP: Qi", group, Qi);
++out:
++ EC_KEY_free(Pi_ec);
++ EVP_PKEY_free(Pi);
++ BN_clear_free(hash_bn);
++ if (ret_group)
++ *ret_group = group2;
++ return Qi;
++fail:
++ EC_POINT_free(Qi);
++ Qi = NULL;
++ goto out;
++}
++
++
++static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
++ const u8 *mac_resp, const char *code,
++ const char *identifier, BN_CTX *bnctx,
++ const EC_GROUP **ret_group)
++{
++ u8 hash[DPP_MAX_HASH_LEN];
++ const u8 *addr[3];
++ size_t len[3];
++ unsigned int num_elem = 0;
++ EC_POINT *Qr = NULL;
++ EVP_PKEY *Pr = NULL;
++ EC_KEY *Pr_ec = NULL;
++ const EC_POINT *Pr_point;
++ BIGNUM *hash_bn = NULL;
++ const EC_GROUP *group = NULL;
++ EC_GROUP *group2 = NULL;
++
++ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
++
++ wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
++ addr[num_elem] = mac_resp;
++ len[num_elem] = ETH_ALEN;
++ num_elem++;
++ if (identifier) {
++ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
++ identifier);
++ addr[num_elem] = (const u8 *) identifier;
++ len[num_elem] = os_strlen(identifier);
++ num_elem++;
++ }
++ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
++ addr[num_elem] = (const u8 *) code;
++ len[num_elem] = os_strlen(code);
++ num_elem++;
++ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
++ goto fail;
++ wpa_hexdump_key(MSG_DEBUG,
++ "DPP: H(MAC-Responder | [identifier |] code)",
++ hash, curve->hash_len);
++ Pr = dpp_pkex_get_role_elem(curve, 0);
++ if (!Pr)
++ goto fail;
++ dpp_debug_print_key("DPP: Pr", Pr);
++ Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
++ if (!Pr_ec)
++ goto fail;
++ Pr_point = EC_KEY_get0_public_key(Pr_ec);
++
++ group = EC_KEY_get0_group(Pr_ec);
++ if (!group)
++ goto fail;
++ group2 = EC_GROUP_dup(group);
++ if (!group2)
++ goto fail;
++ Qr = EC_POINT_new(group2);
++ if (!Qr) {
++ EC_GROUP_free(group2);
++ goto fail;
++ }
++ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
++ if (!hash_bn ||
++ EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
++ goto fail;
++ if (EC_POINT_is_at_infinity(group, Qr)) {
++ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
++ goto fail;
++ }
++ dpp_debug_print_point("DPP: Qr", group, Qr);
++out:
++ EC_KEY_free(Pr_ec);
++ EVP_PKEY_free(Pr);
++ BN_clear_free(hash_bn);
++ if (ret_group)
++ *ret_group = group2;
++ return Qr;
++fail:
++ EC_POINT_free(Qr);
++ Qr = NULL;
++ goto out;
++}
++
++
++#ifdef CONFIG_TESTING_OPTIONS
++static int dpp_test_gen_invalid_key(struct wpabuf *msg,
++ const struct dpp_curve_params *curve)
++{
++ BN_CTX *ctx;
++ BIGNUM *x, *y;
++ int ret = -1;
++ EC_GROUP *group;
++ EC_POINT *point;
++
++ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
++ if (!group)
++ return -1;
++
++ ctx = BN_CTX_new();
++ point = EC_POINT_new(group);
++ x = BN_new();
++ y = BN_new();
++ if (!ctx || !point || !x || !y)
++ goto fail;
++
++ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
++ goto fail;
++
++ /* Generate a random y coordinate that results in a point that is not
++ * on the curve. */
++ for (;;) {
++ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
++ goto fail;
++
++ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
++ ctx) != 1) {
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
++ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
++ * return an error from EC_POINT_set_affine_coordinates_GFp()
++ * when the point is not on the curve. */
++ break;
++#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
++ goto fail;
++#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
++ }
++
++ if (!EC_POINT_is_on_curve(group, point, ctx))
++ break;
++ }
++
++ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0)
++ goto fail;
++
++ ret = 0;
++fail:
++ if (ret < 0)
++ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
++ BN_free(x);
++ BN_free(y);
++ EC_POINT_free(point);
++ BN_CTX_free(ctx);
++
++ return ret;
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
++static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
++{
++ EC_KEY *X_ec = NULL;
++ const EC_POINT *X_point;
++ BN_CTX *bnctx = NULL;
++ const EC_GROUP *group;
++ EC_POINT *Qi = NULL, *M = NULL;
++ struct wpabuf *M_buf = NULL;
++ BIGNUM *Mx = NULL, *My = NULL;
++ struct wpabuf *msg = NULL;
++ size_t attr_len;
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++
++ wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
++
++ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
++ bnctx = BN_CTX_new();
++ if (!bnctx)
++ goto fail;
++ Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
++ pkex->identifier, bnctx, &group);
++ if (!Qi)
++ goto fail;
++
++ /* Generate a random ephemeral keypair x/X */
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_pkex_ephemeral_key_override_len) {
++ const struct dpp_curve_params *tmp_curve;
++
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - override ephemeral key x/X");
++ pkex->x = dpp_set_keypair(&tmp_curve,
++ dpp_pkex_ephemeral_key_override,
++ dpp_pkex_ephemeral_key_override_len);
++ } else {
++ pkex->x = dpp_gen_keypair(curve);
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ pkex->x = dpp_gen_keypair(curve);
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (!pkex->x)
++ goto fail;
++
++ /* M = X + Qi */
++ X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
++ if (!X_ec)
++ goto fail;
++ X_point = EC_KEY_get0_public_key(X_ec);
++ if (!X_point)
++ goto fail;
++ dpp_debug_print_point("DPP: X", group, X_point);
++ M = EC_POINT_new(group);
++ Mx = BN_new();
++ My = BN_new();
++ if (!M || !Mx || !My ||
++ EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
++ EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
++ goto fail;
++ dpp_debug_print_point("DPP: M", group, M);
++
++ /* Initiator -> Responder: group, [identifier,] M */
++ attr_len = 4 + 2;
++ if (pkex->identifier)
++ attr_len += 4 + os_strlen(pkex->identifier);
++ attr_len += 4 + 2 * curve->prime_len;
++ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
++ if (!msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
++ goto skip_finite_cyclic_group;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Finite Cyclic Group attribute */
++ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
++ wpabuf_put_le16(msg, 2);
++ wpabuf_put_le16(msg, curve->ike_group);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_finite_cyclic_group:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Code Identifier attribute */
++ if (pkex->identifier) {
++ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
++ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
++ wpabuf_put_str(msg, pkex->identifier);
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
++ goto out;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* M in Encrypted Key attribute */
++ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
++ wpabuf_put_le16(msg, 2 * curve->prime_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
++ if (dpp_test_gen_invalid_key(msg, curve) < 0)
++ goto fail;
++ goto out;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0)
++ goto fail;
++
++out:
++ wpabuf_free(M_buf);
++ EC_KEY_free(X_ec);
++ EC_POINT_free(M);
++ EC_POINT_free(Qi);
++ BN_clear_free(Mx);
++ BN_clear_free(My);
++ BN_CTX_free(bnctx);
++ return msg;
++fail:
++ wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
++ wpabuf_free(msg);
++ msg = NULL;
++ goto out;
++}
++
++
++static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
++{
++ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
++}
++
++
++struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
++ const u8 *own_mac,
++ const char *identifier,
++ const char *code)
++{
++ struct dpp_pkex *pkex;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
++ MAC2STR(dpp_pkex_own_mac_override));
++ own_mac = dpp_pkex_own_mac_override;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ pkex = os_zalloc(sizeof(*pkex));
++ if (!pkex)
++ return NULL;
++ pkex->msg_ctx = msg_ctx;
++ pkex->initiator = 1;
++ pkex->own_bi = bi;
++ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
++ if (identifier) {
++ pkex->identifier = os_strdup(identifier);
++ if (!pkex->identifier)
++ goto fail;
++ }
++ pkex->code = os_strdup(code);
++ if (!pkex->code)
++ goto fail;
++ pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
++ if (!pkex->exchange_req)
++ goto fail;
++ return pkex;
++fail:
++ dpp_pkex_free(pkex);
++ return NULL;
++}
++
++
++static struct wpabuf *
++dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
++ enum dpp_status_error status,
++ const BIGNUM *Nx, const BIGNUM *Ny)
++{
++ struct wpabuf *msg = NULL;
++ size_t attr_len;
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++
++ /* Initiator -> Responder: DPP Status, [identifier,] N */
++ attr_len = 4 + 1;
++ if (pkex->identifier)
++ attr_len += 4 + os_strlen(pkex->identifier);
++ attr_len += 4 + 2 * curve->prime_len;
++ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
++ if (!msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
++ goto skip_status;
++ }
++
++ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
++ status = 255;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* DPP Status */
++ dpp_build_attr_status(msg, status);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_status:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* Code Identifier attribute */
++ if (pkex->identifier) {
++ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
++ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
++ wpabuf_put_str(msg, pkex->identifier);
++ }
++
++ if (status != DPP_STATUS_OK)
++ goto skip_encrypted_key;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
++ goto skip_encrypted_key;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* N in Encrypted Key attribute */
++ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
++ wpabuf_put_le16(msg, 2 * curve->prime_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
++ if (dpp_test_gen_invalid_key(msg, curve) < 0)
++ goto fail;
++ goto skip_encrypted_key;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
++ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
++ curve->prime_len) < 0)
++ goto fail;
++
++skip_encrypted_key:
++ if (status == DPP_STATUS_BAD_GROUP) {
++ /* Finite Cyclic Group attribute */
++ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
++ wpabuf_put_le16(msg, 2);
++ wpabuf_put_le16(msg, curve->ike_group);
++ }
++
++ return msg;
++fail:
++ wpabuf_free(msg);
++ return NULL;
++}
++
++
++static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
++ const u8 *Mx, size_t Mx_len,
++ const u8 *Nx, size_t Nx_len,
++ const char *code,
++ const u8 *Kx, size_t Kx_len,
++ u8 *z, unsigned int hash_len)
++{
++ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
++ int res;
++ u8 *info, *pos;
++ size_t info_len;
++
++ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
++ */
++
++ /* HKDF-Extract(<>, IKM=K.x) */
++ os_memset(salt, 0, hash_len);
++ if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
++ return -1;
++ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
++ prk, hash_len);
++ info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
++ info = os_malloc(info_len);
++ if (!info)
++ return -1;
++ pos = info;
++ os_memcpy(pos, mac_init, ETH_ALEN);
++ pos += ETH_ALEN;
++ os_memcpy(pos, mac_resp, ETH_ALEN);
++ pos += ETH_ALEN;
++ os_memcpy(pos, Mx, Mx_len);
++ pos += Mx_len;
++ os_memcpy(pos, Nx, Nx_len);
++ pos += Nx_len;
++ os_memcpy(pos, code, os_strlen(code));
++
++ /* HKDF-Expand(PRK, info, L) */
++ if (hash_len == 32)
++ res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
++ z, hash_len);
++ else if (hash_len == 48)
++ res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
++ z, hash_len);
++ else if (hash_len == 64)
++ res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
++ z, hash_len);
++ else
++ res = -1;
++ os_free(info);
++ os_memset(prk, 0, hash_len);
++ if (res < 0)
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
++ z, hash_len);
++ return 0;
++}
++
++
++static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
++ const char *identifier)
++{
++ if (!attr_id && identifier) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: No PKEX code identifier received, but expected one");
++ return 0;
++ }
++
++ if (attr_id && !identifier) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: PKEX code identifier received, but not expecting one");
++ return 0;
++ }
++
++ if (attr_id && identifier &&
++ (os_strlen(identifier) != attr_id_len ||
++ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
++ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
++ return 0;
++ }
++
++ return 1;
++}
++
++
++struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
++ struct dpp_bootstrap_info *bi,
++ const u8 *own_mac,
++ const u8 *peer_mac,
++ const char *identifier,
++ const char *code,
++ const u8 *buf, size_t len)
++{
++ const u8 *attr_group, *attr_id, *attr_key;
++ u16 attr_group_len, attr_id_len, attr_key_len;
++ const struct dpp_curve_params *curve = bi->curve;
++ u16 ike_group;
++ struct dpp_pkex *pkex = NULL;
++ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
++ BN_CTX *bnctx = NULL;
++ const EC_GROUP *group;
++ BIGNUM *Mx = NULL, *My = NULL;
++ EC_KEY *Y_ec = NULL, *X_ec = NULL;;
++ const EC_POINT *Y_point;
++ BIGNUM *Nx = NULL, *Ny = NULL;
++ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
++ size_t Kx_len;
++ int res;
++ EVP_PKEY_CTX *ctx = NULL;
++
++ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "PKEX counter t limit reached - ignore message");
++ return NULL;
++ }
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
++ MAC2STR(dpp_pkex_peer_mac_override));
++ peer_mac = dpp_pkex_peer_mac_override;
++ }
++ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
++ MAC2STR(dpp_pkex_own_mac_override));
++ own_mac = dpp_pkex_own_mac_override;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ attr_id_len = 0;
++ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
++ &attr_id_len);
++ if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
++ return NULL;
++
++ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
++ &attr_group_len);
++ if (!attr_group || attr_group_len != 2) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Missing or invalid Finite Cyclic Group attribute");
++ return NULL;
++ }
++ ike_group = WPA_GET_LE16(attr_group);
++ if (ike_group != curve->ike_group) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Mismatching PKEX curve: peer=%u own=%u",
++ ike_group, curve->ike_group);
++ pkex = os_zalloc(sizeof(*pkex));
++ if (!pkex)
++ goto fail;
++ pkex->own_bi = bi;
++ pkex->failed = 1;
++ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
++ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
++ if (!pkex->exchange_resp)
++ goto fail;
++ return pkex;
++ }
++
++ /* M in Encrypted Key attribute */
++ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
++ &attr_key_len);
++ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
++ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Missing Encrypted Key attribute");
++ return NULL;
++ }
++
++ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
++ bnctx = BN_CTX_new();
++ if (!bnctx)
++ goto fail;
++ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
++ &group);
++ if (!Qi)
++ goto fail;
++
++ /* X' = M - Qi */
++ X = EC_POINT_new(group);
++ M = EC_POINT_new(group);
++ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
++ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
++ if (!X || !M || !Mx || !My ||
++ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
++ EC_POINT_is_at_infinity(group, M) ||
++ !EC_POINT_is_on_curve(group, M, bnctx) ||
++ EC_POINT_invert(group, Qi, bnctx) != 1 ||
++ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
++ EC_POINT_is_at_infinity(group, X) ||
++ !EC_POINT_is_on_curve(group, X, bnctx)) {
++ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Invalid Encrypted Key value");
++ bi->pkex_t++;
++ goto fail;
++ }
++ dpp_debug_print_point("DPP: M", group, M);
++ dpp_debug_print_point("DPP: X'", group, X);
++
++ pkex = os_zalloc(sizeof(*pkex));
++ if (!pkex)
++ goto fail;
++ pkex->t = bi->pkex_t;
++ pkex->msg_ctx = msg_ctx;
++ pkex->own_bi = bi;
++ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
++ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
++ if (identifier) {
++ pkex->identifier = os_strdup(identifier);
++ if (!pkex->identifier)
++ goto fail;
++ }
++ pkex->code = os_strdup(code);
++ if (!pkex->code)
++ goto fail;
++
++ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
++
++ X_ec = EC_KEY_new();
++ if (!X_ec ||
++ EC_KEY_set_group(X_ec, group) != 1 ||
++ EC_KEY_set_public_key(X_ec, X) != 1)
++ goto fail;
++ pkex->x = EVP_PKEY_new();
++ if (!pkex->x ||
++ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
++ goto fail;
++
++ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
++ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
++ if (!Qr)
++ goto fail;
++
++ /* Generate a random ephemeral keypair y/Y */
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_pkex_ephemeral_key_override_len) {
++ const struct dpp_curve_params *tmp_curve;
++
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - override ephemeral key y/Y");
++ pkex->y = dpp_set_keypair(&tmp_curve,
++ dpp_pkex_ephemeral_key_override,
++ dpp_pkex_ephemeral_key_override_len);
++ } else {
++ pkex->y = dpp_gen_keypair(curve);
++ }
++#else /* CONFIG_TESTING_OPTIONS */
++ pkex->y = dpp_gen_keypair(curve);
++#endif /* CONFIG_TESTING_OPTIONS */
++ if (!pkex->y)
++ goto fail;
++
++ /* N = Y + Qr */
++ Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
++ if (!Y_ec)
++ goto fail;
++ Y_point = EC_KEY_get0_public_key(Y_ec);
++ if (!Y_point)
++ goto fail;
++ dpp_debug_print_point("DPP: Y", group, Y_point);
++ N = EC_POINT_new(group);
++ Nx = BN_new();
++ Ny = BN_new();
++ if (!N || !Nx || !Ny ||
++ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
++ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
++ goto fail;
++ dpp_debug_print_point("DPP: N", group, N);
++
++ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
++ Nx, Ny);
++ if (!pkex->exchange_resp)
++ goto fail;
++
++ /* K = y * X' */
++ ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
++ Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
++ Kx, Kx_len);
++
++ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
++ */
++ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
++ pkex->Mx, curve->prime_len,
++ pkex->Nx, curve->prime_len, pkex->code,
++ Kx, Kx_len, pkex->z, curve->hash_len);
++ os_memset(Kx, 0, Kx_len);
++ if (res < 0)
++ goto fail;
++
++ pkex->exchange_done = 1;
++
++out:
++ EVP_PKEY_CTX_free(ctx);
++ BN_CTX_free(bnctx);
++ EC_POINT_free(Qi);
++ EC_POINT_free(Qr);
++ BN_free(Mx);
++ BN_free(My);
++ BN_free(Nx);
++ BN_free(Ny);
++ EC_POINT_free(M);
++ EC_POINT_free(N);
++ EC_POINT_free(X);
++ EC_KEY_free(X_ec);
++ EC_KEY_free(Y_ec);
++ return pkex;
++fail:
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
++ dpp_pkex_free(pkex);
++ pkex = NULL;
++ goto out;
++}
++
++
++static struct wpabuf *
++dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
++ const struct wpabuf *A_pub, const u8 *u)
++{
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++ struct wpabuf *msg = NULL;
++ size_t clear_len, attr_len;
++ struct wpabuf *clear = NULL;
++ u8 *wrapped;
++ u8 octet;
++ const u8 *addr[2];
++ size_t len[2];
++
++ /* {A, u, [bootstrapping info]}z */
++ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
++ clear = wpabuf_alloc(clear_len);
++ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
++ if (!clear || !msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
++ goto skip_bootstrap_key;
++ }
++ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
++ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
++ wpabuf_put_le16(clear, 2 * curve->prime_len);
++ if (dpp_test_gen_invalid_key(clear, curve) < 0)
++ goto fail;
++ goto skip_bootstrap_key;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* A in Bootstrap Key attribute */
++ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
++ wpabuf_put_le16(clear, wpabuf_len(A_pub));
++ wpabuf_put_buf(clear, A_pub);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_bootstrap_key:
++ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
++ goto skip_i_auth_tag;
++ }
++ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
++ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
++ wpabuf_put_le16(clear, curve->hash_len);
++ wpabuf_put_data(clear, u, curve->hash_len - 1);
++ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
++ goto skip_i_auth_tag;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* u in I-Auth tag attribute */
++ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
++ wpabuf_put_le16(clear, curve->hash_len);
++ wpabuf_put_data(clear, u, curve->hash_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_i_auth_tag:
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = DPP_HDR_LEN;
++ octet = 0;
++ addr[1] = &octet;
++ len[1] = sizeof(octet);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
++ if (aes_siv_encrypt(pkex->z, curve->hash_len,
++ wpabuf_head(clear), wpabuf_len(clear),
++ 2, addr, len, wrapped) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++out:
++ wpabuf_free(clear);
++ return msg;
++
++fail:
++ wpabuf_free(msg);
++ msg = NULL;
++ goto out;
++}
++
++
++struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
++ const u8 *peer_mac,
++ const u8 *buf, size_t buflen)
++{
++ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
++ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
++ const EC_GROUP *group;
++ BN_CTX *bnctx = NULL;
++ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++ EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
++ BIGNUM *Nx = NULL, *Ny = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ EC_KEY *Y_ec = NULL;
++ size_t Jx_len, Kx_len;
++ u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
++ const u8 *addr[4];
++ size_t len[4];
++ u8 u[DPP_MAX_HASH_LEN];
++ int res;
++
++ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
++ return NULL;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at PKEX Exchange Response");
++ pkex->failed = 1;
++ return NULL;
++ }
++
++ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
++ MAC2STR(dpp_pkex_peer_mac_override));
++ peer_mac = dpp_pkex_peer_mac_override;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
++
++ attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
++ &attr_status_len);
++ if (!attr_status || attr_status_len != 1) {
++ dpp_pkex_fail(pkex, "No DPP Status attribute");
++ return NULL;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
++
++ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
++ attr_group = dpp_get_attr(buf, buflen,
++ DPP_ATTR_FINITE_CYCLIC_GROUP,
++ &attr_group_len);
++ if (attr_group && attr_group_len == 2) {
++ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
++ "Peer indicated mismatching PKEX group - proposed %u",
++ WPA_GET_LE16(attr_group));
++ return NULL;
++ }
++ }
++
++ if (attr_status[0] != DPP_STATUS_OK) {
++ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
++ return NULL;
++ }
++
++ attr_id_len = 0;
++ attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
++ &attr_id_len);
++ if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
++ pkex->identifier)) {
++ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
++ return NULL;
++ }
++
++ /* N in Encrypted Key attribute */
++ attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
++ &attr_key_len);
++ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
++ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
++ return NULL;
++ }
++
++ /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
++ bnctx = BN_CTX_new();
++ if (!bnctx)
++ goto fail;
++ Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
++ pkex->identifier, bnctx, &group);
++ if (!Qr)
++ goto fail;
++
++ /* Y' = N - Qr */
++ Y = EC_POINT_new(group);
++ N = EC_POINT_new(group);
++ Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
++ Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
++ if (!Y || !N || !Nx || !Ny ||
++ EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
++ EC_POINT_is_at_infinity(group, N) ||
++ !EC_POINT_is_on_curve(group, N, bnctx) ||
++ EC_POINT_invert(group, Qr, bnctx) != 1 ||
++ EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
++ EC_POINT_is_at_infinity(group, Y) ||
++ !EC_POINT_is_on_curve(group, Y, bnctx)) {
++ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
++ pkex->t++;
++ goto fail;
++ }
++ dpp_debug_print_point("DPP: N", group, N);
++ dpp_debug_print_point("DPP: Y'", group, Y);
++
++ pkex->exchange_done = 1;
++
++ /* ECDH: J = a * Y’ */
++ Y_ec = EC_KEY_new();
++ if (!Y_ec ||
++ EC_KEY_set_group(Y_ec, group) != 1 ||
++ EC_KEY_set_public_key(Y_ec, Y) != 1)
++ goto fail;
++ pkex->y = EVP_PKEY_new();
++ if (!pkex->y ||
++ EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
++ goto fail;
++ ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
++ Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
++ Jx, Jx_len);
++
++ /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
++ A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
++ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
++ X_pub = dpp_get_pubkey_point(pkex->x, 0);
++ if (!A_pub || !Y_pub || !X_pub)
++ goto fail;
++ addr[0] = pkex->own_mac;
++ len[0] = ETH_ALEN;
++ addr[1] = wpabuf_head(A_pub);
++ len[1] = wpabuf_len(A_pub) / 2;
++ addr[2] = wpabuf_head(Y_pub);
++ len[2] = wpabuf_len(Y_pub) / 2;
++ addr[3] = wpabuf_head(X_pub);
++ len[3] = wpabuf_len(X_pub) / 2;
++ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
++
++ /* K = x * Y’ */
++ EVP_PKEY_CTX_free(ctx);
++ ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
++ Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
++ Kx, Kx_len);
++
++ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
++ */
++ res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
++ pkex->Mx, curve->prime_len,
++ attr_key /* N.x */, attr_key_len / 2,
++ pkex->code, Kx, Kx_len,
++ pkex->z, curve->hash_len);
++ os_memset(Kx, 0, Kx_len);
++ if (res < 0)
++ goto fail;
++
++ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
++ if (!msg)
++ goto fail;
++
++out:
++ wpabuf_free(A_pub);
++ wpabuf_free(X_pub);
++ wpabuf_free(Y_pub);
++ EC_POINT_free(Qr);
++ EC_POINT_free(Y);
++ EC_POINT_free(N);
++ BN_free(Nx);
++ BN_free(Ny);
++ EC_KEY_free(Y_ec);
++ EVP_PKEY_CTX_free(ctx);
++ BN_CTX_free(bnctx);
++ return msg;
++fail:
++ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
++ goto out;
++}
++
++
++static struct wpabuf *
++dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
++ const struct wpabuf *B_pub, const u8 *v)
++{
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++ struct wpabuf *msg = NULL;
++ const u8 *addr[2];
++ size_t len[2];
++ u8 octet;
++ u8 *wrapped;
++ struct wpabuf *clear = NULL;
++ size_t clear_len, attr_len;
++
++ /* {B, v [bootstrapping info]}z */
++ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
++ clear = wpabuf_alloc(clear_len);
++ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
++ attr_len += 5;
++#endif /* CONFIG_TESTING_OPTIONS */
++ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
++ if (!clear || !msg)
++ goto fail;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
++ goto skip_bootstrap_key;
++ }
++ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
++ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
++ wpabuf_put_le16(clear, 2 * curve->prime_len);
++ if (dpp_test_gen_invalid_key(clear, curve) < 0)
++ goto fail;
++ goto skip_bootstrap_key;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* B in Bootstrap Key attribute */
++ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
++ wpabuf_put_le16(clear, wpabuf_len(B_pub));
++ wpabuf_put_buf(clear, B_pub);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_bootstrap_key:
++ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
++ goto skip_r_auth_tag;
++ }
++ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
++ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
++ wpabuf_put_le16(clear, curve->hash_len);
++ wpabuf_put_data(clear, v, curve->hash_len - 1);
++ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
++ goto skip_r_auth_tag;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ /* v in R-Auth tag attribute */
++ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
++ wpabuf_put_le16(clear, curve->hash_len);
++ wpabuf_put_data(clear, v, curve->hash_len);
++
++#ifdef CONFIG_TESTING_OPTIONS
++skip_r_auth_tag:
++ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
++ goto skip_wrapped_data;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ addr[0] = wpabuf_head_u8(msg) + 2;
++ len[0] = DPP_HDR_LEN;
++ octet = 1;
++ addr[1] = &octet;
++ len[1] = sizeof(octet);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
++ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
++ if (aes_siv_encrypt(pkex->z, curve->hash_len,
++ wpabuf_head(clear), wpabuf_len(clear),
++ 2, addr, len, wrapped) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
++ dpp_build_attr_status(msg, DPP_STATUS_OK);
++ }
++skip_wrapped_data:
++#endif /* CONFIG_TESTING_OPTIONS */
++
++out:
++ wpabuf_free(clear);
++ return msg;
++
++fail:
++ wpabuf_free(msg);
++ msg = NULL;
++ goto out;
++}
++
++
++struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
++ const u8 *hdr,
++ const u8 *buf, size_t buflen)
++{
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++ EVP_PKEY_CTX *ctx = NULL;
++ size_t Jx_len, Lx_len;
++ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
++ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
++ const u8 *wrapped_data, *b_key, *peer_u;
++ u16 wrapped_data_len, b_key_len, peer_u_len = 0;
++ const u8 *addr[4];
++ size_t len[4];
++ u8 octet;
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
++ struct wpabuf *B_pub = NULL;
++ u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at PKEX CR Request");
++ pkex->failed = 1;
++ return NULL;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (!pkex->exchange_done || pkex->failed ||
++ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
++ goto fail;
++
++ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_pkex_fail(pkex,
++ "Missing or invalid required Wrapped Data attribute");
++ goto fail;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ octet = 0;
++ addr[1] = &octet;
++ len[1] = sizeof(octet);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ if (aes_siv_decrypt(pkex->z, curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_pkex_fail(pkex,
++ "AES-SIV decryption failed - possible PKEX code mismatch");
++ pkex->failed = 1;
++ pkex->t++;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
++ &b_key_len);
++ if (!b_key || b_key_len != 2 * curve->prime_len) {
++ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
++ goto fail;
++ }
++ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
++ b_key_len);
++ if (!pkex->peer_bootstrap_key) {
++ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
++ goto fail;
++ }
++ dpp_debug_print_key("DPP: Peer bootstrap public key",
++ pkex->peer_bootstrap_key);
++
++ /* ECDH: J' = y * A' */
++ ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
++ Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
++ Jx, Jx_len);
++
++ /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
++ A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
++ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
++ X_pub = dpp_get_pubkey_point(pkex->x, 0);
++ if (!A_pub || !Y_pub || !X_pub)
++ goto fail;
++ addr[0] = pkex->peer_mac;
++ len[0] = ETH_ALEN;
++ addr[1] = wpabuf_head(A_pub);
++ len[1] = wpabuf_len(A_pub) / 2;
++ addr[2] = wpabuf_head(Y_pub);
++ len[2] = wpabuf_len(Y_pub) / 2;
++ addr[3] = wpabuf_head(X_pub);
++ len[3] = wpabuf_len(X_pub) / 2;
++ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
++ goto fail;
++
++ peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
++ &peer_u_len);
++ if (!peer_u || peer_u_len != curve->hash_len ||
++ os_memcmp(peer_u, u, curve->hash_len) != 0) {
++ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
++ u, curve->hash_len);
++ wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
++ pkex->t++;
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
++
++ /* ECDH: L = b * X' */
++ EVP_PKEY_CTX_free(ctx);
++ ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
++ Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
++ Lx, Lx_len);
++
++ /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
++ B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
++ if (!B_pub)
++ goto fail;
++ addr[0] = pkex->own_mac;
++ len[0] = ETH_ALEN;
++ addr[1] = wpabuf_head(B_pub);
++ len[1] = wpabuf_len(B_pub) / 2;
++ addr[2] = wpabuf_head(X_pub);
++ len[2] = wpabuf_len(X_pub) / 2;
++ addr[3] = wpabuf_head(Y_pub);
++ len[3] = wpabuf_len(Y_pub) / 2;
++ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
++
++ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
++ if (!msg)
++ goto fail;
++
++out:
++ EVP_PKEY_CTX_free(ctx);
++ os_free(unwrapped);
++ wpabuf_free(A_pub);
++ wpabuf_free(B_pub);
++ wpabuf_free(X_pub);
++ wpabuf_free(Y_pub);
++ return msg;
++fail:
++ wpa_printf(MSG_DEBUG,
++ "DPP: PKEX Commit-Reveal Request processing failed");
++ goto out;
++}
++
++
++int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
++ const u8 *buf, size_t buflen)
++{
++ const struct dpp_curve_params *curve = pkex->own_bi->curve;
++ const u8 *wrapped_data, *b_key, *peer_v;
++ u16 wrapped_data_len, b_key_len, peer_v_len = 0;
++ const u8 *addr[4];
++ size_t len[4];
++ u8 octet;
++ u8 *unwrapped = NULL;
++ size_t unwrapped_len = 0;
++ int ret = -1;
++ u8 v[DPP_MAX_HASH_LEN];
++ size_t Lx_len;
++ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
++ EVP_PKEY_CTX *ctx = NULL;
++ struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
++
++#ifdef CONFIG_TESTING_OPTIONS
++ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
++ wpa_printf(MSG_INFO,
++ "DPP: TESTING - stop at PKEX CR Response");
++ pkex->failed = 1;
++ goto fail;
++ }
++#endif /* CONFIG_TESTING_OPTIONS */
++
++ if (!pkex->exchange_done || pkex->failed ||
++ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
++ goto fail;
++
++ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
++ &wrapped_data_len);
++ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
++ dpp_pkex_fail(pkex,
++ "Missing or invalid required Wrapped Data attribute");
++ goto fail;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
++ wrapped_data, wrapped_data_len);
++ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
++ unwrapped = os_malloc(unwrapped_len);
++ if (!unwrapped)
++ goto fail;
++
++ addr[0] = hdr;
++ len[0] = DPP_HDR_LEN;
++ octet = 1;
++ addr[1] = &octet;
++ len[1] = sizeof(octet);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
++ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
++
++ if (aes_siv_decrypt(pkex->z, curve->hash_len,
++ wrapped_data, wrapped_data_len,
++ 2, addr, len, unwrapped) < 0) {
++ dpp_pkex_fail(pkex,
++ "AES-SIV decryption failed - possible PKEX code mismatch");
++ pkex->t++;
++ goto fail;
++ }
++ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
++ unwrapped, unwrapped_len);
++
++ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
++ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
++ goto fail;
++ }
++
++ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
++ &b_key_len);
++ if (!b_key || b_key_len != 2 * curve->prime_len) {
++ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
++ goto fail;
++ }
++ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
++ b_key_len);
++ if (!pkex->peer_bootstrap_key) {
++ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
++ goto fail;
++ }
++ dpp_debug_print_key("DPP: Peer bootstrap public key",
++ pkex->peer_bootstrap_key);
++
++ /* ECDH: L' = x * B' */
++ ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
++ if (!ctx ||
++ EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
++ Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
++ EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "DPP: Failed to derive ECDH shared secret: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
++ Lx, Lx_len);
++
++ /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
++ B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
++ X_pub = dpp_get_pubkey_point(pkex->x, 0);
++ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
++ if (!B_pub || !X_pub || !Y_pub)
++ goto fail;
++ addr[0] = pkex->peer_mac;
++ len[0] = ETH_ALEN;
++ addr[1] = wpabuf_head(B_pub);
++ len[1] = wpabuf_len(B_pub) / 2;
++ addr[2] = wpabuf_head(X_pub);
++ len[2] = wpabuf_len(X_pub) / 2;
++ addr[3] = wpabuf_head(Y_pub);
++ len[3] = wpabuf_len(Y_pub) / 2;
++ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
++ goto fail;
++
++ peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
++ &peer_v_len);
++ if (!peer_v || peer_v_len != curve->hash_len ||
++ os_memcmp(peer_v, v, curve->hash_len) != 0) {
++ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
++ wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
++ v, curve->hash_len);
++ wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
++ pkex->t++;
++ goto fail;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
++
++ ret = 0;
++out:
++ wpabuf_free(B_pub);
++ wpabuf_free(X_pub);
++ wpabuf_free(Y_pub);
++ EVP_PKEY_CTX_free(ctx);
++ os_free(unwrapped);
++ return ret;
++fail:
++ goto out;
++}
++
++
++void dpp_pkex_free(struct dpp_pkex *pkex)
++{
++ if (!pkex)
++ return;
++
++ os_free(pkex->identifier);
++ os_free(pkex->code);
++ EVP_PKEY_free(pkex->x);
++ EVP_PKEY_free(pkex->y);
++ EVP_PKEY_free(pkex->peer_bootstrap_key);
++ wpabuf_free(pkex->exchange_req);
++ wpabuf_free(pkex->exchange_resp);
++ os_free(pkex);
++}
++
++
++#ifdef CONFIG_TESTING_OPTIONS
++char * dpp_corrupt_connector_signature(const char *connector)
++{
++ char *tmp, *pos, *signed3 = NULL;
++ unsigned char *signature = NULL;
++ size_t signature_len = 0, signed3_len;
++
++ tmp = os_zalloc(os_strlen(connector) + 5);
++ if (!tmp)
++ goto fail;
++ os_memcpy(tmp, connector, os_strlen(connector));
++
++ pos = os_strchr(tmp, '.');
++ if (!pos)
++ goto fail;
++
++ pos = os_strchr(pos + 1, '.');
++ if (!pos)
++ goto fail;
++ pos++;
++
++ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
++ pos);
++ signature = base64_url_decode((const unsigned char *) pos,
++ os_strlen(pos), &signature_len);
++ if (!signature || signature_len == 0)
++ goto fail;
++ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
++ signature, signature_len);
++ signature[signature_len - 1] ^= 0x01;
++ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
++ signature, signature_len);
++ signed3 = (char *) base64_url_encode(signature, signature_len,
++ &signed3_len, 0);
++ if (!signed3)
++ goto fail;
++ os_memcpy(pos, signed3, signed3_len);
++ pos[signed3_len] = '\0';
++ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
++ pos);
++
++out:
++ os_free(signature);
++ os_free(signed3);
++ return tmp;
++fail:
++ os_free(tmp);
++ tmp = NULL;
++ goto out;
++}
++#endif /* CONFIG_TESTING_OPTIONS */
++
++
++#ifdef CONFIG_DPP2
++
++struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
++ size_t net_access_key_len)
++{
++ struct wpabuf *pub = NULL;
++ EVP_PKEY *own_key;
++ struct dpp_pfs *pfs;
++
++ pfs = os_zalloc(sizeof(*pfs));
++ if (!pfs)
++ return NULL;
++
++ own_key = dpp_set_keypair(&pfs->curve, net_access_key,
++ net_access_key_len);
++ if (!own_key) {
++ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
++ goto fail;
++ }
++ EVP_PKEY_free(own_key);
++
++ pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
++ if (!pfs->ecdh)
++ goto fail;
++
++ pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
++ pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
++ if (!pub)
++ goto fail;
++
++ pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
++ if (!pfs->ie)
++ goto fail;
++ wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
++ wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
++ wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
++ wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
++ wpabuf_put_buf(pfs->ie, pub);
++ wpabuf_free(pub);
++ wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
++ pfs->ie);
++
++ return pfs;
++fail:
++ wpabuf_free(pub);
++ dpp_pfs_free(pfs);
++ return NULL;
++}
++
++
++int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
++{
++ if (peer_ie_len < 2)
++ return -1;
++ if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
++ wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
++ return -1;
++ }
++
++ pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
++ peer_ie_len - 2);
++ pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
++ if (!pfs->secret) {
++ wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
++ return -1;
++ }
++ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
++ return 0;
++}
++
++
++void dpp_pfs_free(struct dpp_pfs *pfs)
++{
++ if (!pfs)
++ return;
++ crypto_ecdh_deinit(pfs->ecdh);
++ wpabuf_free(pfs->ie);
++ wpabuf_clear_free(pfs->secret);
++ os_free(pfs);
++}
++
++#endif /* CONFIG_DPP2 */
++
++
++static unsigned int dpp_next_id(struct dpp_global *dpp)
++{
++ struct dpp_bootstrap_info *bi;
++ unsigned int max_id = 0;
++
++ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
++ if (bi->id > max_id)
++ max_id = bi->id;
++ }
++ return max_id + 1;
++}
++
++
++static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id)
++{
++ struct dpp_bootstrap_info *bi, *tmp;
++ int found = 0;
++
++ if (!dpp)
++ return -1;
++
++ dl_list_for_each_safe(bi, tmp, &dpp->bootstrap,
++ struct dpp_bootstrap_info, list) {
++ if (id && bi->id != id)
++ continue;
++ found = 1;
++ dl_list_del(&bi->list);
++ dpp_bootstrap_info_free(bi);
++ }
++
++ if (id == 0)
++ return 0; /* flush succeeds regardless of entries found */
++ return found ? 0 : -1;
++}
++
++
++struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
++ const char *uri)
++{
++ struct dpp_bootstrap_info *bi;
++
++ if (!dpp)
++ return NULL;
++
++ bi = dpp_parse_qr_code(uri);
++ if (!bi)
++ return NULL;
++
++ bi->id = dpp_next_id(dpp);
++ dl_list_add(&dpp->bootstrap, &bi->list);
++ return bi;
++}
++
++
++int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
++{
++ char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
++ char *key = NULL;
++ u8 *privkey = NULL;
++ size_t privkey_len = 0;
++ size_t len;
++ int ret = -1;
++ struct dpp_bootstrap_info *bi;
++
++ if (!dpp)
++ return -1;
++
++ bi = os_zalloc(sizeof(*bi));
++ if (!bi)
++ goto fail;
++
++ if (os_strstr(cmd, "type=qrcode"))
++ bi->type = DPP_BOOTSTRAP_QR_CODE;
++ else if (os_strstr(cmd, "type=pkex"))
++ bi->type = DPP_BOOTSTRAP_PKEX;
++ else
++ goto fail;
++
++ chan = get_param(cmd, " chan=");
++ mac = get_param(cmd, " mac=");
++ info = get_param(cmd, " info=");
++ curve = get_param(cmd, " curve=");
++ key = get_param(cmd, " key=");
++
++ if (key) {
++ privkey_len = os_strlen(key) / 2;
++ privkey = os_malloc(privkey_len);
++ if (!privkey ||
++ hexstr2bin(key, privkey, privkey_len) < 0)
++ goto fail;
++ }
++
++ pk = dpp_keygen(bi, curve, privkey, privkey_len);
++ if (!pk)
++ goto fail;
++
++ len = 4; /* "DPP:" */
++ if (chan) {
++ if (dpp_parse_uri_chan_list(bi, chan) < 0)
++ goto fail;
++ len += 3 + os_strlen(chan); /* C:...; */
++ }
++ if (mac) {
++ if (dpp_parse_uri_mac(bi, mac) < 0)
++ goto fail;
++ len += 3 + os_strlen(mac); /* M:...; */
++ }
++ if (info) {
++ if (dpp_parse_uri_info(bi, info) < 0)
++ goto fail;
++ len += 3 + os_strlen(info); /* I:...; */
++ }
++ len += 4 + os_strlen(pk);
++ bi->uri = os_malloc(len + 1);
++ if (!bi->uri)
++ goto fail;
++ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
++ chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
++ mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
++ info ? "I:" : "", info ? info : "", info ? ";" : "",
++ pk);
++ bi->id = dpp_next_id(dpp);
++ dl_list_add(&dpp->bootstrap, &bi->list);
++ ret = bi->id;
++ bi = NULL;
++fail:
++ os_free(curve);
++ os_free(pk);
++ os_free(chan);
++ os_free(mac);
++ os_free(info);
++ str_clear_free(key);
++ bin_clear_free(privkey, privkey_len);
++ dpp_bootstrap_info_free(bi);
++ return ret;
++}
++
++
++struct dpp_bootstrap_info *
++dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id)
++{
++ struct dpp_bootstrap_info *bi;
++
++ if (!dpp)
++ return NULL;
++
++ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
++ if (bi->id == id)
++ return bi;
++ }
++ return NULL;
++}
++
++
++int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id)
++{
++ unsigned int id_val;
++
++ if (os_strcmp(id, "*") == 0) {
++ id_val = 0;
++ } else {
++ id_val = atoi(id);
++ if (id_val == 0)
++ return -1;
++ }
++
++ return dpp_bootstrap_del(dpp, id_val);
++}
++
++
++struct dpp_bootstrap_info *
++dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
++ unsigned int freq)
++{
++ struct dpp_bootstrap_info *bi;
++
++ bi = os_zalloc(sizeof(*bi));
++ if (!bi)
++ return NULL;
++ bi->id = dpp_next_id(dpp);
++ bi->type = DPP_BOOTSTRAP_PKEX;
++ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
++ bi->num_freq = 1;
++ bi->freq[0] = freq;
++ bi->curve = pkex->own_bi->curve;
++ bi->pubkey = pkex->peer_bootstrap_key;
++ pkex->peer_bootstrap_key = NULL;
++ if (dpp_bootstrap_key_hash(bi) < 0) {
++ dpp_bootstrap_info_free(bi);
++ return NULL;
++ }
++ dpp_pkex_free(pkex);
++ dl_list_add(&dpp->bootstrap, &bi->list);
++ return bi;
++}
++
++
++const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
++{
++ struct dpp_bootstrap_info *bi;
++
++ bi = dpp_bootstrap_get_id(dpp, id);
++ if (!bi)
++ return NULL;
++ return bi->uri;
++}
++
++
++int dpp_bootstrap_info(struct dpp_global *dpp, int id,
++ char *reply, int reply_size)
++{
++ struct dpp_bootstrap_info *bi;
++
++ bi = dpp_bootstrap_get_id(dpp, id);
++ if (!bi)
++ return -1;
++ return os_snprintf(reply, reply_size, "type=%s\n"
++ "mac_addr=" MACSTR "\n"
++ "info=%s\n"
++ "num_freq=%u\n"
++ "curve=%s\n",
++ dpp_bootstrap_type_txt(bi->type),
++ MAC2STR(bi->mac_addr),
++ bi->info ? bi->info : "",
++ bi->num_freq,
++ bi->curve->name);
++}
++
++
++void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
++ const u8 *r_bootstrap,
++ struct dpp_bootstrap_info **own_bi,
++ struct dpp_bootstrap_info **peer_bi)
++{
++ struct dpp_bootstrap_info *bi;
++
++ *own_bi = NULL;
++ *peer_bi = NULL;
++ if (!dpp)
++ return;
++
++ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
++ if (!*own_bi && bi->own &&
++ os_memcmp(bi->pubkey_hash, r_bootstrap,
++ SHA256_MAC_LEN) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Found matching own bootstrapping information");
++ *own_bi = bi;
++ }
++
++ if (!*peer_bi && !bi->own &&
++ os_memcmp(bi->pubkey_hash, i_bootstrap,
++ SHA256_MAC_LEN) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "DPP: Found matching peer bootstrapping information");
++ *peer_bi = bi;
++ }
++
++ if (*own_bi && *peer_bi)
++ break;
++ }
++
++}
++
++
++static unsigned int dpp_next_configurator_id(struct dpp_global *dpp)
++{
++ struct dpp_configurator *conf;
++ unsigned int max_id = 0;
++
++ dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator,
++ list) {
++ if (conf->id > max_id)
++ max_id = conf->id;
++ }
++ return max_id + 1;
++}
++
++
++int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
++{
++ char *curve = NULL;
++ char *key = NULL;
++ u8 *privkey = NULL;
++ size_t privkey_len = 0;
++ int ret = -1;
++ struct dpp_configurator *conf = NULL;
++
++ curve = get_param(cmd, " curve=");
++ key = get_param(cmd, " key=");
++
++ if (key) {
++ privkey_len = os_strlen(key) / 2;
++ privkey = os_malloc(privkey_len);
++ if (!privkey ||
++ hexstr2bin(key, privkey, privkey_len) < 0)
++ goto fail;
++ }
++
++ conf = dpp_keygen_configurator(curve, privkey, privkey_len);
++ if (!conf)
++ goto fail;
++
++ conf->id = dpp_next_configurator_id(dpp);
++ dl_list_add(&dpp->configurator, &conf->list);
++ ret = conf->id;
++ conf = NULL;
++fail:
++ os_free(curve);
++ str_clear_free(key);
++ bin_clear_free(privkey, privkey_len);
++ dpp_configurator_free(conf);
++ return ret;
++}
++
++
++static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
++{
++ struct dpp_configurator *conf, *tmp;
++ int found = 0;
++
++ if (!dpp)
++ return -1;
++
++ dl_list_for_each_safe(conf, tmp, &dpp->configurator,
++ struct dpp_configurator, list) {
++ if (id && conf->id != id)
++ continue;
++ found = 1;
++ dl_list_del(&conf->list);
++ dpp_configurator_free(conf);
++ }
++
++ if (id == 0)
++ return 0; /* flush succeeds regardless of entries found */
++ return found ? 0 : -1;
++}
++
++
++int dpp_configurator_remove(struct dpp_global *dpp, const char *id)
++{
++ unsigned int id_val;
++
++ if (os_strcmp(id, "*") == 0) {
++ id_val = 0;
++ } else {
++ id_val = atoi(id);
++ if (id_val == 0)
++ return -1;
++ }
++
++ return dpp_configurator_del(dpp, id_val);
++}
++
++
++int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
++ char *buf, size_t buflen)
++{
++ struct dpp_configurator *conf;
++
++ conf = dpp_configurator_get_id(dpp, id);
++ if (!conf)
++ return -1;
++
++ return dpp_configurator_get_key(conf, buf, buflen);
++}
++
++
++struct dpp_global * dpp_global_init(void)
++{
++ struct dpp_global *dpp;
++
++ dpp = os_zalloc(sizeof(*dpp));
++ if (!dpp)
++ return NULL;
++
++ dl_list_init(&dpp->bootstrap);
++ dl_list_init(&dpp->configurator);
++
++ return dpp;
++}
++
++
++void dpp_global_clear(struct dpp_global *dpp)
++{
++ if (!dpp)
++ return;
++
++ dpp_bootstrap_del(dpp, 0);
++ dpp_configurator_del(dpp, 0);
++}
++
++
++void dpp_global_deinit(struct dpp_global *dpp)
++{
++ dpp_global_clear(dpp);
++ os_free(dpp);
++}
+--- contrib/wpa/src/common/dpp.h.orig
++++ contrib/wpa/src/common/dpp.h
+@@ -0,0 +1,505 @@
++/*
++ * DPP functionality shared between hostapd and wpa_supplicant
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ * Copyright (c) 2018-2019, The Linux Foundation
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef DPP_H
++#define DPP_H
++
++#ifdef CONFIG_DPP
++#include
++
++#include "utils/list.h"
++#include "common/wpa_common.h"
++#include "crypto/sha256.h"
++
++struct crypto_ecdh;
++struct dpp_global;
++
++#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
++
++enum dpp_public_action_frame_type {
++ DPP_PA_AUTHENTICATION_REQ = 0,
++ DPP_PA_AUTHENTICATION_RESP = 1,
++ DPP_PA_AUTHENTICATION_CONF = 2,
++ DPP_PA_PEER_DISCOVERY_REQ = 5,
++ DPP_PA_PEER_DISCOVERY_RESP = 6,
++ DPP_PA_PKEX_EXCHANGE_REQ = 7,
++ DPP_PA_PKEX_EXCHANGE_RESP = 8,
++ DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
++ DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
++ DPP_PA_CONFIGURATION_RESULT = 11,
++};
++
++enum dpp_attribute_id {
++ DPP_ATTR_STATUS = 0x1000,
++ DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
++ DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
++ DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
++ DPP_ATTR_WRAPPED_DATA = 0x1004,
++ DPP_ATTR_I_NONCE = 0x1005,
++ DPP_ATTR_I_CAPABILITIES = 0x1006,
++ DPP_ATTR_R_NONCE = 0x1007,
++ DPP_ATTR_R_CAPABILITIES = 0x1008,
++ DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
++ DPP_ATTR_I_AUTH_TAG = 0x100A,
++ DPP_ATTR_R_AUTH_TAG = 0x100B,
++ DPP_ATTR_CONFIG_OBJ = 0x100C,
++ DPP_ATTR_CONNECTOR = 0x100D,
++ DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
++ DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
++ DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
++ DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
++ DPP_ATTR_ENCRYPTED_KEY = 0x1013,
++ DPP_ATTR_ENROLLEE_NONCE = 0x1014,
++ DPP_ATTR_CODE_IDENTIFIER = 0x1015,
++ DPP_ATTR_TRANSACTION_ID = 0x1016,
++ DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
++ DPP_ATTR_CHANNEL = 0x1018,
++ DPP_ATTR_PROTOCOL_VERSION = 0x1019,
++ DPP_ATTR_ENVELOPED_DATA = 0x101A,
++};
++
++enum dpp_status_error {
++ DPP_STATUS_OK = 0,
++ DPP_STATUS_NOT_COMPATIBLE = 1,
++ DPP_STATUS_AUTH_FAILURE = 2,
++ DPP_STATUS_UNWRAP_FAILURE = 3,
++ DPP_STATUS_BAD_GROUP = 4,
++ DPP_STATUS_CONFIGURE_FAILURE = 5,
++ DPP_STATUS_RESPONSE_PENDING = 6,
++ DPP_STATUS_INVALID_CONNECTOR = 7,
++ DPP_STATUS_NO_MATCH = 8,
++ DPP_STATUS_CONFIG_REJECTED = 9,
++};
++
++#define DPP_CAPAB_ENROLLEE BIT(0)
++#define DPP_CAPAB_CONFIGURATOR BIT(1)
++#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
++
++#define DPP_BOOTSTRAP_MAX_FREQ 30
++#define DPP_MAX_NONCE_LEN 32
++#define DPP_MAX_HASH_LEN 64
++#define DPP_MAX_SHARED_SECRET_LEN 66
++
++struct dpp_curve_params {
++ const char *name;
++ size_t hash_len;
++ size_t aes_siv_key_len;
++ size_t nonce_len;
++ size_t prime_len;
++ const char *jwk_crv;
++ u16 ike_group;
++ const char *jws_alg;
++};
++
++enum dpp_bootstrap_type {
++ DPP_BOOTSTRAP_QR_CODE,
++ DPP_BOOTSTRAP_PKEX,
++};
++
++struct dpp_bootstrap_info {
++ struct dl_list list;
++ unsigned int id;
++ enum dpp_bootstrap_type type;
++ char *uri;
++ u8 mac_addr[ETH_ALEN];
++ char *info;
++ unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
++ unsigned int num_freq;
++ int own;
++ EVP_PKEY *pubkey;
++ u8 pubkey_hash[SHA256_MAC_LEN];
++ const struct dpp_curve_params *curve;
++ unsigned int pkex_t; /* number of failures before dpp_pkex
++ * instantiation */
++};
++
++#define PKEX_COUNTER_T_LIMIT 5
++
++struct dpp_pkex {
++ void *msg_ctx;
++ unsigned int initiator:1;
++ unsigned int exchange_done:1;
++ unsigned int failed:1;
++ struct dpp_bootstrap_info *own_bi;
++ u8 own_mac[ETH_ALEN];
++ u8 peer_mac[ETH_ALEN];
++ char *identifier;
++ char *code;
++ EVP_PKEY *x;
++ EVP_PKEY *y;
++ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
++ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
++ u8 z[DPP_MAX_HASH_LEN];
++ EVP_PKEY *peer_bootstrap_key;
++ struct wpabuf *exchange_req;
++ struct wpabuf *exchange_resp;
++ unsigned int t; /* number of failures on code use */
++ unsigned int exch_req_wait_time;
++ unsigned int exch_req_tries;
++ unsigned int freq;
++};
++
++enum dpp_akm {
++ DPP_AKM_UNKNOWN,
++ DPP_AKM_DPP,
++ DPP_AKM_PSK,
++ DPP_AKM_SAE,
++ DPP_AKM_PSK_SAE,
++ DPP_AKM_SAE_DPP,
++ DPP_AKM_PSK_SAE_DPP,
++};
++
++struct dpp_configuration {
++ u8 ssid[32];
++ size_t ssid_len;
++ enum dpp_akm akm;
++
++ /* For DPP configuration (connector) */
++ os_time_t netaccesskey_expiry;
++
++ /* TODO: groups */
++ char *group_id;
++
++ /* For legacy configuration */
++ char *passphrase;
++ u8 psk[32];
++ int psk_set;
++};
++
++struct dpp_authentication {
++ void *msg_ctx;
++ u8 peer_version;
++ const struct dpp_curve_params *curve;
++ struct dpp_bootstrap_info *peer_bi;
++ struct dpp_bootstrap_info *own_bi;
++ struct dpp_bootstrap_info *tmp_own_bi;
++ u8 waiting_pubkey_hash[SHA256_MAC_LEN];
++ int response_pending;
++ enum dpp_status_error auth_resp_status;
++ enum dpp_status_error conf_resp_status;
++ u8 peer_mac_addr[ETH_ALEN];
++ u8 i_nonce[DPP_MAX_NONCE_LEN];
++ u8 r_nonce[DPP_MAX_NONCE_LEN];
++ u8 e_nonce[DPP_MAX_NONCE_LEN];
++ u8 i_capab;
++ u8 r_capab;
++ EVP_PKEY *own_protocol_key;
++ EVP_PKEY *peer_protocol_key;
++ struct wpabuf *req_msg;
++ struct wpabuf *resp_msg;
++ /* Intersection of possible frequencies for initiating DPP
++ * Authentication exchange */
++ unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
++ unsigned int num_freq, freq_idx;
++ unsigned int curr_freq;
++ unsigned int neg_freq;
++ unsigned int num_freq_iters;
++ size_t secret_len;
++ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
++ size_t Mx_len;
++ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
++ size_t Nx_len;
++ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
++ size_t Lx_len;
++ u8 k1[DPP_MAX_HASH_LEN];
++ u8 k2[DPP_MAX_HASH_LEN];
++ u8 ke[DPP_MAX_HASH_LEN];
++ int initiator;
++ int waiting_auth_resp;
++ int waiting_auth_conf;
++ int auth_req_ack;
++ unsigned int auth_resp_tries;
++ u8 allowed_roles;
++ int configurator;
++ int remove_on_tx_status;
++ int connect_on_tx_status;
++ int waiting_conf_result;
++ int auth_success;
++ struct wpabuf *conf_req;
++ const struct wpabuf *conf_resp; /* owned by GAS server */
++ struct dpp_configuration *conf_ap;
++ struct dpp_configuration *conf_sta;
++ struct dpp_configurator *conf;
++ char *connector; /* received signedConnector */
++ u8 ssid[SSID_MAX_LEN];
++ u8 ssid_len;
++ char passphrase[64];
++ u8 psk[PMK_LEN];
++ int psk_set;
++ enum dpp_akm akm;
++ struct wpabuf *net_access_key;
++ os_time_t net_access_key_expiry;
++ struct wpabuf *c_sign_key;
++#ifdef CONFIG_TESTING_OPTIONS
++ char *config_obj_override;
++ char *discovery_override;
++ char *groups_override;
++ unsigned int ignore_netaccesskey_mismatch:1;
++#endif /* CONFIG_TESTING_OPTIONS */
++};
++
++struct dpp_configurator {
++ struct dl_list list;
++ unsigned int id;
++ int own;
++ EVP_PKEY *csign;
++ char *kid;
++ const struct dpp_curve_params *curve;
++};
++
++struct dpp_introduction {
++ u8 pmkid[PMKID_LEN];
++ u8 pmk[PMK_LEN_MAX];
++ size_t pmk_len;
++};
++
++#ifdef CONFIG_TESTING_OPTIONS
++enum dpp_test_behavior {
++ DPP_TEST_DISABLED = 0,
++ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1,
++ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2,
++ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3,
++ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4,
++ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5,
++ DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6,
++ DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7,
++ DPP_TEST_ZERO_I_CAPAB = 8,
++ DPP_TEST_ZERO_R_CAPAB = 9,
++ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10,
++ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11,
++ DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12,
++ DPP_TEST_NO_I_NONCE_AUTH_REQ = 13,
++ DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14,
++ DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15,
++ DPP_TEST_NO_STATUS_AUTH_RESP = 16,
++ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17,
++ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18,
++ DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19,
++ DPP_TEST_NO_R_NONCE_AUTH_RESP = 20,
++ DPP_TEST_NO_I_NONCE_AUTH_RESP = 21,
++ DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22,
++ DPP_TEST_NO_R_AUTH_AUTH_RESP = 23,
++ DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24,
++ DPP_TEST_NO_STATUS_AUTH_CONF = 25,
++ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26,
++ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27,
++ DPP_TEST_NO_I_AUTH_AUTH_CONF = 28,
++ DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29,
++ DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30,
++ DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31,
++ DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32,
++ DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33,
++ DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34,
++ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35,
++ DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36,
++ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37,
++ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38,
++ DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39,
++ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40,
++ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41,
++ DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42,
++ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43,
++ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44,
++ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45,
++ DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46,
++ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47,
++ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48,
++ DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49,
++ DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50,
++ DPP_TEST_NO_E_NONCE_CONF_REQ = 51,
++ DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52,
++ DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53,
++ DPP_TEST_NO_E_NONCE_CONF_RESP = 54,
++ DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55,
++ DPP_TEST_NO_STATUS_CONF_RESP = 56,
++ DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57,
++ DPP_TEST_INVALID_STATUS_CONF_RESP = 58,
++ DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59,
++ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60,
++ DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61,
++ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62,
++ DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63,
++ DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64,
++ DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65,
++ DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66,
++ DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67,
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68,
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69,
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70,
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71,
++ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72,
++ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73,
++ DPP_TEST_INVALID_STATUS_AUTH_RESP = 74,
++ DPP_TEST_INVALID_STATUS_AUTH_CONF = 75,
++ DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76,
++ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77,
++ DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78,
++ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79,
++ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80,
++ DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81,
++ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82,
++ DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83,
++ DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84,
++ DPP_TEST_STOP_AT_PKEX_CR_REQ = 85,
++ DPP_TEST_STOP_AT_PKEX_CR_RESP = 86,
++ DPP_TEST_STOP_AT_AUTH_REQ = 87,
++ DPP_TEST_STOP_AT_AUTH_RESP = 88,
++ DPP_TEST_STOP_AT_AUTH_CONF = 89,
++ DPP_TEST_STOP_AT_CONF_REQ = 90,
++ DPP_TEST_REJECT_CONFIG = 91,
++};
++
++extern enum dpp_test_behavior dpp_test;
++extern u8 dpp_pkex_own_mac_override[ETH_ALEN];
++extern u8 dpp_pkex_peer_mac_override[ETH_ALEN];
++extern u8 dpp_pkex_ephemeral_key_override[600];
++extern size_t dpp_pkex_ephemeral_key_override_len;
++extern u8 dpp_protocol_key_override[600];
++extern size_t dpp_protocol_key_override_len;
++extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
++extern size_t dpp_nonce_override_len;
++#endif /* CONFIG_TESTING_OPTIONS */
++
++void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
++const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
++int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
++int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
++ const char *chan_list);
++int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
++int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
++struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
++char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
++ const u8 *privkey, size_t privkey_len);
++struct hostapd_hw_modes;
++struct dpp_authentication * dpp_auth_init(void *msg_ctx,
++ struct dpp_bootstrap_info *peer_bi,
++ struct dpp_bootstrap_info *own_bi,
++ u8 dpp_allowed_roles,
++ unsigned int neg_freq,
++ struct hostapd_hw_modes *own_modes,
++ u16 num_modes);
++struct dpp_authentication *
++dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
++ struct dpp_bootstrap_info *peer_bi,
++ struct dpp_bootstrap_info *own_bi,
++ unsigned int freq, const u8 *hdr, const u8 *attr_start,
++ size_t attr_len);
++struct wpabuf *
++dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
++ const u8 *attr_start, size_t attr_len);
++struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
++ const char *json);
++int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
++ const u8 *attr_start, size_t attr_len);
++int dpp_notify_new_qr_code(struct dpp_authentication *auth,
++ struct dpp_bootstrap_info *peer_bi);
++struct dpp_configuration * dpp_configuration_alloc(const char *type);
++int dpp_akm_psk(enum dpp_akm akm);
++int dpp_akm_sae(enum dpp_akm akm);
++int dpp_akm_legacy(enum dpp_akm akm);
++int dpp_akm_dpp(enum dpp_akm akm);
++int dpp_akm_ver2(enum dpp_akm akm);
++int dpp_configuration_valid(const struct dpp_configuration *conf);
++void dpp_configuration_free(struct dpp_configuration *conf);
++int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
++ struct dpp_authentication *auth,
++ const char *cmd);
++void dpp_auth_deinit(struct dpp_authentication *auth);
++struct wpabuf *
++dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
++ size_t attr_len);
++int dpp_conf_resp_rx(struct dpp_authentication *auth,
++ const struct wpabuf *resp);
++enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
++ const u8 *hdr,
++ const u8 *attr_start, size_t attr_len);
++struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
++ enum dpp_status_error status);
++struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
++ size_t len);
++const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
++int dpp_check_attrs(const u8 *buf, size_t len);
++int dpp_key_expired(const char *timestamp, os_time_t *expiry);
++const char * dpp_akm_str(enum dpp_akm akm);
++int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
++ size_t buflen);
++void dpp_configurator_free(struct dpp_configurator *conf);
++struct dpp_configurator *
++dpp_keygen_configurator(const char *curve, const u8 *privkey,
++ size_t privkey_len);
++int dpp_configurator_own_config(struct dpp_authentication *auth,
++ const char *curve, int ap);
++enum dpp_status_error
++dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
++ const u8 *net_access_key, size_t net_access_key_len,
++ const u8 *csign_key, size_t csign_key_len,
++ const u8 *peer_connector, size_t peer_connector_len,
++ os_time_t *expiry);
++struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
++ const u8 *own_mac,
++ const char *identifier,
++ const char *code);
++struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
++ struct dpp_bootstrap_info *bi,
++ const u8 *own_mac,
++ const u8 *peer_mac,
++ const char *identifier,
++ const char *code,
++ const u8 *buf, size_t len);
++struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
++ const u8 *peer_mac,
++ const u8 *buf, size_t len);
++struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
++ const u8 *hdr,
++ const u8 *buf, size_t len);
++int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
++ const u8 *buf, size_t len);
++void dpp_pkex_free(struct dpp_pkex *pkex);
++
++char * dpp_corrupt_connector_signature(const char *connector);
++
++
++struct dpp_pfs {
++ struct crypto_ecdh *ecdh;
++ const struct dpp_curve_params *curve;
++ struct wpabuf *ie;
++ struct wpabuf *secret;
++};
++
++struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
++ size_t net_access_key_len);
++int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
++void dpp_pfs_free(struct dpp_pfs *pfs);
++
++struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
++ const char *uri);
++int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
++struct dpp_bootstrap_info *
++dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
++int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id);
++struct dpp_bootstrap_info *
++dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
++ unsigned int freq);
++const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id);
++int dpp_bootstrap_info(struct dpp_global *dpp, int id,
++ char *reply, int reply_size);
++void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
++ const u8 *r_bootstrap,
++ struct dpp_bootstrap_info **own_bi,
++ struct dpp_bootstrap_info **peer_bi);
++int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
++int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
++int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
++ char *buf, size_t buflen);
++struct dpp_global * dpp_global_init(void);
++void dpp_global_clear(struct dpp_global *dpp);
++void dpp_global_deinit(struct dpp_global *dpp);
++
++#endif /* CONFIG_DPP */
++#endif /* DPP_H */
+--- contrib/wpa/src/common/eapol_common.h.orig
++++ contrib/wpa/src/common/eapol_common.h
+@@ -25,7 +25,7 @@
+ struct ieee8023_hdr {
+ u8 dest[ETH_ALEN];
+ u8 src[ETH_ALEN];
+- u16 ethertype;
++ be16 ethertype;
+ } STRUCT_PACKED;
+
+ #ifdef _MSC_VER
+--- contrib/wpa/src/common/gas.c.orig
++++ contrib/wpa/src/common/gas.c
+@@ -75,7 +75,7 @@
+ }
+
+
+-static struct wpabuf *
++struct wpabuf *
+ gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+ {
+--- contrib/wpa/src/common/gas.h.orig
++++ contrib/wpa/src/common/gas.h
+@@ -14,6 +14,9 @@
+ struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+ struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
++struct wpabuf *
++gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
++ u16 comeback_delay, size_t size);
+ struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+ struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+--- contrib/wpa/src/common/gas_server.c.orig
++++ contrib/wpa/src/common/gas_server.c
+@@ -0,0 +1,487 @@
++/*
++ * Generic advertisement service (GAS) server
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "utils/common.h"
++#include "utils/list.h"
++#include "utils/eloop.h"
++#include "ieee802_11_defs.h"
++#include "gas.h"
++#include "gas_server.h"
++
++
++#define MAX_ADV_PROTO_ID_LEN 10
++#define GAS_QUERY_TIMEOUT 10
++
++struct gas_server_handler {
++ struct dl_list list;
++ u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
++ u8 adv_proto_id_len;
++ struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
++ const u8 *query, size_t query_len);
++ void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
++ void *ctx;
++ struct gas_server *gas;
++};
++
++struct gas_server_response {
++ struct dl_list list;
++ size_t offset;
++ u8 frag_id;
++ struct wpabuf *resp;
++ int freq;
++ u8 dst[ETH_ALEN];
++ u8 dialog_token;
++ struct gas_server_handler *handler;
++};
++
++struct gas_server {
++ struct dl_list handlers; /* struct gas_server_handler::list */
++ struct dl_list responses; /* struct gas_server_response::list */
++ void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
++ unsigned int wait_time);
++ void *ctx;
++};
++
++static void gas_server_free_response(struct gas_server_response *response);
++
++
++static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
++{
++ struct gas_server_response *response = eloop_ctx;
++
++ wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
++ " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
++ response, MAC2STR(response->dst), response->dialog_token,
++ response->freq, response->frag_id,
++ (unsigned long) response->offset,
++ (unsigned long) wpabuf_len(response->resp));
++ response->handler->status_cb(response->handler->ctx,
++ response->resp, 0);
++ response->resp = NULL;
++ dl_list_del(&response->list);
++ gas_server_free_response(response);
++}
++
++
++static void gas_server_free_response(struct gas_server_response *response)
++{
++ if (!response)
++ return;
++ wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
++ eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
++ wpabuf_free(response->resp);
++ os_free(response);
++}
++
++
++static void
++gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
++ const u8 *da, int freq, u8 dialog_token,
++ struct wpabuf *query_resp)
++{
++ size_t max_len = (freq > 56160) ? 928 : 1400;
++ size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
++ size_t resp_frag_len;
++ struct wpabuf *resp;
++ u16 comeback_delay;
++ struct gas_server_response *response;
++
++ if (!query_resp)
++ return;
++
++ response = os_zalloc(sizeof(*response));
++ if (!response) {
++ wpabuf_free(query_resp);
++ return;
++ }
++ wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
++ response->freq = freq;
++ response->handler = handler;
++ os_memcpy(response->dst, da, ETH_ALEN);
++ response->dialog_token = dialog_token;
++ if (hdr_len + wpabuf_len(query_resp) > max_len) {
++ /* Need to use comeback to initiate fragmentation */
++ comeback_delay = 1;
++ resp_frag_len = 0;
++ } else {
++ /* Full response fits into the initial response */
++ comeback_delay = 0;
++ resp_frag_len = wpabuf_len(query_resp);
++ }
++
++ resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
++ comeback_delay,
++ handler->adv_proto_id_len +
++ resp_frag_len);
++ if (!resp) {
++ wpabuf_free(query_resp);
++ gas_server_free_response(response);
++ return;
++ }
++
++ /* Advertisement Protocol element */
++ wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
++ wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
++ wpabuf_put_u8(resp, 0x7f);
++ /* Advertisement Protocol ID */
++ wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
++
++ /* Query Response Length */
++ wpabuf_put_le16(resp, resp_frag_len);
++ if (!comeback_delay)
++ wpabuf_put_buf(resp, query_resp);
++
++ if (comeback_delay) {
++ wpa_printf(MSG_DEBUG,
++ "GAS: Need to fragment query response");
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "GAS: Full query response fits in the GAS Initial Response frame");
++ }
++ response->offset = resp_frag_len;
++ response->resp = query_resp;
++ dl_list_add(&gas->responses, &response->list);
++ gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
++ wpabuf_free(resp);
++ eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
++ gas_server_response_timeout, response, NULL);
++}
++
++
++static int
++gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
++ const u8 *bssid, int freq, u8 dialog_token,
++ const u8 *data, size_t len)
++{
++ const u8 *pos, *end, *adv_proto, *query_req;
++ u8 adv_proto_len;
++ u16 query_req_len;
++ struct gas_server_handler *handler;
++ struct wpabuf *resp;
++
++ wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
++ data, len);
++ pos = data;
++ end = data + len;
++
++ if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
++ wpa_printf(MSG_DEBUG,
++ "GAS: No Advertisement Protocol element found");
++ return -1;
++ }
++ pos++;
++ adv_proto_len = *pos++;
++ if (end - pos < adv_proto_len || adv_proto_len < 2) {
++ wpa_printf(MSG_DEBUG,
++ "GAS: Truncated Advertisement Protocol element");
++ return -1;
++ }
++
++ adv_proto = pos;
++ pos += adv_proto_len;
++ wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
++ adv_proto, adv_proto_len);
++
++ if (end - pos < 2) {
++ wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
++ return -1;
++ }
++ query_req_len = WPA_GET_LE16(pos);
++ pos += 2;
++ if (end - pos < query_req_len) {
++ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
++ return -1;
++ }
++ query_req = pos;
++ pos += query_req_len;
++ wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
++ query_req, query_req_len);
++
++ if (pos < end) {
++ wpa_hexdump(MSG_MSGDUMP,
++ "GAS: Ignored extra data after Query Request field",
++ pos, end - pos);
++ }
++
++ dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
++ list) {
++ if (adv_proto_len < 1 + handler->adv_proto_id_len ||
++ os_memcmp(adv_proto + 1, handler->adv_proto_id,
++ handler->adv_proto_id_len) != 0)
++ continue;
++
++ wpa_printf(MSG_DEBUG,
++ "GAS: Calling handler for the requested Advertisement Protocol ID");
++ resp = handler->req_cb(handler->ctx, sa, query_req,
++ query_req_len);
++ wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
++ resp);
++ gas_server_send_resp(gas, handler, sa, freq, dialog_token,
++ resp);
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "GAS: No registered handler for the requested Advertisement Protocol ID");
++ return -1;
++}
++
++
++static void
++gas_server_handle_rx_comeback_req(struct gas_server_response *response)
++{
++ struct gas_server_handler *handler = response->handler;
++ struct gas_server *gas = handler->gas;
++ size_t max_len = (response->freq > 56160) ? 928 : 1400;
++ size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
++ size_t remaining, resp_frag_len;
++ struct wpabuf *resp;
++
++ remaining = wpabuf_len(response->resp) - response->offset;
++ if (hdr_len + remaining > max_len)
++ resp_frag_len = max_len - hdr_len;
++ else
++ resp_frag_len = remaining;
++ wpa_printf(MSG_DEBUG,
++ "GAS: Sending out %u/%u remaining Query Response octets",
++ (unsigned int) resp_frag_len, (unsigned int) remaining);
++
++ resp = gas_build_comeback_resp(response->dialog_token,
++ WLAN_STATUS_SUCCESS,
++ response->frag_id++,
++ resp_frag_len < remaining, 0,
++ handler->adv_proto_id_len +
++ resp_frag_len);
++ if (!resp) {
++ dl_list_del(&response->list);
++ gas_server_free_response(response);
++ return;
++ }
++
++ /* Advertisement Protocol element */
++ wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
++ wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
++ wpabuf_put_u8(resp, 0x7f);
++ /* Advertisement Protocol ID */
++ wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
++
++ /* Query Response Length */
++ wpabuf_put_le16(resp, resp_frag_len);
++ wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
++ resp_frag_len);
++
++ response->offset += resp_frag_len;
++
++ gas->tx(gas->ctx, response->freq, response->dst, resp,
++ remaining > resp_frag_len ? 2000 : 0);
++ wpabuf_free(resp);
++}
++
++
++static int
++gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
++ const u8 *bssid, int freq, u8 dialog_token)
++{
++ struct gas_server_response *response;
++
++ dl_list_for_each(response, &gas->responses, struct gas_server_response,
++ list) {
++ if (response->dialog_token != dialog_token ||
++ os_memcmp(sa, response->dst, ETH_ALEN) != 0)
++ continue;
++ gas_server_handle_rx_comeback_req(response);
++ return 0;
++ }
++
++ wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
++ " (dialog token %u)", MAC2STR(sa), dialog_token);
++ return -1;
++}
++
++
++/**
++ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
++ * @gas: GAS query data from gas_server_init()
++ * @da: Destination MAC address of the Action frame
++ * @sa: Source MAC address of the Action frame
++ * @bssid: BSSID of the Action frame
++ * @categ: Category of the Action frame
++ * @data: Payload of the Action frame
++ * @len: Length of @data
++ * @freq: Frequency (in MHz) on which the frame was received
++ * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not
++ */
++int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
++ const u8 *bssid, u8 categ, const u8 *data, size_t len,
++ int freq)
++{
++ u8 action, dialog_token;
++ const u8 *pos, *end;
++
++ if (!gas || len < 2)
++ return -1;
++
++ if (categ == WLAN_ACTION_PROTECTED_DUAL)
++ return -1; /* Not supported for now */
++
++ pos = data;
++ end = data + len;
++ action = *pos++;
++ dialog_token = *pos++;
++
++ if (action != WLAN_PA_GAS_INITIAL_REQ &&
++ action != WLAN_PA_GAS_COMEBACK_REQ)
++ return -1; /* Not a GAS request */
++
++ wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
++ " SA=" MACSTR " BSSID=" MACSTR
++ " freq=%d dialog_token=%u len=%u",
++ action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
++ MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
++ (unsigned int) len);
++
++ if (action == WLAN_PA_GAS_INITIAL_REQ)
++ return gas_server_rx_initial_req(gas, da, sa, bssid,
++ freq, dialog_token,
++ pos, end - pos);
++ return gas_server_rx_comeback_req(gas, da, sa, bssid,
++ freq, dialog_token);
++}
++
++
++static void gas_server_handle_tx_status(struct gas_server_response *response,
++ int ack)
++{
++ if (ack && response->offset < wpabuf_len(response->resp)) {
++ wpa_printf(MSG_DEBUG,
++ "GAS: More fragments remaining - keep pending entry");
++ return;
++ }
++
++ if (!ack)
++ wpa_printf(MSG_DEBUG,
++ "GAS: No ACK received - drop pending entry");
++ else
++ wpa_printf(MSG_DEBUG,
++ "GAS: Last fragment of the response sent out - drop pending entry");
++
++ response->handler->status_cb(response->handler->ctx,
++ response->resp, ack);
++ response->resp = NULL;
++ dl_list_del(&response->list);
++ gas_server_free_response(response);
++}
++
++
++void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
++ size_t data_len, int ack)
++{
++ const u8 *pos;
++ u8 action, code, dialog_token;
++ struct gas_server_response *response;
++
++ if (data_len < 24 + 3)
++ return;
++ pos = data + 24;
++ action = *pos++;
++ code = *pos++;
++ dialog_token = *pos++;
++ if (action != WLAN_ACTION_PUBLIC ||
++ (code != WLAN_PA_GAS_INITIAL_RESP &&
++ code != WLAN_PA_GAS_COMEBACK_RESP))
++ return;
++ wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
++ " ack=%d %s dialog_token=%u",
++ MAC2STR(dst), ack,
++ code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
++ dialog_token);
++ dl_list_for_each(response, &gas->responses, struct gas_server_response,
++ list) {
++ if (response->dialog_token != dialog_token ||
++ os_memcmp(dst, response->dst, ETH_ALEN) != 0)
++ continue;
++ gas_server_handle_tx_status(response, ack);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
++}
++
++
++struct gas_server * gas_server_init(void *ctx,
++ void (*tx)(void *ctx, int freq,
++ const u8 *da,
++ struct wpabuf *buf,
++ unsigned int wait_time))
++{
++ struct gas_server *gas;
++
++ gas = os_zalloc(sizeof(*gas));
++ if (!gas)
++ return NULL;
++ gas->ctx = ctx;
++ gas->tx = tx;
++ dl_list_init(&gas->handlers);
++ dl_list_init(&gas->responses);
++ return gas;
++}
++
++
++void gas_server_deinit(struct gas_server *gas)
++{
++ struct gas_server_handler *handler, *tmp;
++ struct gas_server_response *response, *tmp_r;
++
++ if (!gas)
++ return;
++
++ dl_list_for_each_safe(handler, tmp, &gas->handlers,
++ struct gas_server_handler, list) {
++ dl_list_del(&handler->list);
++ os_free(handler);
++ }
++
++ dl_list_for_each_safe(response, tmp_r, &gas->responses,
++ struct gas_server_response, list) {
++ dl_list_del(&response->list);
++ gas_server_free_response(response);
++ }
++
++ os_free(gas);
++}
++
++
++int gas_server_register(struct gas_server *gas,
++ const u8 *adv_proto_id, u8 adv_proto_id_len,
++ struct wpabuf *
++ (*req_cb)(void *ctx, const u8 *sa,
++ const u8 *query, size_t query_len),
++ void (*status_cb)(void *ctx, struct wpabuf *resp,
++ int ok),
++ void *ctx)
++{
++ struct gas_server_handler *handler;
++
++ if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
++ return -1;
++ handler = os_zalloc(sizeof(*handler));
++ if (!handler)
++ return -1;
++
++ os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
++ handler->adv_proto_id_len = adv_proto_id_len;
++ handler->req_cb = req_cb;
++ handler->status_cb = status_cb;
++ handler->ctx = ctx;
++ handler->gas = gas;
++ dl_list_add(&gas->handlers, &handler->list);
++
++ return 0;
++}
+--- contrib/wpa/src/common/gas_server.h.orig
++++ contrib/wpa/src/common/gas_server.h
+@@ -0,0 +1,44 @@
++/*
++ * Generic advertisement service (GAS) server
++ * Copyright (c) 2017, Qualcomm Atheros, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef GAS_SERVER_H
++#define GAS_SERVER_H
++
++#ifdef CONFIG_GAS_SERVER
++
++struct gas_server;
++
++struct gas_server * gas_server_init(void *ctx,
++ void (*tx)(void *ctx, int freq,
++ const u8 *da,
++ struct wpabuf *buf,
++ unsigned int wait_time));
++void gas_server_deinit(struct gas_server *gas);
++int gas_server_register(struct gas_server *gas,
++ const u8 *adv_proto_id, u8 adv_proto_id_len,
++ struct wpabuf *
++ (*req_cb)(void *ctx, const u8 *sa,
++ const u8 *query, size_t query_len),
++ void (*status_cb)(void *ctx, struct wpabuf *resp,
++ int ok),
++ void *ctx);
++int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
++ const u8 *bssid, u8 categ, const u8 *data, size_t len,
++ int freq);
++void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
++ size_t data_len, int ack);
++
++#else /* CONFIG_GAS_SERVER */
++
++static inline void gas_server_deinit(struct gas_server *gas)
++{
++}
++
++#endif /* CONFIG_GAS_SERVER */
++
++#endif /* GAS_SERVER_H */
+--- contrib/wpa/src/common/hw_features_common.c.orig
++++ contrib/wpa/src/common/hw_features_common.c
+@@ -87,14 +87,30 @@
+ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+ int sec_chan)
+ {
+- int ok, j, first;
++ int ok, first;
+ int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+- 149, 157, 184, 192 };
++ 149, 157, 165, 184, 192 };
+ size_t k;
++ struct hostapd_channel_data *p_chan, *s_chan;
++ const int ht40_plus = pri_chan < sec_chan;
+
+- if (pri_chan == sec_chan || !sec_chan)
+- return 1; /* HT40 not used */
++ p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
++ if (!p_chan)
++ return 0;
+
++ if (pri_chan == sec_chan || !sec_chan) {
++ if (chan_pri_allowed(p_chan))
++ return 1; /* HT40 not used */
++
++ wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
++ pri_chan);
++ return 0;
++ }
++
++ s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
++ if (!s_chan)
++ return 0;
++
+ wpa_printf(MSG_DEBUG,
+ "HT40: control channel: %d secondary channel: %d",
+ pri_chan, sec_chan);
+@@ -101,16 +117,9 @@
+
+ /* Verify that HT40 secondary channel is an allowed 20 MHz
+ * channel */
+- ok = 0;
+- for (j = 0; j < mode->num_channels; j++) {
+- struct hostapd_channel_data *chan = &mode->channels[j];
+- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+- chan->chan == sec_chan) {
+- ok = 1;
+- break;
+- }
+- }
+- if (!ok) {
++ if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
++ (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
++ (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
+ wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+ sec_chan);
+ return 0;
+@@ -388,8 +397,10 @@
+ /* fall through */
+ case VHT_CHANWIDTH_80MHZ:
+ data->bandwidth = 80;
+- if ((vht_oper_chwidth == 1 && center_segment1) ||
+- (vht_oper_chwidth == 3 && !center_segment1) ||
++ if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ &&
++ center_segment1) ||
++ (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ &&
++ !center_segment1) ||
+ !sec_channel_offset)
+ return -1;
+ if (!center_segment0) {
+@@ -453,3 +464,157 @@
+
+ return 0;
+ }
++
++
++void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
++ int disabled)
++{
++ /* Masking these out disables HT40 */
++ le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
++ HT_CAP_INFO_SHORT_GI40MHZ);
++
++ if (disabled)
++ htcaps->ht_capabilities_info &= ~msk;
++ else
++ htcaps->ht_capabilities_info |= msk;
++}
++
++
++#ifdef CONFIG_IEEE80211AC
++
++static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap,
++ const char *name)
++{
++ u32 req_cap = conf & cap;
++
++ /*
++ * Make sure we support all requested capabilities.
++ * NOTE: We assume that 'cap' represents a capability mask,
++ * not a discrete value.
++ */
++ if ((hw & req_cap) != req_cap) {
++ wpa_printf(MSG_ERROR,
++ "Driver does not support configured VHT capability [%s]",
++ name);
++ return 0;
++ }
++ return 1;
++}
++
++
++static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
++ unsigned int shift,
++ const char *name)
++{
++ u32 hw_max = hw & mask;
++ u32 conf_val = conf & mask;
++
++ if (conf_val > hw_max) {
++ wpa_printf(MSG_ERROR,
++ "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
++ name, conf_val >> shift, hw_max >> shift);
++ return 0;
++ }
++ return 1;
++}
++
++
++int ieee80211ac_cap_check(u32 hw, u32 conf)
++{
++#define VHT_CAP_CHECK(cap) \
++ do { \
++ if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \
++ return 0; \
++ } while (0)
++
++#define VHT_CAP_CHECK_MAX(cap) \
++ do { \
++ if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
++ #cap)) \
++ return 0; \
++ } while (0)
++
++ VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
++ VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
++ VHT_CAP_CHECK(VHT_CAP_RXLDPC);
++ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
++ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
++ VHT_CAP_CHECK(VHT_CAP_TXSTBC);
++ VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
++ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
++ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
++ VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
++ VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
++ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
++ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
++ VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
++ VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
++ VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
++ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
++ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
++ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
++ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
++
++#undef VHT_CAP_CHECK
++#undef VHT_CAP_CHECK_MAX
++
++ return 1;
++}
++
++#endif /* CONFIG_IEEE80211AC */
++
++
++u32 num_chan_to_bw(int num_chans)
++{
++ switch (num_chans) {
++ case 2:
++ case 4:
++ case 8:
++ return num_chans * 20;
++ default:
++ return 20;
++ }
++}
++
++
++/* check if BW is applicable for channel */
++int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
++ int ht40_plus, int pri)
++{
++ u32 bw_mask;
++
++ switch (bw) {
++ case 20:
++ bw_mask = HOSTAPD_CHAN_WIDTH_20;
++ break;
++ case 40:
++ /* HT 40 MHz support declared only for primary channel,
++ * just skip 40 MHz secondary checking */
++ if (pri && ht40_plus)
++ bw_mask = HOSTAPD_CHAN_WIDTH_40P;
++ else if (pri && !ht40_plus)
++ bw_mask = HOSTAPD_CHAN_WIDTH_40M;
++ else
++ bw_mask = 0;
++ break;
++ case 80:
++ bw_mask = HOSTAPD_CHAN_WIDTH_80;
++ break;
++ case 160:
++ bw_mask = HOSTAPD_CHAN_WIDTH_160;
++ break;
++ default:
++ bw_mask = 0;
++ break;
++ }
++
++ return (chan->allowed_bw & bw_mask) == bw_mask;
++}
++
++
++/* check if channel is allowed to be used as primary */
++int chan_pri_allowed(const struct hostapd_channel_data *chan)
++{
++ return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
++ (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
++}
+--- contrib/wpa/src/common/hw_features_common.h.orig
++++ contrib/wpa/src/common/hw_features_common.h
+@@ -35,5 +35,13 @@
+ int vht_enabled, int sec_channel_offset,
+ int vht_oper_chwidth, int center_segment0,
+ int center_segment1, u32 vht_caps);
++void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
++ int disabled);
++int ieee80211ac_cap_check(u32 hw, u32 conf);
+
++u32 num_chan_to_bw(int num_chans);
++int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
++ int ht40_plus, int pri);
++int chan_pri_allowed(const struct hostapd_channel_data *chan);
++
+ #endif /* HW_FEATURES_COMMON_H */
+--- contrib/wpa/src/common/ieee802_11_common.c.orig
++++ contrib/wpa/src/common/ieee802_11_common.c
+@@ -1,6 +1,6 @@
+ /*
+ * IEEE 802.11 Common routines
+- * Copyright (c) 2002-2015, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -11,6 +11,7 @@
+ #include "common.h"
+ #include "defs.h"
+ #include "wpa_common.h"
++#include "drivers/driver.h"
+ #include "qca-vendor.h"
+ #include "ieee802_11_defs.h"
+ #include "ieee802_11_common.h"
+@@ -115,6 +116,20 @@
+ elems->osen = pos;
+ elems->osen_len = elen;
+ break;
++ case MBO_OUI_TYPE:
++ /* MBO-OCE */
++ elems->mbo = pos;
++ elems->mbo_len = elen;
++ break;
++ case HS20_ROAMING_CONS_SEL_OUI_TYPE:
++ /* Hotspot 2.0 Roaming Consortium Selection */
++ elems->roaming_cons_sel = pos;
++ elems->roaming_cons_sel_len = elen;
++ break;
++ case MULTI_AP_OUI_TYPE:
++ elems->multi_ap = pos;
++ elems->multi_ap_len = elen;
++ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "Unknown WFA "
+ "information element ignored "
+@@ -174,6 +189,108 @@
+ }
+
+
++static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
++ struct ieee802_11_elems *elems,
++ int show_errors)
++{
++ u8 ext_id;
++
++ if (elen < 1) {
++ if (show_errors) {
++ wpa_printf(MSG_MSGDUMP,
++ "short information element (Ext)");
++ }
++ return -1;
++ }
++
++ ext_id = *pos++;
++ elen--;
++
++ switch (ext_id) {
++ case WLAN_EID_EXT_ASSOC_DELAY_INFO:
++ if (elen != 1)
++ break;
++ elems->assoc_delay_info = pos;
++ break;
++ case WLAN_EID_EXT_FILS_REQ_PARAMS:
++ if (elen < 3)
++ break;
++ elems->fils_req_params = pos;
++ elems->fils_req_params_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_KEY_CONFIRM:
++ elems->fils_key_confirm = pos;
++ elems->fils_key_confirm_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_SESSION:
++ if (elen != FILS_SESSION_LEN)
++ break;
++ elems->fils_session = pos;
++ break;
++ case WLAN_EID_EXT_FILS_HLP_CONTAINER:
++ if (elen < 2 * ETH_ALEN)
++ break;
++ elems->fils_hlp = pos;
++ elems->fils_hlp_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
++ if (elen < 1)
++ break;
++ elems->fils_ip_addr_assign = pos;
++ elems->fils_ip_addr_assign_len = elen;
++ break;
++ case WLAN_EID_EXT_KEY_DELIVERY:
++ if (elen < WPA_KEY_RSC_LEN)
++ break;
++ elems->key_delivery = pos;
++ elems->key_delivery_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_WRAPPED_DATA:
++ elems->fils_wrapped_data = pos;
++ elems->fils_wrapped_data_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_PUBLIC_KEY:
++ if (elen < 1)
++ break;
++ elems->fils_pk = pos;
++ elems->fils_pk_len = elen;
++ break;
++ case WLAN_EID_EXT_FILS_NONCE:
++ if (elen != FILS_NONCE_LEN)
++ break;
++ elems->fils_nonce = pos;
++ break;
++ case WLAN_EID_EXT_OWE_DH_PARAM:
++ if (elen < 2)
++ break;
++ elems->owe_dh = pos;
++ elems->owe_dh_len = elen;
++ break;
++ case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
++ elems->password_id = pos;
++ elems->password_id_len = elen;
++ break;
++ case WLAN_EID_EXT_HE_CAPABILITIES:
++ elems->he_capabilities = pos;
++ elems->he_capabilities_len = elen;
++ break;
++ case WLAN_EID_EXT_OCV_OCI:
++ elems->oci = pos;
++ elems->oci_len = elen;
++ break;
++ default:
++ if (show_errors) {
++ wpa_printf(MSG_MSGDUMP,
++ "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
++ ext_id, (unsigned int) elen);
++ }
++ return -1;
++ }
++
++ return 0;
++}
++
++
+ /**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+@@ -186,30 +303,18 @@
+ struct ieee802_11_elems *elems,
+ int show_errors)
+ {
+- size_t left = len;
+- const u8 *pos = start;
++ const struct element *elem;
+ int unknown = 0;
+
+ os_memset(elems, 0, sizeof(*elems));
+
+- while (left >= 2) {
+- u8 id, elen;
++ if (!start)
++ return ParseOK;
+
+- id = *pos++;
+- elen = *pos++;
+- left -= 2;
++ for_each_element(elem, start, len) {
++ u8 id = elem->id, elen = elem->datalen;
++ const u8 *pos = elem->data;
+
+- if (elen > left) {
+- if (show_errors) {
+- wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
+- "parse failed (id=%d elen=%d "
+- "left=%lu)",
+- id, elen, (unsigned long) left);
+- wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
+- }
+- return ParseFailed;
+- }
+-
+ switch (id) {
+ case WLAN_EID_SSID:
+ if (elen > SSID_MAX_LEN) {
+@@ -257,6 +362,10 @@
+ elems->rsn_ie_len = elen;
+ break;
+ case WLAN_EID_PWR_CAPABILITY:
++ if (elen < 2)
++ break;
++ elems->power_capab = pos;
++ elems->power_capab_len = elen;
+ break;
+ case WLAN_EID_SUPPORTED_CHANNELS:
+ elems->supp_channels = pos;
+@@ -352,8 +461,7 @@
+ elems->mic = pos;
+ elems->mic_len = elen;
+ /* after mic everything is encrypted, so stop. */
+- left = elen;
+- break;
++ goto done;
+ case WLAN_EID_MULTI_BAND:
+ if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
+ wpa_printf(MSG_MSGDUMP,
+@@ -366,6 +474,43 @@
+ elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
+ elems->mb_ies.nof_ies++;
+ break;
++ case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
++ elems->supp_op_classes = pos;
++ elems->supp_op_classes_len = elen;
++ break;
++ case WLAN_EID_RRM_ENABLED_CAPABILITIES:
++ elems->rrm_enabled = pos;
++ elems->rrm_enabled_len = elen;
++ break;
++ case WLAN_EID_CAG_NUMBER:
++ elems->cag_number = pos;
++ elems->cag_number_len = elen;
++ break;
++ case WLAN_EID_AP_CSN:
++ if (elen < 1)
++ break;
++ elems->ap_csn = pos;
++ break;
++ case WLAN_EID_FILS_INDICATION:
++ if (elen < 2)
++ break;
++ elems->fils_indic = pos;
++ elems->fils_indic_len = elen;
++ break;
++ case WLAN_EID_DILS:
++ if (elen < 2)
++ break;
++ elems->dils = pos;
++ elems->dils_len = elen;
++ break;
++ case WLAN_EID_FRAGMENT:
++ /* TODO */
++ break;
++ case WLAN_EID_EXTENSION:
++ if (ieee802_11_parse_extension(pos, elen, elems,
++ show_errors))
++ unknown++;
++ break;
+ default:
+ unknown++;
+ if (!show_errors)
+@@ -375,14 +520,19 @@
+ id, elen);
+ break;
+ }
+-
+- left -= elen;
+- pos += elen;
+ }
+
+- if (left)
++ if (!for_each_element_completed(elem, start, len)) {
++ if (show_errors) {
++ wpa_printf(MSG_DEBUG,
++ "IEEE 802.11 element parse failed @%d",
++ (int) (start + len - (const u8 *) elem));
++ wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
++ }
+ return ParseFailed;
++ }
+
++done:
+ return unknown ? ParseUnknown : ParseOK;
+ }
+
+@@ -389,21 +539,14 @@
+
+ int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
+ {
++ const struct element *elem;
+ int count = 0;
+- const u8 *pos, *end;
+
+ if (ies == NULL)
+ return 0;
+
+- pos = ies;
+- end = ies + ies_len;
+-
+- while (pos + 2 <= end) {
+- if (pos + 2 + pos[1] > end)
+- break;
++ for_each_element(elem, ies, ies_len)
+ count++;
+- pos += 2 + pos[1];
+- }
+
+ return count;
+ }
+@@ -413,24 +556,17 @@
+ u32 oui_type)
+ {
+ struct wpabuf *buf;
+- const u8 *end, *pos, *ie;
++ const struct element *elem, *found = NULL;
+
+- pos = ies;
+- end = ies + ies_len;
+- ie = NULL;
+-
+- while (pos + 1 < end) {
+- if (pos + 2 + pos[1] > end)
+- return NULL;
+- if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+- WPA_GET_BE32(&pos[2]) == oui_type) {
+- ie = pos;
++ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
++ if (elem->datalen >= 4 &&
++ WPA_GET_BE32(elem->data) == oui_type) {
++ found = elem;
+ break;
+ }
+- pos += 2 + pos[1];
+ }
+
+- if (ie == NULL)
++ if (!found)
+ return NULL; /* No specified vendor IE found */
+
+ buf = wpabuf_alloc(ies_len);
+@@ -441,13 +577,9 @@
+ * There may be multiple vendor IEs in the message, so need to
+ * concatenate their data fields.
+ */
+- while (pos + 1 < end) {
+- if (pos + 2 + pos[1] > end)
+- break;
+- if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+- WPA_GET_BE32(&pos[2]) == oui_type)
+- wpabuf_put_data(buf, pos + 6, pos[1] - 4);
+- pos += 2 + pos[1];
++ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
++ if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
++ wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
+ }
+
+ return buf;
+@@ -570,7 +702,8 @@
+ {
+ u8 op_class;
+
+- return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel);
++ return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
++ &op_class, channel);
+ }
+
+
+@@ -579,7 +712,7 @@
+ * for HT40 and VHT. DFS channels are not covered.
+ * @freq: Frequency (MHz) to convert
+ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
+- * @vht: 0 - non-VHT, 1 - 80 MHz
++ * @vht: VHT channel width (VHT_CHANWIDTH_*)
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+@@ -588,6 +721,8 @@
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel)
+ {
++ u8 vht_opclass;
++
+ /* TODO: more operating classes */
+
+ if (sec_channel > 1 || sec_channel < -1)
+@@ -631,17 +766,32 @@
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
++ switch (vht) {
++ case VHT_CHANWIDTH_80MHZ:
++ vht_opclass = 128;
++ break;
++ case VHT_CHANWIDTH_160MHZ:
++ vht_opclass = 129;
++ break;
++ case VHT_CHANWIDTH_80P80MHZ:
++ vht_opclass = 130;
++ break;
++ default:
++ vht_opclass = 0;
++ break;
++ }
++
+ /* 5 GHz, channels 36..48 */
+ if (freq >= 5180 && freq <= 5240) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+- if (sec_channel == 1)
++ if (vht_opclass)
++ *op_class = vht_opclass;
++ else if (sec_channel == 1)
+ *op_class = 116;
+ else if (sec_channel == -1)
+ *op_class = 117;
+- else if (vht)
+- *op_class = 128;
+ else
+ *op_class = 115;
+
+@@ -650,19 +800,19 @@
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+- /* 5 GHz, channels 149..161 */
+- if (freq >= 5745 && freq <= 5805) {
++ /* 5 GHz, channels 52..64 */
++ if (freq >= 5260 && freq <= 5320) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+- if (sec_channel == 1)
+- *op_class = 126;
++ if (vht_opclass)
++ *op_class = vht_opclass;
++ else if (sec_channel == 1)
++ *op_class = 119;
+ else if (sec_channel == -1)
+- *op_class = 127;
+- else if (vht)
+- *op_class = 128;
++ *op_class = 120;
+ else
+- *op_class = 124;
++ *op_class = 118;
+
+ *channel = (freq - 5000) / 5;
+
+@@ -674,7 +824,16 @@
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+- *op_class = 125;
++ if (vht_opclass)
++ *op_class = vht_opclass;
++ else if (sec_channel == 1)
++ *op_class = 126;
++ else if (sec_channel == -1)
++ *op_class = 127;
++ else if (freq <= 5805)
++ *op_class = 124;
++ else
++ *op_class = 125;
+
+ *channel = (freq - 5000) / 5;
+
+@@ -681,6 +840,25 @@
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
++ /* 5 GHz, channels 100..140 */
++ if (freq >= 5000 && freq <= 5700) {
++ if ((freq - 5000) % 5)
++ return NUM_HOSTAPD_MODES;
++
++ if (vht_opclass)
++ *op_class = vht_opclass;
++ else if (sec_channel == 1)
++ *op_class = 122;
++ else if (sec_channel == -1)
++ *op_class = 123;
++ else
++ *op_class = 121;
++
++ *channel = (freq - 5000) / 5;
++
++ return HOSTAPD_MODE_IEEE80211A;
++ }
++
+ if (freq >= 5000 && freq < 5900) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+@@ -704,6 +882,41 @@
+ }
+
+
++int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
++ int sec_channel, u8 *op_class, u8 *channel)
++{
++ int vht = CHAN_WIDTH_UNKNOWN;
++
++ switch (chanwidth) {
++ case CHAN_WIDTH_UNKNOWN:
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ case CHAN_WIDTH_40:
++ vht = VHT_CHANWIDTH_USE_HT;
++ break;
++ case CHAN_WIDTH_80:
++ vht = VHT_CHANWIDTH_80MHZ;
++ break;
++ case CHAN_WIDTH_80P80:
++ vht = VHT_CHANWIDTH_80P80MHZ;
++ break;
++ case CHAN_WIDTH_160:
++ vht = VHT_CHANWIDTH_160MHZ;
++ break;
++ }
++
++ if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
++ channel) == NUM_HOSTAPD_MODES) {
++ wpa_printf(MSG_WARNING,
++ "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
++ freq, chanwidth, sec_channel);
++ return -1;
++ }
++
++ return 0;
++}
++
++
+ static const char *const us_op_class_cc[] = {
+ "US", "CA", NULL
+ };
+@@ -941,7 +1154,7 @@
+ return -1;
+ return 5000 + 5 * chan;
+ case 129: /* center freqs 50, 114; 160 MHz */
+- if (chan < 50 || chan > 114)
++ if (chan < 36 || chan > 128)
+ return -1;
+ return 5000 + 5 * chan;
+ case 180: /* 60 GHz band, channels 1..4 */
+@@ -991,10 +1204,24 @@
+ }
+
+
+-int ieee80211_is_dfs(int freq)
++int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
++ u16 num_modes)
+ {
+- /* TODO: this could be more accurate to better cover all domains */
+- return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
++ int i, j;
++
++ if (!modes || !num_modes)
++ return (freq >= 5260 && freq <= 5320) ||
++ (freq >= 5500 && freq <= 5700);
++
++ for (i = 0; i < num_modes; i++) {
++ for (j = 0; j < modes[i].num_channels; j++) {
++ if (modes[i].channels[j].freq == freq &&
++ (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
++ return 1;
++ }
++ }
++
++ return 0;
+ }
+
+
+@@ -1091,27 +1318,27 @@
+ int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+ size_t ies_len)
+ {
++ const struct element *elem;
++
+ os_memset(info, 0, sizeof(*info));
+
+- while (ies_buf && ies_len >= 2 &&
+- info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+- size_t len = 2 + ies_buf[1];
++ if (!ies_buf)
++ return 0;
+
+- if (len > ies_len) {
+- wpa_hexdump(MSG_DEBUG, "Truncated IEs",
+- ies_buf, ies_len);
+- return -1;
+- }
++ for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
++ if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
++ return 0;
+
+- if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
+- wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
+- info->ies[info->nof_ies].ie = ies_buf + 2;
+- info->ies[info->nof_ies].ie_len = ies_buf[1];
+- info->nof_ies++;
+- }
++ wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
++ elem->datalen + 2);
++ info->ies[info->nof_ies].ie = elem->data;
++ info->ies[info->nof_ies].ie_len = elem->datalen;
++ info->nof_ies++;
++ }
+
+- ies_len -= len;
+- ies_buf += len;
++ if (!for_each_element_completed(elem, ies_buf, ies_len)) {
++ wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
++ return -1;
+ }
+
+ return 0;
+@@ -1145,3 +1372,460 @@
+
+ return mb_ies;
+ }
++
++
++const struct oper_class_map global_op_class[] = {
++ { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
++
++ /* Do not enable HT40 on 2.4 GHz for P2P use for now */
++ { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
++
++ { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
++
++ /*
++ * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
++ * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
++ * 80 MHz, but currently use the following definition for simplicity
++ * (these center frequencies are not actual channels, which makes
++ * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
++ * care of removing invalid channels.
++ */
++ { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
++ { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
++ { -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
++};
++
++
++static enum phy_type ieee80211_phy_type_by_freq(int freq)
++{
++ enum hostapd_hw_mode hw_mode;
++ u8 channel;
++
++ hw_mode = ieee80211_freq_to_chan(freq, &channel);
++
++ switch (hw_mode) {
++ case HOSTAPD_MODE_IEEE80211A:
++ return PHY_TYPE_OFDM;
++ case HOSTAPD_MODE_IEEE80211B:
++ return PHY_TYPE_HRDSSS;
++ case HOSTAPD_MODE_IEEE80211G:
++ return PHY_TYPE_ERP;
++ case HOSTAPD_MODE_IEEE80211AD:
++ return PHY_TYPE_DMG;
++ default:
++ return PHY_TYPE_UNSPECIFIED;
++ };
++}
++
++
++/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
++enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
++{
++ if (vht)
++ return PHY_TYPE_VHT;
++ if (ht)
++ return PHY_TYPE_HT;
++
++ return ieee80211_phy_type_by_freq(freq);
++}
++
++
++size_t global_op_class_size = ARRAY_SIZE(global_op_class);
++
++
++/**
++ * get_ie - Fetch a specified information element from IEs buffer
++ * @ies: Information elements buffer
++ * @len: Information elements buffer length
++ * @eid: Information element identifier (WLAN_EID_*)
++ * Returns: Pointer to the information element (id field) or %NULL if not found
++ *
++ * This function returns the first matching information element in the IEs
++ * buffer or %NULL in case the element is not found.
++ */
++const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
++{
++ const struct element *elem;
++
++ if (!ies)
++ return NULL;
++
++ for_each_element_id(elem, eid, ies, len)
++ return &elem->id;
++
++ return NULL;
++}
++
++
++/**
++ * get_ie_ext - Fetch a specified extended information element from IEs buffer
++ * @ies: Information elements buffer
++ * @len: Information elements buffer length
++ * @ext: Information element extension identifier (WLAN_EID_EXT_*)
++ * Returns: Pointer to the information element (id field) or %NULL if not found
++ *
++ * This function returns the first matching information element in the IEs
++ * buffer or %NULL in case the element is not found.
++ */
++const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
++{
++ const struct element *elem;
++
++ if (!ies)
++ return NULL;
++
++ for_each_element_extid(elem, ext, ies, len)
++ return &elem->id;
++
++ return NULL;
++}
++
++
++const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
++{
++ const struct element *elem;
++
++ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
++ if (elem->datalen >= 4 &&
++ vendor_type == WPA_GET_BE32(elem->data))
++ return &elem->id;
++ }
++
++ return NULL;
++}
++
++
++size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
++{
++ /*
++ * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
++ * OUI (3), OUI type (1).
++ */
++ if (len < 6 + attr_len) {
++ wpa_printf(MSG_DEBUG,
++ "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
++ len, attr_len);
++ return 0;
++ }
++
++ *buf++ = WLAN_EID_VENDOR_SPECIFIC;
++ *buf++ = attr_len + 4;
++ WPA_PUT_BE24(buf, OUI_WFA);
++ buf += 3;
++ *buf++ = MBO_OUI_TYPE;
++ os_memcpy(buf, attr, attr_len);
++
++ return 6 + attr_len;
++}
++
++
++size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
++{
++ u8 *pos = buf;
++
++ if (len < 9)
++ return 0;
++
++ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
++ *pos++ = 7; /* len */
++ WPA_PUT_BE24(pos, OUI_WFA);
++ pos += 3;
++ *pos++ = MULTI_AP_OUI_TYPE;
++ *pos++ = MULTI_AP_SUB_ELEM_TYPE;
++ *pos++ = 1; /* len */
++ *pos++ = value;
++
++ return pos - buf;
++}
++
++
++static const struct country_op_class us_op_class[] = {
++ { 1, 115 },
++ { 2, 118 },
++ { 3, 124 },
++ { 4, 121 },
++ { 5, 125 },
++ { 12, 81 },
++ { 22, 116 },
++ { 23, 119 },
++ { 24, 122 },
++ { 25, 126 },
++ { 26, 126 },
++ { 27, 117 },
++ { 28, 120 },
++ { 29, 123 },
++ { 30, 127 },
++ { 31, 127 },
++ { 32, 83 },
++ { 33, 84 },
++ { 34, 180 },
++};
++
++static const struct country_op_class eu_op_class[] = {
++ { 1, 115 },
++ { 2, 118 },
++ { 3, 121 },
++ { 4, 81 },
++ { 5, 116 },
++ { 6, 119 },
++ { 7, 122 },
++ { 8, 117 },
++ { 9, 120 },
++ { 10, 123 },
++ { 11, 83 },
++ { 12, 84 },
++ { 17, 125 },
++ { 18, 180 },
++};
++
++static const struct country_op_class jp_op_class[] = {
++ { 1, 115 },
++ { 30, 81 },
++ { 31, 82 },
++ { 32, 118 },
++ { 33, 118 },
++ { 34, 121 },
++ { 35, 121 },
++ { 36, 116 },
++ { 37, 119 },
++ { 38, 119 },
++ { 39, 122 },
++ { 40, 122 },
++ { 41, 117 },
++ { 42, 120 },
++ { 43, 120 },
++ { 44, 123 },
++ { 45, 123 },
++ { 56, 83 },
++ { 57, 84 },
++ { 58, 121 },
++ { 59, 180 },
++};
++
++static const struct country_op_class cn_op_class[] = {
++ { 1, 115 },
++ { 2, 118 },
++ { 3, 125 },
++ { 4, 116 },
++ { 5, 119 },
++ { 6, 126 },
++ { 7, 81 },
++ { 8, 83 },
++ { 9, 84 },
++};
++
++static u8
++global_op_class_from_country_array(u8 op_class, size_t array_size,
++ const struct country_op_class *country_array)
++{
++ size_t i;
++
++ for (i = 0; i < array_size; i++) {
++ if (country_array[i].country_op_class == op_class)
++ return country_array[i].global_op_class;
++ }
++
++ return 0;
++}
++
++
++u8 country_to_global_op_class(const char *country, u8 op_class)
++{
++ const struct country_op_class *country_array;
++ size_t size;
++ u8 g_op_class;
++
++ if (country_match(us_op_class_cc, country)) {
++ country_array = us_op_class;
++ size = ARRAY_SIZE(us_op_class);
++ } else if (country_match(eu_op_class_cc, country)) {
++ country_array = eu_op_class;
++ size = ARRAY_SIZE(eu_op_class);
++ } else if (country_match(jp_op_class_cc, country)) {
++ country_array = jp_op_class;
++ size = ARRAY_SIZE(jp_op_class);
++ } else if (country_match(cn_op_class_cc, country)) {
++ country_array = cn_op_class;
++ size = ARRAY_SIZE(cn_op_class);
++ } else {
++ /*
++ * Countries that do not match any of the above countries use
++ * global operating classes
++ */
++ return op_class;
++ }
++
++ g_op_class = global_op_class_from_country_array(op_class, size,
++ country_array);
++
++ /*
++ * If the given operating class did not match any of the country's
++ * operating classes, assume that global operating class is used.
++ */
++ return g_op_class ? g_op_class : op_class;
++}
++
++
++const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
++{
++ const struct oper_class_map *op;
++
++ if (country)
++ op_class = country_to_global_op_class(country, op_class);
++
++ op = &global_op_class[0];
++ while (op->op_class && op->op_class != op_class)
++ op++;
++
++ if (!op->op_class)
++ return NULL;
++
++ return op;
++}
++
++
++int oper_class_bw_to_int(const struct oper_class_map *map)
++{
++ switch (map->bw) {
++ case BW20:
++ return 20;
++ case BW40PLUS:
++ case BW40MINUS:
++ return 40;
++ case BW80:
++ return 80;
++ case BW80P80:
++ case BW160:
++ return 160;
++ case BW2160:
++ return 2160;
++ default:
++ return 0;
++ }
++}
++
++
++int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
++ size_t nei_rep_len)
++{
++ u8 *nei_pos = nei_rep;
++ const char *end;
++
++ /*
++ * BSS Transition Candidate List Entries - Neighbor Report elements
++ * neighbor=,,,
++ * ,[,]
++ */
++ while (pos) {
++ u8 *nei_start;
++ long int val;
++ char *endptr, *tmp;
++
++ pos = os_strstr(pos, " neighbor=");
++ if (!pos)
++ break;
++ if (nei_pos + 15 > nei_rep + nei_rep_len) {
++ wpa_printf(MSG_DEBUG,
++ "Not enough room for additional neighbor");
++ return -1;
++ }
++ pos += 10;
++
++ nei_start = nei_pos;
++ *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
++ nei_pos++; /* length to be filled in */
++
++ if (hwaddr_aton(pos, nei_pos)) {
++ wpa_printf(MSG_DEBUG, "Invalid BSSID");
++ return -1;
++ }
++ nei_pos += ETH_ALEN;
++ pos += 17;
++ if (*pos != ',') {
++ wpa_printf(MSG_DEBUG, "Missing BSSID Information");
++ return -1;
++ }
++ pos++;
++
++ val = strtol(pos, &endptr, 0);
++ WPA_PUT_LE32(nei_pos, val);
++ nei_pos += 4;
++ if (*endptr != ',') {
++ wpa_printf(MSG_DEBUG, "Missing Operating Class");
++ return -1;
++ }
++ pos = endptr + 1;
++
++ *nei_pos++ = atoi(pos); /* Operating Class */
++ pos = os_strchr(pos, ',');
++ if (pos == NULL) {
++ wpa_printf(MSG_DEBUG, "Missing Channel Number");
++ return -1;
++ }
++ pos++;
++
++ *nei_pos++ = atoi(pos); /* Channel Number */
++ pos = os_strchr(pos, ',');
++ if (pos == NULL) {
++ wpa_printf(MSG_DEBUG, "Missing PHY Type");
++ return -1;
++ }
++ pos++;
++
++ *nei_pos++ = atoi(pos); /* PHY Type */
++ end = os_strchr(pos, ' ');
++ tmp = os_strchr(pos, ',');
++ if (tmp && (!end || tmp < end)) {
++ /* Optional Subelements (hexdump) */
++ size_t len;
++
++ pos = tmp + 1;
++ end = os_strchr(pos, ' ');
++ if (end)
++ len = end - pos;
++ else
++ len = os_strlen(pos);
++ if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
++ wpa_printf(MSG_DEBUG,
++ "Not enough room for neighbor subelements");
++ return -1;
++ }
++ if (len & 0x01 ||
++ hexstr2bin(pos, nei_pos, len / 2) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "Invalid neighbor subelement info");
++ return -1;
++ }
++ nei_pos += len / 2;
++ pos = end;
++ }
++
++ nei_start[1] = nei_pos - nei_start - 2;
++ }
++
++ return nei_pos - nei_rep;
++}
++
++
++int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
++{
++ if (!ie || ie[1] <= capab / 8)
++ return 0;
++ return !!(ie[2 + capab / 8] & BIT(capab % 8));
++}
+--- contrib/wpa/src/common/ieee802_11_common.h.orig
++++ contrib/wpa/src/common/ieee802_11_common.h
+@@ -1,6 +1,6 @@
+ /*
+ * IEEE 802.11 Common routines
+- * Copyright (c) 2002-2012, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -9,6 +9,17 @@
+ #ifndef IEEE802_11_COMMON_H
+ #define IEEE802_11_COMMON_H
+
++#include "defs.h"
++#include "ieee802_11_defs.h"
++
++struct element {
++ u8 id;
++ u8 datalen;
++ u8 data[];
++} STRUCT_PACKED;
++
++struct hostapd_hw_modes;
++
+ #define MAX_NOF_MB_IES_SUPPORTED 5
+
+ struct mb_ies_info {
+@@ -56,9 +67,33 @@
+ const u8 *bss_max_idle_period;
+ const u8 *ssid_list;
+ const u8 *osen;
++ const u8 *mbo;
+ const u8 *ampe;
+ const u8 *mic;
+ const u8 *pref_freq_list;
++ const u8 *supp_op_classes;
++ const u8 *rrm_enabled;
++ const u8 *cag_number;
++ const u8 *ap_csn;
++ const u8 *fils_indic;
++ const u8 *dils;
++ const u8 *assoc_delay_info;
++ const u8 *fils_req_params;
++ const u8 *fils_key_confirm;
++ const u8 *fils_session;
++ const u8 *fils_hlp;
++ const u8 *fils_ip_addr_assign;
++ const u8 *key_delivery;
++ const u8 *fils_wrapped_data;
++ const u8 *fils_pk;
++ const u8 *fils_nonce;
++ const u8 *owe_dh;
++ const u8 *power_capab;
++ const u8 *roaming_cons_sel;
++ const u8 *password_id;
++ const u8 *oci;
++ const u8 *multi_ap;
++ const u8 *he_capabilities;
+
+ u8 ssid_len;
+ u8 supp_rates_len;
+@@ -85,9 +120,30 @@
+ u8 ext_capab_len;
+ u8 ssid_list_len;
+ u8 osen_len;
++ u8 mbo_len;
+ u8 ampe_len;
+ u8 mic_len;
+ u8 pref_freq_list_len;
++ u8 supp_op_classes_len;
++ u8 rrm_enabled_len;
++ u8 cag_number_len;
++ u8 fils_indic_len;
++ u8 dils_len;
++ u8 fils_req_params_len;
++ u8 fils_key_confirm_len;
++ u8 fils_hlp_len;
++ u8 fils_ip_addr_assign_len;
++ u8 key_delivery_len;
++ u8 fils_wrapped_data_len;
++ u8 fils_pk_len;
++ u8 owe_dh_len;
++ u8 power_capab_len;
++ u8 roaming_cons_sel_len;
++ u8 password_id_len;
++ u8 oci_len;
++ u8 multi_ap_len;
++ u8 he_capabilities_len;
++
+ struct mb_ies_info mb_ies;
+ };
+
+@@ -117,7 +173,11 @@
+ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel);
+-int ieee80211_is_dfs(int freq);
++int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
++ int sec_channel, u8 *op_class, u8 *channel);
++int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
++ u16 num_modes);
++enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
+
+ int supp_rates_11b_only(struct ieee802_11_elems *elems);
+ int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+@@ -125,4 +185,88 @@
+ struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
+
+ const char * fc2str(u16 fc);
++
++struct oper_class_map {
++ enum hostapd_hw_mode mode;
++ u8 op_class;
++ u8 min_chan;
++ u8 max_chan;
++ u8 inc;
++ enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
++ enum { P2P_SUPP, NO_P2P_SUPP } p2p;
++};
++
++extern const struct oper_class_map global_op_class[];
++extern size_t global_op_class_size;
++
++const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
++const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
++const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
++
++size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
++
++size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
++
++struct country_op_class {
++ u8 country_op_class;
++ u8 global_op_class;
++};
++
++u8 country_to_global_op_class(const char *country, u8 op_class);
++
++const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
++int oper_class_bw_to_int(const struct oper_class_map *map);
++
++int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
++ size_t nei_rep_len);
++
++int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
++
++/* element iteration helpers */
++#define for_each_element(_elem, _data, _datalen) \
++ for (_elem = (const struct element *) (_data); \
++ (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
++ (int) sizeof(*_elem) && \
++ (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
++ (int) sizeof(*_elem) + _elem->datalen; \
++ _elem = (const struct element *) (_elem->data + _elem->datalen))
++
++#define for_each_element_id(element, _id, data, datalen) \
++ for_each_element(element, data, datalen) \
++ if (element->id == (_id))
++
++#define for_each_element_extid(element, extid, _data, _datalen) \
++ for_each_element(element, _data, _datalen) \
++ if (element->id == WLAN_EID_EXTENSION && \
++ element->datalen > 0 && \
++ element->data[0] == (extid))
++
++#define for_each_subelement(sub, element) \
++ for_each_element(sub, (element)->data, (element)->datalen)
++
++#define for_each_subelement_id(sub, id, element) \
++ for_each_element_id(sub, id, (element)->data, (element)->datalen)
++
++#define for_each_subelement_extid(sub, extid, element) \
++ for_each_element_extid(sub, extid, (element)->data, (element)->datalen)
++
++/**
++ * for_each_element_completed - Determine if element parsing consumed all data
++ * @element: Element pointer after for_each_element() or friends
++ * @data: Same data pointer as passed to for_each_element() or friends
++ * @datalen: Same data length as passed to for_each_element() or friends
++ *
++ * This function returns 1 if all the data was parsed or considered
++ * while walking the elements. Only use this if your for_each_element()
++ * loop cannot be broken out of, otherwise it always returns 0.
++ *
++ * If some data was malformed, this returns %false since the last parsed
++ * element will not fill the whole remaining data.
++ */
++static inline int for_each_element_completed(const struct element *element,
++ const void *data, size_t datalen)
++{
++ return (const u8 *) element == (const u8 *) data + datalen;
++}
++
+ #endif /* IEEE802_11_COMMON_H */
+--- contrib/wpa/src/common/ieee802_11_defs.h.orig
++++ contrib/wpa/src/common/ieee802_11_defs.h
+@@ -1,6 +1,6 @@
+ /*
+ * IEEE 802.11 Frame type definitions
+- * Copyright (c) 2002-2015, Jouni Malinen
++ * Copyright (c) 2002-2019, Jouni Malinen
+ * Copyright (c) 2007-2008 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+@@ -81,6 +81,9 @@
+ #define WLAN_AUTH_SHARED_KEY 1
+ #define WLAN_AUTH_FT 2
+ #define WLAN_AUTH_SAE 3
++#define WLAN_AUTH_FILS_SK 4
++#define WLAN_AUTH_FILS_SK_PFS 5
++#define WLAN_AUTH_FILS_PK 6
+ #define WLAN_AUTH_LEAP 128
+
+ #define WLAN_AUTH_CHALLENGE_LEN 128
+@@ -94,10 +97,15 @@
+ #define WLAN_CAPABILITY_PBCC BIT(6)
+ #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
+ #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
++#define WLAN_CAPABILITY_QOS BIT(9)
+ #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
++#define WLAN_CAPABILITY_APSD BIT(11)
++#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12)
+ #define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
++#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
++#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
+
+-/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
++/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
+ #define WLAN_STATUS_SUCCESS 0
+ #define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+ #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
+@@ -114,27 +122,23 @@
+ #define WLAN_STATUS_AUTH_TIMEOUT 16
+ #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+ #define WLAN_STATUS_ASSOC_DENIED_RATES 18
+-/* IEEE 802.11b */
+ #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+-#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+-#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+-/* IEEE 802.11h */
+ #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
+ #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
+ #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
+-/* IEEE 802.11g */
+ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
+-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
+ #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
+ #define WLAN_STATUS_R0KH_UNREACHABLE 28
+ #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
+-/* IEEE 802.11w */
+ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
+ #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
+ #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
++#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33
++#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34
++#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35
+ #define WLAN_STATUS_REQUEST_DECLINED 37
+ #define WLAN_STATUS_INVALID_PARAMETERS 38
+-/* IEEE 802.11i */
++#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
+ #define WLAN_STATUS_INVALID_IE 40
+ #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+ #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+@@ -147,11 +151,13 @@
+ #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
+ #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
+ #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
+-/* IEEE 802.11r */
+ #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
+ #define WLAN_STATUS_INVALID_PMKID 53
+ #define WLAN_STATUS_INVALID_MDIE 54
+ #define WLAN_STATUS_INVALID_FTIE 55
++#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56
++#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
++#define WLAN_STATUS_TRY_ANOTHER_BSS 58
+ #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+ #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+ #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+@@ -162,16 +168,44 @@
+ #define WLAN_STATUS_REQ_REFUSED_SSPN 67
+ #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+ #define WLAN_STATUS_INVALID_RSNIE 72
++#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73
++#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74
++#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75
+ #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
+ #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
++#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78
+ #define WLAN_STATUS_TRANSMISSION_FAILURE 79
++#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80
++#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81
+ #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
++#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83
++#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84
++#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85
+ #define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
++#define WLAN_STATUS_PERFORMING_FST_NOW 87
++#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88
++#define WLAN_STATUS_REJECT_U_PID_SETTING 89
++#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92
++#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93
++#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94
+ #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
++#define WLAN_STATUS_REJECT_DSE_BAND 96
++#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97
++#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98
+ #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
++#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100
++#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101
++#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102
++#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103
+ #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
++#define WLAN_STATUS_ENABLEMENT_DENIED 105
++#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106
++#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
++#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
++#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
++#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+
+-/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
++/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
+ #define WLAN_REASON_UNSPECIFIED 1
+ #define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+ #define WLAN_REASON_DEAUTH_LEAVING 3
+@@ -181,10 +215,9 @@
+ #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+ #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+ #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+-/* IEEE 802.11h */
+ #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
+ #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
+-/* IEEE 802.11i */
++#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12
+ #define WLAN_REASON_INVALID_IE 13
+ #define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+ #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+@@ -199,9 +232,26 @@
+ #define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+ #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
+ #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
+-/* IEEE 802.11e */
++#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27
++#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28
++#define WLAN_REASON_BAD_CIPHER_OR_AKM 29
++#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30
++#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31
++#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32
++#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33
+ #define WLAN_REASON_DISASSOC_LOW_ACK 34
+-/* IEEE 802.11s */
++#define WLAN_REASON_EXCEEDED_TXOP 35
++#define WLAN_REASON_STA_LEAVING 36
++#define WLAN_REASON_END_TS_BA_DLS 37
++#define WLAN_REASON_UNKNOWN_TS_BA 38
++#define WLAN_REASON_TIMEOUT 39
++#define WLAN_REASON_PEERKEY_MISMATCH 45
++#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46
++#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47
++#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48
++#define WLAN_REASON_INVALID_PMKID 49
++#define WLAN_REASON_INVALID_MDE 50
++#define WLAN_REASON_INVALID_FTE 51
+ #define WLAN_REASON_MESH_PEERING_CANCELLED 52
+ #define WLAN_REASON_MESH_MAX_PEERS 53
+ #define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
+@@ -211,20 +261,29 @@
+ #define WLAN_REASON_MESH_INVALID_GTK 58
+ #define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
+ #define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
++#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61
++#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62
++#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63
++#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64
++#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65
++#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66
+
+
+-/* Information Element IDs */
++/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
+ #define WLAN_EID_SSID 0
+ #define WLAN_EID_SUPP_RATES 1
+-#define WLAN_EID_FH_PARAMS 2
+ #define WLAN_EID_DS_PARAMS 3
+ #define WLAN_EID_CF_PARAMS 4
+ #define WLAN_EID_TIM 5
+ #define WLAN_EID_IBSS_PARAMS 6
+ #define WLAN_EID_COUNTRY 7
++#define WLAN_EID_REQUEST 10
+ #define WLAN_EID_BSS_LOAD 11
++#define WLAN_EID_EDCA_PARAM_SET 12
++#define WLAN_EID_TSPEC 13
++#define WLAN_EID_TCLAS 14
++#define WLAN_EID_SCHEDULE 15
+ #define WLAN_EID_CHALLENGE 16
+-/* EIDs defined by IEEE 802.11h - START */
+ #define WLAN_EID_PWR_CONSTRAINT 32
+ #define WLAN_EID_PWR_CAPABILITY 33
+ #define WLAN_EID_TPC_REQUEST 34
+@@ -233,49 +292,139 @@
+ #define WLAN_EID_CHANNEL_SWITCH 37
+ #define WLAN_EID_MEASURE_REQUEST 38
+ #define WLAN_EID_MEASURE_REPORT 39
+-#define WLAN_EID_QUITE 40
++#define WLAN_EID_QUIET 40
+ #define WLAN_EID_IBSS_DFS 41
+-/* EIDs defined by IEEE 802.11h - END */
+ #define WLAN_EID_ERP_INFO 42
++#define WLAN_EID_TS_DELAY 43
++#define WLAN_EID_TCLAS_PROCESSING 44
+ #define WLAN_EID_HT_CAP 45
+ #define WLAN_EID_QOS 46
+ #define WLAN_EID_RSN 48
+ #define WLAN_EID_EXT_SUPP_RATES 50
++#define WLAN_EID_AP_CHANNEL_REPORT 51
+ #define WLAN_EID_NEIGHBOR_REPORT 52
++#define WLAN_EID_RCPI 53
+ #define WLAN_EID_MOBILITY_DOMAIN 54
+ #define WLAN_EID_FAST_BSS_TRANSITION 55
+ #define WLAN_EID_TIMEOUT_INTERVAL 56
+ #define WLAN_EID_RIC_DATA 57
++#define WLAN_EID_DSE_REGISTERED_LOCATION 58
+ #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
++#define WLAN_EID_EXT_CHANSWITCH_ANN 60
+ #define WLAN_EID_HT_OPERATION 61
+ #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+-#define WLAN_EID_WAPI 68
++#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63
++#define WLAN_EID_ANTENNA 64
++#define WLAN_EID_RSNI 65
++#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66
++#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67
++#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */
+ #define WLAN_EID_TIME_ADVERTISEMENT 69
+ #define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
++#define WLAN_EID_MULTIPLE_BSSID 71
+ #define WLAN_EID_20_40_BSS_COEXISTENCE 72
+ #define WLAN_EID_20_40_BSS_INTOLERANT 73
+ #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
++#define WLAN_EID_RIC_DESCRIPTOR 75
+ #define WLAN_EID_MMIE 76
++#define WLAN_EID_EVENT_REQUEST 78
++#define WLAN_EID_EVENT_REPORT 79
++#define WLAN_EID_DIAGNOSTIC_REQUEST 80
++#define WLAN_EID_DIAGNOSTIC_REPORT 81
++#define WLAN_EID_LOCATION_PARAMETERS 82
++#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
+ #define WLAN_EID_SSID_LIST 84
++#define WLAN_EID_MULTIPLE_BSSID_INDEX 85
++#define WLAN_EID_FMS_DESCRIPTOR 86
++#define WLAN_EID_FMS_REQUEST 87
++#define WLAN_EID_FMS_RESPONSE 88
++#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89
+ #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+ #define WLAN_EID_TFS_REQ 91
+ #define WLAN_EID_TFS_RESP 92
+ #define WLAN_EID_WNMSLEEP 93
++#define WLAN_EID_TIM_BROADCAST_REQUEST 94
++#define WLAN_EID_TIM_BROADCAST_RESPONSE 95
++#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96
++#define WLAN_EID_CHANNEL_USAGE 97
+ #define WLAN_EID_TIME_ZONE 98
++#define WLAN_EID_DMS_REQUEST 99
++#define WLAN_EID_DMS_RESPONSE 100
+ #define WLAN_EID_LINK_ID 101
++#define WLAN_EID_WAKEUP_SCHEDULE 102
++#define WLAN_EID_CHANNEL_SWITCH_TIMING 104
++#define WLAN_EID_PTI_CONTROL 105
++#define WLAN_EID_TPU_BUFFER_STATUS 106
+ #define WLAN_EID_INTERWORKING 107
+ #define WLAN_EID_ADV_PROTO 108
++#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109
+ #define WLAN_EID_QOS_MAP_SET 110
+ #define WLAN_EID_ROAMING_CONSORTIUM 111
++#define WLAN_EID_EMERGENCY_ALERT_ID 112
+ #define WLAN_EID_MESH_CONFIG 113
+ #define WLAN_EID_MESH_ID 114
++#define WLAN_EID_MESH_LINK_METRIC_REPORT 115
++#define WLAN_EID_CONGESTION_NOTIFICATION 116
+ #define WLAN_EID_PEER_MGMT 117
++#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118
++#define WLAN_EID_MESH_AWAKE_WINDOW 119
++#define WLAN_EID_BEACON_TIMING 120
++#define WLAN_EID_MCCAOP_SETUP_REQUEST 121
++#define WLAN_EID_MCCAOP_SETUP_REPLY 122
++#define WLAN_EID_MCCAOP_ADVERTISEMENT 123
++#define WLAN_EID_MCCAOP_TEARDOWN 124
++#define WLAN_EID_GANN 125
++#define WLAN_EID_RANN 126
+ #define WLAN_EID_EXT_CAPAB 127
++#define WLAN_EID_PREQ 130
++#define WLAN_EID_PREP 131
++#define WLAN_EID_PERR 132
++#define WLAN_EID_PXU 137
++#define WLAN_EID_PXUC 138
+ #define WLAN_EID_AMPE 139
+ #define WLAN_EID_MIC 140
++#define WLAN_EID_DESTINATION_URI 141
++#define WLAN_EID_U_APSD_COEX 142
++#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143
++#define WLAN_EID_EXTENDED_SCHEDULE 144
++#define WLAN_EID_STA_AVAILABILITY 145
++#define WLAN_EID_DMG_TSPEC 146
++#define WLAN_EID_NEXT_DMG_ATI 147
++#define WLAN_EID_DMG_CAPABILITIES 148
++#define WLAN_EID_DMG_OPERATION 151
++#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152
++#define WLAN_EID_DMG_BEAM_REFINEMENT 153
++#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154
+ #define WLAN_EID_CCKM 156
++#define WLAN_EID_AWAKE_WINDOW 157
+ #define WLAN_EID_MULTI_BAND 158
++#define WLAN_EID_ADDBA_EXTENSION 159
++#define WLAN_EID_NEXTPCP_LIST 160
++#define WLAN_EID_PCP_HANDOVER 161
++#define WLAN_EID_DMG_LINK_MARGIN 162
++#define WLAN_EID_SWITCHING_STREAM 163
+ #define WLAN_EID_SESSION_TRANSITION 164
++#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165
++#define WLAN_EID_CLUSTER_REPORT 166
++#define WLAN_EID_REPLAY_CAPABILITIES 167
++#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168
++#define WLAN_EID_BEAMLINK_MAINTENANCE 169
++#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170
++#define WLAN_EID_U_PID 171
++#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172
++#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174
++#define WLAN_EID_QUIET_PERIOD_REQUEST 175
++#define WLAN_EID_QUIET_PERIOD_RESPONSE 177
++#define WLAN_EID_QMF_POLICY 181
++#define WLAN_EID_ECAPC_POLICY 182
++#define WLAN_EID_CLUSTER_TIME_OFFSET 183
++#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184
++#define WLAN_EID_SCS_DESCRIPTOR 185
++#define WLAN_EID_QLOAD_REPORT 186
++#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187
++#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188
++#define WLAN_EID_GCR_GROUP_ADDRESS 189
++#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190
+ #define WLAN_EID_VHT_CAP 191
+ #define WLAN_EID_VHT_OPERATION 192
+ #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+@@ -285,10 +434,124 @@
+ #define WLAN_EID_VHT_AID 197
+ #define WLAN_EID_VHT_QUIET_CHANNEL 198
+ #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
++#define WLAN_EID_UPSIM 200
++#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201
++#define WLAN_EID_TVHT_OPERATION 202
++#define WLAN_EID_DEVICE_LOCATION 204
++#define WLAN_EID_WHITE_SPACE_MAP 205
++#define WLAN_EID_FTM_PARAMETERS 206
+ #define WLAN_EID_VENDOR_SPECIFIC 221
++#define WLAN_EID_CAG_NUMBER 237
++#define WLAN_EID_AP_CSN 239
++#define WLAN_EID_FILS_INDICATION 240
++#define WLAN_EID_DILS 241
++#define WLAN_EID_FRAGMENT 242
++#define WLAN_EID_EXTENSION 255
+
++/* Element ID Extension (EID 255) values */
++#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1
++#define WLAN_EID_EXT_FILS_REQ_PARAMS 2
++#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3
++#define WLAN_EID_EXT_FILS_SESSION 4
++#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
++#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
++#define WLAN_EID_EXT_KEY_DELIVERY 7
++#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8
++#define WLAN_EID_EXT_FTM_SYNC_INFO 9
++#define WLAN_EID_EXT_EXTENDED_REQUEST 10
++#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
++#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
++#define WLAN_EID_EXT_FILS_NONCE 13
++#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
++#define WLAN_EID_EXT_OWE_DH_PARAM 32
++#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
++#define WLAN_EID_EXT_HE_CAPABILITIES 35
++#define WLAN_EID_EXT_HE_OPERATION 36
++#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
++#define WLAN_EID_EXT_OCV_OCI 54
+
+-/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
++/* Extended Capabilities field */
++#define WLAN_EXT_CAPAB_20_40_COEX 0
++#define WLAN_EXT_CAPAB_GLK 1
++#define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2
++#define WLAN_EXT_CAPAB_GLK_GCR 3
++#define WLAN_EXT_CAPAB_PSMP 4
++/* 5 - Reserved */
++#define WLAN_EXT_CAPAB_S_PSMP 6
++#define WLAN_EXT_CAPAB_EVENT 7
++#define WLAN_EXT_CAPAB_DIAGNOSTICS 8
++#define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9
++#define WLAN_EXT_CAPAB_LOCATION_TRACKING 10
++#define WLAN_EXT_CAPAB_FMS 11
++#define WLAN_EXT_CAPAB_PROXY_ARP 12
++#define WLAN_EXT_CAPAB_COLL_INTERF_REP 13
++#define WLAN_EXT_CAPAB_CIVIC_LOCATION 14
++#define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15
++#define WLAN_EXT_CAPAB_TFS 16
++#define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17
++#define WLAN_EXT_CAPAB_TIM_BROADCAST 18
++#define WLAN_EXT_CAPAB_BSS_TRANSITION 19
++#define WLAN_EXT_CAPAB_QOS_TRAFFIC 20
++#define WLAN_EXT_CAPAB_AC_STA_COUNT 21
++#define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22
++#define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23
++#define WLAN_EXT_CAPAB_CHANNEL_USAGE 24
++#define WLAN_EXT_CAPAB_SSID_LIST 25
++#define WLAN_EXT_CAPAB_DMS 26
++#define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27
++#define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28
++#define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29
++#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30
++#define WLAN_EXT_CAPAB_INTERWORKING 31
++#define WLAN_EXT_CAPAB_QOS_MAP 32
++#define WLAN_EXT_CAPAB_EBR 33
++#define WLAN_EXT_CAPAB_SSPN_INTERFACE 34
++/* 35 - Reserved */
++#define WLAN_EXT_CAPAB_MSGCF 36
++#define WLAN_EXT_CAPAB_TDLS 37
++#define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38
++#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39
++#define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40
++#define WLAN_EXT_CAPAB_
++/* 41-43 - Service Interval Granularity */
++#define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44
++#define WLAN_EXT_CAPAB_U_APSD_COEX 45
++#define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46
++#define WLAN_EXT_CAPAB_QAB 47
++#define WLAN_EXT_CAPAB_UTF_8_SSID 48
++#define WLAN_EXT_CAPAB_QMF 49
++#define WLAN_EXT_CAPAB_QMF_RECONFIG 50
++#define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51
++#define WLAN_EXT_CAPAB_ADVANCED_GCR 52
++#define WLAN_EXT_CAPAB_MESH_GCR 53
++#define WLAN_EXT_CAPAB_SCS 54
++#define WLAN_EXT_CAPAB_QLOAD_REPORT 55
++#define WLAN_EXT_CAPAB_ALT_EDCA 56
++#define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57
++#define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58
++/* 59 - Reserved */
++#define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60
++#define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61
++#define WLAN_EXT_CAPAB_OPMODE_NOTIF 62
++#define WLAN_EXT_CAPAB_
++/* 63-64 - Max Number of MSDUs In A-MSDU */
++#define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65
++#define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66
++#define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67
++#define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68
++#define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69
++#define WLAN_EXT_CAPAB_FTM_RESPONDER 70
++#define WLAN_EXT_CAPAB_FTM_INITIATOR 71
++#define WLAN_EXT_CAPAB_FILS 72
++#define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73
++#define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74
++#define WLAN_EXT_CAPAB_PAD 75
++/* 76-79 - Reserved */
++#define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80
++#define WLAN_EXT_CAPAB_SAE_PW_ID 81
++#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
++
++/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
+ #define WLAN_ACTION_SPECTRUM_MGMT 0
+ #define WLAN_ACTION_QOS 1
+ #define WLAN_ACTION_DLS 2
+@@ -302,13 +565,30 @@
+ #define WLAN_ACTION_WNM 10
+ #define WLAN_ACTION_UNPROTECTED_WNM 11
+ #define WLAN_ACTION_TDLS 12
++#define WLAN_ACTION_MESH 13
++#define WLAN_ACTION_MULTIHOP 14
+ #define WLAN_ACTION_SELF_PROTECTED 15
++#define WLAN_ACTION_DMG 16
+ #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+ #define WLAN_ACTION_FST 18
++#define WLAN_ACTION_ROBUST_AV_STREAMING 19
++#define WLAN_ACTION_UNPROTECTED_DMG 20
++#define WLAN_ACTION_VHT 21
++#define WLAN_ACTION_FILS 26
++#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
+ #define WLAN_ACTION_VENDOR_SPECIFIC 127
++/* Note: 128-255 used to report errors by setting category | 0x80 */
+
+-/* Public action codes */
++/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
+ #define WLAN_PA_20_40_BSS_COEX 0
++#define WLAN_PA_DSE_ENABLEMENT 1
++#define WLAN_PA_DSE_DEENABLEMENT 2
++#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3
++#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4
++#define WLAN_PA_DSE_MEASUREMENT_REQ 5
++#define WLAN_PA_DSE_MEASUREMENT_RESP 6
++#define WLAN_PA_MEASUREMENT_PILOT 7
++#define WLAN_PA_DSE_POWER_CONSTRAINT 8
+ #define WLAN_PA_VENDOR_SPECIFIC 9
+ #define WLAN_PA_GAS_INITIAL_REQ 10
+ #define WLAN_PA_GAS_INITIAL_RESP 11
+@@ -315,8 +595,29 @@
+ #define WLAN_PA_GAS_COMEBACK_REQ 12
+ #define WLAN_PA_GAS_COMEBACK_RESP 13
+ #define WLAN_TDLS_DISCOVERY_RESPONSE 14
++#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15
++#define WLAN_PA_QAB_REQUEST_FRAME 16
++#define WLAN_PA_QAB_RESPONSE_FRAME 17
++#define WLAN_PA_QMF_POLICY 18
++#define WLAN_PA_QMF_POLICY_CHANGE 19
++#define WLAN_PA_QLOAD_REQUEST 20
++#define WLAN_PA_QLOAD_REPORT 21
++#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22
++#define WLAN_PA_HCCA_TXOP_RESPONSE 23
++#define WLAN_PA_PUBLIC_KEY 24
++#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25
++#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26
++#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27
++#define WLAN_PA_GDD_ENABLEMENT_REQ 28
++#define WLAN_PA_GDD_ENABLEMENT_RESP 29
++#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30
++#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31
++#define WLAN_PA_FTM_REQUEST 32
++#define WLAN_PA_FTM 33
++#define WLAN_PA_FILS_DISCOVERY 34
+
+-/* Protected Dual of Public Action frames */
++/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
++ * Table 9-332) */
+ #define WLAN_PROT_DSE_ENABLEMENT 1
+ #define WLAN_PROT_DSE_DEENABLEMENT 2
+ #define WLAN_PROT_EXT_CSA 4
+@@ -328,6 +629,21 @@
+ #define WLAN_PROT_GAS_INITIAL_RESP 11
+ #define WLAN_PROT_GAS_COMEBACK_REQ 12
+ #define WLAN_PROT_GAS_COMEBACK_RESP 13
++#define WLAN_PROT_QAB_REQUEST_FRAME 16
++#define WLAN_PROT_QAB_RESPONSE_FRAME 17
++#define WLAN_PROT_QMF_POLICY 18
++#define WLAN_PROT_QMF_POLICY_CHANGE 19
++#define WLAN_PROT_QLOAD_REQUEST 20
++#define WLAN_PROT_QLOAD_REPORT 21
++#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22
++#define WLAN_PROT_HCCA_TXOP_RESPONSE 23
++#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25
++#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26
++#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27
++#define WLAN_PROT_GDD_ENABLEMENT_REQ 28
++#define WLAN_PROT_GDD_ENABLEMENT_RESP 29
++#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30
++#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31
+
+ /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
+ #define WLAN_SA_QUERY_REQUEST 0
+@@ -356,11 +672,25 @@
+ #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+ #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+-/* Radio Measurement capabilities (from RRM Capabilities IE) */
++/* Radio Measurement capabilities (from RM Enabled Capabilities element)
++ * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
+ /* byte 1 (out of 5) */
+ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
+ #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
++#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4)
++#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5)
++#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6)
++/* byte 2 (out of 5) */
++#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
++/* byte 5 (out of 5) */
++#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2)
+
++/*
++ * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range
++ * request) - Minimum AP count
++ */
++#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15
++
+ /* Timeout Interval Type */
+ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
+ #define WLAN_TIMEOUT_KEY_LIFETIME 2
+@@ -382,16 +712,18 @@
+ #define INTERWORKING_ANT_TEST 6
+ #define INTERWORKING_ANT_WILDCARD 15
+
+-/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
++/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */
+ enum adv_proto_id {
+ ACCESS_NETWORK_QUERY_PROTOCOL = 0,
+ MIH_INFO_SERVICE = 1,
+ MIH_CMD_AND_EVENT_DISCOVERY = 2,
+ EMERGENCY_ALERT_SYSTEM = 3,
++ REGISTERED_LOCATION_QUERY_PROTO = 4,
+ ADV_PROTO_VENDOR_SPECIFIC = 221
+ };
+
+-/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
++/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016,
++ * Table 9-271; P802.11ai) */
+ enum anqp_info_id {
+ ANQP_QUERY_LIST = 256,
+ ANQP_CAPABILITY_LIST = 257,
+@@ -407,7 +739,17 @@
+ ANQP_AP_LOCATION_PUBLIC_URI = 267,
+ ANQP_DOMAIN_NAME = 268,
+ ANQP_EMERGENCY_ALERT_URI = 269,
++ ANQP_TDLS_CAPABILITY = 270,
+ ANQP_EMERGENCY_NAI = 271,
++ ANQP_NEIGHBOR_REPORT = 272,
++ ANQP_QUERY_AP_LIST = 273,
++ ANQP_AP_LIST_RESPONSE = 274,
++ ANQP_FILS_REALM_INFO = 275,
++ ANQP_CAG = 276,
++ ANQP_VENUE_URL = 277,
++ ANQP_ADVICE_OF_CHARGE = 278,
++ ANQP_LOCAL_CONTENT = 279,
++ ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280,
+ ANQP_VENDOR_SPECIFIC = 56797
+ };
+
+@@ -442,6 +784,53 @@
+ NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
+ };
+
++/*
++ * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for
++ * measurement requests
++ */
++enum measure_type {
++ MEASURE_TYPE_BASIC = 0,
++ MEASURE_TYPE_CCA = 1,
++ MEASURE_TYPE_RPI_HIST = 2,
++ MEASURE_TYPE_CHANNEL_LOAD = 3,
++ MEASURE_TYPE_NOISE_HIST = 4,
++ MEASURE_TYPE_BEACON = 5,
++ MEASURE_TYPE_FRAME = 6,
++ MEASURE_TYPE_STA_STATISTICS = 7,
++ MEASURE_TYPE_LCI = 8,
++ MEASURE_TYPE_TRANSMIT_STREAM = 9,
++ MEASURE_TYPE_MULTICAST_DIAG = 10,
++ MEASURE_TYPE_LOCATION_CIVIC = 11,
++ MEASURE_TYPE_LOCATION_ID = 12,
++ MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13,
++ MEASURE_TYPE_DIRECTIONAL_MEASURE = 14,
++ MEASURE_TYPE_DIRECTIONAL_STATS = 15,
++ MEASURE_TYPE_FTM_RANGE = 16,
++ MEASURE_TYPE_MEASURE_PAUSE = 255,
++};
++
++/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */
++enum location_subject {
++ LOCATION_SUBJECT_LOCAL = 0,
++ LOCATION_SUBJECT_REMOTE = 1,
++ LOCATION_SUBJECT_3RD_PARTY = 2,
++};
++
++/*
++ * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request
++ */
++enum lci_req_subelem {
++ LCI_REQ_SUBELEM_AZIMUTH_REQ = 1,
++ LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2,
++ LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3,
++ LCI_REQ_SUBELEM_MAX_AGE = 4,
++};
++
++#define FILS_NONCE_LEN 16
++#define FILS_SESSION_LEN 8
++#define FILS_CACHE_ID_LEN 2
++#define FILS_MAX_KEY_AUTH_LEN 48
++
+ #ifdef _MSC_VER
+ #pragma pack(push, 1)
+ #endif /* _MSC_VER */
+@@ -516,11 +905,8 @@
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[];
+ } STRUCT_PACKED beacon;
++ /* probe_req: only variable items: SSID, Supported rates */
+ struct {
+- /* only variable items: SSID, Supported rates */
+- u8 variable[0];
+- } STRUCT_PACKED probe_req;
+- struct {
+ u8 timestamp[8];
+ le16 beacon_int;
+ le16 capab_info;
+@@ -561,10 +947,12 @@
+ struct {
+ u8 action;
+ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
++ u8 variable[]; /* OCI element */
+ } STRUCT_PACKED sa_query_req;
+ struct {
+ u8 action; /* */
+ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
++ u8 variable[]; /* OCI element */
+ } STRUCT_PACKED sa_query_resp;
+ struct {
+ u8 action;
+@@ -618,6 +1006,16 @@
+ u8 variable[];
+ } STRUCT_PACKED bss_tm_query;
+ struct {
++ u8 action; /* 11 */
++ u8 dialog_token;
++ u8 req_info;
++ } STRUCT_PACKED coloc_intf_req;
++ struct {
++ u8 action; /* 12 */
++ u8 dialog_token;
++ u8 variable[];
++ } STRUCT_PACKED coloc_intf_report;
++ struct {
+ u8 action; /* 15 */
+ u8 variable[];
+ } STRUCT_PACKED slf_prot_action;
+@@ -625,6 +1023,11 @@
+ u8 action;
+ u8 variable[];
+ } STRUCT_PACKED fst_action;
++ struct {
++ u8 action;
++ u8 dialog_token;
++ u8 variable[];
++ } STRUCT_PACKED rrm;
+ } u;
+ } STRUCT_PACKED action;
+ } u;
+@@ -631,6 +1034,8 @@
+ } STRUCT_PACKED;
+
+
++#define IEEE80211_MAX_MMPDU_SIZE 2304
++
+ /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
+ #define IEEE80211_HT_MCS_MASK_LEN 10
+
+@@ -690,9 +1095,14 @@
+ u8 selected_pairwise_suite[4];
+ u8 local_nonce[32];
+ u8 peer_nonce[32];
+- u8 mgtk[16];
+- u8 key_rsc[8];
+- u8 key_expiration[4];
++ /* Followed by
++ * Key Replay Counter[8] (optional)
++ * (only in Mesh Group Key Inform/Acknowledge frames)
++ * GTKdata[variable] (optional)
++ * (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4])
++ * IGTKdata[variable] (optional)
++ * (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format)
++ */
+ } STRUCT_PACKED;
+
+ #ifdef _MSC_VER
+@@ -815,6 +1225,7 @@
+ #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2))
+ #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3))
+ #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3))
++#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2
+ #define VHT_CAP_RXLDPC ((u32) BIT(4))
+ #define VHT_CAP_SHORT_GI_80 ((u32) BIT(5))
+ #define VHT_CAP_SHORT_GI_160 ((u32) BIT(6))
+@@ -879,7 +1290,18 @@
+ #define WFD_OUI_TYPE 10
+ #define HS20_IE_VENDOR_TYPE 0x506f9a10
+ #define OSEN_IE_VENDOR_TYPE 0x506f9a12
++#define MBO_IE_VENDOR_TYPE 0x506f9a16
++#define MBO_OUI_TYPE 22
++#define OWE_IE_VENDOR_TYPE 0x506f9a1c
++#define OWE_OUI_TYPE 28
++#define MULTI_AP_OUI_TYPE 0x1B
+
++#define MULTI_AP_SUB_ELEM_TYPE 0x06
++#define MULTI_AP_TEAR_DOWN BIT(4)
++#define MULTI_AP_FRONTHAUL_BSS BIT(5)
++#define MULTI_AP_BACKHAUL_BSS BIT(6)
++#define MULTI_AP_BACKHAUL_STA BIT(7)
++
+ #define WMM_OUI_TYPE 2
+ #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+ #define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+@@ -998,6 +1420,7 @@
+ #define HS20_INDICATION_OUI_TYPE 16
+ #define HS20_ANQP_OUI_TYPE 17
+ #define HS20_OSEN_OUI_TYPE 18
++#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
+ #define HS20_STYPE_QUERY_LIST 1
+ #define HS20_STYPE_CAPABILITY_LIST 2
+ #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
+@@ -1008,19 +1431,122 @@
+ #define HS20_STYPE_OSU_PROVIDERS_LIST 8
+ #define HS20_STYPE_ICON_REQUEST 10
+ #define HS20_STYPE_ICON_BINARY_FILE 11
++#define HS20_STYPE_OPERATOR_ICON_METADATA 12
++#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
+
+ #define HS20_DGAF_DISABLED 0x01
+ #define HS20_PPS_MO_ID_PRESENT 0x02
+ #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+-#define HS20_VERSION 0x10 /* Release 2 */
++#ifndef HS20_VERSION
++#define HS20_VERSION 0x20 /* Release 3 */
++#endif /* HS20_VERSION */
+
+ /* WNM-Notification WFA vendors specific subtypes */
+ #define HS20_WNM_SUB_REM_NEEDED 0
+ #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
++#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2
++#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3
++#define HS20_WNM_T_C_ACCEPTANCE 4
+
+ #define HS20_DEAUTH_REASON_CODE_BSS 0
+ #define HS20_DEAUTH_REASON_CODE_ESS 1
+
++/* MBO v0.0_r19, 4.2: MBO Attributes */
++/* Table 4-5: MBO Attributes */
++/* OCE v0.0.10, Table 4-3: OCE Attributes */
++enum mbo_attr_id {
++ MBO_ATTR_ID_AP_CAPA_IND = 1,
++ MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
++ MBO_ATTR_ID_CELL_DATA_CAPA = 3,
++ MBO_ATTR_ID_ASSOC_DISALLOW = 4,
++ MBO_ATTR_ID_CELL_DATA_PREF = 5,
++ MBO_ATTR_ID_TRANSITION_REASON = 6,
++ MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
++ MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
++ OCE_ATTR_ID_CAPA_IND = 101,
++ OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
++ OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
++ OCE_ATTR_ID_RNR_COMPLETENESS = 104,
++};
++
++/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
++/* Table 4-7: MBO AP Capability Indication Field Values */
++#define MBO_AP_CAPA_CELL_AWARE BIT(6)
++
++/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */
++/* Table 4-10: Reason Code Field Values */
++enum mbo_non_pref_chan_reason {
++ MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
++ MBO_NON_PREF_CHAN_REASON_RSSI = 1,
++ MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
++ MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
++};
++
++/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */
++/* Table 4-13: Cellular Data Connectivity Field */
++enum mbo_cellular_capa {
++ MBO_CELL_CAPA_AVAILABLE = 1,
++ MBO_CELL_CAPA_NOT_AVAILABLE = 2,
++ MBO_CELL_CAPA_NOT_SUPPORTED = 3,
++};
++
++/* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */
++/* Table 4-15: Reason Code Field Values */
++enum mbo_assoc_disallow_reason {
++ MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1,
++ MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2,
++ MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3,
++ MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4,
++ MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5,
++};
++
++/* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */
++/* Table 4-17: Cellular Preference Field Values */
++enum mbo_cell_pref {
++ MBO_CELL_PREF_EXCLUDED = 0,
++ MBO_CELL_PREF_NO_USE = 1,
++ MBO_CELL_PREF_USE = 255
++};
++
++/* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */
++/* Table 4-19: Transition Reason Code Field Values */
++enum mbo_transition_reason {
++ MBO_TRANSITION_REASON_UNSPECIFIED = 0,
++ MBO_TRANSITION_REASON_FRAME_LOSS = 1,
++ MBO_TRANSITION_REASON_DELAY = 2,
++ MBO_TRANSITION_REASON_BANDWIDTH = 3,
++ MBO_TRANSITION_REASON_LOAD_BALANCE = 4,
++ MBO_TRANSITION_REASON_RSSI = 5,
++ MBO_TRANSITION_REASON_RETRANSMISSIONS = 6,
++ MBO_TRANSITION_REASON_INTERFERENCE = 7,
++ MBO_TRANSITION_REASON_GRAY_ZONE = 8,
++ MBO_TRANSITION_REASON_PREMIUM_AP = 9,
++};
++
++/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */
++/* Table 4-21: Transition Rejection Reason Code Field Values */
++enum mbo_transition_reject_reason {
++ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0,
++ MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1,
++ MBO_TRANSITION_REJECT_REASON_DELAY = 2,
++ MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3,
++ MBO_TRANSITION_REJECT_REASON_RSSI = 4,
++ MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5,
++ MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
++};
++
++/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
++#define MBO_ANQP_OUI_TYPE 0x12
++#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
++#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2
++#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF
++
++/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
++#define OCE_RELEASE 1
++#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
++#define OCE_IS_STA_CFON BIT(3)
++#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
++
+ /* Wi-Fi Direct (P2P) */
+
+ #define P2P_OUI_TYPE 9
+@@ -1168,7 +1694,9 @@
+ WFD_SUBELEM_COUPLED_SINK = 6,
+ WFD_SUBELEM_EXT_CAPAB = 7,
+ WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+- WFD_SUBELEM_SESSION_INFO = 9
++ WFD_SUBELEM_SESSION_INFO = 9,
++ WFD_SUBELEM_MAC_INFO = 10,
++ WFD_SUBELEM_R2_DEVICE_INFO = 11,
+ };
+
+ /* 802.11s */
+@@ -1178,6 +1706,14 @@
+ #define MESH_PATH_PROTOCOL_VENDOR 255
+ #define MESH_PATH_METRIC_AIRTIME 1
+ #define MESH_PATH_METRIC_VENDOR 255
++/* IEEE 802.11s - Mesh Capability */
++#define MESH_CAP_ACCEPT_ADDITIONAL_PEER BIT(0)
++#define MESH_CAP_MCCA_SUPPORTED BIT(1)
++#define MESH_CAP_MCCA_ENABLED BIT(2)
++#define MESH_CAP_FORWARDING BIT(3)
++#define MESH_CAP_MBCA_ENABLED BIT(4)
++#define MESH_CAP_TBTT_ADJUSTING BIT(5)
++#define MESH_CAP_MESH_PS_LEVEL BIT(6)
+
+ enum plink_action_field {
+ PLINK_OPEN = 1,
+@@ -1192,42 +1728,7 @@
+
+ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+-/* cipher suite selectors */
+-#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
+-#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
+-#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
+-/* reserved: 0x000FAC03 */
+-#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
+-#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
+-#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
+-#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07
+-#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
+-#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
+-#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
+-#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
+-#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
+-#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
+
+-#define WLAN_CIPHER_SUITE_SMS4 0x00147201
+-
+-#define WLAN_CIPHER_SUITE_CKIP 0x00409600
+-#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601
+-#define WLAN_CIPHER_SUITE_CMIC 0x00409602
+-#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */
+-
+-/* AKM suite selectors */
+-#define WLAN_AKM_SUITE_8021X 0x000FAC01
+-#define WLAN_AKM_SUITE_PSK 0x000FAC02
+-#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
+-#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
+-#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
+-#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
+-#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11
+-#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12
+-#define WLAN_AKM_SUITE_CCKM 0x00409600
+-#define WLAN_AKM_SUITE_OSEN 0x506f9a01
+-
+-
+ /* IEEE 802.11v - WNM Action field values */
+ enum wnm_action {
+ WNM_EVENT_REQ = 0,
+@@ -1280,14 +1781,25 @@
+ WNM_BSS_TM_REJECT_LEAVING_ESS = 8
+ };
+
++/*
++ * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for
++ * neighbor report
++ */
+ #define WNM_NEIGHBOR_TSF 1
+ #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
+ #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
+ #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
+ #define WNM_NEIGHBOR_BEARING 5
++#define WNM_NEIGHBOR_WIDE_BW_CHAN 6
++#define WNM_NEIGHBOR_MEASUREMENT_REPORT 39
++#define WNM_NEIGHBOR_HT_CAPAB 45
++#define WNM_NEIGHBOR_HT_OPER 61
++#define WNM_NEIGHBOR_SEC_CHAN_OFFSET 62
+ #define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
+ #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
+ #define WNM_NEIGHBOR_MULTIPLE_BSSID 71
++#define WNM_NEIGHBOR_VHT_CAPAB 191
++#define WNM_NEIGHBOR_VHT_OPER 192
+
+ /* QoS action */
+ enum qos_action {
+@@ -1356,6 +1868,8 @@
+ u8 link_margin;
+ } STRUCT_PACKED;
+
++#define RRM_CAPABILITIES_IE_LEN 5
++
+ /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+ struct rrm_link_measurement_request {
+ u8 dialog_token;
+@@ -1375,8 +1889,117 @@
+ u8 variable[0];
+ } STRUCT_PACKED;
+
+-#define SSID_MAX_LEN 32
++/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
++struct rrm_measurement_request_element {
++ u8 eid; /* Element ID */
++ u8 len; /* Length */
++ u8 token; /* Measurement Token */
++ u8 mode; /* Measurement Request Mode */
++ u8 type; /* Measurement Type */
++ u8 variable[0]; /* Measurement Request */
++} STRUCT_PACKED;
+
++/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
++#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
++#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
++#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
++#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
++#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
++
++/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
++struct rrm_measurement_beacon_request {
++ u8 oper_class; /* Operating Class */
++ u8 channel; /* Channel Number */
++ le16 rand_interval; /* Randomization Interval (in TUs) */
++ le16 duration; /* Measurement Duration (in TUs) */
++ u8 mode; /* Measurement Mode */
++ u8 bssid[ETH_ALEN]; /* BSSID */
++ u8 variable[0]; /* Optional Subelements */
++} STRUCT_PACKED;
++
++/*
++ * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
++ * request
++ */
++enum beacon_report_mode {
++ BEACON_REPORT_MODE_PASSIVE = 0,
++ BEACON_REPORT_MODE_ACTIVE = 1,
++ BEACON_REPORT_MODE_TABLE = 2,
++};
++
++/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
++/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for
++ * Beacon request */
++#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
++#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
++#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
++#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
++#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
++#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164
++#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
++
++/*
++ * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
++ */
++enum beacon_report_detail {
++ /* No fixed-length fields or elements */
++ BEACON_REPORT_DETAIL_NONE = 0,
++ /* All fixed-length fields and any requested elements in the Request
++ * element if present */
++ BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1,
++ /* All fixed-length fields and elements (default, used when Reporting
++ * Detail subelement is not included in a Beacon request) */
++ BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
++};
++
++/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
++struct rrm_measurement_report_element {
++ u8 eid; /* Element ID */
++ u8 len; /* Length */
++ u8 token; /* Measurement Token */
++ u8 mode; /* Measurement Report Mode */
++ u8 type; /* Measurement Type */
++ u8 variable[0]; /* Measurement Report */
++} STRUCT_PACKED;
++
++/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
++#define MEASUREMENT_REPORT_MODE_ACCEPT 0
++#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
++#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
++#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
++
++/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
++struct rrm_measurement_beacon_report {
++ u8 op_class; /* Operating Class */
++ u8 channel; /* Channel Number */
++ le64 start_time; /* Actual Measurement Start Time
++ * (in TSF of the BSS requesting the measurement) */
++ le16 duration; /* in TUs */
++ u8 report_info; /* Reported Frame Information */
++ u8 rcpi; /* RCPI */
++ u8 rsni; /* RSNI */
++ u8 bssid[ETH_ALEN]; /* BSSID */
++ u8 antenna_id; /* Antenna ID */
++ le32 parent_tsf; /* Parent TSF */
++ u8 variable[0]; /* Optional Subelements */
++} STRUCT_PACKED;
++
++/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
++/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
++ * Beacon report */
++#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
++#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
++#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164
++#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
++
++/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
++ * Reported Frame Body Fragment ID subelement */
++#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
++#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
++
++/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */
++#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3
++
+ /* IEEE Std 802.11ad-2012 - Multi-band element */
+ struct multi_band_ie {
+ u8 eid; /* WLAN_EID_MULTI_BAND */
+@@ -1433,4 +2056,128 @@
+ FST_ACTION_ON_CHANNEL_TUNNEL = 5,
+ };
+
++/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */
++enum phy_type {
++ PHY_TYPE_UNSPECIFIED = 0,
++ PHY_TYPE_FHSS = 1,
++ PHY_TYPE_DSSS = 2,
++ PHY_TYPE_IRBASEBAND = 3,
++ PHY_TYPE_OFDM = 4,
++ PHY_TYPE_HRDSSS = 5,
++ PHY_TYPE_ERP = 6,
++ PHY_TYPE_HT = 7,
++ PHY_TYPE_DMG = 8,
++ PHY_TYPE_VHT = 9,
++};
++
++/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */
++/* BSSID Information Field */
++#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
++#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
++#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1))
++#define NEI_REP_BSSID_INFO_SECURITY BIT(2)
++#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3)
++#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4)
++#define NEI_REP_BSSID_INFO_QOS BIT(5)
++#define NEI_REP_BSSID_INFO_APSD BIT(6)
++#define NEI_REP_BSSID_INFO_RM BIT(7)
++#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8)
++#define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
++#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
++#define NEI_REP_BSSID_INFO_HT BIT(11)
++#define NEI_REP_BSSID_INFO_VHT BIT(12)
++#define NEI_REP_BSSID_INFO_FTM BIT(13)
++
++/*
++ * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
++ * subfields.
++ * Note: These definitions are not the same as other VHT_CHANWIDTH_*.
++ */
++enum nr_chan_width {
++ NR_CHAN_WIDTH_20 = 0,
++ NR_CHAN_WIDTH_40 = 1,
++ NR_CHAN_WIDTH_80 = 2,
++ NR_CHAN_WIDTH_160 = 3,
++ NR_CHAN_WIDTH_80P80 = 4,
++};
++
++struct ieee80211_he_capabilities {
++ u8 he_mac_capab_info[6];
++ u8 he_phy_capab_info[11];
++ u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
++ /* PPE Thresholds (optional) */
++} STRUCT_PACKED;
++
++struct ieee80211_he_operation {
++ u32 he_oper_params; /* HE Operation Parameters[3] and
++ * BSS Color Information[1] */
++ u8 he_mcs_nss_set[2];
++ u8 vht_op_info_chwidth;
++ u8 vht_op_info_chan_center_freq_seg0_idx;
++ u8 vht_op_info_chan_center_freq_seg1_idx;
++ /* Followed by conditional MaxBSSID Indicator subfield (u8) */
++} STRUCT_PACKED;
++
++/* HE Capabilities Information defines */
++#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
++#define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
++#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
++#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0))
++#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4
++#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1))
++
++/* HE Operation defines */
++/* HE Operation Parameters and BSS Color Information fields */
++#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \
++ BIT(2) | BIT(3) | \
++ BIT(4) | BIT(5)))
++#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(6))
++#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(7))
++#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(8) | BIT(9) | \
++ BIT(10)))
++#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 8
++#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(11))
++#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(12) | BIT(13) | \
++ BIT(14) | BIT(15) | \
++ BIT(16) | BIT(17) | \
++ BIT(18) | BIT(19) | \
++ BIT(20) | BIT(21)))
++#define HE_OPERATION_RTS_THRESHOLD_OFFSET 12
++
++struct ieee80211_he_mu_edca_parameter_set {
++ u8 he_qos_info;
++ u8 he_mu_ac_be_param[3];
++ u8 he_mu_ac_bk_param[3];
++ u8 he_mu_ac_vi_param[3];
++ u8 he_mu_ac_vo_param[3];
++} STRUCT_PACKED;
++
++/* HE MU AC parameter record field format */
++/* ACI/AIFSN */
++#define HE_MU_AC_PARAM_ACI_IDX 0
++#define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
++#define HE_MU_AC_PARAM_ACM ((u8) BIT(4))
++#define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6)))
++/* B7: Reserved */
++
++/* ECWmin/ECWmax */
++#define HE_MU_AC_PARAM_ECW_IDX 1
++#define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
++#define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7)))
++
++/* MU EDCA Timer */
++#define HE_MU_AC_PARAM_TIMER_IDX 2
++
++/* HE QoS Info field */
++#define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \
++ BIT(2) | BIT(3)))
++#define HE_QOS_INFO_Q_ACK ((u8) (BIT(4)))
++#define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5)))
++#define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6)))
++/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
++#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
++
++/* DPP Public Action frame identifiers - OUI_WFA */
++#define DPP_OUI_TYPE 0x1A
++
+ #endif /* IEEE802_11_DEFS_H */
+--- contrib/wpa/src/common/ieee802_1x_defs.h.orig
++++ contrib/wpa/src/common/ieee802_1x_defs.h
+@@ -10,8 +10,10 @@
+ #define IEEE802_1X_DEFS_H
+
+ #define CS_ID_LEN 8
+-#define CS_ID_GCM_AES_128 {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01}
++#define CS_ID_GCM_AES_128 0x0080020001000001ULL
+ #define CS_NAME_GCM_AES_128 "GCM-AES-128"
++#define CS_ID_GCM_AES_256 0x0080c20001000002ULL
++#define CS_NAME_GCM_AES_256 "GCM-AES-256"
+
+ enum macsec_policy {
+ /**
+@@ -25,6 +27,12 @@
+ * Disabled MACsec - do not secure sessions.
+ */
+ DO_NOT_SECURE,
++
++ /**
++ * Should secure sessions, and try to use encryption.
++ * Like @SHOULD_SECURE, this follows the key server's decision.
++ */
++ SHOULD_ENCRYPT,
+ };
+
+
+--- contrib/wpa/src/common/ocv.c.orig
++++ contrib/wpa/src/common/ocv.c
+@@ -0,0 +1,172 @@
++/*
++ * Operating Channel Validation (OCV)
++ * Copyright (c) 2018, Mathy Vanhoef
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "drivers/driver.h"
++#include "common/ieee802_11_common.h"
++#include "ocv.h"
++
++/**
++ * Caller of OCV functionality may use various debug output functions, so store
++ * the error here and let the caller use an appropriate debug output function.
++ */
++char ocv_errorstr[256];
++
++
++int ocv_derive_all_parameters(struct oci_info *oci)
++{
++ const struct oper_class_map *op_class_map;
++
++ oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel);
++ if (oci->freq < 0) {
++ wpa_printf(MSG_INFO,
++ "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)",
++ oci->op_class, oci->channel);
++ return -1;
++ }
++
++ op_class_map = get_oper_class(NULL, oci->op_class);
++ if (!op_class_map) {
++ wpa_printf(MSG_INFO,
++ "Error interpreting OCI: Unrecognized opclass (%d)",
++ oci->op_class);
++ return -1;
++ }
++
++ oci->chanwidth = oper_class_bw_to_int(op_class_map);
++ oci->sec_channel = 0;
++ if (op_class_map->bw == BW40PLUS)
++ oci->sec_channel = 1;
++ else if (op_class_map->bw == BW40MINUS)
++ oci->sec_channel = -1;
++
++ return 0;
++}
++
++
++int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos)
++{
++ u8 op_class, channel;
++ u8 *pos = *argpos;
++
++ if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth,
++ ci->sec_channel,
++ &op_class, &channel) < 0) {
++ wpa_printf(MSG_WARNING,
++ "Cannot determine operating class and channel for OCI element");
++ return -1;
++ }
++
++ *pos++ = op_class;
++ *pos++ = channel;
++ *pos++ = ci->seg1_idx;
++
++ *argpos = pos;
++ return 0;
++}
++
++
++int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos)
++{
++ u8 *pos = *argpos;
++
++ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
++ *pos++ = RSN_SELECTOR_LEN + 3;
++ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI);
++ pos += RSN_SELECTOR_LEN;
++
++ *argpos = pos;
++ return ocv_insert_oci(ci, argpos);
++}
++
++
++int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos)
++{
++ *pos++ = WLAN_EID_EXTENSION;
++ *pos++ = 1 + OCV_OCI_LEN;
++ *pos++ = WLAN_EID_EXT_OCV_OCI;
++ return ocv_insert_oci(ci, &pos);
++}
++
++
++int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
++ struct wpa_channel_info *ci, int tx_chanwidth,
++ int tx_seg1_idx)
++{
++ struct oci_info oci;
++
++ if (!oci_ie) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: did not receive mandatory OCI");
++ return -1;
++ }
++
++ if (oci_ie_len != 3) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: received OCI of unexpected length (%d)",
++ (int) oci_ie_len);
++ return -1;
++ }
++
++ os_memset(&oci, 0, sizeof(oci));
++ oci.op_class = oci_ie[0];
++ oci.channel = oci_ie[1];
++ oci.seg1_idx = oci_ie[2];
++ if (ocv_derive_all_parameters(&oci) != 0) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: unable to interpret received OCI");
++ return -1;
++ }
++
++ /* Primary frequency used to send frames to STA must match the STA's */
++ if ((int) ci->frequency != oci.freq) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)",
++ ci->frequency, oci.freq);
++ return -1;
++ }
++
++ /* We shouldn't transmit with a higher bandwidth than the STA supports
++ */
++ if (tx_chanwidth > oci.chanwidth) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
++ tx_chanwidth, oci.chanwidth);
++ return -1;
++ }
++
++ /*
++ * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz
++ * band. In the 5 GHz band it's verified through the primary frequency.
++ * Note that the field ci->sec_channel is only filled in when we use
++ * 40 MHz.
++ */
++ if (tx_chanwidth == 40 && ci->frequency < 2500 &&
++ ci->sec_channel != oci.sec_channel) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
++ ci->sec_channel, oci.sec_channel);
++ return -1;
++ }
++
++ /*
++ * When using a 160 or 80+80 MHz channel to transmit, verify that we use
++ * the same segments as the receiver by comparing frequency segment 1.
++ */
++ if ((ci->chanwidth == CHAN_WIDTH_160 ||
++ ci->chanwidth == CHAN_WIDTH_80P80) &&
++ tx_seg1_idx != oci.seg1_idx) {
++ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
++ "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
++ tx_seg1_idx, oci.seg1_idx);
++ return -1;
++ }
++
++ return 0;
++}
+--- contrib/wpa/src/common/ocv.h.orig
++++ contrib/wpa/src/common/ocv.h
+@@ -0,0 +1,40 @@
++/*
++ * Operating Channel Validation (OCV)
++ * Copyright (c) 2018, Mathy Vanhoef
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef OCV_H
++#define OCV_H
++
++struct wpa_channel_info;
++
++struct oci_info {
++ /* Values in the OCI element */
++ u8 op_class;
++ u8 channel;
++ u8 seg1_idx;
++
++ /* Derived values for easier verification */
++ int freq;
++ int sec_channel;
++ int chanwidth;
++};
++
++#define OCV_OCI_LEN 3
++#define OCV_OCI_EXTENDED_LEN (3 + OCV_OCI_LEN)
++#define OCV_OCI_KDE_LEN (2 + RSN_SELECTOR_LEN + OCV_OCI_LEN)
++
++extern char ocv_errorstr[256];
++
++int ocv_derive_all_parameters(struct oci_info *oci);
++int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos);
++int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos);
++int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos);
++int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
++ struct wpa_channel_info *ci, int tx_chanwidth,
++ int tx_seg1_idx);
++
++#endif /* OCV_H */
+--- contrib/wpa/src/common/privsep_commands.h.orig
++++ contrib/wpa/src/common/privsep_commands.h
+@@ -9,6 +9,7 @@
+ #ifndef PRIVSEP_COMMANDS_H
+ #define PRIVSEP_COMMANDS_H
+
++#include "drivers/driver.h"
+ #include "common/ieee802_11_defs.h"
+
+ enum privsep_cmd {
+@@ -29,8 +30,17 @@
+ PRIVSEP_CMD_AUTHENTICATE,
+ };
+
+-struct privsep_cmd_authenticate
+-{
++#define PRIVSEP_MAX_SCAN_FREQS 50
++
++struct privsep_cmd_scan {
++ unsigned int num_ssids;
++ u8 ssids[WPAS_MAX_SCAN_SSIDS][32];
++ u8 ssid_lens[WPAS_MAX_SCAN_SSIDS];
++ unsigned int num_freqs;
++ u16 freqs[PRIVSEP_MAX_SCAN_FREQS];
++};
++
++struct privsep_cmd_authenticate {
+ int freq;
+ u8 bssid[ETH_ALEN];
+ u8 ssid[SSID_MAX_LEN];
+@@ -42,13 +52,12 @@
+ int wep_tx_keyidx;
+ int local_state_change;
+ int p2p;
+- size_t sae_data_len;
++ size_t auth_data_len;
+ /* followed by ie_len bytes of ie */
+- /* followed by sae_data_len bytes of sae_data */
++ /* followed by auth_data_len bytes of auth_data */
+ };
+
+-struct privsep_cmd_associate
+-{
++struct privsep_cmd_associate {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+@@ -64,8 +73,7 @@
+ /* followed by wpa_ie_len bytes of wpa_ie */
+ };
+
+-struct privsep_cmd_set_key
+-{
++struct privsep_cmd_set_key {
+ int alg;
+ u8 addr[ETH_ALEN];
+ int key_idx;
+@@ -84,7 +92,6 @@
+ PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+ PRIVSEP_EVENT_INTERFACE_STATUS,
+ PRIVSEP_EVENT_PMKID_CANDIDATE,
+- PRIVSEP_EVENT_STKSTART,
+ PRIVSEP_EVENT_FT_RESPONSE,
+ PRIVSEP_EVENT_RX_EAPOL,
+ PRIVSEP_EVENT_SCAN_STARTED,
+--- contrib/wpa/src/common/qca-vendor.h.orig
++++ contrib/wpa/src/common/qca-vendor.h
+@@ -1,6 +1,7 @@
+ /*
+ * Qualcomm Atheros OUI and vendor specific assignments
+- * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
++ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
++ * Copyright (c) 2018, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -41,8 +42,12 @@
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ * ranges to avoid to reduce issues due to interference or internal
+- * co-existence information in the driver. The event data structure is
+- * defined in struct qca_avoid_freq_list.
++ * co-existence information in the driver. These frequencies aim to
++ * minimize the traffic but not to totally avoid the traffic. That said
++ * for a P2P use case, these frequencies are allowed for the P2P
++ * discovery/negotiation but avoid the group to get formed on these
++ * frequencies. The event data structure is defined in
++ * struct qca_avoid_freq_list.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
+ * for DFS offloading.
+@@ -49,7 +54,10 @@
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
+ * NAN Request/Response and NAN Indication messages. These messages are
+- * interpreted between the framework and the firmware component.
++ * interpreted between the framework and the firmware component. While
++ * sending the command from userspace to the driver, payload is not
++ * encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN
++ * is used when receiving vendor events in userspace from the driver.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ * used to configure PMK to the driver even when not connected. This can
+@@ -89,6 +97,433 @@
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
+ * which supports DFS offloading, to indicate a radar pattern has been
+ * detected. The channel is now unusable.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
++ * based on enum wifi_logger_supported_features. Attributes defined in
++ * enum qca_wlan_vendor_attr_get_logger_features.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular
++ * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the
++ * attribute for this command. Attributes defined in
++ * enum qca_wlan_vendor_attr_wifi_logger_start.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS
++ * capabilities of the driver, parameters includes the attributes defined
++ * in enum qca_wlan_vendor_attr_tdls_get_capabilities.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload
++ * sending of certain periodic IP packet to firmware, attributes defined in
++ * enum qca_wlan_vendor_attr_offloaded_packets.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI
++ * monitoring, defines min and max RSSI which are configured for RSSI
++ * monitoring. Also used to notify the RSSI breach and provides the BSSID
++ * and RSSI value that was breached. Attributes defined in
++ * enum qca_wlan_vendor_attr_rssi_monitoring.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN
++ * Data Path (NDP) related operations, attributes defined in
++ * enum qca_wlan_vendor_attr_ndp_params.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable
++ * Neighbour Discovery offload, attributes defined in
++ * enum qca_wlan_vendor_attr_nd_offload.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various
++ * configuration parameter for BPF packet filter, attributes defined in
++ * enum qca_wlan_vendor_attr_packet_filter.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware
++ * maximum supported size, attributes defined in
++ * enum qca_wlan_vendor_drv_info.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various
++ * data about wake reasons and datapath IP statistics, attributes defined
++ * in enum qca_wlan_vendor_attr_wake_stats.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
++ * for IEEE 802.11 communicating outside the context of a basic service
++ * set, called OCB command. Uses the attributes defines in
++ * enum qca_wlan_vendor_attr_ocb_set_config.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB
++ * UTC time. Use the attributes defines in
++ * enum qca_wlan_vendor_attr_ocb_set_utc_time.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start
++ * sending OCB timing advert frames. Uses the attributes defines in
++ * enum qca_wlan_vendor_attr_ocb_start_timing_advert.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop
++ * OCB timing advert. Uses the attributes defines in
++ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF
++ * timer value. Uses the attributes defines in
++ * enum qca_wlan_vendor_attr_ocb_get_tsf_resp.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the
++ * link properties of the respective interface. As an event, is used
++ * to notify the connected station's status. The attributes for this
++ * command are defined in enum qca_wlan_vendor_attr_link_properties.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
++ * start the P2P Listen offload function in device and pass the listen
++ * channel, period, interval, count, device types, and vendor specific
++ * information elements to the device driver and firmware.
++ * Uses the attributes defines in
++ * enum qca_wlan_vendor_attr_p2p_listen_offload.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to
++ * indicate stop request/response of the P2P Listen offload function in
++ * device. As an event, it indicates either the feature stopped after it
++ * was already running or feature has actually failed to start. Uses the
++ * attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts
++ * beaconing, this sub command provides the driver, the frequencies on the
++ * 5 GHz band to check for any radar activity. Driver selects one channel
++ * from this priority list provided through
++ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts
++ * to check for radar activity on it. If no radar activity is detected
++ * during the channel availability check period, driver internally switches
++ * to the selected frequency of operation. If the frequency is zero, driver
++ * internally selects a channel. The status of this conditional switch is
++ * indicated through an event using the same sub command through
++ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are
++ * listed in qca_wlan_vendor_attr_sap_conditional_chan_switch.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the
++ * attributes defined in enum qca_wlan_gpio_attr.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities.
++ * This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which
++ * capabilities are to be fetched and other
++ * enum qca_wlan_vendor_attr_get_hw_capability attributes to return the
++ * requested capabilities.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension.
++ * enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this
++ * command and event.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for
++ * indoor location features. Capabilities are reported in
++ * QCA_WLAN_VENDOR_ATTR_LOC_CAPA.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM
++ * (fine timing measurement) session with one or more peers.
++ * Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and
++ * peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS.
++ * On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT
++ * events will be reported, followed by
++ * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate
++ * end of session.
++ * Refer to IEEE P802.11-REVmc/D7.0, 11.24.6
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session.
++ * A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with
++ * status code indicating session was aborted.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement
++ * results for one peer. Results are reported in
++ * QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when
++ * FTM session is finished, either successfully or aborted by
++ * request.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder
++ * mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether
++ * to enable or disable the responder. LCI/LCR reports can be
++ * configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and
++ * QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple
++ * times to update the LCI/LCR reports.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of
++ * arrival) measurement with a single peer. Specify peer MAC address in
++ * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in
++ * QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel
++ * scan results cache and use the frequency from there).
++ * Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE.
++ * Measurement result is reported in
++ * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify
++ * peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports
++ * the AOA measurement result.
++ * Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
++ * success/failure status is reported in
++ * QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS.
++ * Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
++ * The antenna array(s) used in the measurement are reported in
++ * QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given
++ * data as per the given parameters.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a
++ * specific chain.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level
++ * configuration for a DMG RF sector. Specify sector index in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules
++ * to return sector information for in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration
++ * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the
++ * exact time where information was captured in
++ * QCA_WLAN_VENDOR_ATTR_TSF.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level
++ * configuration for a DMG RF sector. Specify sector index in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration
++ * for one or more DMG RF modules in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected
++ * DMG RF sector for a station. This is the sector that the HW
++ * will use to communicate with the station. Specify the MAC address
++ * of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not
++ * needed for unassociated station). Specify sector type to return in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected
++ * sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
++ * Also return the exact time where the information was captured
++ * in QCA_WLAN_VENDOR_ATTR_TSF.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the
++ * selected DMG RF sector for a station. This is the sector that
++ * the HW will use to communicate with the station.
++ * Specify the MAC address of associated station/AP/PCP in
++ * QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in
++ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index
++ * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
++ * The selected sector will be locked such that it will not be
++ * modified like it normally does (for example when station
++ * moves around). To unlock the selected sector for a station
++ * pass the special value 0xFFFF in the sector index. To unlock
++ * all connected stations also pass a broadcast MAC address.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior
++ * in the host driver. The different TDLS configurations are defined
++ * by the attributes in enum qca_wlan_vendor_attr_tdls_configuration.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE
++ * capabilities. The response uses the attributes defined in
++ * enum qca_wlan_vendor_attr_get_he_capabilities.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was
++ * started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command
++ * carries the scan cookie of the corresponding scan request. The scan
++ * cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific
++ * Absorption Rate (SAR) power limits. A critical regulation for
++ * FCC compliance, OEMs require methods to set SAR limits on TX
++ * power of WLAN/WWAN. enum qca_vendor_attr_sar_limits
++ * attributes are used with this command.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the
++ * host driver for offloading the implementation of Auto Channel Selection
++ * (ACS) to an external user space entity. This interface is used as the
++ * event from the host driver to the user space entity and also as the
++ * request from the user space entity to the host driver. The event from
++ * the host driver is used by the user space entity as an indication to
++ * start the ACS functionality. The attributes used by this event are
++ * represented by the enum qca_wlan_vendor_attr_external_acs_event.
++ * User space entity uses the same interface to inform the host driver with
++ * selected channels after the ACS operation using the attributes defined
++ * by enum qca_wlan_vendor_attr_external_acs_channels.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the
++ * requisite information leading to a power save failure. The information
++ * carried as part of this event is represented by the
++ * enum qca_attr_chip_power_save_failure attributes.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics
++ * collection. Uses attributes defined in enum qca_attr_nud_stats_set.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These
++ * statistics are represented by the enum qca_attr_nud_stats_get
++ * attributes.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch
++ * the BSS transition status, whether accept or reject, for a list of
++ * candidate BSSIDs provided by the userspace. This uses the vendor
++ * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify
++ * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an
++ * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response
++ * the driver shall specify array of
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
++ * specific QCA module. The trace levels are represented by
++ * enum qca_attr_trace_level attributes.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement
++ * Protocol antenna limit in different modes. See enum
++ * qca_wlan_vendor_attr_brp_ant_limit_mode.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
++ * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
++ * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
++ * identifying the operation in success case.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
++ * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to
++ * be stopped.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the
++ * specific interface. This can be used to modify some of the low level
++ * scan parameters (off channel dwell time, home channel time) in the
++ * driver/firmware. These parameters are maintained within the host driver.
++ * This command is valid only when the interface is in the connected state.
++ * These scan parameters shall be reset by the driver/firmware once
++ * disconnected. The attributes used with this command are defined in
++ * enum qca_wlan_vendor_attr_active_tos.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the
++ * driver has detected an internal failure. This event carries the
++ * information indicating the reason that triggered this detection. The
++ * attributes for this command are defined in
++ * enum qca_wlan_vendor_attr_hang.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values
++ * of spectral parameters used. The spectral scan parameters are specified
++ * by enum qca_wlan_vendor_attr_spectral_scan.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats
++ * for spectral scan functionality. The debug stats are specified by
++ * enum qca_wlan_vendor_attr_spectral_diag_stats.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral
++ * scan system capabilities. The capabilities are specified
++ * by enum qca_wlan_vendor_attr_spectral_cap.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current
++ * status of spectral scan. The status values are specified
++ * by enum qca_wlan_vendor_attr_spectral_scan_status.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush
++ * peer pending packets. Specify the peer MAC address in
++ * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets
++ * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed
++ * in enum qca_wlan_vendor_attr_flush_pending.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative
++ * RF Operating Parameter (RROP) information. The attributes for this
++ * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is
++ * intended for use by external Auto Channel Selection applications.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate
++ * (SAR) power limits. This is a companion to the command
++ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the
++ * settings currently in use. The attributes returned by this command are
++ * defined by enum qca_vendor_attr_sar_limits.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of
++ * the WLAN hardware MAC. Also, provides the WLAN netdev interface
++ * information attached to the respective MAC.
++ * This works both as a query (user space asks the current mode) or event
++ * interface (driver advertising the current mode to the user space).
++ * Driver does not trigger this event for temporary hardware mode changes.
++ * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion,
++ * channel change, etc.) are updated with this event. Attributes for this
++ * interface are defined in enum qca_wlan_vendor_attr_mac.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold
++ * per peer per TID. Attributes for this command are define in
++ * enum qca_wlan_set_qdepth_thresh_attr.
++ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action
++ * guide for WLAN driver. Request to suspend of driver and FW if the
++ * temperature is higher than the suspend threshold; resume action is
++ * requested to driver if the temperature is lower than the resume
++ * threshold. In user poll mode, request temperature data by user. For test
++ * purpose, getting thermal shutdown configuration parameters is needed.
++ * Attributes for this interface are defined in
++ * enum qca_wlan_vendor_attr_thermal_cmd.
++ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from
++ * driver. Thermal temperature and indication of resume completion are
++ * reported as thermal events. The attributes for this command are defined
++ * in enum qca_wlan_vendor_attr_thermal_event.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi
++ * test configuration. Attributes for this command are defined in
++ * enum qca_wlan_vendor_attr_wifi_test_config.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an
++ * RX filter to receive frames from stations that are active on the
++ * operating channel, but not associated with the local device (e.g., STAs
++ * associated with other APs). Filtering is done based on a list of BSSIDs
++ * and STA MAC addresses added by the user. This command is also used to
++ * fetch the statistics of unassociated stations. The attributes used with
++ * this command are defined in enum qca_wlan_vendor_attr_bss_filter.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor
++ * command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN,
++ * carried a payload which was a binary blob of data. The command was not
++ * extendable to send more information. The newer version carries the
++ * legacy blob encapsulated within an attribute and can be extended with
++ * additional vendor attributes that can enhance the NAN command interface.
++ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered
++ * or stopped within driver/firmware in order to initiate roaming. The
++ * attributes used with this event are defined in enum
++ * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events
++ * in few cases, e.g., if the host processor is sleeping when this event
++ * is generated in firmware.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to
++ * configure parameters per peer to capture Channel Frequency Response
++ * (CFR) and enable Periodic CFR capture. The attributes for this command
++ * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes
++ * in throughput dynamically. The driver estimates the throughput based on
++ * number of packets being transmitted/received per second and indicates
++ * the changes in throughput to user space. Userspace tools can use this
++ * information to configure kernel's TCP parameters in order to achieve
++ * peak throughput. Optionally, the driver will also send guidance on
++ * modifications to kernel's TCP parameters which can be referred by
++ * userspace tools. The attributes used with this event are defined in enum
++ * qca_wlan_vendor_attr_throughput_change.
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set
++ * priorities among different types of traffic during coex scenarios.
++ * Current supported prioritization is among WLAN/BT/ZIGBEE with different
++ * profiles mentioned in enum qca_coex_config_profiles. The associated
++ * attributes used with this command are defined in enum
++ * qca_vendor_attr_coex_config.
++ *
++ * Based on the config provided, FW will boost the weight and prioritize
++ * the traffic for that subsystem (WLAN/BT/Zigbee).
++ *
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query
++ * the supported AKM suite selectorss from the driver. It returns the list
++ * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES.
++ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware
++ * state from the driver. It returns the firmware state in the attribute
++ * QCA_WLAN_VENDOR_ATTR_FW_STATE.
++ * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand
++ * is used by the driver to flush per-peer cached statistics to user space
++ * application. This interface is used as an event from the driver to
++ * user space application. Attributes for this event are specified in
++ * enum qca_wlan_vendor_attr_peer_stats_cache_params.
++ * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be
++ * sent in the event.
++ * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to
++ * improve the success rate of Zigbee joining network.
++ * Due to PTA master limitation, Zigbee joining network success rate is
++ * low while WLAN is working. The WLAN driver needs to configure some
++ * parameters including Zigbee state and specific WLAN periods to enhance
++ * PTA master. All these parameters are delivered by the attributes
++ * defined in enum qca_mpta_helper_vendor_attr.
+ */
+ enum qca_nl80211_vendor_subcmds {
+ QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+@@ -98,7 +533,7 @@
+ QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11,
+ QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
+- QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
++ QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+@@ -140,7 +575,33 @@
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
+- /* 61-90 - reserved for QCA */
++ QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61,
++ QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62,
++ QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63,
++ QCA_NL80211_VENDOR_SUBCMD_ROAM = 64,
++ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65,
++ QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66,
++ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67,
++ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68,
++ QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69,
++ QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70,
++ QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71,
++ QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72,
++ QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73,
++ /* Wi-Fi configuration subcommands */
++ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74,
++ QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75,
++ QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76,
++ QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77,
++ QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78,
++ QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79,
++ QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80,
++ QCA_NL80211_VENDOR_SUBCMD_NDP = 81,
++ QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82,
++ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83,
++ QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84,
++ QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85,
++ /* 86-90 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
+@@ -156,14 +617,91 @@
+ QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
+ QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
+ QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
++ QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
++ QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
++ QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108,
++ QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
++ /* 110..114 - reserved for QCA */
++ QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
++ /* 116..117 - reserved for QCA */
++ QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118,
++ QCA_NL80211_VENDOR_SUBCMD_TSF = 119,
++ QCA_NL80211_VENDOR_SUBCMD_WISA = 120,
++ /* 121 - reserved for QCA */
++ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122,
++ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123,
++ QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124,
++ QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125,
++ QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126,
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127,
++ /* FTM/indoor location subcommands */
++ QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
++ QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
++ QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130,
++ QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131,
++ QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132,
++ QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133,
++ QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
++ QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
++ QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
++ QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137,
++ QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138,
++ /* DMG low level RF sector operations */
++ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
++ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
++ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
++ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
++ QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143,
++ QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144,
++ QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145,
++ QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
++ QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147,
++ QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148,
++ QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149,
++ QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
++ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
++ QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
++ QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
++ QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
++ QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160,
++ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161,
++ /* Flush peer pending data */
++ QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162,
++ QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163,
++ QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164,
++ QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165,
++ QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166,
++ /* Thermal shutdown commands to protect wifi chip */
++ QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167,
++ QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168,
++ /* Wi-Fi test configuration subcommand */
++ QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169,
++ /* Frame filter operations for other BSSs/unassociated STAs */
++ QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170,
++ QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171,
++ QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172,
++ QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173,
++ QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
++ QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
++ QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176,
++ QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
++ QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
++ QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
+ };
+
+-
+ enum qca_wlan_vendor_attr {
+ QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+ QCA_WLAN_VENDOR_ATTR_DFS = 1,
+- /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
++ /* Used only when driver sends vendor events to the userspace under the
++ * command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends
++ * commands to the driver.
++ */
+ QCA_WLAN_VENDOR_ATTR_NAN = 2,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3,
+@@ -170,7 +708,8 @@
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
+- * by enum qca_roaming_policy. */
++ * by enum qca_roaming_policy.
++ */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
+ QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+@@ -185,17 +724,220 @@
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
+ /* Unsigned 32-bit value from enum qca_set_band. */
+ QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
++ /* Dummy (NOP) attribute for 64 bit padding */
++ QCA_WLAN_VENDOR_ATTR_PAD = 13,
++ /* Unique FTM session cookie (Unsigned 64 bit). Specified in
++ * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in
++ * the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and
++ * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14,
++ /* Indoor location capabilities, returned by
++ * QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA.
++ * see enum qca_wlan_vendor_attr_loc_capa.
++ */
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15,
++ /* Array of nested attributes containing information about each peer
++ * in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info
++ * for supported attributes for each peer.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16,
++ /* Array of nested attributes containing measurement results for
++ * one or more peers, reported by the
++ * QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event.
++ * See enum qca_wlan_vendor_attr_peer_result for list of supported
++ * attributes.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17,
++ /* Flag attribute for enabling or disabling responder functionality. */
++ QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18,
++ /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
++ * command to specify the LCI report that will be sent by
++ * the responder during a measurement exchange. The format is
++ * defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19,
++ /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
++ * command to specify the location civic report that will
++ * be sent by the responder during a measurement exchange.
++ * The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20,
++ /* Session/measurement completion status code,
++ * reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and
++ * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
++ * see enum qca_vendor_attr_loc_session_status.
++ */
++ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21,
++ /* Initial dialog token used by responder (0 if not specified),
++ * unsigned 8 bit value.
++ */
++ QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22,
++ /* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS
++ * and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if
++ * AOA measurements are needed as part of an FTM session.
++ * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See
++ * enum qca_wlan_vendor_attr_aoa_type.
++ */
++ QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23,
++ /* A bit mask (unsigned 32 bit value) of antenna arrays used
++ * by indoor location measurements. Refers to the antenna
++ * arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS.
++ */
++ QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24,
++ /* AOA measurement data. Its contents depends on the AOA measurement
++ * type and antenna array mask:
++ * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values,
++ * phase of the strongest CIR path for each antenna in the measured
++ * array(s).
++ * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16
++ * values, phase and amplitude of the strongest CIR path for each
++ * antenna in the measured array(s).
++ */
++ QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
++ * to specify the chain number (unsigned 32 bit value) to inquire
++ * the corresponding antenna RSSI value
++ */
++ QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
++ * to report the specific antenna RSSI value (unsigned 32 bit value)
++ */
++ QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
++ /* Frequency in MHz, various uses. Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_FREQ = 28,
++ /* TSF timer value, unsigned 64 bit value.
++ * May be returned by various commands.
++ */
++ QCA_WLAN_VENDOR_ATTR_TSF = 29,
++ /* DMG RF sector index, unsigned 16 bit number. Valid values are
++ * 0..127 for sector indices or 65535 as special value used to
++ * unlock sector selection in
++ * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR.
++ */
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30,
++ /* DMG RF sector type, unsigned 8 bit value. One of the values
++ * in enum qca_wlan_vendor_attr_dmg_rf_sector_type.
++ */
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31,
++ /* Bitmask of DMG RF modules for which information is requested. Each
++ * bit corresponds to an RF module with the same index as the bit
++ * number. Unsigned 32 bit number but only low 8 bits can be set since
++ * all DMG chips currently have up to 8 RF modules.
++ */
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32,
++ /* Array of nested attributes where each entry is DMG RF sector
++ * configuration for a single RF module.
++ * Attributes for each entry are taken from enum
++ * qca_wlan_vendor_attr_dmg_rf_sector_cfg.
++ * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG
++ * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG.
++ */
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command
++ * to report frame aggregation statistics to userspace.
++ */
++ QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34,
++ QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35,
++ /* Unsigned 8-bit value representing MBO transition reason code as
++ * provided by the AP used by subcommand
++ * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is
++ * specified by the userspace in the request to the driver.
++ */
++ QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36,
++ /* Array of nested attributes, BSSID and status code, used by subcommand
++ * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each
++ * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info.
++ * The userspace space specifies the list/array of candidate BSSIDs in
++ * the order of preference in the request. The driver specifies the
++ * status code, for each BSSID in the list, in the response. The
++ * acceptable candidates are listed in the order preferred by the
++ * driver.
++ */
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
++ * See enum qca_wlan_vendor_attr_brp_ant_limit_mode.
++ */
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
++ * to define the number of antennas to use for BRP.
++ * different purpose in each ANT_LIMIT_MODE:
++ * DISABLE - ignored
++ * EFFECTIVE - upper limit to number of antennas to be used
++ * FORCE - exact number of antennas to be used
++ * unsigned 8 bit value
++ */
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
++ * to report the corresponding antenna index to the chain RSSI value
++ */
++ QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
++ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report
++ * the specific antenna EVM value (unsigned 32 bit value). With a
++ * determinate group of antennas, the driver specifies the EVM value
++ * for each antenna ID, and application extract them in user space.
++ */
++ QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41,
++ /*
++ * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report
++ * wlan firmware current state. FW state is an unsigned 8 bit value,
++ * one of the values in enum qca_wlan_vendor_attr_fw_state.
++ */
++ QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
++
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+ };
+
+-
+ enum qca_roaming_policy {
+ QCA_ROAMING_NOT_ALLOWED,
+ QCA_ROAMING_ALLOWED_WITHIN_ESS,
+ };
+
++/**
++ * enum qca_roam_reason - Represents the reason codes for roaming. Used by
++ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON.
++ *
++ * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below
++ * reasons.
++ *
++ * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached
++ * the configured threshold.
++ *
++ * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured
++ * beacon misses from the then connected AP.
++ *
++ * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported
++ * by the connected AP.
++ *
++ * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better
++ * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor.
++ *
++ * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel
++ * or environment being very noisy or congested.
++ *
++ * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request
++ * from the user (user space).
++ *
++ * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from
++ * the connected AP.
++ *
++ * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization
++ * breaching out the configured threshold.
++ */
++enum qca_roam_reason {
++ QCA_ROAM_REASON_UNKNOWN,
++ QCA_ROAM_REASON_PER,
++ QCA_ROAM_REASON_BEACON_MISS,
++ QCA_ROAM_REASON_POOR_RSSI,
++ QCA_ROAM_REASON_BETTER_RSSI,
++ QCA_ROAM_REASON_CONGESTION,
++ QCA_ROAM_REASON_USER_TRIGGER,
++ QCA_ROAM_REASON_BTM,
++ QCA_ROAM_REASON_BSS_LOAD,
++};
++
+ enum qca_wlan_vendor_attr_roam_auth {
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+@@ -205,6 +947,44 @@
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
++ /* Indicates the status of re-association requested by user space for
++ * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID.
++ * Type u16.
++ * Represents the status code from AP. Use
++ * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the
++ * real status code for failures.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS,
++ /* This attribute indicates that the old association was maintained when
++ * a re-association is requested by user space and that re-association
++ * attempt fails (i.e., cannot connect to the requested BSS, but can
++ * remain associated with the BSS with which the association was in
++ * place when being requested to roam). Used along with
++ * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current
++ * re-association status. Type flag.
++ * This attribute is applicable only for re-association failure cases.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION,
++ /* This attribute specifies the PMK if one was newly generated during
++ * FILS roaming. This is added to the PMKSA cache and is used in
++ * subsequent connections with PMKSA caching.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11,
++ /* This attribute specifies the PMKID used/generated for the current
++ * FILS roam. This is used in subsequent connections with PMKSA caching.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12,
++ /* A 16-bit unsigned value specifying the next sequence number to use
++ * in ERP message in the currently associated realm. This is used in
++ * doing subsequent ERP based connections in the same realm.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
++ /* A 16-bit unsigned value representing the reasons for the roaming.
++ * Defined by enum qca_roam_reason.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
++
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+@@ -211,6 +991,43 @@
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+ };
+
++enum qca_wlan_vendor_attr_p2p_listen_offload {
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0,
++ /* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one
++ * of the social channels.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
++ /* A 32-bit unsigned value; the P2P listen offload period (ms).
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
++ /* A 32-bit unsigned value; the P2P listen interval duration (ms).
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
++ /* A 32-bit unsigned value; number of interval times the firmware needs
++ * to run the offloaded P2P listen operation before it stops.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
++ /* An array of arbitrary binary data with one or more 8-byte values.
++ * The device types include both primary and secondary device types.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
++ /* An array of unsigned 8-bit characters; vendor information elements.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
++ /* A 32-bit unsigned value; a control flag to indicate whether listen
++ * results need to be flushed to wpa_supplicant.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG,
++ /* A 8-bit unsigned value; reason code for P2P listen offload stop
++ * event.
++ */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX =
++ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1
++};
++
+ enum qca_wlan_vendor_attr_acs_offload {
+ QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+@@ -247,11 +1064,38 @@
+ * after roaming, rather than having the user space wpa_supplicant do it.
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
+ * band selection based on channel selection results.
++ * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
++ * simultaneous off-channel operations.
++ * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P
++ * Listen offload; a mechanism where the station's firmware takes care of
++ * responding to incoming Probe Request frames received from other P2P
++ * Devices whilst in Listen state, rather than having the user space
++ * wpa_supplicant do it. Information from received P2P requests are
++ * forwarded from firmware to host whenever the host processor wakes up.
++ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA
++ * specific features.
++ * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific
++ * features.
++ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON
++ * specific features only. If a Device sets this bit but not the
++ * %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
++ * this Device may not support all OCE AP functionalities but can support
++ * only OCE STA-CFON functionalities.
++ * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
++ * managed regulatory.
++ * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
+ */
+ enum qca_wlan_vendor_features {
+ QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
++ QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
++ QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3,
++ QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4,
++ QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
++ QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
++ QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
++ QCA_WLAN_VENDOR_FEATURE_TWT = 8,
+ NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
+ };
+
+@@ -277,6 +1121,112 @@
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
+ };
+
++/**
++ * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set
++ * OCB config
++ *
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the
++ * configuration
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be
++ * scheduled
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel
++ * information
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL
++ * active state configuration
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as
++ * OCB_CONFIG_FLAG_80211_FRAME_MODE
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to
++ * use in the case that a packet is sent without a TX control header
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the
++ * last TA received that the local time set by TA is synchronous to other
++ * communicating OCB STAs.
++ */
++enum qca_wlan_vendor_attr_ocb_set_config {
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set
++ * UTC time
++ *
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of
++ * 10 bytes
++ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of
++ * 5 bytes
++ */
++enum qca_wlan_vendor_attr_ocb_set_utc_time {
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX =
++ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes
++ * to start sending timing advert frames
++ *
++ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency
++ * on which to send the frames
++ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times
++ * the frame is sent in 5 seconds
++ */
++enum qca_wlan_vendor_attr_ocb_start_timing_advert {
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1,
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2,
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX =
++ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes
++ * to stop timing advert
++ *
++ * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel
++ * frequency on which to stop the timing advert
++ */
++enum qca_wlan_vendor_attr_ocb_stop_timing_advert {
++ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1,
++ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX =
++ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to
++ * get TSF timer value
++ *
++ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the
++ * timer
++ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer
++ */
++enum qca_wlan_vendor_attr_ocb_get_tsf_resp {
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1,
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2,
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX =
++ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1
++};
++
+ enum qca_vendor_attr_get_preferred_freq_list {
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
+ /* A 32-unsigned value; the interface type/mode for which the preferred
+@@ -289,6 +1239,12 @@
+ * from kernel space to user space.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
++ /* An array of nested values as per enum qca_wlan_vendor_attr_pcl
++ * attribute. Each element contains frequency (MHz), weight, and flag
++ * bit mask indicating how the frequency should be used in P2P
++ * negotiation; sent from kernel space to user space.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
+@@ -324,6 +1280,67 @@
+ QCA_SETBAND_2G,
+ };
+
++/**
++ * enum qca_access_policy - Access control policy
++ *
++ * Access control policy is applied on the configured IE
++ * (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE).
++ * To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY.
++ *
++ * @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match
++ * the specific configuration (IE) set, i.e., allow all the
++ * connections which do not match the configuration.
++ * @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match
++ * the specific configuration (IE) set, i.e., deny all the
++ * connections which do not match the configuration.
++ */
++enum qca_access_policy {
++ QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED,
++ QCA_ACCESS_POLICY_DENY_UNLESS_LISTED,
++};
++
++/**
++ * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture
++ * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32)
++ * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value
++ * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized
++ * SOC timer value at TSF capture
++ */
++enum qca_vendor_attr_tsf_cmd {
++ QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_TSF_CMD,
++ QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
++ QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
++ QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TSF_MAX =
++ QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_tsf_operation: TSF driver commands
++ * @QCA_TSF_CAPTURE: Initiate TSF Capture
++ * @QCA_TSF_GET: Get TSF capture value
++ * @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
++ */
++enum qca_tsf_cmd {
++ QCA_TSF_CAPTURE,
++ QCA_TSF_GET,
++ QCA_TSF_SYNC_GET,
++};
++
++/**
++ * enum qca_vendor_attr_wisa_cmd
++ * @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32)
++ * WISA setup vendor commands
++ */
++enum qca_vendor_attr_wisa_cmd {
++ QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_WISA_MODE,
++ QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_WISA_MAX =
++ QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1
++};
++
+ /* IEEE 802.11 Vendor Specific elements */
+
+ /**
+@@ -349,9 +1366,5347 @@
+ *
+ * This vendor element may be included in GO Negotiation Request, P2P
+ * Invitation Request, and Provision Discovery Request frames.
++ *
++ * @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element.
++ * This element can be used for pre-standard publication testing of HE
++ * before P802.11ax draft assigns the element ID. The payload of this
++ * vendor specific element is defined by the latest P802.11ax draft.
++ * Please note that the draft is still work in progress and this element
++ * payload is subject to change.
++ *
++ * @QCA_VENDOR_ELEM_HE_OPER: HE Operation element.
++ * This element can be used for pre-standard publication testing of HE
++ * before P802.11ax draft assigns the element ID. The payload of this
++ * vendor specific element is defined by the latest P802.11ax draft.
++ * Please note that the draft is still work in progress and this element
++ * payload is subject to change.
++ *
++ * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set
++ * element).
++ * This element can be used for pre-standard publication testing of HE
++ * before P802.11ax draft assigns the element ID extension. The payload of
++ * this vendor specific element is defined by the latest P802.11ax draft
++ * (not including the Element ID Extension field). Please note that the
++ * draft is still work in progress and this element payload is subject to
++ * change.
++ *
++ * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element.
++ * This element can be used for pre-standard publication testing of HE
++ * before P802.11ax draft assigns the element ID extension. The payload of
++ * this vendor specific element is defined by the latest P802.11ax draft
++ * (not including the Element ID Extension field). Please note that the
++ * draft is still work in progress and this element payload is subject to
++ * change.
++ *
++ * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element.
++ * This element can be used for pre-standard publication testing of HE
++ * before P802.11ax draft assigns the element ID extension. The payload of
++ * this vendor specific element is defined by the latest P802.11ax draft
++ * (not including the Element ID Extension field). Please note that the
++ * draft is still work in progress and this element payload is subject to
++ * change.
+ */
+ enum qca_vendor_element_id {
+ QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
++ QCA_VENDOR_ELEM_HE_CAPAB = 1,
++ QCA_VENDOR_ELEM_HE_OPER = 2,
++ QCA_VENDOR_ELEM_RAPS = 3,
++ QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
++ QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
+ };
+
++/**
++ * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
++ * with frequencies to be scanned (in MHz)
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
++ * rates to be included
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
++ * at non CCK rate in 2GHz band
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
++ * driver for the specific scan request
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
++ * request decoded as in enum scan_status
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
++ * scan flag is set
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
++ * randomisation
++ * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the
++ * specific BSSID to scan for.
++ */
++enum qca_wlan_vendor_attr_scan {
++ QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
++ QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1,
++ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2,
++ QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3,
++ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4,
++ QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5,
++ QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6,
++ QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7,
++ QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8,
++ QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9,
++ QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10,
++ QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11,
++ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
++ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
++};
++
++/**
++ * enum scan_status - Specifies the valid values the vendor scan attribute
++ * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
++ *
++ * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
++ * new scan results
++ * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
++ */
++enum scan_status {
++ VENDOR_SCAN_STATUS_NEW_RESULTS,
++ VENDOR_SCAN_STATUS_ABORTED,
++ VENDOR_SCAN_STATUS_MAX,
++};
++
++/**
++ * enum qca_vendor_attr_ota_test - Specifies the values for vendor
++ * command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST
++ * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test
++ */
++enum qca_vendor_attr_ota_test {
++ QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID,
++ /* 8-bit unsigned value to indicate if OTA test is enabled */
++ QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX =
++ QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_vendor_attr_txpower_scale - vendor sub commands index
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value
++ */
++enum qca_vendor_attr_txpower_scale {
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID,
++ /* 8-bit unsigned value to indicate the scaling of tx power */
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX =
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease
++ *
++ * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB.
++ */
++enum qca_vendor_attr_txpower_decr_db {
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
++ /* 8-bit unsigned value to indicate the reduction of TX power in dB for
++ * a virtual interface.
++ */
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX =
++ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
++};
++
++/* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and
++ * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands.
++ */
++enum qca_wlan_vendor_attr_config {
++ QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0,
++ /* Unsigned 32-bit value to set the DTIM period.
++ * Whether the wifi chipset wakes at every dtim beacon or a multiple of
++ * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3
++ * DTIM beacons.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1,
++ /* Unsigned 32-bit value to set the wifi_iface stats averaging factor
++ * used to calculate statistics like average the TSF offset or average
++ * number of frame leaked.
++ * For instance, upon Beacon frame reception:
++ * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000
++ * For instance, when evaluating leaky APs:
++ * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2,
++ /* Unsigned 32-bit value to configure guard time, i.e., when
++ * implementing IEEE power management based on frame control PM bit, how
++ * long the driver waits before shutting down the radio and after
++ * receiving an ACK frame for a Data frame with PM bit set.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3,
++ /* Unsigned 32-bit value to change the FTM capability dynamically */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4,
++ /* Unsigned 16-bit value to configure maximum TX rate dynamically */
++ QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5,
++ /* Unsigned 32-bit value to configure the number of continuous
++ * Beacon Miss which shall be used by the firmware to penalize
++ * the RSSI.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6,
++ /* Unsigned 8-bit value to configure the channel avoidance indication
++ * behavior. Firmware to send only one indication and ignore duplicate
++ * indications when set to avoid multiple Apps wakeups.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
++ /* 8-bit unsigned value to configure the maximum TX MPDU for
++ * aggregation.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
++ /* 8-bit unsigned value to configure the maximum RX MPDU for
++ * aggregation.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
++ /* 8-bit unsigned value to configure the Non aggregrate/11g sw
++ * retry threshold (0 disable, 31 max).
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
++ /* 8-bit unsigned value to configure the aggregrate sw
++ * retry threshold (0 disable, 31 max).
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
++ /* 8-bit unsigned value to configure the MGMT frame
++ * retry threshold (0 disable, 31 max).
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
++ /* 8-bit unsigned value to configure the CTRL frame
++ * retry threshold (0 disable, 31 max).
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
++ /* 8-bit unsigned value to configure the propagation delay for
++ * 2G/5G band (0~63, units in us)
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
++ /* Unsigned 32-bit value to configure the number of unicast TX fail
++ * packet count. The peer is disconnected once this threshold is
++ * reached.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
++ /* Attribute used to set scan default IEs to the driver.
++ *
++ * These IEs can be used by scan operations that will be initiated by
++ * the driver/firmware.
++ *
++ * For further scan requests coming to the driver, these IEs should be
++ * merged with the IEs received along with scan request coming to the
++ * driver. If a particular IE is present in the scan default IEs but not
++ * present in the scan request, then that IE should be added to the IEs
++ * sent in the Probe Request frames for that scan request.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
++ /* Unsigned 32-bit attribute for generic commands */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
++ /* Unsigned 32-bit value attribute for generic commands */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18,
++ /* Unsigned 32-bit data attribute for generic command response */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
++ /* Unsigned 32-bit length attribute for
++ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
++ /* Unsigned 32-bit flags attribute for
++ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
++ /* Unsigned 32-bit, defining the access policy.
++ * See enum qca_access_policy. Used with
++ * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
++ /* Sets the list of full set of IEs for which a specific access policy
++ * has to be applied. Used along with
++ * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
++ * Zero length payload can be used to clear this access constraint.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
++ /* Unsigned 32-bit, specifies the interface index (netdev) for which the
++ * corresponding configurations are applied. If the interface index is
++ * not specified, the configurations are attributed to the respective
++ * wiphy.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
++ /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
++ /* 8-bit unsigned value to configure the driver and below layers to
++ * ignore the assoc disallowed set by APs while connecting
++ * 1-Ignore, 0-Don't ignore
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
++ /* 32-bit unsigned value to trigger antenna diversity features:
++ * 1-Enable, 0-Disable
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
++ /* 32-bit unsigned value to configure specific chain antenna */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
++ /* 32-bit unsigned value to trigger cycle selftest
++ * 1-Enable, 0-Disable
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
++ /* 32-bit unsigned to configure the cycle time of selftest
++ * the unit is micro-second
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
++ /* 32-bit unsigned value to set reorder timeout for AC_VO */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
++ /* 32-bit unsigned value to set reorder timeout for AC_VI */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32,
++ /* 32-bit unsigned value to set reorder timeout for AC_BE */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33,
++ /* 32-bit unsigned value to set reorder timeout for AC_BK */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
++ /* 6-byte MAC address to point out the specific peer */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
++ /* 32-bit unsigned value to set window size for specific peer */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
++ /* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
++ /* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
++ /* 32-bit unsigned value to configure 5 or 10 MHz channel width for
++ * station device while in disconnect state. The attribute use the
++ * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz,
++ * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
++ * 10 MHz channel width, the station will not connect to a BSS using 20
++ * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
++ * clear this constraint.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
++ /* 32-bit unsigned value to configure the propagation absolute delay
++ * for 2G/5G band (units in us)
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
++ /* 32-bit unsigned value to set probe period */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
++ /* 32-bit unsigned value to set stay period */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42,
++ /* 32-bit unsigned value to set snr diff */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43,
++ /* 32-bit unsigned value to set probe dwell time */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44,
++ /* 32-bit unsigned value to set mgmt snr weight */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45,
++ /* 32-bit unsigned value to set data snr weight */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46,
++ /* 32-bit unsigned value to set ack snr weight */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47,
++ /* 32-bit unsigned value to configure the listen interval.
++ * This is in units of beacon intervals. This configuration alters
++ * the negotiated listen interval with the AP during the connection.
++ * It is highly recommended to configure a value less than or equal to
++ * the one negotiated during the association. Configuring any greater
++ * value can have adverse effects (frame loss, AP disassociating STA,
++ * etc.).
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48,
++ /*
++ * 8 bit unsigned value that is set on an AP/GO virtual interface to
++ * disable operations that would cause the AP/GO to leave its operating
++ * channel.
++ *
++ * This will restrict the scans to the AP/GO operating channel and the
++ * channels of the other band, if DBS is supported.A STA/CLI interface
++ * brought up after this setting is enabled, will be restricted to
++ * connecting to devices only on the AP/GO interface's operating channel
++ * or on the other band in DBS case. P2P supported channel list is
++ * modified, to only include AP interface's operating-channel and the
++ * channels of the other band if DBS is supported.
++ *
++ * These restrictions are only applicable as long as the AP/GO interface
++ * is alive. If the AP/GO interface is brought down then this
++ * setting/restriction is forgotten.
++ *
++ * If this variable is set on an AP/GO interface while a multi-channel
++ * concurrent session is active, it has no effect on the operation of
++ * the current interfaces, other than restricting the scan to the AP/GO
++ * operating channel and the other band channels if DBS is supported.
++ * However, if the STA is brought down and restarted then the new STA
++ * connection will either be formed on the AP/GO channel or on the
++ * other band in a DBS case. This is because of the scan being
++ * restricted on these channels as mentioned above.
++ *
++ * 1-Restrict / 0-Don't restrict offchannel operations.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49,
++ /*
++ * 8 bit unsigned value to enable/disable LRO (Large Receive Offload)
++ * on an interface.
++ * 1 - Enable, 0 - Disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50,
++
++ /*
++ * 8 bit unsigned value to globally enable/disable scan
++ * 1 - Enable, 0 - Disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
++
++ /* 8-bit unsigned value to set the total beacon miss count
++ * This parameter will set the total beacon miss count.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
++
++ /* Unsigned 32-bit value to configure the number of continuous
++ * Beacon Miss which shall be used by the firmware to penalize
++ * the RSSI for BTC.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53,
++
++ /* 8-bit unsigned value to configure the driver and below layers to
++ * enable/disable all FILS features.
++ * 0-enable, 1-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
++
++ /* 16-bit unsigned value to configure the level of WLAN latency
++ * module. See enum qca_wlan_vendor_attr_config_latency_level.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55,
++
++ /* 8-bit unsigned value indicating the driver to use the RSNE as-is from
++ * the connect interface. Exclusively used for the scenarios where the
++ * device is used as a test bed device with special functionality and
++ * not recommended for production. This helps driver to not validate the
++ * RSNE passed from user space and thus allow arbitrary IE data to be
++ * used for testing purposes.
++ * 1-enable, 0-disable.
++ * Applications set/reset this configuration. If not reset, this
++ * parameter remains in use until the driver is unloaded.
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56,
++
++ /* 8-bit unsigned value to trigger green Tx power saving.
++ * 1-Enable, 0-Disable
++ */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
++ */
++enum qca_wlan_vendor_attr_sap_config {
++ QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0,
++ /* 1 - reserved for QCA */
++ /* List of frequencies on which AP is expected to operate.
++ * This is irrespective of ACS configuration. This list is a priority
++ * based one and is looked for before the AP is created to ensure the
++ * best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in
++ * the system.
++ */
++ QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2,
++
++ QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP
++ * conditional channel switch
++ */
++enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
++ /* Priority based frequency list (an array of u32 values in host byte
++ * order)
++ */
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
++ /* Status of the conditional switch (u32).
++ * 0: Success, Non-zero: Failure
++ */
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2,
++
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX =
++ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_gpio_attr - Parameters for GPIO configuration
++ */
++enum qca_wlan_gpio_attr {
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
++ /* Unsigned 32-bit attribute for GPIO command */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND,
++ /* Unsigned 32-bit attribute for GPIO PIN number to configure */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM,
++ /* Unsigned 32-bit attribute for GPIO value to configure */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE,
++ /* Unsigned 32-bit attribute for GPIO pull type */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE,
++ /* Unsigned 32-bit attribute for GPIO interrupt mode */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX =
++ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1
++};
++
++/**
++ * qca_wlan_set_qdepth_thresh_attr - Parameters for setting
++ * MSDUQ depth threshold per peer per tid in the target
++ *
++ * Associated Vendor Command:
++ * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH
++ */
++enum qca_wlan_set_qdepth_thresh_attr {
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0,
++ /* 6-byte MAC address */
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR,
++ /* Unsigned 32-bit attribute for holding the TID */
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID,
++ /* Unsigned 32-bit attribute for holding the update mask
++ * bit 0 - Update high priority msdu qdepth threshold
++ * bit 1 - Update low priority msdu qdepth threshold
++ * bit 2 - Update UDP msdu qdepth threshold
++ * bit 3 - Update Non UDP msdu qdepth threshold
++ * rest of bits are reserved
++ */
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK,
++ /* Unsigned 32-bit attribute for holding the threshold value */
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST,
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX =
++ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
++ */
++enum qca_wlan_vendor_attr_get_hw_capability {
++ QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID,
++ /* Antenna isolation
++ * An attribute used in the response.
++ * The content of this attribute is encoded in a byte array. Each byte
++ * value is an antenna isolation value. The array length is the number
++ * of antennas.
++ */
++ QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
++ /* Request HW capability
++ * An attribute used in the request.
++ * The content of this attribute is a u32 array for one or more of
++ * hardware capabilities (attribute IDs) that are being requested. Each
++ * u32 value has a value from this
++ * enum qca_wlan_vendor_attr_get_hw_capability
++ * identifying which capabilities are requested.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX =
++ QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring
++ * offload which is an extension for LL_STATS.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms.
++ * If MAC counters do not exceed the threshold, FW will report monitored
++ * link layer counters periodically as this setting. The first report is
++ * always triggered by this timer.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99).
++ * For each MAC layer counter, FW holds two copies. One is the current value.
++ * The other is the last report. Once a current counter's increment is larger
++ * than the threshold, FW will indicate that counter to host even if the
++ * monitoring timer does not expire.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same
++ * failure code.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code
++ * 1: TX packet discarded
++ * 2: No ACK
++ * 3: Postpone
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold.
++ * Threshold for all monitored parameters. If per counter dedicated threshold
++ * is not enabled, this threshold will take effect.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this
++ * event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters
++ * Bit0: TX counter unit in MSDU
++ * Bit1: TX counter unit in MPDU
++ * Bit2: TX counter unit in PPDU
++ * Bit3: TX counter unit in byte
++ * Bit4: Dropped MSDUs
++ * Bit5: Dropped Bytes
++ * Bit6: MPDU retry counter
++ * Bit7: MPDU failure counter
++ * Bit8: PPDU failure counter
++ * Bit9: MPDU aggregation counter
++ * Bit10: MCS counter for ACKed MPDUs
++ * Bit11: MCS counter for Failed MPDUs
++ * Bit12: TX Delay counter
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters
++ * Bit0: MAC RX counter unit in MPDU
++ * Bit1: MAC RX counter unit in byte
++ * Bit2: PHY RX counter unit in PPDU
++ * Bit3: PHY RX counter unit in byte
++ * Bit4: Disorder counter
++ * Bit5: Retry counter
++ * Bit6: Duplication counter
++ * Bit7: Discard counter
++ * Bit8: MPDU aggregation size counter
++ * Bit9: MCS counter
++ * Bit10: Peer STA power state change (wake to sleep) counter
++ * Bit11: Peer STA power save counter, total time in PS mode
++ * Bit12: Probe request counter
++ * Bit13: Other management frames counter
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA
++ * Bit0: Idle time
++ * Bit1: TX time
++ * Bit2: time RX in current bss
++ * Bit3: Out of current bss time
++ * Bit4: Wireless medium busy time
++ * Bit5: RX in bad condition time
++ * Bit6: TX in bad condition time
++ * Bit7: time wlan card not available
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal
++ * Bit0: Per channel SNR counter
++ * Bit1: Per channel noise floor counter
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM:
++ * aggregation stats buffer length
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats
++ * buffer for ACKed MPDUs.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats
++ * buffer for failed MPDUs.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE:
++ * length of delay stats array.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets
++ * flagged as retransmissions
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets
++ * flagged as duplicated
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX
++ * packets discarded
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation
++ * stats buffer.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs
++ * stats buffer.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe
++ * requests received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt
++ * frames received
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time
++ * there is no TX, nor RX, nor interference.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time
++ * transmitting packets.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time
++ * for receiving.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time
++ * interference detected.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time
++ * receiving packets with errors.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time
++ * TX no-ACK.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time
++ * the chip is unable to work in normal conditions.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time
++ * receiving packets in current BSS.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time
++ * receiving packets not in current BSS.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL:
++ * This is a container for per antenna signal stats.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64
++ * Absolute timestamp from 1970/1/1, unit in ms. After receiving the
++ * message, user layer APP could call gettimeofday to get another
++ * timestamp and calculate transfer delay for the message.
++ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32
++ * Real period for this measurement, unit in us.
++ */
++enum qca_wlan_vendor_attr_ll_stats_ext {
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
++
++ /* Attributes for configurations */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD,
++
++ /* Peer STA power state change */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG,
++
++ /* TX failure event */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS,
++
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS,
++
++ /* MAC counters */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER,
++
++ /* Sub-attributes for PEER_AC_TX */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY,
++
++ /* Sub-attributes for PEER_AC_RX */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT,
++
++ /* Sub-attributes for CCA_BSS */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL,
++
++ /* sub-attribute for BSS_RX_TIME */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME,
++
++ /* Sub-attributes for PEER_SIGNAL */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF,
++
++ /* Sub-attributes for IFACE_BSS */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON,
++
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
++
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
++};
++
++/* Attributes for FTM commands and events */
++
++/**
++ * enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities
++ *
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See
++ * enum qca_wlan_vendor_attr_loc_capa_flags.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number
++ * of measurement sessions that can run concurrently.
++ * Default is one session (no session concurrency).
++ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique
++ * peers that are supported in running sessions. For example,
++ * if the value is 8 and maximum number of sessions is 2, you can
++ * have one session with 8 unique peers, or 2 sessions with 4 unique
++ * peers each, and so on.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number
++ * of bursts per peer, as an exponent (2^value). Default is 0,
++ * meaning no multi-burst support.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number
++ * of measurement exchanges allowed in a single burst.
++ * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement
++ * types. A bit mask (unsigned 32 bit value), each bit corresponds
++ * to an AOA type as defined by enum qca_vendor_attr_aoa_type.
++ */
++enum qca_wlan_vendor_attr_loc_capa {
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS,
++ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS,
++ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS,
++ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP,
++ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST,
++ QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX =
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags
++ *
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver
++ * can be configured as an FTM responder (for example, an AP that
++ * services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
++ * will be supported if set.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
++ * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
++ * will be supported if set.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
++ * supports immediate (ASAP) response.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
++ * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports
++ * requesting AOA measurements as part of an FTM session.
++ */
++enum qca_wlan_vendor_attr_loc_capa_flags {
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3,
++ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_peer_info: Information about
++ * a single peer in a measurement session.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related
++ * to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of
++ * FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0
++ * 9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for
++ * list of supported attributes.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for
++ * secure measurement.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
++ * measurement every bursts. If 0 or not specified,
++ * AOA measurements will be disabled for this peer.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
++ * the measurement frames are exchanged. Optional; if not
++ * specified, try to locate the peer in the kernel scan
++ * results cache and use frequency from there.
++ */
++enum qca_wlan_vendor_attr_ftm_peer_info {
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags,
++ * per-peer
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request
++ * immediate (ASAP) response from peer.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request
++ * LCI report from peer. The LCI report includes the absolute
++ * location of the peer in "official" coordinates (similar to GPS).
++ * See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request
++ * Location civic report from peer. The LCR includes the location
++ * of the peer in free-form format. See IEEE P802.11-REVmc/D7.0,
++ * 11.24.6.7 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set,
++ * request a secure measurement.
++ * QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided.
++ */
++enum qca_wlan_vendor_attr_ftm_peer_meas_flags {
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements
++ * to perform in a single burst.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to
++ * perform, specified as an exponent (2^value).
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst
++ * instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts,
++ * as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must
++ * be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION.
++ */
++enum qca_wlan_vendor_attr_ftm_meas_param {
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID,
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST,
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP,
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION,
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX =
++ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported
++ * peer.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement
++ * request for this peer.
++ * See enum qca_wlan_vendor_attr_ftm_peer_result_status.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related
++ * to measurement results for this peer.
++ * See enum qca_wlan_vendor_attr_ftm_peer_result_flags.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when
++ * request failed and peer requested not to send an additional request
++ * for this number of seconds.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received
++ * from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
++ * 9.4.2.22.10.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when
++ * received from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
++ * 9.4.2.22.13.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer
++ * overridden some measurement request parameters. See
++ * enum qca_wlan_vendor_attr_ftm_meas_param.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement
++ * for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement
++ * results. Each entry is a nested attribute defined
++ * by enum qca_wlan_vendor_attr_ftm_meas.
++ */
++enum qca_wlan_vendor_attr_ftm_peer_result {
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX =
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_peer_result_status
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results
++ * will be provided. Peer may have overridden some measurement parameters,
++ * in which case overridden parameters will be report by
++ * QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable
++ * of performing the measurement request. No more results will be sent
++ * for this peer in this session.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request
++ * failed, and requested not to send an additional request for number
++ * of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS
++ * attribute.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation
++ * failed. Request was not sent over the air.
++ */
++enum qca_wlan_vendor_attr_ftm_peer_result_status {
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED,
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags
++ * for measurement result, per-peer
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set,
++ * measurement completed for this peer. No more results will be reported
++ * for this peer in this session.
++ */
++enum qca_wlan_vendor_attr_ftm_peer_result_flags {
++ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0,
++};
++
++/**
++ * enum qca_vendor_attr_loc_session_status: Session completion status code
++ *
++ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed
++ * successfully.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted
++ * by request.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request
++ * was invalid and was not started.
++ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error
++ * and did not complete normally (for example out of resources).
++ */
++enum qca_vendor_attr_loc_session_status {
++ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK,
++ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED,
++ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID,
++ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as
++ * recorded by responder, in picoseconds.
++ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at
++ * initiator, in picoseconds.
++ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by
++ * initiator, in picoseconds.
++ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at
++ * responder, in picoseconds.
++ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded
++ * during this measurement exchange. Optional and will be provided if
++ * the hardware can measure it.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by
++ * responder. Not always provided.
++ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by
++ * responder. Not always provided.
++ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by
++ * initiator. Not always provided.
++ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by
++ * initiator. Not always provided.
++ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
++ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding.
++ */
++enum qca_wlan_vendor_attr_ftm_meas {
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX =
++ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_aoa_type - AOA measurement type
++ *
++ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest
++ * CIR (channel impulse response) path for each antenna.
++ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude
++ * of the strongest CIR path for each antenna.
++ */
++enum qca_wlan_vendor_attr_aoa_type {
++ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE,
++ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP,
++ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX
++};
++
++/**
++ * enum qca_wlan_vendor_attr_encryption_test - Attributes to
++ * validate encryption engine
++ *
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute.
++ * This will be included if the request is for decryption; if not included,
++ * the request is treated as a request for encryption by default.
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value
++ * indicating the key cipher suite. Takes same values as
++ * NL80211_ATTR_KEY_CIPHER.
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value
++ * Key Id to be used for encryption
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values.
++ * Key (TK) to be used for encryption/decryption
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values.
++ * Packet number to be specified for encryption/decryption
++ * 6 bytes for TKIP/CCMP/GCMP.
++ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values
++ * representing the 802.11 packet (header + payload + FCS) that
++ * needs to be encrypted/decrypted.
++ * Encrypted/decrypted response from the driver will also be sent
++ * to userspace with the same attribute.
++ */
++enum qca_wlan_vendor_attr_encryption_test {
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX =
++ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of
++ * sector for DMG RF sector operations.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector
++ */
++enum qca_wlan_vendor_attr_dmg_rf_sector_type {
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX
++};
++
++/**
++ * enum qca_wlan_vendor_attr_fw_state - State of firmware
++ *
++ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state
++ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active
++ */
++enum qca_wlan_vendor_attr_fw_state {
++ QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR,
++ QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE,
++ QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX
++};
++
++/**
++ * BRP antenna limit mode
++ *
++ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
++ * antenna limit, BRP will be performed as usual.
++ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal
++ * antennas limit. the hardware may use less antennas than the
++ * maximum limit.
++ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will
++ * use exactly the specified number of antennas for BRP.
++ */
++enum qca_wlan_vendor_attr_brp_ant_limit_mode {
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE,
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE,
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE,
++ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX
++};
++
++/**
++ * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
++ * DMG RF sector configuration for a single RF module.
++ * The values are defined in a compact way which closely matches
++ * the way it is stored in HW registers.
++ * The configuration provides values for 32 antennas and 8 distribution
++ * amplifiers, and together describes the characteristics of the RF
++ * sector - such as a beam in some direction with some gain.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index
++ * of RF module for this configuration.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge
++ * amplifier gain index. Unsigned 32 bit number containing
++ * bits for all 32 antennas.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge
++ * amplifier gain index. Unsigned 32 bit number containing
++ * bits for all 32 antennas.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge
++ * amplifier gain index. Unsigned 32 bit number containing
++ * bits for all 32 antennas.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values
++ * for first 16 antennas, 2 bits per antenna.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values
++ * for last 16 antennas, 2 bits per antenna.
++ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains
++ * DTYPE values (3 bits) for each distribution amplifier, followed
++ * by X16 switch bits for each distribution amplifier. There are
++ * total of 8 distribution amplifiers.
++ */
++enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX =
++ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
++};
++
++enum qca_wlan_vendor_attr_ll_stats_set {
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_ll_stats_clr {
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
++ /* Unsigned 32bit bitmap for clearing statistics
++ * All radio statistics 0x00000001
++ * cca_busy_time (within radio statistics) 0x00000002
++ * All channel stats (within radio statistics) 0x00000004
++ * All scan statistics (within radio statistics) 0x00000008
++ * All interface statistics 0x00000010
++ * All tx rate statistics (within interface statistics) 0x00000020
++ * All ac statistics (with in interface statistics) 0x00000040
++ * All contention (min, max, avg) statistics (within ac statisctics)
++ * 0x00000080.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1,
++ /* Unsigned 8 bit value: Request to stop statistics collection */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2,
++
++ /* Unsigned 32 bit bitmap: Response from the driver
++ * for the cleared statistics
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3,
++ /* Unsigned 8 bit value: Response from driver/firmware
++ * for the stop request
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX =
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_ll_stats_get {
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
++ /* Unsigned 32 bit value provided by the caller issuing the GET stats
++ * command. When reporting the stats results, the driver uses the same
++ * value to indicate which GET request the results correspond to.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1,
++ /* Unsigned 32 bit value - bit mask to identify what statistics are
++ * requested for retrieval.
++ * Radio Statistics 0x00000001
++ * Interface Statistics 0x00000020
++ * All Peer Statistics 0x00000040
++ * Peer Statistics 0x00000080
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_ll_stats_results {
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
++ /* Unsigned 32bit value. Used by the driver; must match the request id
++ * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5,
++ /* Signed 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6,
++ /* Signed 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7,
++ /* Signed 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
++ * nested within the interface stats.
++ */
++
++ /* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
++ * Type = enum wifi_interface_mode.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9,
++ /* Interface MAC address. An array of 6 Unsigned int8 */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10,
++ /* Type = enum wifi_connection_state, e.g., DISCONNECTED,
++ * AUTHENTICATING, etc. valid for STA, CLI only.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11,
++ /* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12,
++ /* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13,
++ /* NULL terminated SSID. An array of 33 Unsigned 8bit values */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14,
++ /* BSSID. An array of 6 unsigned 8 bit values */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15,
++ /* Country string advertised by AP. An array of 3 unsigned 8 bit
++ * values.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16,
++ /* Country string for this association. An array of 3 unsigned 8 bit
++ * values.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
++ * be nested within the interface stats.
++ */
++
++ /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26,
++ /* Unsigned int 32 value corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27,
++ /* Unsigned int 32 values corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28,
++ /* Unsigned int 32 values corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29,
++ /* Unsigned int 32 values corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30,
++ /* Unsigned int 32 values corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31,
++ /* Unsigned int 32 values corresponding to respective AC */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32,
++ /* Unsigned 32 bit value. Number of peers */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
++ * nested within the interface stats.
++ */
++
++ /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34,
++ /* MAC addr corresponding to respective peer. An array of 6 unsigned
++ * 8 bit values.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35,
++ /* Unsigned int 32 bit value representing capabilities corresponding
++ * to respective peer.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36,
++ /* Unsigned 32 bit value. Number of rates */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
++ * are nested within the rate stat.
++ */
++
++ /* Wi-Fi Rate - separate attributes defined for individual fields */
++
++ /* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38,
++ /* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39,
++ /* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
++ /* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
++ * in the units of 0.5 Mbps HT/VHT it would be MCS index
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
++
++ /* Unsigned 32 bit value. Bit rate in units of 100 kbps */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
++ * nested within the peer info stats.
++ */
++
++ /* Unsigned int 32 bit value. Number of successfully transmitted data
++ * packets, i.e., with ACK received corresponding to the respective
++ * rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43,
++ /* Unsigned int 32 bit value. Number of received data packets
++ * corresponding to the respective rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44,
++ /* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK
++ * received corresponding to the respective rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45,
++ /* Unsigned int 32 bit value. Total number of data packet retries for
++ * the respective rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46,
++ /* Unsigned int 32 bit value. Total number of short data packet retries
++ * for the respective rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47,
++ /* Unsigned int 32 bit value. Total number of long data packet retries
++ * for the respective rate.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48,
++
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake
++ * accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50,
++ /* Unsigned 32 bit value. Total number of msecs the radio is
++ * transmitting accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51,
++ /* Unsigned 32 bit value. Total number of msecs the radio is in active
++ * receive accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to all scan accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to NAN accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to GSCAN accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to roam scan accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to PNO scan accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57,
++ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
++ * to Hotspot 2.0 scans and GAS exchange accruing over time.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58,
++ /* Unsigned 32 bit value. Number of channels. */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could
++ * be nested within the channel stats.
++ */
++
++ /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60,
++ /* Unsigned 32 bit value. Primary 20 MHz channel. */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61,
++ /* Unsigned 32 bit value. Center frequency (MHz) first segment. */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62,
++ /* Unsigned 32 bit value. Center frequency (MHz) second segment. */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63,
++
++ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be
++ * nested within the radio stats.
++ */
++
++ /* Unsigned int 32 bit value representing total number of msecs the
++ * radio is awake on that channel accruing over time, corresponding to
++ * the respective channel.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64,
++ /* Unsigned int 32 bit value representing total number of msecs the CCA
++ * register is busy accruing over time corresponding to the respective
++ * channel.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65,
++
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
++
++ /* Signifies the nested list of channel attributes
++ * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
++
++ /* Signifies the nested list of peer info attributes
++ * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_*
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68,
++
++ /* Signifies the nested list of rate info attributes
++ * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69,
++
++ /* Signifies the nested list of wmm info attributes
++ * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_*
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70,
++
++ /* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates
++ * that more stats, e.g., peers or radio, are to follow in the next
++ * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
++ * Otherwise, it is set to 0.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71,
++
++ /* Unsigned 64 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77,
++
++ /* Number of msecs the radio spent in transmitting for each power level
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78,
++
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81,
++ /* Unsigned 32 bit value */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
++
++ /* Unsigned int 32 value.
++ * Pending MSDUs corresponding to respective AC.
++ */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
++ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_ll_stats_type {
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3,
++
++ /* keep last */
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST,
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX =
++ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
++ * TDLS configuration to the host driver.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger
++ * mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode
++ * represents the different TDLS trigger modes.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within
++ * which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number
++ * of packets shall meet the criteria for implicit TDLS setup.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets
++ * within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD
++ * to initiate a TDLS setup.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate
++ * a TDLS Discovery to the peer.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of
++ * discovery attempts to know the TDLS capability of the peer. A peer is
++ * marked as TDLS not capable if there is no response for all the attempts.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32)
++ * within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD
++ * number of TX / RX frames meet the criteria for TDLS teardown.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32)
++ * of Tx/Rx packets within a duration
++ * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold
++ * corresponding to the RSSI of the peer below which a TDLS setup is
++ * triggered.
++ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold
++ * corresponding to the RSSI of the peer above which a TDLS teardown is
++ * triggered.
++ */
++enum qca_wlan_vendor_attr_tdls_configuration {
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1,
++
++ /* Attributes configuring the TDLS Implicit Trigger */
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in
++ * the driver
++ *
++ * The following are the different values for
++ * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE.
++ *
++ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown
++ * the TDLS connection to a respective peer comes from the user space.
++ * wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN,
++ * TDLS_DISCOVER to do this.
++ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS
++ * setup/teardown to the eligible peer once the configured criteria
++ * (such as TX/RX threshold, RSSI) is met. The attributes
++ * in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to
++ * the different configuration criteria for the TDLS trigger from the
++ * host driver.
++ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger
++ * the TDLS setup / teardown through the implicit mode only to the
++ * configured MAC addresses (wpa_supplicant, with tdls_external_control=1,
++ * configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands).
++ * External mode works on top of the implicit mode. Thus the host driver
++ * is expected to configure in TDLS Implicit mode too to operate in
++ * External mode.
++ * Configuring External mode alone without Implicit mode is invalid.
++ *
++ * All the above implementations work as expected only when the host driver
++ * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing
++ * that the TDLS message exchange is not internal to the host driver, but
++ * depends on wpa_supplicant to do the message exchange.
++ */
++enum qca_wlan_vendor_tdls_trigger_mode {
++ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0,
++ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1,
++ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2,
++};
++
++/**
++ * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0
++ * that is hard-coded in the Board Data File (BDF).
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1
++ * that is hard-coded in the Board Data File (BDF).
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2
++ * that is hard-coded in the Board Data File (BDF).
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3
++ * that is hard-coded in the Board Data File (BDF).
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4
++ * that is hard-coded in the Board Data File (BDF).
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any
++ * source of SAR power limits, thereby disabling the SAR power
++ * limit feature.
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power
++ * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power
++ * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
++ *
++ * This enumerates the valid set of values that may be supplied for
++ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
++ * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in
++ * the response to an instance of the
++ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
++ */
++enum qca_vendor_attr_sar_limits_selections {
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7,
++};
++
++/**
++ * enum qca_vendor_attr_sar_limits_spec_modulations -
++ * SAR limits specification modulation
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK -
++ * CCK modulation
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM -
++ * OFDM modulation
++ *
++ * This enumerates the valid set of values that may be supplied for
++ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an
++ * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an
++ * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor
++ * command or in the response to an instance of the
++ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
++ */
++enum qca_vendor_attr_sar_limits_spec_modulations {
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1,
++};
++
++/**
++ * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to
++ * select which SAR power limit table should be used. Valid
++ * values are enumerated in enum
++ * %qca_vendor_attr_sar_limits_selections. The existing SAR
++ * power limit selection is unchanged if this attribute is not
++ * present.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value
++ * which specifies the number of SAR power limit specifications
++ * which will follow.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power
++ * limit specifications. The number of specifications is
++ * specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each
++ * specification contains a set of
++ * QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A
++ * specification is uniquely identified by the attributes
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND,
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always
++ * contains as a payload the attribute
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT,
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX.
++ * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is
++ * needed based upon the value of
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to
++ * indicate for which band this specification applies. Valid
++ * values are enumerated in enum %nl80211_band (although not all
++ * bands may be supported by a given device). If the attribute is
++ * not supplied then the specification will be applied to all
++ * supported bands.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value
++ * to indicate for which antenna chain this specification
++ * applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the
++ * attribute is not supplied then the specification will be
++ * applied to all chains.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32)
++ * value to indicate for which modulation scheme this
++ * specification applies. Valid values are enumerated in enum
++ * %qca_vendor_attr_sar_limits_spec_modulations. If the attribute
++ * is not supplied then the specification will be applied to all
++ * modulation schemes.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32)
++ * value to specify the actual power limit value in units of 0.5
++ * dBm (i.e., a value of 11 represents 5.5 dBm).
++ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32)
++ * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles.
++ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
++ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0.
++ *
++ * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS
++ * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS.
++ */
++enum qca_vendor_attr_sar_limits {
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8,
++
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
++ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command.
++ */
++enum qca_wlan_vendor_attr_get_wifi_info {
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1,
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX =
++ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1,
++};
++
++/*
++ * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command.
++ */
++enum qca_wlan_vendor_attr_wifi_logger_start {
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1,
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2,
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX =
++ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_logger_results {
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0,
++
++ /* Unsigned 32-bit value; must match the request Id supplied by
++ * Wi-Fi HAL in the corresponding subcmd NL msg.
++ */
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1,
++
++ /* Unsigned 32-bit value; used to indicate the size of memory
++ * dump to be allocated.
++ */
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX =
++ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_roaming_config_params {
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0,
++
++ QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2,
++
++ /* Attributes for wifi_set_ssid_white_list */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5,
++
++ /* Attributes for set_roam_params */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12,
++
++ /* Attribute for set_lazy_roam */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13,
++
++ /* Attribute for set_lazy_roam with preferences */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
++
++ /* Attribute for set_blacklist bssid params */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
++ /* Flag attribute indicates this BSSID blacklist as a hint */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX =
++ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1,
++};
++
++/*
++ * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
++ */
++enum qca_wlan_vendor_attr_roam_subcmd {
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX =
++ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_gscan_config_params {
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0,
++
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1,
++
++ /* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command.
++ */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND
++ = 2,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS
++ = 3,
++
++ /* Attributes for input params used by
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command.
++ */
++
++ /* Unsigned 32-bit value; channel frequency */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4,
++ /* Unsigned 32-bit value; dwell time in ms. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5,
++ /* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6,
++ /* Unsigned 8-bit value; channel class */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7,
++
++ /* Unsigned 8-bit value; bucket index, 0 based */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8,
++ /* Unsigned 8-bit value; band. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9,
++ /* Unsigned 32-bit value; desired period, in ms. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10,
++ /* Unsigned 8-bit value; report events semantics. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11,
++ /* Unsigned 32-bit value. Followed by a nested array of
++ * GSCAN_CHANNEL_SPEC_* attributes.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12,
++
++ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes.
++ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13,
++
++ /* Unsigned 32-bit value; base timer period in ms. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14,
++ /* Unsigned 32-bit value; number of APs to store in each scan in the
++ * BSSID/RSSI history buffer (keep the highest RSSI APs).
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15,
++ /* Unsigned 8-bit value; in %, when scan buffer is this much full, wake
++ * up AP.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
++ = 16,
++
++ /* Unsigned 8-bit value; number of scan bucket specs; followed by a
++ * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size
++ * of the array is determined by NUM_BUCKETS.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17,
++
++ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes.
++ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18,
++
++ /* Unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH
++ = 19,
++ /* Unsigned 32-bit value; maximum number of results to be returned. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX
++ = 20,
++
++ /* An array of 6 x unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24,
++
++ /* Number of hotlist APs as unsigned 32-bit value, followed by a nested
++ * array of AP_THRESHOLD_PARAM attributes and values. The size of the
++ * array is determined by NUM_AP.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25,
++
++ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes.
++ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26,
++
++ /* Unsigned 32-bit value; number of samples for averaging RSSI. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE
++ = 27,
++ /* Unsigned 32-bit value; number of samples to confirm AP loss. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE
++ = 28,
++ /* Unsigned 32-bit value; number of APs breaching threshold. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29,
++ /* Unsigned 32-bit value; number of APs. Followed by an array of
++ * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30,
++ /* Unsigned 32-bit value; number of samples to confirm AP loss. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE
++ = 31,
++ /* Unsigned 32-bit value. If max_period is non zero or different than
++ * period, then this bucket is an exponential backoff bucket.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32,
++ /* Unsigned 32-bit value. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33,
++ /* Unsigned 32-bit value. For exponential back off bucket, number of
++ * scans to perform for a given period.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34,
++ /* Unsigned 8-bit value; in number of scans, wake up AP after these
++ * many scans.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
++ = 35,
++
++ /* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command.
++ */
++ /* Unsigned 3-2bit value; number of samples to confirm SSID loss. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE
++ = 36,
++ /* Number of hotlist SSIDs as unsigned 32-bit value, followed by a
++ * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The
++ * size of the array is determined by NUM_SSID.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37,
++ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_*
++ * attributes.
++ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38,
++
++ /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39,
++ /* Unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42,
++ /* Unsigned 32-bit value; a bitmask with additional gscan config flag.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX =
++ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_gscan_results {
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0,
++
++ /* Unsigned 32-bit value; must match the request Id supplied by
++ * Wi-Fi HAL in the corresponding subcmd NL msg.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1,
++
++ /* Unsigned 32-bit value; used to indicate the status response from
++ * firmware/driver for the vendor sub-command.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2,
++
++ /* GSCAN Valid Channels attributes */
++ /* Unsigned 32bit value; followed by a nested array of CHANNELS. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3,
++ /* An array of NUM_CHANNELS x unsigned 32-bit value integers
++ * representing channel numbers.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4,
++
++ /* GSCAN Capabilities attributes */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
++ = 7,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
++ = 8,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
++ = 9,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
++ = 11,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
++ = 12,
++
++ /* GSCAN Attributes used with
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command.
++ */
++
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13,
++
++ /* GSCAN attributes used with
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command.
++ */
++
++ /* An array of NUM_RESULTS_AVAILABLE x
++ * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_*
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14,
++
++ /* Unsigned 64-bit value; age of sample at the time of retrieval */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15,
++ /* 33 x unsigned 8-bit value; NULL terminated SSID */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16,
++ /* An array of 6 x unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17,
++ /* Unsigned 32-bit value; channel frequency in MHz */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18,
++ /* Signed 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21,
++ /* Unsigned 16-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22,
++ /* Unsigned 16-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23,
++ /* Unsigned 32-bit value; size of the IE DATA blob */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24,
++ /* An array of IE_LENGTH x unsigned 8-bit value; blob of all the
++ * information elements found in the beacon; this data should be a
++ * packed list of wifi_information_element objects, one after the
++ * other.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25,
++
++ /* Unsigned 8-bit value; set by driver to indicate more scan results are
++ * available.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26,
++
++ /* GSCAN attributes for
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command.
++ */
++ /* Unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28,
++
++ /* GSCAN attributes for
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command.
++ */
++ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
++ * to indicate number of results.
++ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
++ * list of results.
++ */
++
++ /* GSCAN attributes for
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command.
++ */
++ /* An array of 6 x unsigned 8-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL
++ = 30,
++ /* Unsigned 32-bit value. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI
++ = 31,
++ /* A nested array of signed 32-bit RSSI values. Size of the array is
++ * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST
++ = 32,
++
++ /* GSCAN attributes used with
++ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command.
++ */
++ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
++ * to indicate number of gscan cached results returned.
++ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate
++ * the list of gscan cached results.
++ */
++
++ /* An array of NUM_RESULTS_AVAILABLE x
++ * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_*
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33,
++ /* Unsigned 32-bit value; a unique identifier for the scan unit. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34,
++ /* Unsigned 32-bit value; a bitmask w/additional information about scan.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35,
++ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
++ * to indicate number of wifi scan results/bssids retrieved by the scan.
++ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
++ * list of wifi scan results returned for each cached result block.
++ */
++
++ /* GSCAN attributes for
++ * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command.
++ */
++ /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for
++ * number of results.
++ * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
++ * list of wifi scan results returned for each
++ * wifi_passpoint_match_result block.
++ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE.
++ */
++
++ /* GSCAN attributes for
++ * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command.
++ */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES
++ = 36,
++ /* A nested array of
++ * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_*
++ * attributes. Array size =
++ * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37,
++
++ /* Unsigned 32-bit value; network block id for the matched network */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38,
++ /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
++ * list of wifi scan results returned for each
++ * wifi_passpoint_match_result block.
++ */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39,
++ /* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values;
++ * ANQP data in the information_element format.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40,
++
++ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41,
++ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42,
++ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
++ = 43,
++ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
++ = 44,
++
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45,
++
++ /* Unsigned 32-bit value; a GSCAN Capabilities attribute.
++ * This is used to limit the maximum number of BSSIDs while sending
++ * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes
++ * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and
++ * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID.
++ */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
++ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1,
++};
++
++enum qca_wlan_vendor_attr_pno_config_params {
++ QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0,
++ /* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command.
++ */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1,
++ /* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_*
++ * attributes. Array size =
++ * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2,
++
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3,
++ /* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded
++ * realm, 0 if unspecified.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4,
++ /* An array of 16 x unsigned 32-bit value; roaming consortium ids to
++ * match, 0 if unspecified.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5,
++ /* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if
++ * unspecified.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6,
++
++ /* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command.
++ */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7,
++ /* Array of nested
++ * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_*
++ * attributes. Array size =
++ * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8,
++ /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9,
++ /* Signed 8-bit value; threshold for considering this SSID as found,
++ * required granularity for this threshold is 4 dBm to 8 dBm.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD
++ = 10,
++ /* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11,
++ /* Unsigned 8-bit value; auth bit field for matching WPA IE */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12,
++ /* Unsigned 8-bit to indicate ePNO type;
++ * It takes values from qca_wlan_epno_type
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13,
++
++ /* Nested attribute to send the channel list */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14,
++
++ /* Unsigned 32-bit value; indicates the interval between PNO scan
++ * cycles in msec.
++ */
++ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15,
++ QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16,
++ QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17,
++ QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18,
++ QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19,
++ QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
++ QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
++ QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
++ /* Unsigned 32-bit value, representing the PNO Request ID */
++ QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_PNO_MAX =
++ QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_acs_select_reason: This represents the different reasons why
++ * the ACS has to be triggered. These values are used by
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
++ */
++enum qca_wlan_vendor_acs_select_reason {
++ /* Represents the reason that the ACS triggered during the AP start */
++ QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT,
++ /* Represents the reason that DFS found with the current channel */
++ QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
++ /* Represents the reason that LTE co-exist in the current band. */
++ QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
++};
++
++/**
++ * qca_wlan_vendor_attr_external_acs_policy: Attribute values for
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the
++ * external ACS policies to select the channels w.r.t. the PCL weights.
++ * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and
++ * their PCL weights.)
++ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to
++ * select a channel with non-zero PCL weight.
++ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a
++ * channel with non-zero PCL weight.
++ *
++ */
++enum qca_wlan_vendor_attr_external_acs_policy {
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED,
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY,
++};
++
++/**
++ * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
++ * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
++ */
++enum qca_wlan_vendor_channel_prop_flags {
++ /* Bits 0, 1, 2, and 3 are reserved */
++
++ /* Turbo channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4,
++ /* CCK channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5,
++ /* OFDM channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6,
++ /* 2.4 GHz spectrum channel. */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7,
++ /* 5 GHz spectrum channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8,
++ /* Only passive scan allowed */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9,
++ /* Dynamic CCK-OFDM channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10,
++ /* GFSK channel (FHSS PHY) */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11,
++ /* Radar found on channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12,
++ /* 11a static turbo channel only */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13,
++ /* Half rate channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14,
++ /* Quarter rate channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15,
++ /* HT 20 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16,
++ /* HT 40 with extension channel above */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17,
++ /* HT 40 with extension channel below */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18,
++ /* HT 40 intolerant */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19,
++ /* VHT 20 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20,
++ /* VHT 40 with extension channel above */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21,
++ /* VHT 40 with extension channel below */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22,
++ /* VHT 80 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23,
++ /* HT 40 intolerant mark bit for ACS use */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24,
++ /* Channel temporarily blocked due to noise */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25,
++ /* VHT 160 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26,
++ /* VHT 80+80 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27,
++ /* HE 20 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28,
++ /* HE 40 with extension channel above */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29,
++ /* HE 40 with extension channel below */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30,
++ /* HE 40 intolerant */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31,
++};
++
++/**
++ * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a
++ * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is
++ * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2.
++ */
++enum qca_wlan_vendor_channel_prop_flags_2 {
++ /* HE 40 intolerant mark bit for ACS use */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0,
++ /* HE 80 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1,
++ /* HE 160 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2,
++ /* HE 80+80 channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3,
++};
++
++/**
++ * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for
++ * each channel. This is used by
++ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT.
++ */
++enum qca_wlan_vendor_channel_prop_flags_ext {
++ /* Radar found on channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0,
++ /* DFS required on channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1,
++ /* DFS required on channel for 2nd band of 80+80 */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2,
++ /* If channel has been checked for DFS */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3,
++ /* Excluded in 802.11d */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4,
++ /* Channel Switch Announcement received on this channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5,
++ /* Ad-hoc is not allowed */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6,
++ /* Station only channel */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
++ /* DFS radar history for slave device (STA mode) */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8,
++ /* DFS CAC valid for slave device (STA mode) */
++ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9,
++};
++
++/**
++ * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel
++ * information. These attributes are sent as part of
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following
++ * attributes correspond to a single channel.
++ */
++enum qca_wlan_vendor_external_acs_event_chan_info_attr {
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0,
++
++ /* A bitmask (u32) with flags specified in
++ * enum qca_wlan_vendor_channel_prop_flags.
++ */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1,
++ /* A bitmask (u32) with flags specified in
++ * enum qca_wlan_vendor_channel_prop_flags_ext.
++ */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2,
++ /* frequency in MHz (u32) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3,
++ /* maximum regulatory transmission power (u32) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4,
++ /* maximum transmission power (u32) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5,
++ /* minimum transmission power (u32) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6,
++ /* regulatory class id (u8) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7,
++ /* maximum antenna gain in (u8) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8,
++ /* VHT segment 0 (u8) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
++ /* VHT segment 1 (u8) */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
++ /* A bitmask (u32) with flags specified in
++ * enum qca_wlan_vendor_channel_prop_flags_2.
++ */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
++ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_attr_pcl: Represents attributes for
++ * preferred channel list (PCL). These attributes are sent as part of
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and
++ * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST.
++ */
++enum qca_wlan_vendor_attr_pcl {
++ QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0,
++
++ /* Channel number (u8) */
++ QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1,
++ /* Channel weightage (u8) */
++ QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2,
++ /* Channel frequency (u32) in MHz */
++ QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3,
++ /* Channel flags (u32)
++ * bit 0 set: channel to be used for GO role,
++ * bit 1 set: channel to be used on CLI role,
++ * bit 2 set: channel must be considered for operating channel
++ * selection & peer chosen operating channel should be
++ * one of the channels with this flag set,
++ * bit 3 set: channel should be excluded in GO negotiation
++ */
++ QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4,
++};
++
++/**
++ * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command
++ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by
++ * host driver.
++ */
++enum qca_wlan_vendor_attr_external_acs_event {
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0,
++
++ /* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason.
++ * This helps ACS module to understand why ACS needs to be started.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1,
++ /* Flag attribute to indicate if driver supports spectral scanning */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2,
++ /* Flag attribute to indicate if 11ac is offloaded to firmware */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
++ /* Flag attribute to indicate if driver provides additional channel
++ * capability as part of scan operation
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
++ /* Flag attribute to indicate interface status is UP */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5,
++ /* Operating mode (u8) of interface. Takes one of enum nl80211_iftype
++ * values.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6,
++ /* Channel width (u8). It takes one of enum nl80211_chan_width values.
++ * This is the upper bound of channel width. ACS logic should try to get
++ * a channel with the specified width and if not found, look for lower
++ * values.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7,
++ /* This (u8) will hold values of one of enum nl80211_bands */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8,
++ /* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode
++ * values
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9,
++ /* Array of (u32) supported frequency list among which ACS should choose
++ * best frequency.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10,
++ /* Preferred channel list by the driver which will have array of nested
++ * values as per enum qca_wlan_vendor_attr_pcl attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11,
++ /* Array of nested attribute for each channel. It takes attr as defined
++ * in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
++ /* External ACS policy such as PCL mandatory, PCL preferred, etc.
++ * It uses values defined in enum
++ * qca_wlan_vendor_attr_external_acs_policy.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
++ /* Reference RF Operating Parameter (RROP) availability information
++ * (u16). It uses values defined in enum
++ * qca_wlan_vendor_attr_rropavail_info.
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX =
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels
++ * in priority order as decided after ACS operation in userspace.
++ */
++enum qca_wlan_vendor_attr_external_acs_channels {
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
++
++ /* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1,
++
++ /* Array of nested values for each channel with following attributes:
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND,
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
++ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
++ */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2,
++ /* This (u8) will hold values of one of enum nl80211_bands */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3,
++ /* Primary channel (u8) */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4,
++ /* Secondary channel (u8) used for HT 40 MHz channels */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5,
++ /* VHT seg0 channel (u8) */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6,
++ /* VHT seg1 channel (u8) */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7,
++ /* Channel width (u8). Takes one of enum nl80211_chan_width values. */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
++ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1
++};
++
++enum qca_chip_power_save_failure_reason {
++ /* Indicates if the reason for the failure is due to a protocol
++ * layer/module.
++ */
++ QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
++ /* Indicates if the reason for the failure is due to a hardware issue.
++ */
++ QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
++};
++
++/**
++ * qca_attr_chip_power_save_failure: Attributes to vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite
++ * information leading to the power save failure.
++ */
++enum qca_attr_chip_power_save_failure {
++ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
++ /* Reason to cause the power save failure.
++ * These reasons are represented by
++ * enum qca_chip_power_save_failure_reason.
++ */
++ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
++
++ /* keep last */
++ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
++ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
++ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various
++ * data types for which the stats have to get collected.
++ */
++enum qca_wlan_vendor_nud_stats_data_pkt_flags {
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4,
++ /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get
++ * to represent the stats of respective data type.
++ */
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6,
++ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7,
++};
++
++enum qca_wlan_vendor_nud_stats_set_data_pkt_info {
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0,
++ /* Represents the data packet type to be monitored (u32).
++ * Host driver tracks the stats corresponding to each data frame
++ * represented by these flags.
++ * These data packets are represented by
++ * enum qca_wlan_vendor_nud_stats_data_pkt_flags
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1,
++ /* Name corresponding to the DNS frame for which the respective DNS
++ * stats have to get monitored (string). Max string length 255.
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2,
++ /* source port on which the respective proto stats have to get
++ * collected (u32).
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3,
++ /* destination port on which the respective proto stats have to get
++ * collected (u32).
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4,
++ /* IPv4 address for which the destined data packets have to be
++ * monitored. (in network byte order), u32.
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5,
++ /* IPv6 address for which the destined data packets have to be
++ * monitored. (in network byte order), 16 bytes array.
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6,
++
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST,
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX =
++ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite
++ * information to start/stop the NUD statistics collection.
++ */
++enum qca_attr_nud_stats_set {
++ QCA_ATTR_NUD_STATS_SET_INVALID = 0,
++
++ /* Flag to start/stop the NUD statistics collection.
++ * Start - If included, Stop - If not included
++ */
++ QCA_ATTR_NUD_STATS_SET_START = 1,
++ /* IPv4 address of the default gateway (in network byte order), u32 */
++ QCA_ATTR_NUD_STATS_GW_IPV4 = 2,
++ /* Represents the list of data packet types to be monitored.
++ * Host driver tracks the stats corresponding to each data frame
++ * represented by these flags.
++ * These data packets are represented by
++ * enum qca_wlan_vendor_nud_stats_set_data_pkt_info
++ */
++ QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3,
++
++ /* keep last */
++ QCA_ATTR_NUD_STATS_SET_LAST,
++ QCA_ATTR_NUD_STATS_SET_MAX =
++ QCA_ATTR_NUD_STATS_SET_LAST - 1,
++};
++
++enum qca_attr_nud_data_stats {
++ QCA_ATTR_NUD_DATA_STATS_INVALID = 0,
++ /* Data packet type for which the stats are collected (u32).
++ * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags
++ */
++ QCA_ATTR_NUD_STATS_PKT_TYPE = 1,
++ /* Name corresponding to the DNS frame for which the respective DNS
++ * stats are monitored (string). Max string length 255.
++ */
++ QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2,
++ /* source port on which the respective proto stats are collected (u32).
++ */
++ QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3,
++ /* destination port on which the respective proto stats are collected
++ * (u32).
++ */
++ QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4,
++ /* IPv4 address for which the destined data packets have to be
++ * monitored. (in network byte order), u32.
++ */
++ QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5,
++ /* IPv6 address for which the destined data packets have to be
++ * monitored. (in network byte order), 16 bytes array.
++ */
++ QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6,
++ /* Data packet Request count received from netdev (u32). */
++ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7,
++ /* Data packet Request count sent to lower MAC from upper MAC (u32). */
++ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8,
++ /* Data packet Request count received by lower MAC from upper MAC
++ * (u32)
++ */
++ QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9,
++ /* Data packet Request count successfully transmitted by the device
++ * (u32)
++ */
++ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10,
++ /* Data packet Response count received by lower MAC (u32) */
++ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11,
++ /* Data packet Response count received by upper MAC (u32) */
++ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12,
++ /* Data packet Response count delivered to netdev (u32) */
++ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13,
++ /* Data Packet Response count that are dropped out of order (u32) */
++ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14,
++
++ /* keep last */
++ QCA_ATTR_NUD_DATA_STATS_LAST,
++ QCA_ATTR_NUD_DATA_STATS_MAX =
++ QCA_ATTR_NUD_DATA_STATS_LAST - 1,
++};
++
++/**
++ * qca_attr_nud_stats_get: Attributes to vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite
++ * NUD statistics collected when queried.
++ */
++enum qca_attr_nud_stats_get {
++ QCA_ATTR_NUD_STATS_GET_INVALID = 0,
++ /* ARP Request count from netdev (u32) */
++ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1,
++ /* ARP Request count sent to lower MAC from upper MAC (u32) */
++ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2,
++ /* ARP Request count received by lower MAC from upper MAC (u32) */
++ QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3,
++ /* ARP Request count successfully transmitted by the device (u32) */
++ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4,
++ /* ARP Response count received by lower MAC (u32) */
++ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5,
++ /* ARP Response count received by upper MAC (u32) */
++ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6,
++ /* ARP Response count delivered to netdev (u32) */
++ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7,
++ /* ARP Response count dropped due to out of order reception (u32) */
++ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8,
++ /* Flag indicating if the station's link to the AP is active.
++ * Active Link - If included, Inactive link - If not included
++ */
++ QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9,
++ /* Flag indicating if there is any duplicate address detected (DAD).
++ * Yes - If detected, No - If not detected.
++ */
++ QCA_ATTR_NUD_STATS_IS_DAD = 10,
++ /* List of Data packet types for which the stats are requested.
++ * This list does not carry ARP stats as they are done by the
++ * above attributes. Represented by enum qca_attr_nud_data_stats.
++ */
++ QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11,
++
++ /* keep last */
++ QCA_ATTR_NUD_STATS_GET_LAST,
++ QCA_ATTR_NUD_STATS_GET_MAX =
++ QCA_ATTR_NUD_STATS_GET_LAST - 1,
++};
++
++enum qca_wlan_btm_candidate_status {
++ QCA_STATUS_ACCEPT = 0,
++ QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1,
++ QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2,
++ QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3,
++ QCA_STATUS_REJECT_LOW_RSSI = 4,
++ QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5,
++ QCA_STATUS_REJECT_UNKNOWN = 6,
++};
++
++enum qca_wlan_vendor_attr_btm_candidate_info {
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0,
++
++ /* 6-byte MAC address representing the BSSID of transition candidate */
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1,
++ /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status
++ * returned by the driver. It says whether the BSSID provided in
++ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by
++ * the driver, if not it specifies the reason for rejection.
++ * Note that the user-space can overwrite the transition reject reason
++ * codes provided by driver based on more information.
++ */
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX =
++ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1,
++};
++
++enum qca_attr_trace_level {
++ QCA_ATTR_TRACE_LEVEL_INVALID = 0,
++ /*
++ * Nested array of the following attributes:
++ * QCA_ATTR_TRACE_LEVEL_MODULE,
++ * QCA_ATTR_TRACE_LEVEL_MASK.
++ */
++ QCA_ATTR_TRACE_LEVEL_PARAM = 1,
++ /*
++ * Specific QCA host driver module. Please refer to the QCA host
++ * driver implementation to get the specific module ID.
++ */
++ QCA_ATTR_TRACE_LEVEL_MODULE = 2,
++ /* Different trace level masks represented in the QCA host driver. */
++ QCA_ATTR_TRACE_LEVEL_MASK = 3,
++
++ /* keep last */
++ QCA_ATTR_TRACE_LEVEL_AFTER_LAST,
++ QCA_ATTR_TRACE_LEVEL_MAX =
++ QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities
++ */
++enum qca_wlan_vendor_attr_get_he_capabilities {
++ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0,
++ /* Whether HE capabilities is supported
++ * (u8 attribute: 0 = not supported, 1 = supported)
++ */
++ QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1,
++ /* HE PHY capabilities, array of 3 u32 values */
++ QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2,
++ /* HE MAC capabilities (u32 attribute) */
++ QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3,
++ /* HE MCS map (u32 attribute) */
++ QCA_WLAN_VENDOR_ATTR_HE_MCS = 4,
++ /* Number of SS (u32 attribute) */
++ QCA_WLAN_VENDOR_ATTR_NUM_SS = 5,
++ /* RU count (u32 attribute) */
++ QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6,
++ /* PPE threshold data, array of 8 u32 values */
++ QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX =
++ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters
++ */
++enum qca_wlan_vendor_attr_spectral_scan {
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0,
++ /* Number of times the chip enters spectral scan mode before
++ * deactivating spectral scans. When set to 0, chip will enter spectral
++ * scan mode continuously. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1,
++ /* Spectral scan period. Period increment resolution is 256*Tclk,
++ * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2,
++ /* Spectral scan priority. u32 attribute. */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3,
++ /* Number of FFT data points to compute. u32 attribute. */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4,
++ /* Enable targeted gain change before starting the spectral scan FFT.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5,
++ /* Restart a queued spectral scan. u32 attribute. */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6,
++ /* Noise floor reference number for the calculation of bin power.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7,
++ /* Disallow spectral scan triggers after TX/RX packets by setting
++ * this delay value to roughly SIFS time period or greater.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8,
++ /* Number of strong bins (inclusive) per sub-channel, below
++ * which a signal is declared a narrow band tone. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9,
++ /* Specify the threshold over which a bin is declared strong (for
++ * scan bandwidth analysis). u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10,
++ /* Spectral scan report mode. u32 attribute. */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11,
++ /* RSSI report mode, if the ADC RSSI is below
++ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR,
++ * then FFTs will not trigger, but timestamps and summaries get
++ * reported. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12,
++ /* ADC RSSI must be greater than or equal to this threshold (signed dB)
++ * to ensure spectral scan reporting with normal error code.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13,
++ /* Format of frequency bin magnitude for spectral scan triggered FFTs:
++ * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)).
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14,
++ /* Format of FFT report to software for spectral scan triggered FFTs.
++ * 0: No FFT report (only spectral scan summary report)
++ * 1: 2-dword summary of metrics for each completed FFT + spectral scan
++ * report
++ * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled
++ * bins (in-band) per FFT + spectral scan summary report
++ * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled
++ * bins (all) per FFT + spectral scan summary report
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15,
++ /* Number of LSBs to shift out in order to scale the FFT bins.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16,
++ /* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes
++ * in dBm power. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17,
++ /* Per chain enable mask to select input ADC for search FFT.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18,
++ /* An unsigned 64-bit integer provided by host driver to identify the
++ * spectral scan request. This attribute is included in the scan
++ * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START
++ * and used as an attribute in
++ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the
++ * specific scan to be stopped.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
++ /* Skip interval for FFT reports. u32 attribute */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20,
++ /* Set to report only one set of FFT results.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21,
++ /* Debug level for spectral module in driver.
++ * 0 : Verbosity level 0
++ * 1 : Verbosity level 1
++ * 2 : Verbosity level 2
++ * 3 : Matched filterID display
++ * 4 : One time dump of FFT report
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22,
++ /* Type of spectral scan request. u32 attribute.
++ * It uses values defined in enum
++ * qca_wlan_vendor_attr_spectral_scan_request_type.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23,
++
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS.
++ */
++enum qca_wlan_vendor_attr_spectral_diag_stats {
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0,
++ /* Number of spectral TLV signature mismatches.
++ * u64 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1,
++ /* Number of spectral phyerror events with insufficient length when
++ * parsing for secondary 80 search FFT report. u64 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2,
++ /* Number of spectral phyerror events without secondary 80
++ * search FFT report. u64 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3,
++ /* Number of spectral phyerror events with vht operation segment 1 id
++ * mismatches in search fft report. u64 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4,
++ /* Number of spectral phyerror events with vht operation segment 2 id
++ * mismatches in search fft report. u64 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5,
++
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX =
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO.
++ */
++enum qca_wlan_vendor_attr_spectral_cap {
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0,
++ /* Flag attribute to indicate phydiag capability */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1,
++ /* Flag attribute to indicate radar detection capability */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2,
++ /* Flag attribute to indicate spectral capability */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3,
++ /* Flag attribute to indicate advanced spectral capability */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4,
++ /* Spectral hardware generation. u32 attribute.
++ * It uses values defined in enum
++ * qca_wlan_vendor_spectral_scan_cap_hw_gen.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5,
++ /* Spectral bin scaling formula ID. u16 attribute.
++ * It uses values defined in enum
++ * qca_wlan_vendor_spectral_scan_cap_formula_id.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6,
++ /* Spectral bin scaling param - low level offset.
++ * s16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7,
++ /* Spectral bin scaling param - high level offset.
++ * s16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8,
++ /* Spectral bin scaling param - RSSI threshold.
++ * s16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9,
++ /* Spectral bin scaling param - default AGC max gain.
++ * u8 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10,
++
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS.
++ */
++enum qca_wlan_vendor_attr_spectral_scan_status {
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0,
++ /* Flag attribute to indicate whether spectral scan is enabled */
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1,
++ /* Flag attribute to indicate whether spectral scan is in progress*/
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2,
++
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX =
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for
++ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the
++ * spectral scan request types.
++ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to
++ * set the spectral parameters and start scan.
++ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to
++ * only set the spectral parameters.
++ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to
++ * only start the spectral scan.
++ */
++enum qca_wlan_vendor_attr_spectral_scan_request_type {
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN,
++ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG,
++};
++
++/**
++ * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for
++ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
++ * spectral hardware generation.
++ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1
++ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2
++ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3
++ */
++enum qca_wlan_vendor_spectral_scan_cap_hw_gen {
++ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0,
++ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1,
++ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2,
++};
++
++enum qca_wlan_vendor_tos {
++ QCA_WLAN_VENDOR_TOS_BK = 0,
++ QCA_WLAN_VENDOR_TOS_BE = 1,
++ QCA_WLAN_VENDOR_TOS_VI = 2,
++ QCA_WLAN_VENDOR_TOS_VO = 3,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS.
++ */
++enum qca_wlan_vendor_attr_active_tos {
++ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0,
++ /* Type Of Service - Represented by qca_wlan_vendor_tos */
++ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1,
++ /* Flag attribute representing the start (attribute included) or stop
++ * (attribute not included) of the respective TOS.
++ */
++ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2,
++};
++
++enum qca_wlan_vendor_hang_reason {
++ /* Unspecified reason */
++ QCA_WLAN_HANG_REASON_UNSPECIFIED = 0,
++ /* No Map for the MAC entry for the received frame */
++ QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1,
++ /* Peer deletion timeout happened */
++ QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2,
++ /* Peer unmap timeout */
++ QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3,
++ /* Scan request timed out */
++ QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4,
++ /* Consecutive Scan attempt failures */
++ QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5,
++ /* Unable to get the message buffer */
++ QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6,
++ /* Current command processing is timedout */
++ QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7,
++ /* Timeout for an ACK from FW for suspend request */
++ QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8,
++ /* Timeout for an ACK from FW for resume request */
++ QCA_WLAN_HANG_RESUME_TIMEOUT = 9,
++ /* Transmission timeout for consecutive data frames */
++ QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10,
++ /* Timeout for the TX completion status of data frame */
++ QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11,
++ /* DXE failure for TX/RX, DXE resource unavailability */
++ QCA_WLAN_HANG_DXE_FAILURE = 12,
++ /* WMI pending commands exceed the maximum count */
++ QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_hang - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_HANG.
++ */
++enum qca_wlan_vendor_attr_hang {
++ QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0,
++ /* Reason for the hang - u32 attribute with a value from enum
++ * qca_wlan_vendor_hang_reason.
++ */
++ QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1,
++
++ QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_HANG_MAX =
++ QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_flush_pending - Attributes for
++ * flushing pending traffic in firmware.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address.
++ * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending
++ * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK,
++ * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to
++ * flush packets with access category.
++ */
++enum qca_wlan_vendor_attr_flush_pending {
++ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1,
++ QCA_WLAN_VENDOR_ATTR_AC = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX =
++ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1,
++};
++
++/**
++ * qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for
++ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
++ * Spectral bin scaling formula ID.
++ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling
++ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain
++ * and RSSI threshold based formula.
++ */
++enum qca_wlan_vendor_spectral_scan_cap_formula_id {
++ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0,
++ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative
++ * RF Operating Parameter (RROP) information is available, and if so, at which
++ * point in the application-driver interaction sequence it can be retrieved by
++ * the application from the driver. This point may vary by architecture and
++ * other factors. This is a u16 value.
++ */
++enum qca_wlan_vendor_attr_rropavail_info {
++ /* RROP information is unavailable. */
++ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE,
++ /* RROP information is available and the application can retrieve the
++ * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
++ * event from the driver.
++ */
++ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START,
++ /* RROP information is available only after a vendor specific scan
++ * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has
++ * successfully completed. The application can retrieve the information
++ * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from
++ * the driver.
++ */
++ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific
++ * Representative RF Operating Parameter (RROP) information. It is sent for the
++ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is
++ * intended for use by external Auto Channel Selection applications. It provides
++ * guidance values for some RF parameters that are used by the system during
++ * operation. These values could vary by channel, band, radio, and so on.
++ */
++enum qca_wlan_vendor_attr_rrop_info {
++ QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0,
++
++ /* Representative Tx Power List (RTPL) which has an array of nested
++ * values as per attributes in enum qca_wlan_vendor_attr_rtplinst.
++ */
++ QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1,
++
++ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX =
++ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list
++ * entry instances in the Representative Tx Power List (RTPL). It provides
++ * simplified power values intended for helping external Auto channel Selection
++ * applications compare potential Tx power performance between channels, other
++ * operating conditions remaining identical. These values are not necessarily
++ * the actual Tx power values that will be used by the system. They are also not
++ * necessarily the max or average values that will be used. Instead, they are
++ * relative, summarized keys for algorithmic use computed by the driver or
++ * underlying firmware considering a number of vendor specific factors.
++ */
++enum qca_wlan_vendor_attr_rtplinst {
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0,
++
++ /* Primary channel number (u8) */
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1,
++ /* Representative Tx power in dBm (s32) with emphasis on throughput. */
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2,
++ /* Representative Tx power in dBm (s32) with emphasis on range. */
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3,
++
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX =
++ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_config_latency_level - Level for
++ * wlan latency module.
++ *
++ * There will be various of Wi-Fi functionality like scan/roaming/adaptive
++ * power saving which would causing data exchange out of service, this
++ * would be a big impact on latency. For latency sensitive applications over
++ * Wi-Fi are intolerant to such operations and thus would configure them
++ * to meet their respective needs. It is well understood by such applications
++ * that altering the default behavior would degrade the Wi-Fi functionality
++ * w.r.t the above pointed WLAN operations.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL:
++ * Default WLAN operation level which throughput orientated.
++ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE:
++ * Use moderate level to improve latency by limit scan duration.
++ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
++ * Use low latency level to benifit application like concurrent
++ * downloading or video streaming via constraint scan/adaptive PS.
++ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
++ * Use ultra low latency level to benefit for gaming/voice
++ * application via constraint scan/roaming/adaptive PS.
++ */
++enum qca_wlan_vendor_attr_config_latency_level {
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX =
++ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
++ */
++enum qca_wlan_vendor_attr_mac {
++ QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0,
++
++ /* MAC mode info list which has an array of nested values as
++ * per attributes in enum qca_wlan_vendor_attr_mac_mode_info.
++ */
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_MAC_MAX =
++ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected
++ * Wi-Fi netdev interface on a respective MAC.
++ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO.
++ */
++enum qca_wlan_vendor_attr_mac_iface_info {
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0,
++ /* Wi-Fi netdev's interface index (u32) */
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1,
++ /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX =
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information.
++ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the
++ * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
++ */
++enum qca_wlan_vendor_attr_mac_info {
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0,
++ /* Hardware MAC ID associated for the MAC (u32) */
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1,
++ /* Band supported by the MAC at a given point.
++ * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum
++ * nl80211_band.
++ */
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2,
++ /* Refers to list of WLAN netdev interfaces associated with this MAC.
++ * Represented by enum qca_wlan_vendor_attr_mac_iface_info.
++ */
++ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX =
++ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET.
++ */
++enum qca_wlan_vendor_attr_get_logger_features {
++ QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0,
++ /* Unsigned 32-bit enum value of wifi_logger_supported_features */
++ QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_LOGGER_MAX =
++ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1,
++};
++
++/**
++ * enum wifi_logger_supported_features - Values for supported logger features
++ */
++enum wifi_logger_supported_features {
++ WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)),
++ WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)),
++ WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)),
++ WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)),
++ WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)),
++ WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)),
++ WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)),
++ WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)),
++ WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)),
++};
++
++/**
++ * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get
++ * capabilities features
++ */
++enum qca_wlan_tdls_caps_features_supported {
++ WIFI_TDLS_SUPPORT = (1 << (0)),
++ WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)),
++ WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2))
++};
++
++/**
++ * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES.
++ */
++enum qca_wlan_vendor_attr_tdls_get_capabilities {
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0,
++ /* Indicates the max concurrent sessions */
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS,
++ /* Indicates the support for features */
++ /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported
++ */
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX =
++ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_offloaded_packets_sending_control - Offload packets control
++ * command used as value for the attribute
++ * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL.
++ */
++enum qca_wlan_offloaded_packets_sending_control {
++ QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0,
++ QCA_WLAN_OFFLOADED_PACKETS_SENDING_START,
++ QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP
++};
++
++/**
++ * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS.
++ */
++enum qca_wlan_vendor_attr_offloaded_packets {
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0,
++ /* Takes valid value from the enum
++ * qca_wlan_offloaded_packets_sending_control
++ * Unsigned 32-bit value
++ */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID,
++ /* array of u8 len: Max packet size */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA,
++ /* 6-byte MAC address used to represent source MAC address */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR,
++ /* 6-byte MAC address used to represent destination MAC address */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
++ /* Unsigned 32-bit value, in milli seconds */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
++ /* This optional unsigned 16-bit attribute is used for specifying
++ * ethernet protocol type. If not specified ethertype defaults to IPv4.
++ */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX =
++ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values
++ * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL.
++ */
++enum qca_wlan_rssi_monitoring_control {
++ QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0,
++ QCA_WLAN_RSSI_MONITORING_START,
++ QCA_WLAN_RSSI_MONITORING_STOP,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI.
++ */
++enum qca_wlan_vendor_attr_rssi_monitoring {
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0,
++ /* Takes valid value from the enum
++ * qca_wlan_rssi_monitoring_control
++ * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control
++ */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
++ /* Unsigned 32-bit value */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
++ /* Signed 8-bit value in dBm */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI,
++ /* Signed 8-bit value in dBm */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI,
++ /* attributes to be used/received in callback */
++ /* 6-byte MAC address used to represent current BSSID MAC address */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
++ /* Signed 8-bit value indicating the current RSSI */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX =
++ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_NDP.
++ */
++enum qca_wlan_vendor_attr_ndp_params {
++ QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0,
++ /* Unsigned 32-bit value
++ * enum of sub commands values in qca_wlan_ndp_sub_cmd
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
++ /* Unsigned 16-bit value */
++ QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
++ /* NL attributes for data used NDP SUB cmds */
++ /* Unsigned 32-bit value indicating a service info */
++ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID,
++ /* Unsigned 32-bit value; channel frequency in MHz */
++ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL,
++ /* Interface Discovery MAC address. An array of 6 Unsigned int8 */
++ QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR,
++ /* Interface name on which NDP is being created */
++ QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR,
++ /* Unsigned 32-bit value for security */
++ /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */
++ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY,
++ /* Unsigned 32-bit value for QoS */
++ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
++ /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */
++ QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
++ /* Unsigned 32-bit value for NDP instance Id */
++ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
++ /* Array of instance Ids */
++ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
++ /* Unsigned 32-bit value for initiator/responder NDP response code
++ * accept/reject
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
++ /* NDI MAC address. An array of 6 Unsigned int8 */
++ QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR,
++ /* Unsigned 32-bit value errors types returned by driver
++ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
++ * NanStatusType includes these values.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
++ /* Unsigned 32-bit value error values returned by driver
++ * The nan_i.h in AOSP project platform/hardware/qcom/wlan
++ * NanInternalStatusType includes these values.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
++ /* Unsigned 32-bit value for Channel setup configuration
++ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
++ * NanDataPathChannelCfg includes these values.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG,
++ /* Unsigned 32-bit value for Cipher Suite Shared Key Type */
++ QCA_WLAN_VENDOR_ATTR_NDP_CSID,
++ /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */
++ QCA_WLAN_VENDOR_ATTR_NDP_PMK,
++ /* Security Context Identifier that contains the PMKID
++ * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_SCID,
++ /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */
++ QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE,
++ /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */
++ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME,
++ /* Unsigned 32-bit bitmap indicating schedule update
++ * BIT_0: NSS Update
++ * BIT_1: Channel list update
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON,
++ /* Unsigned 32-bit value for NSS */
++ QCA_WLAN_VENDOR_ATTR_NDP_NSS,
++ /* Unsigned 32-bit value for NUMBER NDP CHANNEL */
++ QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS,
++ /* Unsigned 32-bit value for CHANNEL BANDWIDTH
++ * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH,
++ /* Array of channel/band width */
++ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO,
++ /* IPv6 address used by NDP (in network byte order), 16 bytes array.
++ * This attribute is used and optional for ndp request, ndp response,
++ * ndp indication, and ndp confirm.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27,
++ /* Unsigned 16-bit value indicating transport port used by NDP.
++ * This attribute is used and optional for ndp response, ndp indication,
++ * and ndp confirm.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28,
++ /* Unsigned 8-bit value indicating protocol used by NDP and assigned by
++ * the Internet Assigned Numbers Authority (IANA) as per:
++ * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
++ * This attribute is used and optional for ndp response, ndp indication,
++ * and ndp confirm.
++ */
++ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29,
++ /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE.
++ * 1:support 0:not support
++ */
++ QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX =
++ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1,
++};
++
++enum qca_wlan_ndp_sub_cmd {
++ QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0,
++ /* Command to create a NAN data path interface */
++ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1,
++ /* Command to delete a NAN data path interface */
++ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2,
++ /* Command to initiate a NAN data path session */
++ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3,
++ /* Command to notify if the NAN data path session was sent */
++ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4,
++ /* Command to respond to NAN data path session */
++ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5,
++ /* Command to notify on the responder about the response */
++ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6,
++ /* Command to initiate a NAN data path end */
++ QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7,
++ /* Command to notify the if end request was sent */
++ QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8,
++ /* Command to notify the peer about the end request */
++ QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9,
++ /* Command to confirm the NAN data path session is complete */
++ QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10,
++ /* Command to indicate the peer about the end request being received */
++ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11,
++ /* Command to indicate the peer of schedule update */
++ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12
++};
++
++/**
++ * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD.
++ */
++enum qca_wlan_vendor_attr_nd_offload {
++ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0,
++ /* Flag to set Neighbour Discovery offload */
++ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG,
++ /* Keep last */
++ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX =
++ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1,
++};
++
++/**
++ * enum packet_filter_sub_cmd - Packet filter sub commands
++ */
++enum packet_filter_sub_cmd {
++ /**
++ * Write packet filter program and/or data. The driver/firmware should
++ * disable APF before writing into local buffer and re-enable APF after
++ * writing is done.
++ */
++ QCA_WLAN_SET_PACKET_FILTER = 1,
++ /* Get packet filter feature capabilities from driver */
++ QCA_WLAN_GET_PACKET_FILTER = 2,
++ /**
++ * Write packet filter program and/or data. User space will send the
++ * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command
++ * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key
++ * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over
++ * enable/disable is given to user space with this command. Also,
++ * user space sends the length of program portion in the buffer within
++ * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH.
++ */
++ QCA_WLAN_WRITE_PACKET_FILTER = 3,
++ /* Read packet filter program and/or data */
++ QCA_WLAN_READ_PACKET_FILTER = 4,
++ /* Enable APF feature */
++ QCA_WLAN_ENABLE_PACKET_FILTER = 5,
++ /* Disable APF feature */
++ QCA_WLAN_DISABLE_PACKET_FILTER = 6,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by
++ * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER.
++ */
++enum qca_wlan_vendor_attr_packet_filter {
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0,
++ /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
++ /* Unsigned 32-bit value indicating the packet filter version */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION,
++ /* Unsigned 32-bit value indicating the packet filter id */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID,
++ /**
++ * Unsigned 32-bit value indicating the packet filter size including
++ * program + data.
++ */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE,
++ /* Unsigned 32-bit value indicating the packet filter current offset */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
++ /* Program and/or data in bytes */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
++ /* Unsigned 32-bit value of the length of the program section in packet
++ * filter buffer.
++ */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX =
++ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE.
++ */
++enum qca_wlan_vendor_drv_info {
++ QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0,
++ /* Maximum Message size info between firmware & HOST
++ * Unsigned 32-bit value
++ */
++ QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX =
++ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor
++ * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS.
++ */
++enum qca_wlan_vendor_attr_wake_stats {
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0,
++ /* Unsigned 32-bit value indicating the total count of wake event */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE,
++ /* Array of individual wake count, each index representing wake reason
++ */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR,
++ /* Unsigned 32-bit value representing wake count array */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ,
++ /* Unsigned 32-bit total wake count value of driver/fw */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE,
++ /* Array of wake stats of driver/fw */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR,
++ /* Unsigned 32-bit total wake count value of driver/fw */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ,
++ /* Unsigned 32-bit total wake count value of packets received */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE,
++ /* Unsigned 32-bit wake count value unicast packets received */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT,
++ /* Unsigned 32-bit wake count value multicast packets received */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT,
++ /* Unsigned 32-bit wake count value broadcast packets received */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT,
++ /* Unsigned 32-bit wake count value of ICMP packets */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT,
++ /* Unsigned 32-bit wake count value of ICMP6 packets */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT,
++ /* Unsigned 32-bit value ICMP6 router advertisement */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA,
++ /* Unsigned 32-bit value ICMP6 neighbor advertisement */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA,
++ /* Unsigned 32-bit value ICMP6 neighbor solicitation */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS,
++ /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT,
++ /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT,
++ /* Unsigned 32-bit wake count value of receive side multicast */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT,
++ /* Unsigned 32-bit wake count value of a given RSSI breach */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT,
++ /* Unsigned 32-bit wake count value of low RSSI */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT,
++ /* Unsigned 32-bit value GSCAN count */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT,
++ /* Unsigned 32-bit value PNO complete count */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT,
++ /* Unsigned 32-bit value PNO match count */
++ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT,
++ /* keep last */
++ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST,
++ QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX =
++ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set
++ * cmd value. Used for NL attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
++ */
++enum qca_wlan_vendor_attr_thermal_cmd {
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0,
++ /* The value of command, driver will implement different operations
++ * according to this value. It uses values defined in
++ * enum qca_wlan_vendor_attr_thermal_cmd_type.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX =
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1
++};
++
++/**
++ * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for
++ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd
++ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the
++ * thermal command types sent to driver.
++ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to
++ * get thermal shutdown configuration parameters for display. Parameters
++ * responded from driver are defined in
++ * enum qca_wlan_vendor_attr_get_thermal_params_rsp.
++ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to
++ * get temperature. Host should respond with a temperature data. It is defined
++ * in enum qca_wlan_vendor_attr_thermal_get_temperature.
++ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal
++ * suspend action.
++ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal
++ * resume action.
++ */
++enum qca_wlan_vendor_attr_thermal_cmd_type {
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes
++ * to get chip temperature by user.
++ * enum values are used for NL attributes for data used by
++ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used
++ * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
++ */
++enum qca_wlan_vendor_attr_thermal_get_temperature {
++ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0,
++ /* Temperature value (degree Celsius) from driver.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX =
++ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes
++ * to get configuration parameters of thermal shutdown feature. Enum values are
++ * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data
++ * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
++ */
++enum qca_wlan_vendor_attr_get_thermal_params_rsp {
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0,
++ /* Indicate if the thermal shutdown feature is enabled.
++ * NLA_FLAG attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN,
++ /* Indicate if the auto mode is enabled.
++ * Enable: Driver triggers the suspend/resume action.
++ * Disable: User space triggers the suspend/resume action.
++ * NLA_FLAG attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN,
++ /* Thermal resume threshold (degree Celsius). Issue the resume command
++ * if the temperature value is lower than this threshold.
++ * u16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH,
++ /* Thermal warning threshold (degree Celsius). FW reports temperature
++ * to driver if it's higher than this threshold.
++ * u16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH,
++ /* Thermal suspend threshold (degree Celsius). Issue the suspend command
++ * if the temperature value is higher than this threshold.
++ * u16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH,
++ /* FW reports temperature data periodically at this interval (ms).
++ * u16 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX =
++ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to
++ * report thermal events from driver to user space.
++ * enum values are used for NL attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command.
++ */
++enum qca_wlan_vendor_attr_thermal_event {
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0,
++ /* Temperature value (degree Celsius) from driver.
++ * u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE,
++ /* Indication of resume completion from power save mode.
++ * NLA_FLAG attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX =
++ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1,
++};
++
++/**
++ * enum he_fragmentation_val - HE fragmentation support values
++ * Indicates level of dynamic fragmentation that is supported by
++ * a STA as a recipient.
++ * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2
++ * (HE MAC Capabilities Information field) and are used in HE Capabilities
++ * element to advertise the support. These values are validated in the driver
++ * to check the device capability and advertised in the HE Capabilities
++ * element. These values are used to configure testbed device to allow the
++ * advertised hardware capabilities to be downgraded for testing purposes.
++ *
++ * @HE_FRAG_DISABLE: no support for dynamic fragmentation
++ * @HE_FRAG_LEVEL1: support for dynamic fragments that are
++ * contained within an MPDU or S-MPDU, no support for dynamic fragments
++ * within an A-MPDU that is not an S-MPDU.
++ * @HE_FRAG_LEVEL2: support for dynamic fragments that are
++ * contained within an MPDU or S-MPDU and support for up to one dynamic
++ * fragment for each MSDU, each A-MSDU if supported by the recipient, and
++ * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an
++ * MPDU or S-MPDU.
++ * @HE_FRAG_LEVEL3: support for dynamic fragments that are
++ * contained within an MPDU or S-MPDU and support for multiple dynamic
++ * fragments for each MSDU and for each A-MSDU if supported by the
++ * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic
++ * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU.
++ */
++enum he_fragmentation_val {
++ HE_FRAG_DISABLE,
++ HE_FRAG_LEVEL1,
++ HE_FRAG_LEVEL2,
++ HE_FRAG_LEVEL3,
++};
++
++/**
++ * enum he_mcs_config - HE MCS support configuration
++ *
++ * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth.
++ * These values are used in driver to configure the HE MCS map to advertise
++ * Tx/Rx MCS map in HE capability and these values are applied for all the
++ * streams supported by the device. To configure MCS for different bandwidths,
++ * vendor command needs to be sent using this attribute with appropriate value.
++ * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS
++ * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11
++ * send this command using HE MCS config attribute with value HE_160_MCS0_11.
++ * These values are used to configure testbed device to allow the advertised
++ * hardware capabilities to be downgraded for testing purposes. The enum values
++ * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and
++ * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map.
++ * These values are validated in the driver before setting the MCS map and
++ * driver returns error if the input is other than these enum values.
++ *
++ * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7
++ * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9
++ * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11
++ * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7
++ * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9
++ * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11
++ * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7
++ * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9
++ * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11
++ */
++enum he_mcs_config {
++ HE_80_MCS0_7 = 0,
++ HE_80_MCS0_9 = 1,
++ HE_80_MCS0_11 = 2,
++ HE_160_MCS0_7 = 4,
++ HE_160_MCS0_9 = 5,
++ HE_160_MCS0_11 = 6,
++ HE_80P80_MCS0_7 = 8,
++ HE_80P80_MCS0_9 = 9,
++ HE_80P80_MCS0_11 = 10,
++};
++
++/**
++ * enum qca_wlan_ba_session_config - BA session configuration
++ *
++ * Indicates the configuration values for BA session configuration attribute.
++ *
++ * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration.
++ * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID.
++ */
++enum qca_wlan_ba_session_config {
++ QCA_WLAN_ADD_BA = 1,
++ QCA_WLAN_DELETE_BA = 2,
++};
++
++/**
++ * enum qca_wlan_ac_type - Access category type
++ *
++ * Indicates the access category type value.
++ *
++ * @QCA_WLAN_AC_BE: BE access category
++ * @QCA_WLAN_AC_BK: BK access category
++ * @QCA_WLAN_AC_VI: VI access category
++ * @QCA_WLAN_AC_VO: VO access category
++ * @QCA_WLAN_AC_ALL: All ACs
++ */
++enum qca_wlan_ac_type {
++ QCA_WLAN_AC_BE = 0,
++ QCA_WLAN_AC_BK = 1,
++ QCA_WLAN_AC_VI = 2,
++ QCA_WLAN_AC_VO = 3,
++ QCA_WLAN_AC_ALL = 4,
++};
++
++/**
++ * enum qca_wlan_he_ltf_cfg - HE LTF configuration
++ *
++ * Indicates the HE LTF configuration value.
++ *
++ * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF,
++ * based on the GI setting
++ * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF
++ * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF
++ * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF
++ */
++enum qca_wlan_he_ltf_cfg {
++ QCA_WLAN_HE_LTF_AUTO = 0,
++ QCA_WLAN_HE_LTF_1X = 1,
++ QCA_WLAN_HE_LTF_2X = 2,
++ QCA_WLAN_HE_LTF_4X = 3,
++};
++
++/**
++ * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration
++ *
++ * Indicates the HE trigger frame MAC padding duration value.
++ *
++ * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to
++ * process the trigger frame.
++ * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for
++ * trigger frame.
++ * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for
++ * trigger frame.
++ */
++enum qca_wlan_he_mac_padding_dur {
++ QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0,
++ QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1,
++ QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2,
++};
++
++/**
++ * enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration
++ *
++ * Indicates the HE Operating mode control channel width setting value.
++ *
++ * @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz
++ * @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz
++ * @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz
++ * @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz
++ */
++enum qca_wlan_he_om_ctrl_ch_bw {
++ QCA_WLAN_HE_OM_CTRL_BW_20M = 0,
++ QCA_WLAN_HE_OM_CTRL_BW_40M = 1,
++ QCA_WLAN_HE_OM_CTRL_BW_80M = 2,
++ QCA_WLAN_HE_OM_CTRL_BW_160M = 3,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
++ * HE operating mode control transmit request. These attributes are
++ * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
++ * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value
++ * indicates the maximum number of spatial streams, NSS, that the STA
++ * supports in reception for PPDU bandwidths less than or equal to 80 MHz
++ * and is set to NSS - 1.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value
++ * indicates the operating channel width supported by the STA for both
++ * reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value
++ * indicates the all trigger based UL MU operations by the STA.
++ * 0 - UL MU operations are enabled by the STA.
++ * 1 - All triggered UL MU transmissions are suspended by the STA.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value
++ * indicates the maximum number of space-time streams, NSTS, that
++ * the STA supports in transmission and is set to NSTS - 1.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value
++ * combined with the UL MU Disable subfield and the recipient's setting
++ * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC
++ * capabilities to determine which HE TB PPDUs are possible by the
++ * STA to transmit.
++ * 0 - UL MU data operations are enabled by the STA.
++ * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable
++ * bit is not set, else UL MU Tx is suspended.
++ *
++ */
++enum qca_wlan_vendor_attr_he_omi_tx {
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX =
++ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
++};
++
++/* Attributes for data used by
++ * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
++ */
++enum qca_wlan_vendor_attr_wifi_test_config {
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0,
++ /* 8-bit unsigned value to configure the driver to enable/disable
++ * WMM feature. This attribute is used to configure testbed device.
++ * 1-enable, 0-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1,
++
++ /* 8-bit unsigned value to configure the driver to accept/reject
++ * the addba request from peer. This attribute is used to configure
++ * the testbed device.
++ * 1-accept addba, 0-reject addba
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2,
++
++ /* 8-bit unsigned value to configure the driver to send or not to
++ * send the addba request to peer.
++ * This attribute is used to configure the testbed device.
++ * 1-send addba, 0-do not send addba
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3,
++
++ /* 8-bit unsigned value to indicate the HE fragmentation support.
++ * Uses enum he_fragmentation_val values.
++ * This attribute is used to configure the testbed device to
++ * allow the advertised hardware capabilities to be downgraded
++ * for testing purposes.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4,
++
++ /* 8-bit unsigned value to indicate the HE MCS support.
++ * Uses enum he_mcs_config values.
++ * This attribute is used to configure the testbed device to
++ * allow the advertised hardware capabilities to be downgraded
++ * for testing purposes.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5,
++
++ /* 8-bit unsigned value to configure the driver to allow or not to
++ * allow the connection with WEP/TKIP in HT/VHT/HE modes.
++ * This attribute is used to configure the testbed device.
++ * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6,
++
++ /* 8-bit unsigned value to configure the driver to add a
++ * new BA session or delete the existing BA session for
++ * given TID. ADDBA command uses the buffer size and TID
++ * configuration if user specifies the values else default
++ * value for buffer size is used for all TIDs if the TID
++ * also not specified. For DEL_BA command TID value is
++ * required to process the command.
++ * Uses enum qca_wlan_ba_session_config values.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7,
++
++ /* 16-bit unsigned value to configure the buffer size in addba
++ * request and response frames.
++ * This attribute is used to configure the testbed device.
++ * The range of the value is 0 to 256.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8,
++
++ /* 8-bit unsigned value to configure the buffer size in addba
++ * request and response frames.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9,
++
++ /* 8-bit unsigned value to configure the no ack policy.
++ * To configure no ack policy, access category value is
++ * required to process the command.
++ * This attribute is used to configure the testbed device.
++ * 1 - enable no ack, 0 - disable no ack.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10,
++
++ /* 8-bit unsigned value to configure the AC for no ack policy
++ * This attribute is used to configure the testbed device.
++ * Uses the enum qca_wlan_ac_type values.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11,
++
++ /* 8-bit unsigned value to configure the HE LTF
++ * This attribute is used to configure the testbed device.
++ * Uses the enum qca_wlan_he_ltf_cfg values.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12,
++
++ /* 8-bit unsigned value to configure the tx beamformee.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13,
++
++ /* 8-bit unsigned value to configure the tx beamformee number
++ * of space-time streams.
++ * This attribute is used to configure the testbed device.
++ * The range of the value is 0 to 8.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14,
++
++ /* 8-bit unsigned value to configure the MU EDCA params for given AC
++ * This attribute is used to configure the testbed device.
++ * Uses the enum qca_wlan_ac_type values.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15,
++
++ /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC
++ * To configure MU EDCA AIFSN value, MU EDCA access category value
++ * is required to process the command.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16,
++
++ /* 8-bit unsigned value to configure the MU EDCA ECW min value for
++ * given AC.
++ * To configure MU EDCA ECW min value, MU EDCA access category value
++ * is required to process the command.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17,
++
++ /* 8-bit unsigned value to configure the MU EDCA ECW max value for
++ * given AC.
++ * To configure MU EDCA ECW max value, MU EDCA access category value
++ * is required to process the command.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18,
++
++ /* 8-bit unsigned value to configure the MU EDCA timer for given AC
++ * To configure MU EDCA timer value, MU EDCA access category value
++ * is required to process the command.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19,
++
++ /* 8-bit unsigned value to configure the HE trigger frame MAC padding
++ * duration.
++ * This attribute is used to configure the testbed device.
++ * Uses the enum qca_wlan_he_mac_padding_dur values.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20,
++
++ /* 8-bit unsigned value to override the MU EDCA params to defaults
++ * regardless of the AP beacon MU EDCA params. If it is enabled use
++ * the default values else use the MU EDCA params from AP beacon.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21,
++
++ /* 8-bit unsigned value to configure the support for receiving
++ * an MPDU that contains an operating mode control subfield.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22,
++
++ /* Nested attribute values required to setup the TWT session.
++ * enum qca_wlan_vendor_attr_twt_setup provides the necessary
++ * information to set up the session. It contains broadcast flags,
++ * set_up flags, trigger value, flow type, flow ID, wake interval
++ * exponent, protection, target wake time, wake duration, wake interval
++ * mantissa. These nested attributes are used to setup a host triggered
++ * TWT session.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23,
++
++ /* This nested attribute is used to terminate the current TWT session.
++ * It does not currently carry any attributes.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24,
++
++ /* This nested attribute is used to suspend the current TWT session.
++ * It does not currently carry any attributes.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25,
++
++ /* Nested attribute values to indicate the request for resume.
++ * This attribute is used to resume the TWT session.
++ * enum qca_wlan_vendor_attr_twt_resume provides the necessary
++ * parameters required to resume the TWT session.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26,
++
++ /* 8-bit unsigned value to set the HE operating mode control
++ * (OM CTRL) Channel Width subfield.
++ * The Channel Width subfield indicates the operating channel width
++ * supported by the STA for both reception and transmission.
++ * Uses the enum qca_wlan_he_om_ctrl_ch_bw values.
++ * This setting is cleared with the
++ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
++ * flag attribute to reset defaults.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27,
++
++ /* 8-bit unsigned value to configure the number of spatial
++ * streams in HE operating mode control field.
++ * This setting is cleared with the
++ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
++ * flag attribute to reset defaults.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28,
++
++ /* Flag attribute to configure the UL MU disable bit in
++ * HE operating mode control field.
++ * This setting is cleared with the
++ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
++ * flag attribute to reset defaults.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29,
++
++ /* Flag attribute to clear the previously set HE operating mode
++ * control field configuration.
++ * This attribute is used to configure the testbed device to reset
++ * defaults to clear any previously set HE operating mode control
++ * field configuration.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30,
++
++ /* 8-bit unsigned value to configure HE single user PPDU
++ * transmission. By default this setting is disabled and it
++ * is disabled in the reset defaults of the device configuration.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31,
++
++ /* 8-bit unsigned value to configure action frame transmission
++ * in HE trigger based PPDU transmission.
++ * By default this setting is disabled and it is disabled in
++ * the reset defaults of the device configuration.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32,
++
++ /* Nested attribute to indicate HE operating mode control field
++ * transmission. It contains operating mode control field Nss,
++ * channel bandwidth, Tx Nsts and UL MU disable attributes.
++ * These nested attributes are used to send HE operating mode control
++ * with configured values.
++ * Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes.
++ * This attribute is used to configure the testbed device.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33,
++
++ /* 8-bit unsigned value to configure +HTC_HE support to indicate the
++ * support for the reception of a frame that carries an HE variant
++ * HT Control field.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34,
++
++ /* 8-bit unsigned value to configure VHT support in 2.4G band.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35,
++
++ /* 8-bit unsigned value to configure HE testbed defaults.
++ * This attribute is used to configure the testbed device.
++ * 1-set the device HE capabilities to testbed defaults.
++ * 0-reset the device HE capabilities to supported config.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36,
++
++ /* 8-bit unsigned value to configure TWT request support.
++ * This attribute is used to configure the testbed device.
++ * 1-enable, 0-disable.
++ */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
++ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
++ * The user can add/delete the filter by specifying the BSSID/STA MAC address in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the
++ * statistics of an unassociated station by specifying the MAC address in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in
++ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get
++ * the statistics of all unassociated stations by specifying the Broadcast MAC
++ * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with
++ * above procedure. In the response, driver shall specify statistics
++ * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS.
++ */
++enum qca_wlan_vendor_attr_bss_filter {
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1,
++ /* Other BSS filter type, unsigned 8 bit value. One of the values
++ * in enum qca_wlan_vendor_bss_filter_type.
++ */
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2,
++ /* Other BSS filter action, unsigned 8 bit value. One of the values
++ * in enum qca_wlan_vendor_bss_filter_action.
++ */
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3,
++ /* Array of nested attributes where each entry is the statistics
++ * information of the specified station that belong to another BSS.
++ * Attributes for each entry are taken from enum
++ * qca_wlan_vendor_bss_filter_sta_stats.
++ * Other BSS station configured in
++ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type
++ * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA.
++ * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER
++ * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET.
++ */
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX =
++ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_bss_filter_type - Type of
++ * filter used in other BSS filter operations. Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
++ *
++ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter
++ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter
++ */
++enum qca_wlan_vendor_bss_filter_type {
++ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID,
++ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA,
++};
++
++/**
++ * enum qca_wlan_vendor_bss_filter_action - Type of
++ * action in other BSS filter operations. Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
++ *
++ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter
++ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter
++ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics
++ */
++enum qca_wlan_vendor_bss_filter_action {
++ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD,
++ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL,
++ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET,
++};
++
++/**
++ * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for
++ * the statistics of a specific unassociated station belonging to another BSS.
++ * The statistics provides information of the unassociated station
++ * filtered by other BSS operation - such as MAC, signal value.
++ * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
++ *
++ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station.
++ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength
++ * of the station. Unsigned 8 bit number containing RSSI.
++ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host
++ * driver for the last received RSSI. Unsigned 64 bit number containing
++ * nanoseconds from the boottime.
++ */
++enum qca_wlan_vendor_bss_filter_sta_stats {
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0,
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1,
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2,
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST,
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX =
++ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1
++};
++
++/* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute
++ * QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
++ */
++enum qca_wlan_nan_ext_subcmd_type {
++ /* Subcmd of type NAN Enable Request */
++ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1,
++ /* Subcmd of type NAN Disable Request */
++ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_nan_params - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
++ */
++enum qca_wlan_vendor_attr_nan_params {
++ QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0,
++ /* Carries NAN command for firmware component. Every vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a
++ * payload containing the NAN command. NLA_BINARY attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1,
++ /* Indicates the type of NAN command sent with
++ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type
++ * describes the possible range of values. This attribute is mandatory
++ * if the command being issued is either
++ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or
++ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2,
++ /* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz
++ * band. This attribute is mandatory when command type is
++ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3,
++ /* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz
++ * band. This attribute is optional and should be included when command
++ * type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery
++ * has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX =
++ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1
++};
++
++/**
++ * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
++ * TWT (Target Wake Time) setup request. These attributes are sent as part of
++ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
++ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
++ * Disable (flag attribute not present) - Individual TWT
++ * Enable (flag attribute present) - Broadcast TWT.
++ * Individual means the session is between the STA and the AP.
++ * This session is established using a separate negotiation between
++ * STA and AP.
++ * Broadcast means the session is across multiple STAs and an AP. The
++ * configuration parameters are announced in Beacon frames by the AP.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
++ * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
++ * specify the TWT request type
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
++ * Enable (flag attribute present) - TWT with trigger support.
++ * Disable (flag attribute not present) - TWT without trigger support.
++ * Trigger means the AP will send the trigger frame to allow STA to send data.
++ * Without trigger, the STA will wait for the MU EDCA timer before
++ * transmitting the data.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
++ * 0 - Announced TWT - In this mode, STA may skip few service periods to
++ * save more power. If STA wants to wake up, it will send a PS-POLL/QoS
++ * NULL frame to AP.
++ * 1 - Unannounced TWT - The STA will wakeup during every SP.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
++ * Flow ID is the unique identifier for each TWT session.
++ * Currently this is not required and dialog ID will be set to zero.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
++ * This attribute (exp) is used along with the mantissa to derive the
++ * wake interval using the following formula:
++ * pow(2,exp) = wake_intvl_us/wake_intvl_mantis
++ * Wake interval is the interval between 2 successive SP.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
++ * Enable (flag attribute present) - Protection required.
++ * Disable (flag attribute not present) - Protection not required.
++ * If protection is enabled, then the AP will use protection
++ * mechanism using RTS/CTS to self to reserve the airtime.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
++ * This attribute is used as the SP offset which is the offset from
++ * TSF after which the wake happens. The units are in microseconds. If
++ * this attribute is not provided, then the value will be set to zero.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
++ * This is the duration of the service period. The units are in TU.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
++ * This attribute is used to configure wake interval mantissa.
++ * The units are in TU.
++ */
++enum qca_wlan_vendor_attr_twt_setup {
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
++ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
++ * TWT (Target Wake Time) resume request. These attributes are sent as part of
++ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
++ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
++ * This attribute is used as the SP offset which is the offset from
++ * TSF after which the wake happens. The units are in microseconds.
++ * If this attribute is not provided, then the value will be set to
++ * zero.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
++ * This attribute represents the next TWT subfield size.
++ */
++enum qca_wlan_vendor_attr_twt_resume {
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX =
++ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
++ * Represents the setup type being requested for TWT.
++ * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
++ * parameters but relying on AP to fill the parameters during the negotiation.
++ * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested
++ * values which the AP may accept or AP may provide alternative parameters
++ * which the STA may accept.
++ * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
++ * alternate parameters than the requested ones.
++ */
++enum qca_wlan_vendor_twt_setup_req_type {
++ QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
++ QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
++ QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
++};
++
++/**
++ * enum qca_wlan_roam_scan_event_type - Type of roam scan event
++ *
++ * Indicates the type of roam scan event sent by firmware/driver.
++ *
++ * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type.
++ * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type.
++ */
++enum qca_wlan_roam_scan_event_type {
++ QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0,
++ QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1,
++};
++
++/**
++ * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason
++ *
++ * Indicates the reason for triggering roam scan by firmware/driver.
++ *
++ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP.
++ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate.
++ */
++enum qca_wlan_roam_scan_trigger_reason {
++ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0,
++ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report
++ * roam scan related details from driver/firmware to user space. enum values
++ * are used for NL attributes sent with
++ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command.
++ */
++enum qca_wlan_vendor_attr_roam_scan {
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0,
++ /* Encapsulates type of roam scan event being reported. enum
++ * qca_wlan_roam_scan_event_type describes the possible range of
++ * values. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1,
++ /* Encapsulates reason for triggering roam scan. enum
++ * qca_wlan_roam_scan_trigger_reason describes the possible range of
++ * values. u32 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX =
++ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by
++ * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor
++ * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG.
++ */
++enum qca_wlan_vendor_cfr_method {
++ /* CFR method using QOS Null frame */
++ QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0,
++};
++
++/**
++ * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command
++ * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer
++ * Channel Frequency Response capture parameters and enable periodic CFR
++ * capture.
++ */
++enum qca_wlan_vendor_peer_cfr_capture_attr {
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
++ /* 6-byte MAC address of the peer.
++ * This attribute is mandatory.
++ */
++ QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1,
++ /* Enable peer CFR Capture, flag attribute.
++ * This attribute is mandatory to enable peer CFR capture.
++ * If this attribute is not present, peer CFR capture is disabled.
++ */
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2,
++ /* BW of measurement, attribute uses the values in enum nl80211_chan_width
++ * Supported values: 20, 40, 80, 80+80, 160.
++ * Note that all targets may not support all bandwidths.
++ * u8 attribute. This attribute is mandatory if attribute
++ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
++ */
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3,
++ /* Periodicity of CFR measurement in msec.
++ * Periodicity should be a multiple of Base timer.
++ * Current Base timer value supported is 10 msecs (default).
++ * 0 for one shot capture. u32 attribute.
++ * This attribute is mandatory if attribute
++ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
++ */
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4,
++ /* Method used to capture Channel Frequency Response.
++ * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method.
++ * u8 attribute. This attribute is mandatory if attribute
++ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
++ */
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5,
++ /* Enable periodic CFR capture, flag attribute.
++ * This attribute is mandatory to enable Periodic CFR capture.
++ * If this attribute is not present, periodic CFR capture is disabled.
++ */
++ QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6,
++
++ /* Keep last */
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX =
++ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_throughput_level - Current throughput level
++ *
++ * Indicates the current level of throughput calculated by the driver. The
++ * driver may choose different thresholds to decide whether the throughput level
++ * is low or medium or high based on variety of parameters like physical link
++ * capacity of the current connection, the number of packets being dispatched
++ * per second, etc. The throughput level events might not be consistent with the
++ * actual current throughput value being observed.
++ *
++ * @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput
++ * @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput
++ * @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput
++ */
++enum qca_wlan_throughput_level {
++ QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0,
++ QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1,
++ QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to
++ * report throughput changes from the driver to user space. enum values are used
++ * for netlink attributes sent with
++ * %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command.
++ */
++enum qca_wlan_vendor_attr_throughput_change {
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0,
++ /* Indicates the direction of throughput in which the change is being
++ * reported. u8 attribute. Value is 0 for TX and 1 for RX.
++ */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1,
++ /* Indicates the newly observed throughput level. enum
++ * qca_wlan_throughput_level describes the possible range of values.
++ * u8 attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2,
++ /* Indicates the driver's guidance on the new value to be set to
++ * kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The
++ * driver may optionally include this attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3,
++ /* Indicates the driver's guidance on the new value to be set to
++ * kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible
++ * values are from -31 to 31. The driver may optionally include this
++ * attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4,
++ /* Indicates the driver's guidance on the new value to be set to
++ * kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may
++ * optionally include this attribute.
++ */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5,
++
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX =
++ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_coex_config_profiles - This enum defines different types of
++ * traffic streams that can be prioritized one over the other during coex
++ * scenarios.
++ * The types defined in this enum are categorized in the below manner.
++ * 0 - 31 values corresponds to WLAN
++ * 32 - 63 values corresponds to BT
++ * 64 - 95 values corresponds to Zigbee
++ * @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA
++ * @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA
++ * @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA
++ * @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA
++ * @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA
++ * @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP
++ * @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP
++ * @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP
++ * @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP
++ * @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP
++ * @QCA_BT_A2DP: Prioritize BT A2DP
++ * @QCA_BT_BLE: Prioritize BT BLE
++ * @QCA_BT_SCO: Prioritize BT SCO
++ * @QCA_ZB_LOW: Prioritize Zigbee Low
++ * @QCA_ZB_HIGH: Prioritize Zigbee High
++ */
++enum qca_coex_config_profiles {
++ /* 0 - 31 corresponds to WLAN */
++ QCA_WIFI_STA_DISCOVERY = 0,
++ QCA_WIFI_STA_CONNECTION = 1,
++ QCA_WIFI_STA_CLASS_3_MGMT = 2,
++ QCA_WIFI_STA_DATA = 3,
++ QCA_WIFI_STA_ALL = 4,
++ QCA_WIFI_SAP_DISCOVERY = 5,
++ QCA_WIFI_SAP_CONNECTION = 6,
++ QCA_WIFI_SAP_CLASS_3_MGMT = 7,
++ QCA_WIFI_SAP_DATA = 8,
++ QCA_WIFI_SAP_ALL = 9,
++ QCA_WIFI_CASE_MAX = 31,
++ /* 32 - 63 corresponds to BT */
++ QCA_BT_A2DP = 32,
++ QCA_BT_BLE = 33,
++ QCA_BT_SCO = 34,
++ QCA_BT_CASE_MAX = 63,
++ /* 64 - 95 corresponds to Zigbee */
++ QCA_ZB_LOW = 64,
++ QCA_ZB_HIGH = 65,
++ QCA_ZB_CASE_MAX = 95,
++ /* 0xff is default value if the u8 profile value is not set. */
++ QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255
++};
++
++/**
++ * enum qca_vendor_attr_coex_config_types - Coex configurations types.
++ * This enum defines the valid set of values of coex configuration types. These
++ * values may used by attribute
++ * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the
++ * weights to default values.
++ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config
++ * weights with configurability value.
++ */
++enum qca_vendor_attr_coex_config_types {
++ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1,
++ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2,
++};
++
++/**
++ * enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes
++ *
++ * @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable
++ * length array of 8-bit values from enum qca_coex_config_profiles.
++ * FW will prioritize the profiles in the order given in the array encapsulated
++ * in this attribute.
++ * For example:
++ * -----------------------------------------------------------------------
++ * | 1 | 34 | 32 | 65 |
++ * -----------------------------------------------------------------------
++ * If the attribute contains the values defined in above array then it means
++ * 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH.
++ * 2) BT_SCO has priority over BT_A2DP.
++ * 3) BT_A2DP has priority over ZIGBEE HIGH.
++ * Profiles which are not listed in this array shall not be preferred over the
++ * profiles which are listed in the array as a part of this attribute.
++ */
++enum qca_vendor_attr_coex_config {
++ QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
++ QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1,
++
++ /* Keep last */
++ QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST,
++ QCA_VENDOR_ATTR_COEX_CONFIG_MAX =
++ QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config
++ * attributes
++ * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG
++ *
++ * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute.
++ * Indicate config type.
++ * The config types are 32-bit values from qca_vendor_attr_coex_config_types
++ *
++ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute.
++ * Indicate the Priority 1 profiles.
++ * The profiles are 8-bit values from enum qca_coex_config_profiles.
++ * In same priority level, maximum to 4 profiles can be set here.
++ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute.
++ * Indicate the Priority 2 profiles.
++ * The profiles are 8-bit values from enum qca_coex_config_profiles.
++ * In same priority level, maximum to 4 profiles can be set here.
++ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute.
++ * Indicate the Priority 3 profiles.
++ * The profiles are 8-bit values from enum qca_coex_config_profiles.
++ * In same priority level, maximum to 4 profiles can be set here.
++ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute.
++ * Indicate the Priority 4 profiles.
++ * The profiles are 8-bit values from enum qca_coex_config_profiles.
++ * In same priority level, maximum to 4 profiles can be set here.
++ * NOTE:
++ * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority
++ * arrangement:
++ * 1: In the same u32 attribute (priority x), the profiles enum values own
++ * same priority level.
++ * 2: 0xff is default value if the u8 profile value is not set.
++ * 3: max to 4 rules/profiles in same priority level.
++ * 4: max to 4 priority level (priority 1 - priority 4)
++ * 5: one priority level only supports one scenario from WLAN/BT/ZB,
++ * hybrid rules not support.
++ * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will
++ * remain blank to reset all parameters.
++ * For example:
++ *
++ * If the attributes as follow:
++ * priority 1:
++ * ------------------------------------
++ * | 0xff | 0 | 1 | 2 |
++ * ------------------------------------
++ * priority 2:
++ * -------------------------------------
++ * | 0xff | 0xff | 0xff | 32 |
++ * -------------------------------------
++ * priority 3:
++ * -------------------------------------
++ * | 0xff | 0xff | 0xff | 65 |
++ * -------------------------------------
++ * then it means:
++ * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
++ * owns same priority level.
++ * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
++ * has priority over BT_A2DP and ZB_HIGH.
++ * 3: BT_A2DP has priority over ZB_HIGH.
++ */
++
++enum qca_vendor_attr_coex_config_three_way {
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5,
++
++ /* Keep last */
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST,
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX =
++ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
++ * (STA/AP) for the connected link.
++ * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a
++ * &struct nl80211_sta_flag_update for the respective connected link. MAC
++ * address of the peer represented by
++ * QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR.
++ */
++enum qca_wlan_vendor_attr_link_properties {
++ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0,
++ /* 1 - 3 are reserved */
++ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4,
++ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5,
++
++ /* Keep last */
++ QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST,
++ QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX =
++ QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
++};
++
++/**
++ * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type
++ * This enum defines the valid set of values of peer stats cache types. These
++ * values are used by attribute
++ * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics
++ * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics
++ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn
++ * statistics
++ */
++enum qca_vendor_attr_peer_stats_cache_type {
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0,
++
++ QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS,
++ QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS,
++ QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS,
++};
++
++/**
++ * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines
++ * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH
++ * Information in these attributes is used to flush peer rate statistics from
++ * the driver to user application.
++ *
++ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute
++ * Indicate peer statistics cache type.
++ * The statistics types are 32-bit values from
++ * enum qca_vendor_attr_peer_stats_cache_type.
++ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array
++ * of size 6 octets, representing the peer MAC address.
++ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute
++ * containing buffer of statistics to send to application layer entity.
++ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
++ * representing a cookie for peer unique session.
++ */
++enum qca_wlan_vendor_attr_peer_stats_cache_params {
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
++
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1,
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
++
++ /* Keep last */
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX =
++ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1
++};
++
++/**
++ * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state
++ * This enum defines all the possible states of Zigbee, which can be
++ * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute.
++ *
++ * @ZIGBEE_IDLE: Zigbee in idle state
++ * @ZIGBEE_FORM_NETWORK: Zigbee forming network
++ * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network
++ * @ZIGBEE_JOIN: Zigbee joining network
++ * @ZIGBEE_NETWORK_UP: Zigbee network is up
++ * @ZIGBEE_HMI: Zigbee in HMI mode
++ */
++enum qca_mpta_helper_attr_zigbee_state {
++ ZIGBEE_IDLE = 0,
++ ZIGBEE_FORM_NETWORK = 1,
++ ZIGBEE_WAIT_JOIN = 2,
++ ZIGBEE_JOIN = 3,
++ ZIGBEE_NETWORK_UP = 4,
++ ZIGBEE_HMI = 5,
++};
++
++/*
++ * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command
++ * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG.
++ */
++enum qca_mpta_helper_vendor_attr {
++ QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0,
++ /* Optional attribute used to update Zigbee state.
++ * enum qca_mpta_helper_attr_zigbee_state.
++ * NLA_U32 attribute.
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1,
++ /* Optional attribute used to configure WLAN duration for Shape-OCS
++ * during interrupt.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION.
++ * Value range 0 ~ 300 (ms).
++ * NLA_U32 attribute.
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2,
++ /* Optional attribute used to configure non-WLAN duration for Shape-OCS
++ * during interrupt.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION.
++ * Value range 0 ~ 300 (ms).
++ * NLA_U32 attribute.
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3,
++ /* Optional attribute used to configure WLAN duration for Shape-OCS
++ * monitor period.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION.
++ * Value range 0 ~ 300 (ms)
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4,
++ /* Optional attribute used to configure non-WLAN duration for Shape-OCS
++ * monitor period.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION.
++ * Value range 0 ~ 300 (ms)
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5,
++ /* Optional attribute used to configure OCS interrupt duration.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION.
++ * Value range 1000 ~ 20000 (ms)
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6,
++ /* Optional attribute used to configure OCS monitor duration.
++ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION.
++ * Value range 1000 ~ 20000 (ms)
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7,
++ /* Optional attribute used to notify WLAN firmware the current Zigbee
++ * channel.
++ * Value range 11 ~ 26
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8,
++ /* Optional attribute used to configure WLAN mute duration.
++ * Value range 0 ~ 400 (ms)
++ * NLA_U32 attribute
++ */
++ QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9,
++
++ /* keep last */
++ QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST,
++ QCA_MPTA_HELPER_VENDOR_ATTR_MAX =
++ QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
++};
++
+ #endif /* QCA_VENDOR_H */
+--- contrib/wpa/src/common/sae.c.orig
++++ contrib/wpa/src/common/sae.c
+@@ -1,6 +1,6 @@
+ /*
+ * Simultaneous authentication of equals
+- * Copyright (c) 2012-2015, Jouni Malinen
++ * Copyright (c) 2012-2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -9,6 +9,7 @@
+ #include "includes.h"
+
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "crypto/crypto.h"
+ #include "crypto/sha256.h"
+ #include "crypto/random.h"
+@@ -17,10 +18,33 @@
+ #include "sae.h"
+
+
++static int sae_suitable_group(int group)
++{
++#ifdef CONFIG_TESTING_OPTIONS
++ /* Allow all groups for testing purposes in non-production builds. */
++ return 1;
++#else /* CONFIG_TESTING_OPTIONS */
++ /* Enforce REVmd rules on which SAE groups are suitable for production
++ * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
++ * defined over a prime field whose prime is >= 256 bits. Furthermore,
++ * ECC groups defined over a characteristic 2 finite field and ECC
++ * groups with a co-factor greater than 1 are not suitable. */
++ return group == 19 || group == 20 || group == 21 ||
++ group == 28 || group == 29 || group == 30 ||
++ group == 15 || group == 16 || group == 17 || group == 18;
++#endif /* CONFIG_TESTING_OPTIONS */
++}
++
++
+ int sae_set_group(struct sae_data *sae, int group)
+ {
+ struct sae_temporary_data *tmp;
+
++ if (!sae_suitable_group(group)) {
++ wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
++ return -1;
++ }
++
+ sae_clear_data(sae);
+ tmp = sae->tmp = os_zalloc(sizeof(*tmp));
+ if (tmp == NULL)
+@@ -29,6 +53,8 @@
+ /* First, check if this is an ECC group */
+ tmp->ec = crypto_ec_init(group);
+ if (tmp->ec) {
++ wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d",
++ group);
+ sae->group = group;
+ tmp->prime_len = crypto_ec_prime_len(tmp->ec);
+ tmp->prime = crypto_ec_get_prime(tmp->ec);
+@@ -39,6 +65,8 @@
+ /* Not an ECC group, check FFC */
+ tmp->dh = dh_groups_get(group);
+ if (tmp->dh) {
++ wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d",
++ group);
+ sae->group = group;
+ tmp->prime_len = tmp->dh->prime_len;
+ if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
+@@ -66,6 +94,8 @@
+ }
+
+ /* Unsupported group */
++ wpa_printf(MSG_DEBUG,
++ "SAE: Group %d not supported by the crypto library", group);
+ return -1;
+ }
+
+@@ -88,6 +118,7 @@
+ crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
+ crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
+ wpabuf_free(tmp->anti_clogging_token);
++ os_free(tmp->pw_id);
+ bin_clear_free(tmp, sizeof(*tmp));
+ sae->tmp = NULL;
+ }
+@@ -201,12 +232,14 @@
+
+ static int is_quadratic_residue_blind(struct sae_data *sae,
+ const u8 *prime, size_t bits,
+- const struct crypto_bignum *qr,
+- const struct crypto_bignum *qnr,
++ const u8 *qr, const u8 *qnr,
+ const struct crypto_bignum *y_sqr)
+ {
+- struct crypto_bignum *r, *num;
++ struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
+ int r_odd, check, res = -1;
++ u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
++ size_t prime_len = sae->tmp->prime_len;
++ unsigned int mask;
+
+ /*
+ * Use the blinding technique to mask y_sqr while determining
+@@ -217,7 +250,7 @@
+ * r = a random number between 1 and p-1, inclusive
+ * num = (v * r * r) modulo p
+ */
+- r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
++ r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
+ if (!r)
+ return -1;
+
+@@ -227,23 +260,25 @@
+ crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+ goto fail;
+
+- if (r_odd) {
+- /*
+- * num = (num * qr) module p
+- * LGR(num, p) = 1 ==> quadratic residue
+- */
+- if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+- goto fail;
+- check = 1;
+- } else {
+- /*
+- * num = (num * qnr) module p
+- * LGR(num, p) = -1 ==> quadratic residue
+- */
+- if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+- goto fail;
+- check = -1;
+- }
++ /*
++ * Need to minimize differences in handling different cases, so try to
++ * avoid branches and timing differences.
++ *
++ * If r_odd:
++ * num = (num * qr) module p
++ * LGR(num, p) = 1 ==> quadratic residue
++ * else:
++ * num = (num * qnr) module p
++ * LGR(num, p) = -1 ==> quadratic residue
++ */
++ mask = const_time_is_zero(r_odd);
++ const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
++ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
++ if (!qr_or_qnr ||
++ crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
++ goto fail;
++ /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
++ check = const_time_select_int(mask, -1, 1);
+
+ res = crypto_bignum_legendre(num, sae->tmp->prime);
+ if (res == -2) {
+@@ -250,35 +285,35 @@
+ res = -1;
+ goto fail;
+ }
+- res = res == check;
++ /* branchless version of res = res == check
++ * (res is -1, 0, or 1; check is -1 or 1) */
++ mask = const_time_eq(res, check);
++ res = const_time_select_int(mask, 1, 0);
+ fail:
+ crypto_bignum_deinit(num, 1);
+ crypto_bignum_deinit(r, 1);
++ crypto_bignum_deinit(qr_or_qnr, 1);
+ return res;
+ }
+
+
+ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+- const u8 *prime,
+- const struct crypto_bignum *qr,
+- const struct crypto_bignum *qnr,
+- struct crypto_bignum **ret_x_cand)
++ const u8 *prime, const u8 *qr, const u8 *qnr,
++ u8 *pwd_value)
+ {
+- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+ struct crypto_bignum *y_sqr, *x_cand;
+ int res;
+ size_t bits;
+
+- *ret_x_cand = NULL;
+-
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+ /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+ bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+- sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+- prime, sae->tmp->prime_len, pwd_value, bits);
++ if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
++ prime, sae->tmp->prime_len, pwd_value, bits) < 0)
++ return -1;
+ if (bits % 8)
+- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
++ buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+ pwd_value, sae->tmp->prime_len);
+
+@@ -289,23 +324,18 @@
+ if (!x_cand)
+ return -1;
+ y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+- if (!y_sqr) {
+- crypto_bignum_deinit(x_cand, 1);
++ crypto_bignum_deinit(x_cand, 1);
++ if (!y_sqr)
+ return -1;
+- }
+
+ res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+ crypto_bignum_deinit(y_sqr, 1);
+- if (res <= 0) {
+- crypto_bignum_deinit(x_cand, 1);
+- return res;
+- }
+-
+- *ret_x_cand = x_cand;
+- return 1;
++ return res;
+ }
+
+
++/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
++ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
+ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+ struct crypto_bignum *pwe)
+ {
+@@ -312,30 +342,43 @@
+ u8 pwd_value[SAE_MAX_PRIME_LEN];
+ size_t bits = sae->tmp->prime_len * 8;
+ u8 exp[1];
+- struct crypto_bignum *a, *b;
+- int res;
++ struct crypto_bignum *a, *b = NULL;
++ int res, is_val;
++ u8 pwd_value_valid;
+
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+ /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+- sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+- sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+- bits);
+- if (bits % 8)
+- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
++ if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
++ sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
++ bits) < 0)
++ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+ sae->tmp->prime_len);
+
+- if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+- {
+- wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+- return 0;
+- }
++ /* Check whether pwd-value < p */
++ res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
++ sae->tmp->prime_len);
++ /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
++ * the negative sign can be used to fill the mask for constant time
++ * selection */
++ pwd_value_valid = const_time_fill_msb(res);
+
++ /* If pwd-value >= p, force pwd-value to be < p and perform the
++ * calculations anyway to hide timing difference. The derived PWE will
++ * be ignored in that case. */
++ pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
++
+ /* PWE = pwd-value^((p-1)/r) modulo p */
+
++ res = -1;
+ a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
++ if (!a)
++ goto fail;
+
++ /* This is an optimization based on the used group that does not depend
++ * on the password in any way, so it is fine to use separate branches
++ * for this step without constant time operations. */
+ if (sae->tmp->dh->safe_prime) {
+ /*
+ * r = (p-1)/2 for the group used here, so this becomes:
+@@ -349,33 +392,34 @@
+ b = crypto_bignum_init_set(exp, sizeof(exp));
+ if (b == NULL ||
+ crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+- crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+- crypto_bignum_deinit(b, 0);
+- b = NULL;
+- }
++ crypto_bignum_div(b, sae->tmp->order, b) < 0)
++ goto fail;
+ }
+
+- if (a == NULL || b == NULL)
+- res = -1;
+- else
+- res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
++ if (!b)
++ goto fail;
+
+- crypto_bignum_deinit(a, 0);
+- crypto_bignum_deinit(b, 0);
++ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
++ if (res < 0)
++ goto fail;
+
+- if (res < 0) {
+- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+- return -1;
+- }
++ /* There were no fatal errors in calculations, so determine the return
++ * value using constant time operations. We get here for number of
++ * invalid cases which are cleared here after having performed all the
++ * computation. PWE is valid if pwd-value was less than prime and
++ * PWE > 1. Start with pwd-value check first and then use constant time
++ * operations to clear res to 0 if PWE is 0 or 1.
++ */
++ res = const_time_select_u8(pwd_value_valid, 1, 0);
++ is_val = crypto_bignum_is_zero(pwe);
++ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
++ is_val = crypto_bignum_is_one(pwe);
++ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+
+- /* if (PWE > 1) --> found */
+- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+- return 0;
+- }
+-
+- wpa_printf(MSG_DEBUG, "SAE: PWE found");
+- return 1;
++fail:
++ crypto_bignum_deinit(a, 1);
++ crypto_bignum_deinit(b, 1);
++ return res;
+ }
+
+
+@@ -417,31 +461,39 @@
+
+ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
+ const u8 *addr2, const u8 *password,
+- size_t password_len)
++ size_t password_len, const char *identifier)
+ {
+ u8 counter, k = 40;
+ u8 addrs[2 * ETH_ALEN];
+- const u8 *addr[2];
+- size_t len[2];
+- u8 dummy_password[32];
+- size_t dummy_password_len;
++ const u8 *addr[3];
++ size_t len[3];
++ size_t num_elem;
++ u8 *dummy_password, *tmp_password;
+ int pwd_seed_odd = 0;
+ u8 prime[SAE_MAX_ECC_PRIME_LEN];
+ size_t prime_len;
+- struct crypto_bignum *x = NULL, *qr, *qnr;
++ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
++ u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+ size_t bits;
+- int res;
++ int res = -1;
++ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++ * mask */
+
+- dummy_password_len = password_len;
+- if (dummy_password_len > sizeof(dummy_password))
+- dummy_password_len = sizeof(dummy_password);
+- if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+- return -1;
++ os_memset(x_bin, 0, sizeof(x_bin));
+
++ dummy_password = os_malloc(password_len);
++ tmp_password = os_malloc(password_len);
++ if (!dummy_password || !tmp_password ||
++ random_get_bytes(dummy_password, password_len) < 0)
++ goto fail;
++
+ prime_len = sae->tmp->prime_len;
+ if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+ prime_len) < 0)
+- return -1;
++ goto fail;
+ bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+
+ /*
+@@ -449,24 +501,36 @@
+ * (qnr) modulo p for blinding purposes during the loop.
+ */
+ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+- &qr, &qnr) < 0)
+- return -1;
++ &qr, &qnr) < 0 ||
++ crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
++ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
++ goto fail;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+ password, password_len);
++ if (identifier)
++ wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
++ identifier);
+
+ /*
+ * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+- * base = password
++ * base = password [|| identifier]
+ * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+ * base || counter)
+ */
+ sae_pwd_seed_key(addr1, addr2, addrs);
+
+- addr[0] = password;
++ addr[0] = tmp_password;
+ len[0] = password_len;
+- addr[1] = &counter;
+- len[1] = sizeof(counter);
++ num_elem = 1;
++ if (identifier) {
++ addr[num_elem] = (const u8 *) identifier;
++ len[num_elem] = os_strlen(identifier);
++ num_elem++;
++ }
++ addr[num_elem] = &counter;
++ len[num_elem] = sizeof(counter);
++ num_elem++;
+
+ /*
+ * Continue for at least k iterations to protect against side-channel
+@@ -473,9 +537,8 @@
+ * attacks that attempt to determine the number of iterations required
+ * in the loop.
+ */
+- for (counter = 1; counter <= k || !x; counter++) {
++ for (counter = 1; counter <= k || !found; counter++) {
+ u8 pwd_seed[SHA256_MAC_LEN];
+- struct crypto_bignum *x_cand;
+
+ if (counter > 200) {
+ /* This should not happen in practice */
+@@ -483,40 +546,49 @@
+ break;
+ }
+
+- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+- pwd_seed) < 0)
++ wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
++ const_time_select_bin(found, dummy_password, password,
++ password_len, tmp_password);
++ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
++ addr, len, pwd_seed) < 0)
+ break;
+
+ res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+- prime, qr, qnr, &x_cand);
++ prime, qr_bin, qnr_bin, x_cand_bin);
++ const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
++ x_bin);
++ pwd_seed_odd = const_time_select_u8(
++ found, pwd_seed_odd,
++ pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
++ os_memset(pwd_seed, 0, sizeof(pwd_seed));
+ if (res < 0)
+ goto fail;
+- if (res > 0 && !x) {
+- wpa_printf(MSG_DEBUG,
+- "SAE: Selected pwd-seed with counter %u",
+- counter);
+- x = x_cand;
+- pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+- os_memset(pwd_seed, 0, sizeof(pwd_seed));
++ /* Need to minimize differences in handling res == 0 and 1 here
++ * to avoid differences in timing and instruction cache access,
++ * so use const_time_select_*() to make local copies of the
++ * values based on whether this loop iteration was the one that
++ * found the pwd-seed/x. */
+
+- /*
+- * Use a dummy password for the following rounds, if
+- * any.
+- */
+- addr[0] = dummy_password;
+- len[0] = dummy_password_len;
+- } else if (res > 0) {
+- crypto_bignum_deinit(x_cand, 1);
+- }
++ /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
++ * (with res converted to 0/0xff) handles this in constant time.
++ */
++ found |= res * 0xff;
++ wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
++ res, found);
+ }
+
+- if (!x) {
++ if (!found) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ res = -1;
+ goto fail;
+ }
+
++ x = crypto_bignum_init_set(x_bin, prime_len);
++ if (!x) {
++ res = -1;
++ goto fail;
++ }
++
+ if (!sae->tmp->pwe_ecc)
+ sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+ if (!sae->tmp->pwe_ecc)
+@@ -525,7 +597,6 @@
+ res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+ sae->tmp->pwe_ecc, x,
+ pwd_seed_odd);
+- crypto_bignum_deinit(x, 1);
+ if (res < 0) {
+ /*
+ * This should not happen since we already checked that there
+@@ -537,27 +608,49 @@
+ fail:
+ crypto_bignum_deinit(qr, 0);
+ crypto_bignum_deinit(qnr, 0);
++ os_free(dummy_password);
++ bin_clear_free(tmp_password, password_len);
++ crypto_bignum_deinit(x, 1);
++ os_memset(x_bin, 0, sizeof(x_bin));
++ os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
+
+ return res;
+ }
+
+
++static int sae_modp_group_require_masking(int group)
++{
++ /* Groups for which pwd-value is likely to be >= p frequently */
++ return group == 22 || group == 23 || group == 24;
++}
++
++
+ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+ const u8 *addr2, const u8 *password,
+- size_t password_len)
++ size_t password_len, const char *identifier)
+ {
+- u8 counter;
++ u8 counter, k, sel_counter = 0;
+ u8 addrs[2 * ETH_ALEN];
+- const u8 *addr[2];
+- size_t len[2];
+- int found = 0;
++ const u8 *addr[3];
++ size_t len[3];
++ size_t num_elem;
++ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++ * mask */
++ u8 mask;
++ struct crypto_bignum *pwe;
++ size_t prime_len = sae->tmp->prime_len * 8;
++ u8 *pwe_buf;
+
+- if (sae->tmp->pwe_ffc == NULL) {
+- sae->tmp->pwe_ffc = crypto_bignum_init();
+- if (sae->tmp->pwe_ffc == NULL)
+- return -1;
+- }
++ crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
++ sae->tmp->pwe_ffc = NULL;
+
++ /* Allocate a buffer to maintain selected and candidate PWE for constant
++ * time selection. */
++ pwe_buf = os_zalloc(prime_len * 2);
++ pwe = crypto_bignum_init();
++ if (!pwe_buf || !pwe)
++ goto fail;
++
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+ password, password_len);
+
+@@ -564,16 +657,25 @@
+ /*
+ * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+ * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+- * password || counter)
++ * password [|| identifier] || counter)
+ */
+ sae_pwd_seed_key(addr1, addr2, addrs);
+
+ addr[0] = password;
+ len[0] = password_len;
+- addr[1] = &counter;
+- len[1] = sizeof(counter);
++ num_elem = 1;
++ if (identifier) {
++ addr[num_elem] = (const u8 *) identifier;
++ len[num_elem] = os_strlen(identifier);
++ num_elem++;
++ }
++ addr[num_elem] = &counter;
++ len[num_elem] = sizeof(counter);
++ num_elem++;
+
+- for (counter = 1; !found; counter++) {
++ k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
++
++ for (counter = 1; counter <= k || !found; counter++) {
+ u8 pwd_seed[SHA256_MAC_LEN];
+ int res;
+
+@@ -583,20 +685,37 @@
+ break;
+ }
+
+- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+- pwd_seed) < 0)
++ wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
++ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
++ addr, len, pwd_seed) < 0)
+ break;
+- res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
++ res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
++ /* res is -1 for fatal failure, 0 if a valid PWE was not found,
++ * or 1 if a valid PWE was found. */
+ if (res < 0)
+ break;
+- if (res > 0) {
+- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+- found = 1;
+- }
++ /* Store the candidate PWE into the second half of pwe_buf and
++ * the selected PWE in the beginning of pwe_buf using constant
++ * time selection. */
++ if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
++ prime_len) < 0)
++ break;
++ const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
++ prime_len, pwe_buf);
++ sel_counter = const_time_select_u8(found, sel_counter, counter);
++ mask = const_time_eq_u8(res, 1);
++ found = const_time_select_u8(found, found, mask);
+ }
+
+- return found ? 0 : -1;
++ if (!found)
++ goto fail;
++
++ wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
++ sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
++fail:
++ crypto_bignum_deinit(pwe, 1);
++ bin_clear_free(pwe_buf, prime_len * 2);
++ return sae->tmp->pwe_ffc ? 0 : -1;
+ }
+
+
+@@ -696,13 +815,15 @@
+
+ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+ const u8 *password, size_t password_len,
+- struct sae_data *sae)
++ const char *identifier, struct sae_data *sae)
+ {
+ if (sae->tmp == NULL ||
+ (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+- password_len) < 0) ||
++ password_len,
++ identifier) < 0) ||
+ (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+- password_len) < 0) ||
++ password_len,
++ identifier) < 0) ||
+ sae_derive_commit(sae) < 0)
+ return -1;
+ return 0;
+@@ -811,11 +932,13 @@
+ crypto_bignum_mod(tmp, sae->tmp->order, tmp);
+ crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
+ wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+- sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+- val, sae->tmp->prime_len, keys, sizeof(keys));
++ if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
++ val, sae->tmp->prime_len, keys, sizeof(keys)) < 0)
++ goto fail;
+ os_memset(keyseed, 0, sizeof(keyseed));
+ os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
+ os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
++ os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
+ os_memset(keys, 0, sizeof(keys));
+ wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+@@ -840,7 +963,7 @@
+
+
+ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+- const struct wpabuf *token)
++ const struct wpabuf *token, const char *identifier)
+ {
+ u8 *pos;
+
+@@ -874,6 +997,16 @@
+ wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
+ pos, sae->tmp->prime_len);
+ }
++
++ if (identifier) {
++ /* Password Identifier element */
++ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
++ wpabuf_put_u8(buf, 1 + os_strlen(identifier));
++ wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
++ wpabuf_put_str(buf, identifier);
++ wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
++ identifier);
++ }
+ }
+
+
+@@ -919,25 +1052,70 @@
+ }
+
+
++static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
++{
++ return end - pos >= 3 &&
++ pos[0] == WLAN_EID_EXTENSION &&
++ pos[1] >= 1 &&
++ end - pos - 2 >= pos[1] &&
++ pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
++}
++
++
+ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
+ const u8 *end, const u8 **token,
+ size_t *token_len)
+ {
+- if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+- size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
+- sae->tmp->prime_len);
+- wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+- if (token)
+- *token = *pos;
+- if (token_len)
+- *token_len = tlen;
+- *pos += tlen;
+- } else {
+- if (token)
+- *token = NULL;
+- if (token_len)
+- *token_len = 0;
++ size_t scalar_elem_len, tlen;
++ const u8 *elem;
++
++ if (token)
++ *token = NULL;
++ if (token_len)
++ *token_len = 0;
++
++ scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
++ if (scalar_elem_len >= (size_t) (end - *pos))
++ return; /* No extra data beyond peer scalar and element */
++
++ /* It is a bit difficult to parse this now that there is an
++ * optional variable length Anti-Clogging Token field and
++ * optional variable length Password Identifier element in the
++ * frame. We are sending out fixed length Anti-Clogging Token
++ * fields, so use that length as a requirement for the received
++ * token and check for the presence of possible Password
++ * Identifier element based on the element header information.
++ */
++ tlen = end - (*pos + scalar_elem_len);
++
++ if (tlen < SHA256_MAC_LEN) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
++ (unsigned int) tlen);
++ return;
+ }
++
++ elem = *pos + scalar_elem_len;
++ if (sae_is_password_id_elem(elem, end)) {
++ /* Password Identifier element takes out all available
++ * extra octets, so there can be no Anti-Clogging token in
++ * this frame. */
++ return;
++ }
++
++ elem += SHA256_MAC_LEN;
++ if (sae_is_password_id_elem(elem, end)) {
++ /* Password Identifier element is included in the end, so
++ * remove its length from the Anti-Clogging token field. */
++ tlen -= 2 + elem[1];
++ }
++
++ wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
++ if (token)
++ *token = *pos;
++ if (token_len)
++ *token_len = tlen;
++ *pos += tlen;
+ }
+
+
+@@ -946,7 +1124,7 @@
+ {
+ struct crypto_bignum *peer_scalar;
+
+- if (*pos + sae->tmp->prime_len > end) {
++ if (sae->tmp->prime_len > end - *pos) {
+ wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+@@ -989,12 +1167,12 @@
+ }
+
+
+-static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
++static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
+ const u8 *end)
+ {
+ u8 prime[SAE_MAX_ECC_PRIME_LEN];
+
+- if (pos + 2 * sae->tmp->prime_len > end) {
++ if (2 * sae->tmp->prime_len > end - *pos) {
+ wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+ "commit-element");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+@@ -1005,8 +1183,8 @@
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /* element x and y coordinates < p */
+- if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
+- os_memcmp(pos + sae->tmp->prime_len, prime,
++ if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
++ os_memcmp(*pos + sae->tmp->prime_len, prime,
+ sae->tmp->prime_len) >= 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+ "element");
+@@ -1014,13 +1192,13 @@
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+- pos, sae->tmp->prime_len);
++ *pos, sae->tmp->prime_len);
+ wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+- pos + sae->tmp->prime_len, sae->tmp->prime_len);
++ *pos + sae->tmp->prime_len, sae->tmp->prime_len);
+
+ crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
+ sae->tmp->peer_commit_element_ecc =
+- crypto_ec_point_from_bin(sae->tmp->ec, pos);
++ crypto_ec_point_from_bin(sae->tmp->ec, *pos);
+ if (sae->tmp->peer_commit_element_ecc == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+@@ -1030,27 +1208,29 @@
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
++ *pos += 2 * sae->tmp->prime_len;
++
+ return WLAN_STATUS_SUCCESS;
+ }
+
+
+-static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
++static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
+ const u8 *end)
+ {
+ struct crypto_bignum *res, *one;
+ const u8 one_bin[1] = { 0x01 };
+
+- if (pos + sae->tmp->prime_len > end) {
++ if (sae->tmp->prime_len > end - *pos) {
+ wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+ "commit-element");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+- wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
++ wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
+ sae->tmp->prime_len);
+
+ crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
+ sae->tmp->peer_commit_element_ffc =
+- crypto_bignum_init_set(pos, sae->tmp->prime_len);
++ crypto_bignum_init_set(*pos, sae->tmp->prime_len);
+ if (sae->tmp->peer_commit_element_ffc == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ /* 1 < element < p - 1 */
+@@ -1078,11 +1258,13 @@
+ }
+ crypto_bignum_deinit(res, 0);
+
++ *pos += sae->tmp->prime_len;
++
+ return WLAN_STATUS_SUCCESS;
+ }
+
+
+-static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
++static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
+ const u8 *end)
+ {
+ if (sae->tmp->dh)
+@@ -1091,6 +1273,44 @@
+ }
+
+
++static int sae_parse_password_identifier(struct sae_data *sae,
++ const u8 *pos, const u8 *end)
++{
++ wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
++ pos, end - pos);
++ if (!sae_is_password_id_elem(pos, end)) {
++ if (sae->tmp->pw_id) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: No Password Identifier included, but expected one (%s)",
++ sae->tmp->pw_id);
++ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
++ }
++ os_free(sae->tmp->pw_id);
++ sae->tmp->pw_id = NULL;
++ return WLAN_STATUS_SUCCESS; /* No Password Identifier */
++ }
++
++ if (sae->tmp->pw_id &&
++ (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
++ os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) {
++ wpa_printf(MSG_DEBUG,
++ "SAE: The included Password Identifier does not match the expected one (%s)",
++ sae->tmp->pw_id);
++ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
++ }
++
++ os_free(sae->tmp->pw_id);
++ sae->tmp->pw_id = os_malloc(pos[1]);
++ if (!sae->tmp->pw_id)
++ return WLAN_STATUS_UNSPECIFIED_FAILURE;
++ os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1);
++ sae->tmp->pw_id[pos[1] - 1] = '\0';
++ wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
++ sae->tmp->pw_id, pos[1] - 1);
++ return WLAN_STATUS_SUCCESS;
++}
++
++
+ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+ const u8 **token, size_t *token_len, int *allowed_groups)
+ {
+@@ -1098,7 +1318,7 @@
+ u16 res;
+
+ /* Check Finite Cyclic Group */
+- if (pos + 2 > end)
++ if (end - pos < 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
+ if (res != WLAN_STATUS_SUCCESS)
+@@ -1114,10 +1334,15 @@
+ return res;
+
+ /* commit-element */
+- res = sae_parse_commit_element(sae, pos, end);
++ res = sae_parse_commit_element(sae, &pos, end);
+ if (res != WLAN_STATUS_SUCCESS)
+ return res;
+
++ /* Optional Password Identifier element */
++ res = sae_parse_password_identifier(sae, pos, end);
++ if (res != WLAN_STATUS_SUCCESS)
++ return res;
++
+ /*
+ * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
+ * the values we sent which would be evidence of a reflection attack.
+@@ -1233,7 +1458,8 @@
+ /* Send-Confirm */
+ sc = wpabuf_put(buf, 0);
+ wpabuf_put_le16(buf, sae->send_confirm);
+- sae->send_confirm++;
++ if (sae->send_confirm < 0xffff)
++ sae->send_confirm++;
+
+ if (sae->tmp->ec)
+ sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+@@ -1261,23 +1487,31 @@
+
+ wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+
+- if (sae->tmp == NULL) {
++ if (!sae->tmp || !sae->peer_commit_scalar ||
++ !sae->tmp->own_commit_scalar) {
+ wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+ return -1;
+ }
+
+- if (sae->tmp->ec)
++ if (sae->tmp->ec) {
++ if (!sae->tmp->peer_commit_element_ecc ||
++ !sae->tmp->own_commit_element_ecc)
++ return -1;
+ sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+ sae->tmp->peer_commit_element_ecc,
+ sae->tmp->own_commit_scalar,
+ sae->tmp->own_commit_element_ecc,
+ verifier);
+- else
++ } else {
++ if (!sae->tmp->peer_commit_element_ffc ||
++ !sae->tmp->own_commit_element_ffc)
++ return -1;
+ sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+ sae->tmp->peer_commit_element_ffc,
+ sae->tmp->own_commit_scalar,
+ sae->tmp->own_commit_element_ffc,
+ verifier);
++ }
+
+ if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
+@@ -1290,3 +1524,19 @@
+
+ return 0;
+ }
++
++
++const char * sae_state_txt(enum sae_state state)
++{
++ switch (state) {
++ case SAE_NOTHING:
++ return "Nothing";
++ case SAE_COMMITTED:
++ return "Committed";
++ case SAE_CONFIRMED:
++ return "Confirmed";
++ case SAE_ACCEPTED:
++ return "Accepted";
++ }
++ return "?";
++}
+--- contrib/wpa/src/common/sae.h.orig
++++ contrib/wpa/src/common/sae.h
+@@ -39,15 +39,24 @@
+ struct crypto_bignum *prime_buf;
+ struct crypto_bignum *order_buf;
+ struct wpabuf *anti_clogging_token;
++ char *pw_id;
++ int vlan_id;
++ u8 bssid[ETH_ALEN];
+ };
+
++enum sae_state {
++ SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED
++};
++
+ struct sae_data {
+- enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
++ enum sae_state state;
+ u16 send_confirm;
+ u8 pmk[SAE_PMK_LEN];
++ u8 pmkid[SAE_PMKID_LEN];
+ struct crypto_bignum *peer_commit_scalar;
+ int group;
+- int sync;
++ unsigned int sync; /* protocol instance variable: Sync */
++ u16 rc; /* protocol instance variable: Rc (received send-confirm) */
+ struct sae_temporary_data *tmp;
+ };
+
+@@ -57,14 +66,15 @@
+
+ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+ const u8 *password, size_t password_len,
+- struct sae_data *sae);
++ const char *identifier, struct sae_data *sae);
+ int sae_process_commit(struct sae_data *sae);
+ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+- const struct wpabuf *token);
++ const struct wpabuf *token, const char *identifier);
+ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+ const u8 **token, size_t *token_len, int *allowed_groups);
+ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
++const char * sae_state_txt(enum sae_state state);
+
+ #endif /* SAE_H */
+--- contrib/wpa/src/common/version.h.orig
++++ contrib/wpa/src/common/version.h
+@@ -5,6 +5,10 @@
+ #define VERSION_STR_POSTFIX ""
+ #endif /* VERSION_STR_POSTFIX */
+
+-#define VERSION_STR "2.5" VERSION_STR_POSTFIX
++#ifndef GIT_VERSION_STR_POSTFIX
++#define GIT_VERSION_STR_POSTFIX ""
++#endif /* GIT_VERSION_STR_POSTFIX */
+
++#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
++
+ #endif /* VERSION_H */
+--- contrib/wpa/src/common/wpa_common.c.orig
++++ contrib/wpa/src/common/wpa_common.c
+@@ -1,6 +1,6 @@
+ /*
+ * WPA/RSN - Shared functions for supplicant and authenticator
+- * Copyright (c) 2002-2015, Jouni Malinen
++ * Copyright (c) 2002-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -13,6 +13,7 @@
+ #include "crypto/sha1.h"
+ #include "crypto/sha256.h"
+ #include "crypto/sha384.h"
++#include "crypto/sha512.h"
+ #include "crypto/aes_wrap.h"
+ #include "crypto/crypto.h"
+ #include "ieee802_11_defs.h"
+@@ -20,31 +21,155 @@
+ #include "wpa_common.h"
+
+
+-static unsigned int wpa_kck_len(int akmp)
++static unsigned int wpa_kck_len(int akmp, size_t pmk_len)
+ {
+- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
++ switch (akmp) {
++ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
++ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return 24;
+- return 16;
++ case WPA_KEY_MGMT_FILS_SHA256:
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ case WPA_KEY_MGMT_FILS_SHA384:
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return 0;
++ case WPA_KEY_MGMT_DPP:
++ return pmk_len / 2;
++ case WPA_KEY_MGMT_OWE:
++ return pmk_len / 2;
++ default:
++ return 16;
++ }
+ }
+
+
+-static unsigned int wpa_kek_len(int akmp)
++#ifdef CONFIG_IEEE80211R
++static unsigned int wpa_kck2_len(int akmp)
+ {
+- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
++ switch (akmp) {
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ return 16;
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return 24;
++ default:
++ return 0;
++ }
++}
++#endif /* CONFIG_IEEE80211R */
++
++
++static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
++{
++ switch (akmp) {
++ case WPA_KEY_MGMT_FILS_SHA384:
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return 64;
++ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
++ case WPA_KEY_MGMT_FILS_SHA256:
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return 32;
+- return 16;
++ case WPA_KEY_MGMT_DPP:
++ return pmk_len <= 32 ? 16 : 32;
++ case WPA_KEY_MGMT_OWE:
++ return pmk_len <= 32 ? 16 : 32;
++ default:
++ return 16;
++ }
+ }
+
+
+-unsigned int wpa_mic_len(int akmp)
++#ifdef CONFIG_IEEE80211R
++static unsigned int wpa_kek2_len(int akmp)
+ {
+- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
++ switch (akmp) {
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ return 16;
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return 32;
++ default:
++ return 0;
++ }
++}
++#endif /* CONFIG_IEEE80211R */
++
++
++unsigned int wpa_mic_len(int akmp, size_t pmk_len)
++{
++ switch (akmp) {
++ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
++ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return 24;
+- return 16;
++ case WPA_KEY_MGMT_FILS_SHA256:
++ case WPA_KEY_MGMT_FILS_SHA384:
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return 0;
++ case WPA_KEY_MGMT_DPP:
++ return pmk_len / 2;
++ case WPA_KEY_MGMT_OWE:
++ return pmk_len / 2;
++ default:
++ return 16;
++ }
+ }
+
+
+ /**
++ * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
++ * @akmp: WPA_KEY_MGMT_* used in key derivation
++ * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise
++ */
++int wpa_use_akm_defined(int akmp)
++{
++ return akmp == WPA_KEY_MGMT_OSEN ||
++ akmp == WPA_KEY_MGMT_OWE ||
++ akmp == WPA_KEY_MGMT_DPP ||
++ akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
++ wpa_key_mgmt_sae(akmp) ||
++ wpa_key_mgmt_suite_b(akmp) ||
++ wpa_key_mgmt_fils(akmp);
++}
++
++
++/**
++ * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC
++ * @akmp: WPA_KEY_MGMT_* used in key derivation
++ * Returns: 1 if CMAC is used; 0 otherwise
++ */
++int wpa_use_cmac(int akmp)
++{
++ return akmp == WPA_KEY_MGMT_OSEN ||
++ akmp == WPA_KEY_MGMT_OWE ||
++ akmp == WPA_KEY_MGMT_DPP ||
++ wpa_key_mgmt_ft(akmp) ||
++ wpa_key_mgmt_sha256(akmp) ||
++ wpa_key_mgmt_sae(akmp) ||
++ wpa_key_mgmt_suite_b(akmp);
++}
++
++
++/**
++ * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data
++ * @akmp: WPA_KEY_MGMT_* used in key derivation
++ * Returns: 1 if AES Keywrap is used; 0 otherwise
++ *
++ * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether
++ * to use AES Keywrap based on the negotiated pairwise cipher. This function
++ * does not cover those special cases.
++ */
++int wpa_use_aes_key_wrap(int akmp)
++{
++ return akmp == WPA_KEY_MGMT_OSEN ||
++ akmp == WPA_KEY_MGMT_OWE ||
++ akmp == WPA_KEY_MGMT_DPP ||
++ wpa_key_mgmt_ft(akmp) ||
++ wpa_key_mgmt_sha256(akmp) ||
++ wpa_key_mgmt_sae(akmp) ||
++ wpa_key_mgmt_suite_b(akmp);
++}
++
++
++/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @key_len: KCK length in octets
+@@ -67,14 +192,22 @@
+ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+ const u8 *buf, size_t len, u8 *mic)
+ {
+- u8 hash[SHA384_MAC_LEN];
++ u8 hash[SHA512_MAC_LEN];
+
++ if (key_len == 0) {
++ wpa_printf(MSG_DEBUG,
++ "WPA: KCK not set - cannot calculate MIC");
++ return -1;
++ }
++
+ switch (ver) {
+ #ifndef CONFIG_FIPS
+ case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
++ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
+ return hmac_md5(key, key_len, buf, len, mic);
+ #endif /* CONFIG_FIPS */
+ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
++ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
+ if (hmac_sha1(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, MD5_MAC_LEN);
+@@ -81,16 +214,28 @@
+ break;
+ #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+ case WPA_KEY_INFO_TYPE_AES_128_CMAC:
++ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
+ return omac1_aes_128(key, buf, len, mic);
+ #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+ case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+ switch (akmp) {
++#ifdef CONFIG_SAE
++ case WPA_KEY_MGMT_SAE:
++ case WPA_KEY_MGMT_FT_SAE:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
++ return omac1_aes_128(key, buf, len, mic);
++#endif /* CONFIG_SAE */
+ #ifdef CONFIG_HS20
+ case WPA_KEY_MGMT_OSEN:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
+ return omac1_aes_128(key, buf, len, mic);
+ #endif /* CONFIG_HS20 */
+ #ifdef CONFIG_SUITEB
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, MD5_MAC_LEN);
+@@ -98,16 +243,79 @@
+ #endif /* CONFIG_SUITEB */
+ #ifdef CONFIG_SUITEB192
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ break;
+ #endif /* CONFIG_SUITEB192 */
++#ifdef CONFIG_OWE
++ case WPA_KEY_MGMT_OWE:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
++ (unsigned int) key_len * 8 * 2);
++ if (key_len == 128 / 8) {
++ if (hmac_sha256(key, key_len, buf, len, hash))
++ return -1;
++ } else if (key_len == 192 / 8) {
++ if (hmac_sha384(key, key_len, buf, len, hash))
++ return -1;
++ } else if (key_len == 256 / 8) {
++ if (hmac_sha512(key, key_len, buf, len, hash))
++ return -1;
++ } else {
++ wpa_printf(MSG_INFO,
++ "OWE: Unsupported KCK length: %u",
++ (unsigned int) key_len);
++ return -1;
++ }
++ os_memcpy(mic, hash, key_len);
++ break;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ case WPA_KEY_MGMT_DPP:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
++ (unsigned int) key_len * 8 * 2);
++ if (key_len == 128 / 8) {
++ if (hmac_sha256(key, key_len, buf, len, hash))
++ return -1;
++ } else if (key_len == 192 / 8) {
++ if (hmac_sha384(key, key_len, buf, len, hash))
++ return -1;
++ } else if (key_len == 256 / 8) {
++ if (hmac_sha512(key, key_len, buf, len, hash))
++ return -1;
++ } else {
++ wpa_printf(MSG_INFO,
++ "DPP: Unsupported KCK length: %u",
++ (unsigned int) key_len);
++ return -1;
++ }
++ os_memcpy(mic, hash, key_len);
++ break;
++#endif /* CONFIG_DPP */
++#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384)
++ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)");
++ if (hmac_sha384(key, key_len, buf, len, hash))
++ return -1;
++ os_memcpy(mic, hash, 24);
++ break;
++#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */
+ default:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
++ akmp);
+ return -1;
+ }
+ break;
+ default:
++ wpa_printf(MSG_DEBUG,
++ "WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
++ ver);
+ return -1;
+ }
+
+@@ -132,21 +340,32 @@
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+- * Min(ANonce, SNonce) || Max(ANonce, SNonce))
++ * Min(ANonce, SNonce) || Max(ANonce, SNonce)
++ * [ || Z.x ])
+ *
+- * STK = PRF-X(SMK, "Peer key expansion",
+- * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
+- * Min(INonce, PNonce) || Max(INonce, PNonce))
++ * The optional Z.x component is used only with DPP and that part is not defined
++ * in IEEE 802.11.
+ */
+ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+- struct wpa_ptk *ptk, int akmp, int cipher)
++ struct wpa_ptk *ptk, int akmp, int cipher,
++ const u8 *z, size_t z_len)
+ {
+- u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
++#define MAX_Z_LEN 66 /* with NIST P-521 */
++ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
++ size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
+ u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+ size_t ptk_len;
+
++ if (pmk_len == 0) {
++ wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation");
++ return -1;
++ }
++
++ if (z_len > MAX_Z_LEN)
++ return -1;
++
+ if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
+ os_memcpy(data, addr1, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+@@ -165,29 +384,74 @@
+ WPA_NONCE_LEN);
+ }
+
+- ptk->kck_len = wpa_kck_len(akmp);
+- ptk->kek_len = wpa_kek_len(akmp);
++ if (z && z_len) {
++ os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
++ data_len += z_len;
++ }
++
++ ptk->kck_len = wpa_kck_len(akmp, pmk_len);
++ ptk->kek_len = wpa_kek_len(akmp, pmk_len);
+ ptk->tk_len = wpa_cipher_key_len(cipher);
++ if (ptk->tk_len == 0) {
++ wpa_printf(MSG_ERROR,
++ "WPA: Unsupported cipher (0x%x) used in PTK derivation",
++ cipher);
++ return -1;
++ }
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+-#ifdef CONFIG_SUITEB192
+- if (wpa_key_mgmt_sha384(akmp))
+- sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+- tmp, ptk_len);
+- else
+-#endif /* CONFIG_SUITEB192 */
+-#ifdef CONFIG_IEEE80211W
+- if (wpa_key_mgmt_sha256(akmp))
+- sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+- tmp, ptk_len);
+- else
+-#endif /* CONFIG_IEEE80211W */
+- sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
++ if (wpa_key_mgmt_sha384(akmp)) {
++#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
++ if (sha384_prf(pmk, pmk_len, label, data, data_len,
++ tmp, ptk_len) < 0)
++ return -1;
++#else /* CONFIG_SUITEB192 || CONFIG_FILS */
++ return -1;
++#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
++ } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
++#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
++ if (sha256_prf(pmk, pmk_len, label, data, data_len,
++ tmp, ptk_len) < 0)
++ return -1;
++#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
++ return -1;
++#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
++#ifdef CONFIG_DPP
++ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
++ if (sha256_prf(pmk, pmk_len, label, data, data_len,
++ tmp, ptk_len) < 0)
++ return -1;
++ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
++ if (sha384_prf(pmk, pmk_len, label, data, data_len,
++ tmp, ptk_len) < 0)
++ return -1;
++ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
++ if (sha512_prf(pmk, pmk_len, label, data, data_len,
++ tmp, ptk_len) < 0)
++ return -1;
++ } else if (akmp == WPA_KEY_MGMT_DPP) {
++ wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
++ (unsigned int) pmk_len);
++ return -1;
++#endif /* CONFIG_DPP */
++ } else {
++ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
++ if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
++ ptk_len) < 0)
++ return -1;
++ }
+
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
+ MAC2STR(addr1), MAC2STR(addr2));
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
++ if (z && z_len)
++ wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
+
+@@ -200,11 +464,292 @@
+ os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+
++ ptk->kek2_len = 0;
++ ptk->kck2_len = 0;
++
+ os_memset(tmp, 0, sizeof(tmp));
++ os_memset(data, 0, data_len);
+ return 0;
+ }
+
++#ifdef CONFIG_FILS
+
++int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
++ const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
++ size_t dh_ss_len, u8 *pmk, size_t *pmk_len)
++{
++ u8 nonces[2 * FILS_NONCE_LEN];
++ const u8 *addr[2];
++ size_t len[2];
++ size_t num_elem;
++ int res;
++
++ /* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */
++ wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation");
++
++ if (wpa_key_mgmt_sha384(akmp))
++ *pmk_len = SHA384_MAC_LEN;
++ else if (wpa_key_mgmt_sha256(akmp))
++ *pmk_len = SHA256_MAC_LEN;
++ else
++ return -1;
++
++ wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len);
++ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len);
++
++ os_memcpy(nonces, snonce, FILS_NONCE_LEN);
++ os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN);
++ addr[0] = rmsk;
++ len[0] = rmsk_len;
++ num_elem = 1;
++ if (dh_ss) {
++ addr[1] = dh_ss;
++ len[1] = dh_ss_len;
++ num_elem++;
++ }
++ if (wpa_key_mgmt_sha384(akmp))
++ res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
++ addr, len, pmk);
++ else
++ res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
++ addr, len, pmk);
++ if (res == 0)
++ wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
++ else
++ *pmk_len = 0;
++ return res;
++}
++
++
++int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
++ u8 *pmkid)
++{
++ const u8 *addr[1];
++ size_t len[1];
++ u8 hash[SHA384_MAC_LEN];
++ int res;
++
++ /* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */
++ addr[0] = reauth;
++ len[0] = reauth_len;
++ if (wpa_key_mgmt_sha384(akmp))
++ res = sha384_vector(1, addr, len, hash);
++ else if (wpa_key_mgmt_sha256(akmp))
++ res = sha256_vector(1, addr, len, hash);
++ else
++ return -1;
++ if (res)
++ return res;
++ os_memcpy(pmkid, hash, PMKID_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
++ return 0;
++}
++
++
++int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
++ const u8 *snonce, const u8 *anonce, const u8 *dhss,
++ size_t dhss_len, struct wpa_ptk *ptk,
++ u8 *ick, size_t *ick_len, int akmp, int cipher,
++ u8 *fils_ft, size_t *fils_ft_len)
++{
++ u8 *data, *pos;
++ size_t data_len;
++ u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
++ FILS_FT_MAX_LEN];
++ size_t key_data_len;
++ const char *label = "FILS PTK Derivation";
++ int ret = -1;
++
++ /*
++ * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
++ * SPA || AA || SNonce || ANonce [ || DHss ])
++ * ICK = L(FILS-Key-Data, 0, ICK_bits)
++ * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
++ * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
++ * If doing FT initial mobility domain association:
++ * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
++ * FILS-FT_bits)
++ */
++ data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
++ data = os_malloc(data_len);
++ if (!data)
++ goto err;
++ pos = data;
++ os_memcpy(pos, spa, ETH_ALEN);
++ pos += ETH_ALEN;
++ os_memcpy(pos, aa, ETH_ALEN);
++ pos += ETH_ALEN;
++ os_memcpy(pos, snonce, FILS_NONCE_LEN);
++ pos += FILS_NONCE_LEN;
++ os_memcpy(pos, anonce, FILS_NONCE_LEN);
++ pos += FILS_NONCE_LEN;
++ if (dhss)
++ os_memcpy(pos, dhss, dhss_len);
++
++ ptk->kck_len = 0;
++ ptk->kek_len = wpa_kek_len(akmp, pmk_len);
++ ptk->tk_len = wpa_cipher_key_len(cipher);
++ if (wpa_key_mgmt_sha384(akmp))
++ *ick_len = 48;
++ else if (wpa_key_mgmt_sha256(akmp))
++ *ick_len = 32;
++ else
++ goto err;
++ key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
++
++ if (fils_ft && fils_ft_len) {
++ if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) {
++ *fils_ft_len = 32;
++ } else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) {
++ *fils_ft_len = 48;
++ } else {
++ *fils_ft_len = 0;
++ fils_ft = NULL;
++ }
++ key_data_len += *fils_ft_len;
++ }
++
++ if (wpa_key_mgmt_sha384(akmp)) {
++ wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
++ if (sha384_prf(pmk, pmk_len, label, data, data_len,
++ tmp, key_data_len) < 0)
++ goto err;
++ } else {
++ wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
++ if (sha256_prf(pmk, pmk_len, label, data, data_len,
++ tmp, key_data_len) < 0)
++ goto err;
++ }
++
++ wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
++ " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
++ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
++ if (dhss)
++ wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
++
++ os_memcpy(ick, tmp, *ick_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len);
++
++ os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len);
++
++ os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
++
++ if (fils_ft && fils_ft_len) {
++ os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len,
++ *fils_ft_len);
++ wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT",
++ fils_ft, *fils_ft_len);
++ }
++
++ ptk->kek2_len = 0;
++ ptk->kck2_len = 0;
++
++ os_memset(tmp, 0, sizeof(tmp));
++ ret = 0;
++err:
++ bin_clear_free(data, data_len);
++ return ret;
++}
++
++
++int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
++ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
++ const u8 *g_sta, size_t g_sta_len,
++ const u8 *g_ap, size_t g_ap_len,
++ int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
++ size_t *key_auth_len)
++{
++ const u8 *addr[6];
++ size_t len[6];
++ size_t num_elem = 4;
++ int res;
++
++ wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
++ " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
++ wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
++ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
++ wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
++
++ /*
++ * For (Re)Association Request frame (STA->AP):
++ * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
++ * [ || gSTA || gAP ])
++ */
++ addr[0] = snonce;
++ len[0] = FILS_NONCE_LEN;
++ addr[1] = anonce;
++ len[1] = FILS_NONCE_LEN;
++ addr[2] = sta_addr;
++ len[2] = ETH_ALEN;
++ addr[3] = bssid;
++ len[3] = ETH_ALEN;
++ if (g_sta && g_ap_len && g_ap && g_ap_len) {
++ addr[4] = g_sta;
++ len[4] = g_sta_len;
++ addr[5] = g_ap;
++ len[5] = g_ap_len;
++ num_elem = 6;
++ }
++
++ if (wpa_key_mgmt_sha384(akmp)) {
++ *key_auth_len = 48;
++ res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
++ key_auth_sta);
++ } else if (wpa_key_mgmt_sha256(akmp)) {
++ *key_auth_len = 32;
++ res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
++ key_auth_sta);
++ } else {
++ return -1;
++ }
++ if (res < 0)
++ return res;
++
++ /*
++ * For (Re)Association Response frame (AP->STA):
++ * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC
++ * [ || gAP || gSTA ])
++ */
++ addr[0] = anonce;
++ addr[1] = snonce;
++ addr[2] = bssid;
++ addr[3] = sta_addr;
++ if (g_sta && g_ap_len && g_ap && g_ap_len) {
++ addr[4] = g_ap;
++ len[4] = g_ap_len;
++ addr[5] = g_sta;
++ len[5] = g_sta_len;
++ }
++
++ if (wpa_key_mgmt_sha384(akmp))
++ res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
++ key_auth_ap);
++ else if (wpa_key_mgmt_sha256(akmp))
++ res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
++ key_auth_ap);
++ if (res < 0)
++ return res;
++
++ wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)",
++ key_auth_sta, *key_auth_len);
++ wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)",
++ key_auth_ap, *key_auth_len);
++
++ return 0;
++}
++
++#endif /* CONFIG_FILS */
++
++
+ #ifdef CONFIG_IEEE80211R
+ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+ const u8 *ap_addr, u8 transaction_seqnum,
+@@ -216,14 +761,23 @@
+ const u8 *addr[9];
+ size_t len[9];
+ size_t i, num_elem = 0;
+- u8 zero_mic[16];
++ u8 zero_mic[24];
++ size_t mic_len, fte_fixed_len;
+
+- if (kck_len != 16) {
++ if (kck_len == 16) {
++ mic_len = 16;
++#ifdef CONFIG_SHA384
++ } else if (kck_len == 24) {
++ mic_len = 24;
++#endif /* CONFIG_SHA384 */
++ } else {
+ wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
+ (unsigned int) kck_len);
+ return -1;
+ }
+
++ fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len;
++
+ addr[num_elem] = sta_addr;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+@@ -247,7 +801,7 @@
+ num_elem++;
+ }
+ if (ftie) {
+- if (ftie_len < 2 + sizeof(struct rsn_ftie))
++ if (ftie_len < 2 + fte_fixed_len)
+ return -1;
+
+ /* IE hdr and mic_control */
+@@ -256,14 +810,14 @@
+ num_elem++;
+
+ /* MIC field with all zeros */
+- os_memset(zero_mic, 0, sizeof(zero_mic));
++ os_memset(zero_mic, 0, mic_len);
+ addr[num_elem] = zero_mic;
+- len[num_elem] = sizeof(zero_mic);
++ len[num_elem] = mic_len;
+ num_elem++;
+
+ /* Rest of FTIE */
+- addr[num_elem] = ftie + 2 + 2 + 16;
+- len[num_elem] = ftie_len - (2 + 2 + 16);
++ addr[num_elem] = ftie + 2 + 2 + mic_len;
++ len[num_elem] = ftie_len - (2 + 2 + mic_len);
+ num_elem++;
+ }
+ if (ric) {
+@@ -274,7 +828,17 @@
+
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+- if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
++#ifdef CONFIG_SHA384
++ if (kck_len == 24) {
++ u8 hash[SHA384_MAC_LEN];
++
++ if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
++ return -1;
++ os_memcpy(mic, hash, 24);
++ }
++#endif /* CONFIG_SHA384 */
++ if (kck_len == 16 &&
++ omac1_aes_128_vector(kck, num_elem, addr, len, mic))
+ return -1;
+
+ return 0;
+@@ -282,7 +846,7 @@
+
+
+ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+- struct wpa_ft_ies *parse)
++ struct wpa_ft_ies *parse, int use_sha384)
+ {
+ const u8 *end, *pos;
+
+@@ -289,41 +853,63 @@
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+- pos = ie + sizeof(struct rsn_ftie);
++ pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
++ sizeof(struct rsn_ftie));
+ end = ie + ie_len;
++ wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
+
+- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+- switch (pos[0]) {
++ while (end - pos >= 2) {
++ u8 id, len;
++
++ id = *pos++;
++ len = *pos++;
++ if (len > end - pos) {
++ wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
++ break;
++ }
++
++ switch (id) {
+ case FTIE_SUBELEM_R1KH_ID:
+- if (pos[1] != FT_R1KH_ID_LEN) {
+- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+- "length in FTIE: %d", pos[1]);
++ if (len != FT_R1KH_ID_LEN) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Invalid R1KH-ID length in FTIE: %d",
++ len);
+ return -1;
+ }
+- parse->r1kh_id = pos + 2;
++ parse->r1kh_id = pos;
+ break;
+ case FTIE_SUBELEM_GTK:
+- parse->gtk = pos + 2;
+- parse->gtk_len = pos[1];
++ parse->gtk = pos;
++ parse->gtk_len = len;
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+- "length in FTIE: %d", pos[1]);
++ if (len < 1 || len > FT_R0KH_ID_MAX_LEN) {
++ wpa_printf(MSG_DEBUG,
++ "FT: Invalid R0KH-ID length in FTIE: %d",
++ len);
+ return -1;
+ }
+- parse->r0kh_id = pos + 2;
+- parse->r0kh_id_len = pos[1];
++ parse->r0kh_id = pos;
++ parse->r0kh_id_len = len;
+ break;
+ #ifdef CONFIG_IEEE80211W
+ case FTIE_SUBELEM_IGTK:
+- parse->igtk = pos + 2;
+- parse->igtk_len = pos[1];
++ parse->igtk = pos;
++ parse->igtk_len = len;
+ break;
+ #endif /* CONFIG_IEEE80211W */
++#ifdef CONFIG_OCV
++ case FTIE_SUBELEM_OCI:
++ parse->oci = pos;
++ parse->oci_len = len;
++ break;
++#endif /* CONFIG_OCV */
++ default:
++ wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
++ break;
+ }
+
+- pos += 2 + pos[1];
++ pos += len;
+ }
+
+ return 0;
+@@ -331,7 +917,7 @@
+
+
+ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+- struct wpa_ft_ies *parse)
++ struct wpa_ft_ies *parse, int use_sha384)
+ {
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+@@ -338,7 +924,13 @@
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
++ int update_use_sha384 = 0;
+
++ if (use_sha384 < 0) {
++ use_sha384 = 0;
++ update_use_sha384 = 1;
++ }
++
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+@@ -345,11 +937,19 @@
+
+ pos = ies;
+ end = ies + ies_len;
+- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+- switch (pos[0]) {
++ while (end - pos >= 2) {
++ u8 id, len;
++
++ id = *pos++;
++ len = *pos++;
++ if (len > end - pos)
++ break;
++
++ switch (id) {
+ case WLAN_EID_RSN:
+- parse->rsn = pos + 2;
+- parse->rsn_len = pos[1];
++ wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len);
++ parse->rsn = pos;
++ parse->rsn_len = len;
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+@@ -360,34 +960,77 @@
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
++ parse->key_mgmt = data.key_mgmt;
++ parse->pairwise_cipher = data.pairwise_cipher;
++ if (update_use_sha384) {
++ use_sha384 =
++ wpa_key_mgmt_sha384(parse->key_mgmt);
++ update_use_sha384 = 0;
++ }
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+- if (pos[1] < sizeof(struct rsn_mdie))
++ wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
++ if (len < sizeof(struct rsn_mdie))
+ return -1;
+- parse->mdie = pos + 2;
+- parse->mdie_len = pos[1];
++ parse->mdie = pos;
++ parse->mdie_len = len;
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+- if (pos[1] < sizeof(*ftie))
++ wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
++ if (use_sha384) {
++ const struct rsn_ftie_sha384 *ftie_sha384;
++
++ if (len < sizeof(*ftie_sha384))
++ return -1;
++ ftie_sha384 =
++ (const struct rsn_ftie_sha384 *) pos;
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
++ ftie_sha384->mic_control, 2);
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
++ ftie_sha384->mic,
++ sizeof(ftie_sha384->mic));
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
++ ftie_sha384->anonce,
++ WPA_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
++ ftie_sha384->snonce,
++ WPA_NONCE_LEN);
++ prot_ie_count = ftie_sha384->mic_control[1];
++ if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
++ return -1;
++ break;
++ }
++
++ if (len < sizeof(*ftie))
+ return -1;
+- ftie = (const struct rsn_ftie *) (pos + 2);
++ ftie = (const struct rsn_ftie *) pos;
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
++ ftie->mic_control, 2);
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
++ ftie->mic, sizeof(ftie->mic));
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
++ ftie->anonce, WPA_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
++ ftie->snonce, WPA_NONCE_LEN);
+ prot_ie_count = ftie->mic_control[1];
+- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
++ if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+- if (pos[1] != 5)
++ wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval",
++ pos, len);
++ if (len != 5)
+ break;
+- parse->tie = pos + 2;
+- parse->tie_len = pos[1];
++ parse->tie = pos;
++ parse->tie_len = len;
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+- parse->ric = pos;
++ parse->ric = pos - 2;
+ break;
+ }
+
+- pos += 2 + pos[1];
++ pos += len;
+ }
+
+ if (prot_ie_count == 0)
+@@ -416,13 +1059,15 @@
+ }
+
+ /* Determine the end of the RIC IE(s) */
+- pos = parse->ric;
+- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+- prot_ie_count) {
+- prot_ie_count--;
+- pos += 2 + pos[1];
++ if (parse->ric) {
++ pos = parse->ric;
++ while (end - pos >= 2 && 2 + pos[1] <= end - pos &&
++ prot_ie_count) {
++ prot_ie_count--;
++ pos += 2 + pos[1];
++ }
++ parse->ric_len = pos - parse->ric;
+ }
+- parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+@@ -475,6 +1120,10 @@
+ return WPA_KEY_MGMT_FT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
+ return WPA_KEY_MGMT_FT_PSK;
++#ifdef CONFIG_SHA384
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384)
++ return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
++#endif /* CONFIG_SHA384 */
+ #endif /* CONFIG_IEEE80211R */
+ #ifdef CONFIG_IEEE80211W
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
+@@ -492,6 +1141,22 @@
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
++ return WPA_KEY_MGMT_FILS_SHA256;
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
++ return WPA_KEY_MGMT_FILS_SHA384;
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
++ return WPA_KEY_MGMT_FT_FILS_SHA256;
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
++ return WPA_KEY_MGMT_FT_FILS_SHA384;
++#ifdef CONFIG_OWE
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
++ return WPA_KEY_MGMT_OWE;
++#endif /* CONFIG_OWE */
++#ifdef CONFIG_DPP
++ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
++ return WPA_KEY_MGMT_DPP;
++#endif /* CONFIG_DPP */
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
+ return WPA_KEY_MGMT_OSEN;
+ return 0;
+@@ -561,6 +1226,9 @@
+ pos = rsn_ie + 6;
+ left = rsn_ie_len - 6;
+
++ data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
++ data->has_group = 1;
++ data->key_mgmt = WPA_KEY_MGMT_OSEN;
+ data->proto = WPA_PROTO_OSEN;
+ } else {
+ const struct rsn_ie_hdr *hdr;
+@@ -581,9 +1249,12 @@
+
+ if (left >= RSN_SELECTOR_LEN) {
+ data->group_cipher = rsn_selector_to_bitfield(pos);
++ data->has_group = 1;
+ if (!wpa_cipher_valid_group(data->group_cipher)) {
+- wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+- __func__, data->group_cipher);
++ wpa_printf(MSG_DEBUG,
++ "%s: invalid group cipher 0x%x (%08x)",
++ __func__, data->group_cipher,
++ WPA_GET_BE32(pos));
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+@@ -604,6 +1275,8 @@
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
++ if (count)
++ data->has_pairwise = 1;
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+@@ -671,9 +1344,10 @@
+ if (left >= 4) {
+ data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
+ if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
+- wpa_printf(MSG_DEBUG, "%s: Unsupported management "
+- "group cipher 0x%x", __func__,
+- data->mgmt_group_cipher);
++ wpa_printf(MSG_DEBUG,
++ "%s: Unsupported management group cipher 0x%x (%08x)",
++ __func__, data->mgmt_group_cipher,
++ WPA_GET_BE32(pos));
+ return -10;
+ }
+ pos += RSN_SELECTOR_LEN;
+@@ -821,6 +1495,15 @@
+ }
+
+
++int wpa_default_rsn_cipher(int freq)
++{
++ if (freq > 56160)
++ return WPA_CIPHER_GCMP; /* DMG */
++
++ return WPA_CIPHER_CCMP;
++}
++
++
+ #ifdef CONFIG_IEEE80211R
+
+ /**
+@@ -828,27 +1511,39 @@
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.3
+ */
+-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+- const u8 *ssid, size_t ssid_len,
+- const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
++int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
++ const u8 *ssid, size_t ssid_len,
++ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
++ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
++ int use_sha384)
+ {
+ u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
+ FT_R0KH_ID_MAX_LEN + ETH_ALEN];
+- u8 *pos, r0_key_data[48], hash[32];
++ u8 *pos, r0_key_data[64], hash[48];
+ const u8 *addr[2];
+ size_t len[2];
++ size_t q = use_sha384 ? 48 : 32;
++ size_t r0_key_data_len = q + 16;
+
+ /*
+ * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+ * SSIDlength || SSID || MDID || R0KHlength ||
+ * R0KH-ID || S0KH-ID)
+- * XXKey is either the second 256 bits of MSK or PSK.
+- * PMK-R0 = L(R0-Key-Data, 0, 256)
+- * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
++ * XXKey is either the second 256 bits of MSK or PSK; or the first
++ * 384 bits of MSK for FT-EAP-SHA384.
++ * PMK-R0 = L(R0-Key-Data, 0, Q)
++ * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
++ * Q = 384 for FT-EAP-SHA384; otherwise, 256
+ */
+ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+- return;
++ return -1;
++ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
++ use_sha384 ? "SHA384" : "SHA256");
++ wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
++ wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
++ wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
++ wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len);
++ wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id));
+ pos = buf;
+ *pos++ = ssid_len;
+ os_memcpy(pos, ssid, ssid_len);
+@@ -861,20 +1556,51 @@
+ os_memcpy(pos, s0kh_id, ETH_ALEN);
+ pos += ETH_ALEN;
+
+- sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+- r0_key_data, sizeof(r0_key_data));
+- os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
++#ifdef CONFIG_SHA384
++ if (use_sha384) {
++ if (xxkey_len != SHA384_MAC_LEN) {
++ wpa_printf(MSG_ERROR,
++ "FT: Unexpected XXKey length %d (expected %d)",
++ (int) xxkey_len, SHA384_MAC_LEN);
++ return -1;
++ }
++ if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
++ r0_key_data, r0_key_data_len) < 0)
++ return -1;
++ }
++#endif /* CONFIG_SHA384 */
++ if (!use_sha384) {
++ if (xxkey_len != PMK_LEN) {
++ wpa_printf(MSG_ERROR,
++ "FT: Unexpected XXKey length %d (expected %d)",
++ (int) xxkey_len, PMK_LEN);
++ return -1;
++ }
++ if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
++ r0_key_data, r0_key_data_len) < 0)
++ return -1;
++ }
++ os_memcpy(pmk_r0, r0_key_data, q);
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
+
+ /*
+- * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
++ * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt)
+ */
+ addr[0] = (const u8 *) "FT-R0N";
+ len[0] = 6;
+- addr[1] = r0_key_data + PMK_LEN;
++ addr[1] = &r0_key_data[q];
+ len[1] = 16;
+
+- sha256_vector(2, addr, len, hash);
++#ifdef CONFIG_SHA384
++ if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
++ return -1;
++#endif /* CONFIG_SHA384 */
++ if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
++ return -1;
+ os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
++ os_memset(r0_key_data, 0, sizeof(r0_key_data));
++ return 0;
+ }
+
+
+@@ -883,16 +1609,16 @@
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+- const u8 *s1kh_id, u8 *pmk_r1_name)
++int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
++ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
+ {
+- u8 hash[32];
++ u8 hash[48];
+ const u8 *addr[4];
+ size_t len[4];
+
+ /*
+- * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
+- * R1KH-ID || S1KH-ID))
++ * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
++ * R1KH-ID || S1KH-ID))
+ */
+ addr[0] = (const u8 *) "FT-R1N";
+ len[0] = 6;
+@@ -903,8 +1629,14 @@
+ addr[3] = s1kh_id;
+ len[3] = ETH_ALEN;
+
+- sha256_vector(4, addr, len, hash);
++#ifdef CONFIG_SHA384
++ if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
++ return -1;
++#endif /* CONFIG_SHA384 */
++ if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
++ return -1;
+ os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
++ return 0;
+ }
+
+
+@@ -913,14 +1645,20 @@
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+- const u8 *r1kh_id, const u8 *s1kh_id,
+- u8 *pmk_r1, u8 *pmk_r1_name)
++int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
++ const u8 *pmk_r0_name,
++ const u8 *r1kh_id, const u8 *s1kh_id,
++ u8 *pmk_r1, u8 *pmk_r1_name)
+ {
+ u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
+ u8 *pos;
+
+ /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
++ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
++ pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
++ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
++ wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
+ pos = buf;
+ os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
+ pos += FT_R1KH_ID_LEN;
+@@ -927,9 +1665,26 @@
+ os_memcpy(pos, s1kh_id, ETH_ALEN);
+ pos += ETH_ALEN;
+
+- sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
++#ifdef CONFIG_SHA384
++ if (pmk_r0_len == SHA384_MAC_LEN &&
++ sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
++ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
++ return -1;
++#endif /* CONFIG_SHA384 */
++ if (pmk_r0_len == PMK_LEN &&
++ sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
++ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
++ return -1;
++ if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
++ wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
++ (int) pmk_r0_len);
++ return -1;
++ }
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
+
+- wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
++ return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
++ pmk_r1_name,
++ pmk_r0_len == SHA384_MAC_LEN);
+ }
+
+
+@@ -938,7 +1693,8 @@
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.5
+ */
+-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
++int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len,
++ const u8 *snonce, const u8 *anonce,
+ const u8 *sta_addr, const u8 *bssid,
+ const u8 *pmk_r1_name,
+ struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
+@@ -947,13 +1703,21 @@
+ u8 *pos, hash[32];
+ const u8 *addr[6];
+ size_t len[6];
+- u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+- size_t ptk_len;
++ u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
++ size_t ptk_len, offset;
++ int use_sha384 = wpa_key_mgmt_sha384(akmp);
+
+ /*
+ * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
+ * BSSID || STA-ADDR)
+ */
++ wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
++ use_sha384 ? "SHA384" : "SHA256");
++ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
++ wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
++ wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
++ wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR,
++ MAC2STR(bssid), MAC2STR(sta_addr));
+ pos = buf;
+ os_memcpy(pos, snonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+@@ -964,17 +1728,45 @@
+ os_memcpy(pos, sta_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+
+- ptk->kck_len = wpa_kck_len(akmp);
+- ptk->kek_len = wpa_kek_len(akmp);
++ ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
++ ptk->kck2_len = wpa_kck2_len(akmp);
++ ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
++ ptk->kek2_len = wpa_kek2_len(akmp);
+ ptk->tk_len = wpa_cipher_key_len(cipher);
+- ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
++ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
++ ptk->kck2_len + ptk->kek2_len;
+
+- sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
++#ifdef CONFIG_SHA384
++ if (use_sha384) {
++ if (pmk_r1_len != SHA384_MAC_LEN) {
++ wpa_printf(MSG_ERROR,
++ "FT: Unexpected PMK-R1 length %d (expected %d)",
++ (int) pmk_r1_len, SHA384_MAC_LEN);
++ return -1;
++ }
++ if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
++ buf, pos - buf, tmp, ptk_len) < 0)
++ return -1;
++ }
++#endif /* CONFIG_SHA384 */
++ if (!use_sha384) {
++ if (pmk_r1_len != PMK_LEN) {
++ wpa_printf(MSG_ERROR,
++ "FT: Unexpected PMK-R1 length %d (expected %d)",
++ (int) pmk_r1_len, PMK_LEN);
++ return -1;
++ }
++ if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
++ buf, pos - buf, tmp, ptk_len) < 0)
++ return -1;
++ }
++ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
+
+ /*
+ * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
+ * ANonce || BSSID || STA-ADDR))
+ */
++ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+ addr[0] = pmk_r1_name;
+ len[0] = WPA_PMK_NAME_LEN;
+ addr[1] = (const u8 *) "FT-PTKN";
+@@ -988,15 +1780,28 @@
+ addr[5] = sta_addr;
+ len[5] = ETH_ALEN;
+
+- sha256_vector(6, addr, len, hash);
++ if (sha256_vector(6, addr, len, hash) < 0)
++ return -1;
+ os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+
+ os_memcpy(ptk->kck, tmp, ptk->kck_len);
+- os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+- os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
++ offset = ptk->kck_len;
++ os_memcpy(ptk->kek, tmp + offset, ptk->kek_len);
++ offset += ptk->kek_len;
++ os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
++ offset += ptk->tk_len;
++ os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
++ offset += ptk->kck2_len;
++ os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
++ if (ptk->kck2_len)
++ wpa_hexdump_key(MSG_DEBUG, "FT: KCK2",
++ ptk->kck2, ptk->kck2_len);
++ if (ptk->kek2_len)
++ wpa_hexdump_key(MSG_DEBUG, "FT: KEK2",
++ ptk->kek2, ptk->kek2_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+@@ -1015,29 +1820,48 @@
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+- * @use_sha256: Whether to use SHA256-based KDF
++ * @akmp: Negotiated key management protocol
+ *
+- * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+- * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
++ * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
++ * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
++ * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
++ * AKM: 00-0F-AC:11
++ * See rsn_pmkid_suite_b()
++ * AKM: 00-0F-AC:12
++ * See rsn_pmkid_suite_b_192()
++ * AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17
++ * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
++ * Otherwise:
++ * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
+ */
+ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+- u8 *pmkid, int use_sha256)
++ u8 *pmkid, int akmp)
+ {
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+- unsigned char hash[SHA256_MAC_LEN];
++ unsigned char hash[SHA384_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+-#ifdef CONFIG_IEEE80211W
+- if (use_sha256)
++ if (0) {
++#if defined(CONFIG_FILS) || defined(CONFIG_SHA384)
++ } else if (wpa_key_mgmt_sha384(akmp)) {
++ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
++ hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
++#endif /* CONFIG_FILS || CONFIG_SHA384 */
++#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
++ } else if (wpa_key_mgmt_sha256(akmp)) {
++ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
+ hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
+- else
+-#endif /* CONFIG_IEEE80211W */
++#endif /* CONFIG_IEEE80211W || CONFIG_FILS */
++ } else {
++ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
+ hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
++ }
++ wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ }
+
+@@ -1134,6 +1958,14 @@
+ return "GCMP-256";
+ case WPA_CIPHER_CCMP_256:
+ return "CCMP-256";
++ case WPA_CIPHER_AES_128_CMAC:
++ return "BIP";
++ case WPA_CIPHER_BIP_GMAC_128:
++ return "BIP-GMAC-128";
++ case WPA_CIPHER_BIP_GMAC_256:
++ return "BIP-GMAC-256";
++ case WPA_CIPHER_BIP_CMAC_256:
++ return "BIP-CMAC-256";
+ case WPA_CIPHER_GTK_NOT_USED:
+ return "GTK_NOT_USED";
+ default:
+@@ -1163,11 +1995,15 @@
+ "WPA2-PSK" : "WPA-PSK";
+ case WPA_KEY_MGMT_NONE:
+ return "NONE";
++ case WPA_KEY_MGMT_WPA_NONE:
++ return "WPA-NONE";
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return "IEEE 802.1X (no WPA)";
+ #ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return "FT-EAP";
++ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
++ return "FT-EAP-SHA384";
+ case WPA_KEY_MGMT_FT_PSK:
+ return "FT-PSK";
+ #endif /* CONFIG_IEEE80211R */
+@@ -1189,6 +2025,18 @@
+ return "WPA2-EAP-SUITE-B";
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ return "WPA2-EAP-SUITE-B-192";
++ case WPA_KEY_MGMT_FILS_SHA256:
++ return "FILS-SHA256";
++ case WPA_KEY_MGMT_FILS_SHA384:
++ return "FILS-SHA384";
++ case WPA_KEY_MGMT_FT_FILS_SHA256:
++ return "FT-FILS-SHA256";
++ case WPA_KEY_MGMT_FT_FILS_SHA384:
++ return "FT-FILS-SHA384";
++ case WPA_KEY_MGMT_OWE:
++ return "OWE";
++ case WPA_KEY_MGMT_DPP:
++ return "DPP";
+ default:
+ return "UNKNOWN";
+ }
+@@ -1197,28 +2045,36 @@
+
+ u32 wpa_akm_to_suite(int akm)
+ {
++ if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
++ return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+ if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
+- return WLAN_AKM_SUITE_FT_8021X;
++ return RSN_AUTH_KEY_MGMT_FT_802_1X;
+ if (akm & WPA_KEY_MGMT_FT_PSK)
+- return WLAN_AKM_SUITE_FT_PSK;
+- if (akm & WPA_KEY_MGMT_IEEE8021X)
+- return WLAN_AKM_SUITE_8021X;
++ return RSN_AUTH_KEY_MGMT_FT_PSK;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
+- return WLAN_AKM_SUITE_8021X_SHA256;
++ return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ if (akm & WPA_KEY_MGMT_IEEE8021X)
+- return WLAN_AKM_SUITE_8021X;
++ return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (akm & WPA_KEY_MGMT_PSK_SHA256)
+- return WLAN_AKM_SUITE_PSK_SHA256;
++ return RSN_AUTH_KEY_MGMT_PSK_SHA256;
+ if (akm & WPA_KEY_MGMT_PSK)
+- return WLAN_AKM_SUITE_PSK;
++ return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ if (akm & WPA_KEY_MGMT_CCKM)
+- return WLAN_AKM_SUITE_CCKM;
++ return RSN_AUTH_KEY_MGMT_CCKM;
+ if (akm & WPA_KEY_MGMT_OSEN)
+- return WLAN_AKM_SUITE_OSEN;
++ return RSN_AUTH_KEY_MGMT_OSEN;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+- return WLAN_AKM_SUITE_8021X_SUITE_B;
++ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+- return WLAN_AKM_SUITE_8021X_SUITE_B_192;
++ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
++ if (akm & WPA_KEY_MGMT_FILS_SHA256)
++ return RSN_AUTH_KEY_MGMT_FILS_SHA256;
++ if (akm & WPA_KEY_MGMT_FILS_SHA384)
++ return RSN_AUTH_KEY_MGMT_FILS_SHA384;
++ if (akm & WPA_KEY_MGMT_FT_FILS_SHA256)
++ return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
++ if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
++ return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ return 0;
+ }
+
+@@ -1260,14 +2116,14 @@
+ }
+
+
+-#ifdef CONFIG_IEEE80211R
+-int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
++#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
++int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
+ {
+ u8 *start, *end, *rpos, *rend;
+ int added = 0;
+
+ start = ies;
+- end = ies + ies_len;
++ end = ies + *ies_len;
+
+ while (start < end) {
+ if (*start == WLAN_EID_RSN)
+@@ -1320,11 +2176,29 @@
+ added += 2 + PMKID_LEN;
+ start[1] += 2 + PMKID_LEN;
+ } else {
++ u16 num_pmkid;
++
++ if (rend - rpos < 2)
++ return -1;
++ num_pmkid = WPA_GET_LE16(rpos);
+ /* PMKID-Count was included; use it */
+- if (WPA_GET_LE16(rpos) != 0) {
+- wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
+- "in RSN IE in EAPOL-Key data");
+- return -1;
++ if (num_pmkid != 0) {
++ u8 *after;
++
++ if (num_pmkid * PMKID_LEN > rend - rpos - 2)
++ return -1;
++ /*
++ * PMKID may have been included in RSN IE in
++ * (Re)Association Request frame, so remove the old
++ * PMKID(s) first before adding the new one.
++ */
++ wpa_printf(MSG_DEBUG,
++ "FT: Remove %u old PMKID(s) from RSN IE",
++ num_pmkid);
++ after = rpos + 2 + num_pmkid * PMKID_LEN;
++ os_memmove(rpos + 2, after, rend - after);
++ start[1] -= num_pmkid * PMKID_LEN;
++ added -= num_pmkid * PMKID_LEN;
+ }
+ WPA_PUT_LE16(rpos, 1);
+ rpos += 2;
+@@ -1337,9 +2211,11 @@
+ wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
+ "(PMKID inserted)", start, 2 + start[1]);
+
+- return added;
++ *ies_len += added;
++
++ return 0;
+ }
+-#endif /* CONFIG_IEEE80211R */
++#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
+
+
+ int wpa_cipher_key_len(int cipher)
+@@ -1378,7 +2254,7 @@
+ }
+
+
+-int wpa_cipher_to_alg(int cipher)
++enum wpa_alg wpa_cipher_to_alg(int cipher)
+ {
+ switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+@@ -1573,6 +2449,14 @@
+ val |= WPA_CIPHER_NONE;
+ else if (os_strcmp(start, "GTK_NOT_USED") == 0)
+ val |= WPA_CIPHER_GTK_NOT_USED;
++ else if (os_strcmp(start, "AES-128-CMAC") == 0)
++ val |= WPA_CIPHER_AES_128_CMAC;
++ else if (os_strcmp(start, "BIP-GMAC-128") == 0)
++ val |= WPA_CIPHER_BIP_GMAC_128;
++ else if (os_strcmp(start, "BIP-GMAC-256") == 0)
++ val |= WPA_CIPHER_BIP_GMAC_256;
++ else if (os_strcmp(start, "BIP-CMAC-256") == 0)
++ val |= WPA_CIPHER_BIP_CMAC_256;
+ else {
+ os_free(buf);
+ return -1;
+@@ -1628,6 +2512,34 @@
+ return -1;
+ pos += ret;
+ }
++ if (ciphers & WPA_CIPHER_AES_128_CMAC) {
++ ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC",
++ pos == start ? "" : delim);
++ if (os_snprintf_error(end - pos, ret))
++ return -1;
++ pos += ret;
++ }
++ if (ciphers & WPA_CIPHER_BIP_GMAC_128) {
++ ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
++ pos == start ? "" : delim);
++ if (os_snprintf_error(end - pos, ret))
++ return -1;
++ pos += ret;
++ }
++ if (ciphers & WPA_CIPHER_BIP_GMAC_256) {
++ ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
++ pos == start ? "" : delim);
++ if (os_snprintf_error(end - pos, ret))
++ return -1;
++ pos += ret;
++ }
++ if (ciphers & WPA_CIPHER_BIP_CMAC_256) {
++ ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
++ pos == start ? "" : delim);
++ if (os_snprintf_error(end - pos, ret))
++ return -1;
++ pos += ret;
++ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ ret = os_snprintf(pos, end - pos, "%sNONE",
+ pos == start ? "" : delim);
+@@ -1662,3 +2574,29 @@
+ return WPA_CIPHER_CCMP_256;
+ return WPA_CIPHER_CCMP;
+ }
++
++
++#ifdef CONFIG_FILS
++int fils_domain_name_hash(const char *domain, u8 *hash)
++{
++ char buf[255], *wpos = buf;
++ const char *pos = domain;
++ size_t len;
++ const u8 *addr[1];
++ u8 mac[SHA256_MAC_LEN];
++
++ for (len = 0; len < sizeof(buf) && *pos; len++) {
++ if (isalpha(*pos) && isupper(*pos))
++ *wpos++ = tolower(*pos);
++ else
++ *wpos++ = *pos;
++ pos++;
++ }
++
++ addr[0] = (const u8 *) buf;
++ if (sha256_vector(1, addr, &len, mac) < 0)
++ return -1;
++ os_memcpy(hash, mac, 2);
++ return 0;
++}
++#endif /* CONFIG_FILS */
+--- contrib/wpa/src/common/wpa_common.h.orig
++++ contrib/wpa/src/common/wpa_common.h
+@@ -1,6 +1,6 @@
+ /*
+ * WPA definitions shared between hostapd and wpa_supplicant
+- * Copyright (c) 2002-2015, Jouni Malinen
++ * Copyright (c) 2002-2018, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -12,6 +12,8 @@
+ /* IEEE 802.11i */
+ #define PMKID_LEN 16
+ #define PMK_LEN 32
++#define PMK_LEN_SUITE_B_192 48
++#define PMK_LEN_MAX 64
+ #define WPA_REPLAY_COUNTER_LEN 8
+ #define WPA_NONCE_LEN 32
+ #define WPA_KEY_RSC_LEN 8
+@@ -18,6 +20,8 @@
+ #define WPA_GMK_LEN 32
+ #define WPA_GTK_MAX_LEN 32
+
++#define OWE_DH_GROUP 19
++
+ #define WPA_ALLOWED_PAIRWISE_CIPHERS \
+ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
+ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
+@@ -25,6 +29,9 @@
+ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
+ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+ WPA_CIPHER_GTK_NOT_USED)
++#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \
++(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \
++WPA_CIPHER_BIP_CMAC_256)
+
+ #define WPA_SELECTOR_LEN 4
+ #define WPA_VERSION 1
+@@ -46,10 +53,8 @@
+
+ #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+ #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+-#ifdef CONFIG_IEEE80211R
+ #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+ #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+-#endif /* CONFIG_IEEE80211R */
+ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+ #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+ #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+@@ -57,17 +62,24 @@
+ #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+ #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+ #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+-#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
+-RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
++#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
++#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
++#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
++#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
++#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
++#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
+ #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
+ #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
++#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
+
+ #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
++#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+ #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+ #if 0
+ #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+ #endif
+ #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
++#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+ #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+ #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+ #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+@@ -76,6 +88,12 @@
+ #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+ #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+ #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
++#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1)
++#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0)
++#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1)
++#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2)
++/* KRK is defined for nl80211 use only */
++#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255)
+
+ /* EAPOL-Key Key Data Encapsulation
+ * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
+@@ -86,12 +104,6 @@
+ #endif
+ #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+ #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+-#ifdef CONFIG_PEERKEY
+-#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+-#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+-#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+-#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+-#endif /* CONFIG_PEERKEY */
+ #ifdef CONFIG_IEEE80211W
+ #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+ #endif /* CONFIG_IEEE80211W */
+@@ -98,6 +110,7 @@
+ #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+ #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+ #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
++#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+
+ #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+ #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+@@ -136,7 +149,8 @@
+ #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
+ #define WPA_CAPABILITY_PBAC BIT(12)
+ #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
+-/* B14-B15: Reserved */
++#define WPA_CAPABILITY_OCVC BIT(14)
++/* B15: Reserved */
+
+
+ /* IEEE 802.11r */
+@@ -177,30 +191,17 @@
+ u8 key_iv[16];
+ u8 key_rsc[WPA_KEY_RSC_LEN];
+ u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+- u8 key_mic[16];
+- u8 key_data_length[2]; /* big endian */
+- /* followed by key_data_length bytes of key_data */
++ /* variable length Key MIC field */
++ /* big endian 2-octet Key Data Length field */
++ /* followed by Key Data Length bytes of Key Data */
+ } STRUCT_PACKED;
+
+-struct wpa_eapol_key_192 {
+- u8 type;
+- /* Note: key_info, key_length, and key_data_length are unaligned */
+- u8 key_info[2]; /* big endian */
+- u8 key_length[2]; /* big endian */
+- u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+- u8 key_nonce[WPA_NONCE_LEN];
+- u8 key_iv[16];
+- u8 key_rsc[WPA_KEY_RSC_LEN];
+- u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+- u8 key_mic[24];
+- u8 key_data_length[2]; /* big endian */
+- /* followed by key_data_length bytes of key_data */
+-} STRUCT_PACKED;
+-
+-#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
+-#define WPA_KCK_MAX_LEN 24
+-#define WPA_KEK_MAX_LEN 32
++#define WPA_EAPOL_KEY_MIC_MAX_LEN 32
++#define WPA_KCK_MAX_LEN 32
++#define WPA_KEK_MAX_LEN 64
+ #define WPA_TK_MAX_LEN 32
++#define FILS_ICK_MAX_LEN 48
++#define FILS_FT_MAX_LEN 48
+
+ /**
+ * struct wpa_ptk - WPA Pairwise Transient Key
+@@ -210,9 +211,13 @@
+ u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
+ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
+ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
++ u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
++ u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
+ size_t kck_len;
+ size_t kek_len;
+ size_t tk_len;
++ size_t kck2_len;
++ size_t kek2_len;
+ int installed; /* 1 if key has already been installed to driver */
+ };
+
+@@ -281,22 +286,6 @@
+ } STRUCT_PACKED;
+
+
+-#ifdef CONFIG_PEERKEY
+-enum {
+- STK_MUI_4WAY_STA_AP = 1,
+- STK_MUI_4WAY_STAT_STA = 2,
+- STK_MUI_GTK = 3,
+- STK_MUI_SMK = 4
+-};
+-
+-enum {
+- STK_ERR_STA_NR = 1,
+- STK_ERR_STA_NRSN = 2,
+- STK_ERR_CPHR_NS = 3,
+- STK_ERR_NO_STSL = 4
+-};
+-#endif /* CONFIG_PEERKEY */
+-
+ struct rsn_error_kde {
+ be16 mui;
+ be16 error_type;
+@@ -327,10 +316,19 @@
+ /* followed by optional parameters */
+ } STRUCT_PACKED;
+
++struct rsn_ftie_sha384 {
++ u8 mic_control[2];
++ u8 mic[24];
++ u8 anonce[WPA_NONCE_LEN];
++ u8 snonce[WPA_NONCE_LEN];
++ /* followed by optional parameters */
++} STRUCT_PACKED;
++
+ #define FTIE_SUBELEM_R1KH_ID 1
+ #define FTIE_SUBELEM_GTK 2
+ #define FTIE_SUBELEM_R0KH_ID 3
+ #define FTIE_SUBELEM_IGTK 4
++#define FTIE_SUBELEM_OCI 5
+
+ struct rsn_rdie {
+ u8 id;
+@@ -349,7 +347,24 @@
+ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+- struct wpa_ptk *ptk, int akmp, int cipher);
++ struct wpa_ptk *ptk, int akmp, int cipher,
++ const u8 *z, size_t z_len);
++int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
++ const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
++ size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
++int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
++ u8 *pmkid);
++int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
++ const u8 *snonce, const u8 *anonce, const u8 *dhss,
++ size_t dhss_len, struct wpa_ptk *ptk,
++ u8 *ick, size_t *ick_len, int akmp, int cipher,
++ u8 *fils_ft, size_t *fils_ft_len);
++int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
++ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
++ const u8 *g_sta, size_t g_sta_len,
++ const u8 *g_ap, size_t g_ap_len,
++ int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
++ size_t *key_auth_len);
+
+ #ifdef CONFIG_IEEE80211R
+ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+@@ -358,17 +373,19 @@
+ const u8 *ftie, size_t ftie_len,
+ const u8 *rsnie, size_t rsnie_len,
+ const u8 *ric, size_t ric_len, u8 *mic);
+-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+- const u8 *ssid, size_t ssid_len,
+- const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+- const u8 *s1kh_id, u8 *pmk_r1_name);
+-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+- const u8 *r1kh_id, const u8 *s1kh_id,
+- u8 *pmk_r1, u8 *pmk_r1_name);
+-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+- const u8 *sta_addr, const u8 *bssid,
++int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
++ const u8 *ssid, size_t ssid_len,
++ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
++ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
++ int use_sha384);
++int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
++ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
++int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
++ const u8 *pmk_r0_name,
++ const u8 *r1kh_id, const u8 *s1kh_id,
++ u8 *pmk_r1, u8 *pmk_r1_name);
++int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce,
++ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+ const u8 *pmk_r1_name,
+ struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
+ #endif /* CONFIG_IEEE80211R */
+@@ -376,7 +393,9 @@
+ struct wpa_ie_data {
+ int proto;
+ int pairwise_cipher;
++ int has_pairwise;
+ int group_cipher;
++ int has_group;
+ int key_mgmt;
+ int capabilities;
+ size_t num_pmkid;
+@@ -389,9 +408,10 @@
+ struct wpa_ie_data *data);
+ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
++int wpa_default_rsn_cipher(int freq);
+
+ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+- u8 *pmkid, int use_sha256);
++ u8 *pmkid, int akmp);
+ #ifdef CONFIG_SUITEB
+ int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid);
+@@ -419,7 +439,7 @@
+ int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len);
+-int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
++int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
+
+ struct wpa_ft_ies {
+ const u8 *mdie;
+@@ -438,15 +458,22 @@
+ size_t tie_len;
+ const u8 *igtk;
+ size_t igtk_len;
++#ifdef CONFIG_OCV
++ const u8 *oci;
++ size_t oci_len;
++#endif /* CONFIG_OCV */
+ const u8 *ric;
+ size_t ric_len;
++ int key_mgmt;
++ int pairwise_cipher;
+ };
+
+-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
++int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
++ int use_sha384);
+
+ int wpa_cipher_key_len(int cipher);
+ int wpa_cipher_rsc_len(int cipher);
+-int wpa_cipher_to_alg(int cipher);
++enum wpa_alg wpa_cipher_to_alg(int cipher);
+ int wpa_cipher_valid_group(int cipher);
+ int wpa_cipher_valid_pairwise(int cipher);
+ int wpa_cipher_valid_mgmt_group(int cipher);
+@@ -458,6 +485,10 @@
+ int wpa_parse_cipher(const char *value);
+ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
+ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
+-unsigned int wpa_mic_len(int akmp);
++unsigned int wpa_mic_len(int akmp, size_t pmk_len);
++int wpa_use_akm_defined(int akmp);
++int wpa_use_cmac(int akmp);
++int wpa_use_aes_key_wrap(int akmp);
++int fils_domain_name_hash(const char *domain, u8 *hash);
+
+ #endif /* WPA_COMMON_H */
+--- contrib/wpa/src/common/wpa_ctrl.c.orig
++++ contrib/wpa/src/common/wpa_ctrl.c
+@@ -11,6 +11,8 @@
+ #ifdef CONFIG_CTRL_IFACE
+
+ #ifdef CONFIG_CTRL_IFACE_UNIX
++#include
++#include
+ #include
+ #include
+ #include
+@@ -133,6 +135,19 @@
+ return NULL;
+ }
+ tries++;
++#ifdef ANDROID
++ /* Set client socket file permissions so that bind() creates the client
++ * socket with these permissions and there is no need to try to change
++ * them with chmod() after bind() which would have potential issues with
++ * race conditions. These permissions are needed to make sure the server
++ * side (wpa_supplicant or hostapd) can reply to the control interface
++ * messages.
++ *
++ * The lchown() calls below after bind() are also part of the needed
++ * operations to allow the response to go through. Those are using the
++ * no-deference-symlinks version to avoid races. */
++ fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++#endif /* ANDROID */
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ if (errno == EADDRINUSE && tries < 2) {
+@@ -151,10 +166,9 @@
+ }
+
+ #ifdef ANDROID
+- chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ /* Set group even if we do not have privileges to change owner */
+- chown(ctrl->local.sun_path, -1, AID_WIFI);
+- chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
++ lchown(ctrl->local.sun_path, -1, AID_WIFI);
++ lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+
+ if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
+ if (socket_local_client_connect(
+@@ -532,6 +546,8 @@
+ FD_ZERO(&rfds);
+ FD_SET(ctrl->s, &rfds);
+ res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
++ if (res < 0 && errno == EINTR)
++ continue;
+ if (res < 0)
+ return res;
+ if (FD_ISSET(ctrl->s, &rfds)) {
+@@ -538,7 +554,8 @@
+ res = recv(ctrl->s, reply, *reply_len, 0);
+ if (res < 0)
+ return res;
+- if (res > 0 && reply[0] == '<') {
++ if ((res > 0 && reply[0] == '<') ||
++ (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) {
+ /* This is an unsolicited message from
+ * wpa_supplicant, not the reply to the
+ * request. Use msg_cb to report this to the
+--- contrib/wpa/src/common/wpa_ctrl.h.orig
++++ contrib/wpa/src/common/wpa_ctrl.h
+@@ -1,6 +1,6 @@
+ /*
+ * wpa_supplicant/hostapd control interface library
+- * Copyright (c) 2004-2006, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -50,10 +50,19 @@
+ #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
+ /** EAP status */
+ #define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
++/** Retransmit the previous request packet */
++#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT "
++#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 "
+ /** EAP authentication completed successfully */
+ #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
++#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 "
+ /** EAP authentication failed (EAP-Failure received) */
+ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
++#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 "
++/** EAP authentication failed due to no response received */
++#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE "
++#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 "
++#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE "
+ /** Network block temporarily disabled (e.g., due to authentication failure) */
+ #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
+ /** Temporarily disabled network block re-enabled */
+@@ -74,9 +83,29 @@
+ #define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
+ /** Change in the signal level was reported by the driver */
+ #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
++/** Beacon loss reported by the driver */
++#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
+ /** Regulatory domain channel */
+ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
++/** Channel switch (followed by freq= and other channel parameters) */
++#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
++/** SAE authentication failed due to unknown password identifier */
++#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
++ "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
+
++/** IP subnet status change notification
++ *
++ * When using an offloaded roaming mechanism where driver/firmware takes care
++ * of roaming and IP subnet validation checks post-roaming, this event can
++ * indicate whether IP subnet has changed.
++ *
++ * The event has a status=<0/1/2> parameter where
++ * 0 = unknown
++ * 1 = IP subnet unchanged (can continue to use the old IP address)
++ * 2 = IP subnet changed (need to get a new IP address)
++ */
++#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
++
+ /** RSN IBSS 4-way handshakes completed with specified peer */
+ #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
+
+@@ -126,6 +155,33 @@
+ #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
+ #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+
++/* DPP events */
++#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
++#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
++#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
++#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
++#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
++#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
++#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
++#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
++#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
++#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
++#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
++#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
++#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
++#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
++#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
++#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
++#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
++#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
++#define DPP_EVENT_RX "DPP-RX "
++#define DPP_EVENT_TX "DPP-TX "
++#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS "
++#define DPP_EVENT_FAIL "DPP-FAIL "
++#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT "
++#define DPP_EVENT_INTRO "DPP-INTRO "
++#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
++
+ /* MESH events */
+ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
+ #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
+@@ -174,6 +230,7 @@
+ #define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
+ #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+ #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
++#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED "
+ #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+ #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
+ #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+@@ -212,8 +269,18 @@
+ /* parameters: */
+ #define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
+
++#define RX_ANQP "RX-ANQP "
++#define RX_HS20_ANQP "RX-HS20-ANQP "
++#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
++#define RX_HS20_ICON "RX-HS20-ICON "
++#define RX_MBO_ANQP "RX-MBO-ANQP "
++
++/* parameters: */
++#define RX_VENUE_URL "RX-VENUE-URL "
++
+ #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
+ #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
++#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
+
+ #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
+ #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
+@@ -232,10 +299,14 @@
+ #define AP_STA_CONNECTED "AP-STA-CONNECTED "
+ #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+ #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
++#define AP_STA_POLL_OK "AP-STA-POLL-OK "
+
+ #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
+ #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
+
++#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
++#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
++
+ #define AP_EVENT_ENABLED "AP-ENABLED "
+ #define AP_EVENT_DISABLED "AP-DISABLED "
+
+@@ -251,12 +322,56 @@
+ #define DFS_EVENT_CAC_START "DFS-CAC-START "
+ #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+ #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
++#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
+
+ #define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
++#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
++#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
++
+ /* BSS Transition Management Response frame received */
+ #define BSS_TM_RESP "BSS-TM-RESP "
+
++/* Collocated Interference Request frame received;
++ * parameters: */
++#define COLOC_INTF_REQ "COLOC-INTF-REQ "
++/* Collocated Interference Report frame received;
++ * parameters: */
++#define COLOC_INTF_REPORT "COLOC-INTF-REPORT "
++
++/* MBO IE with cellular data connection preference received */
++#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
++
++/* BSS Transition Management Request received with MBO transition reason */
++#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
++
++/* parameters: */
++#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS "
++/* parameters: */
++#define BEACON_RESP_RX "BEACON-RESP-RX "
++
++/* PMKSA cache entry added; parameters: */
++#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
++/* PMKSA cache entry removed; parameters: */
++#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED "
++
++/* FILS HLP Container receive; parameters: dst= src= frame=
++ */
++#define FILS_HLP_RX "FILS-HLP-RX "
++
++/* Event to indicate Probe Request frame;
++ * parameters: sa= signal= */
++#define RX_PROBE_REQUEST "RX-PROBE-REQUEST "
++
++/* Event to indicate station's HT/VHT operation mode change information */
++#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED "
++#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED "
++#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED "
++
++/* New interface addition or removal for 4addr WDS SDA */
++#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
++#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
++
+ /* BSS command information masks */
+
+ #define WPA_BSS_MASK_ALL 0xFFFDFFFF
+@@ -282,6 +397,9 @@
+ #define WPA_BSS_MASK_SNR BIT(19)
+ #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
+ #define WPA_BSS_MASK_FST BIT(21)
++#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
++#define WPA_BSS_MASK_BEACON_IE BIT(23)
++#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
+
+
+ /* VENDOR_ELEM_* frame id values */
+@@ -300,6 +418,7 @@
+ VENDOR_ELEM_P2P_ASSOC_REQ = 11,
+ VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+ VENDOR_ELEM_ASSOC_REQ = 13,
++ VENDOR_ELEM_PROBE_REQ = 14,
+ NUM_VENDOR_ELEM_FRAMES
+ };
+
+@@ -354,7 +473,7 @@
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+- * of the reply. This function will block for up to two seconds while waiting
++ * of the reply. This function will block for up to 10 seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+--- contrib/wpa/src/common/wpa_helpers.c.orig
++++ contrib/wpa/src/common/wpa_helpers.c
+@@ -172,7 +172,8 @@
+ if (ctrl == NULL)
+ return -1;
+ len = sizeof(buf);
+- if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
++ if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len,
++ NULL) < 0) {
+ wpa_ctrl_close(ctrl);
+ return -1;
+ }
+@@ -221,7 +222,8 @@
+ if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
+ && strlen(ip) > 0) {
+ printf("IP address found: '%s'\n", ip);
+- return 0;
++ if (strncmp(ip, "169.254.", 8) != 0)
++ return 0;
+ }
+ ctrl = wpa_open_ctrl(ifname);
+ if (ctrl == NULL)
+--- contrib/wpa/src/crypto/aes-cbc.c.orig
++++ contrib/wpa/src/crypto/aes-cbc.c
+@@ -28,6 +28,9 @@
+ u8 *pos = data;
+ int i, j, blocks;
+
++ if (TEST_FAIL())
++ return -1;
++
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+@@ -61,6 +64,9 @@
+ u8 *pos = data;
+ int i, j, blocks;
+
++ if (TEST_FAIL())
++ return -1;
++
+ ctx = aes_decrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+--- contrib/wpa/src/crypto/aes-ctr.c.orig
++++ contrib/wpa/src/crypto/aes-ctr.c
+@@ -1,5 +1,5 @@
+ /*
+- * AES-128 CTR
++ * AES-128/192/256 CTR
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen
+ *
+@@ -14,15 +14,16 @@
+ #include "aes_wrap.h"
+
+ /**
+- * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+- * @key: Key for encryption (16 bytes)
++ * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption
++ * @key: Key for encryption (key_len bytes)
++ * @key_len: Length of the key (16, 24, or 32 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+-int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+- u8 *data, size_t data_len)
++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++ u8 *data, size_t data_len)
+ {
+ void *ctx;
+ size_t j, len, left = data_len;
+@@ -30,7 +31,7 @@
+ u8 *pos = data;
+ u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+- ctx = aes_encrypt_init(key, 16);
++ ctx = aes_encrypt_init(key, key_len);
+ if (ctx == NULL)
+ return -1;
+ os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+@@ -53,3 +54,18 @@
+ aes_encrypt_deinit(ctx);
+ return 0;
+ }
++
++
++/**
++ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
++ * @key: Key for encryption (key_len bytes)
++ * @nonce: Nonce for counter mode (16 bytes)
++ * @data: Data to encrypt in-place
++ * @data_len: Length of data in bytes
++ * Returns: 0 on success, -1 on failure
++ */
++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
++ u8 *data, size_t data_len)
++{
++ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
++}
+--- contrib/wpa/src/crypto/aes-internal-dec.c.orig
++++ contrib/wpa/src/crypto/aes-internal-dec.c
+@@ -147,10 +147,12 @@
+ PUTU32(pt + 12, s3);
+ }
+
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+ {
+ u32 *rk = ctx;
+ rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
++ return 0;
+ }
+
+
+--- contrib/wpa/src/crypto/aes-internal-enc.c.orig
++++ contrib/wpa/src/crypto/aes-internal-enc.c
+@@ -99,6 +99,10 @@
+ {
+ u32 *rk;
+ int res;
++
++ if (TEST_FAIL())
++ return NULL;
++
+ rk = os_malloc(AES_PRIV_SIZE);
+ if (rk == NULL)
+ return NULL;
+@@ -112,10 +116,11 @@
+ }
+
+
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+ {
+ u32 *rk = ctx;
+ rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
++ return 0;
+ }
+
+
+--- contrib/wpa/src/crypto/aes-omac1.c.orig
++++ contrib/wpa/src/crypto/aes-omac1.c
+@@ -48,6 +48,9 @@
+ const u8 *pos, *end;
+ size_t i, e, left, total_len;
+
++ if (TEST_FAIL())
++ return -1;
++
+ ctx = aes_encrypt_init(key, key_len);
+ if (ctx == NULL)
+ return -1;
+--- contrib/wpa/src/crypto/aes-siv.c.orig
++++ contrib/wpa/src/crypto/aes-siv.c
+@@ -61,26 +61,33 @@
+ }
+
+
+-static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
+- size_t *len, u8 *mac)
++static int aes_s2v(const u8 *key, size_t key_len,
++ size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
+ {
+ u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+ u8 *buf = NULL;
+ int ret;
+ size_t i;
++ const u8 *data[1];
++ size_t data_len[1];
+
+ if (!num_elem) {
+ os_memcpy(tmp, zero, sizeof(zero));
+ tmp[AES_BLOCK_SIZE - 1] = 1;
+- return omac1_aes_128(key, tmp, sizeof(tmp), mac);
++ data[0] = tmp;
++ data_len[0] = sizeof(tmp);
++ return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
+ }
+
+- ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
++ data[0] = zero;
++ data_len[0] = sizeof(zero);
++ ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_elem - 1; i++) {
+- ret = omac1_aes_128(key, addr[i], len[i], tmp2);
++ ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i],
++ tmp2);
+ if (ret)
+ return ret;
+
+@@ -88,13 +95,13 @@
+ xor(tmp, tmp2);
+ }
+ if (len[i] >= AES_BLOCK_SIZE) {
+- buf = os_malloc(len[i]);
++ buf = os_memdup(addr[i], len[i]);
+ if (!buf)
+ return -ENOMEM;
+
+- os_memcpy(buf, addr[i], len[i]);
+ xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
+- ret = omac1_aes_128(key, buf, len[i], mac);
++ data[0] = buf;
++ ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
+ bin_clear_free(buf, len[i]);
+ return ret;
+ }
+@@ -103,24 +110,32 @@
+ pad_block(tmp2, addr[i], len[i]);
+ xor(tmp, tmp2);
+
+- return omac1_aes_128(key, tmp, sizeof(tmp), mac);
++ data[0] = tmp;
++ data_len[0] = sizeof(tmp);
++ return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
+ }
+
+
+-int aes_siv_encrypt(const u8 *key, const u8 *pw,
+- size_t pwlen, size_t num_elem,
+- const u8 *addr[], const size_t *len, u8 *out)
++int aes_siv_encrypt(const u8 *key, size_t key_len,
++ const u8 *pw, size_t pwlen,
++ size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *out)
+ {
+ const u8 *_addr[6];
+ size_t _len[6];
+- const u8 *k1 = key, *k2 = key + 16;
++ const u8 *k1, *k2;
+ u8 v[AES_BLOCK_SIZE];
+ size_t i;
+ u8 *iv, *crypt_pw;
+
+- if (num_elem > ARRAY_SIZE(_addr) - 1)
++ if (num_elem > ARRAY_SIZE(_addr) - 1 ||
++ (key_len != 32 && key_len != 48 && key_len != 64))
+ return -1;
+
++ key_len /= 2;
++ k1 = key;
++ k2 = key + key_len;
++
+ for (i = 0; i < num_elem; i++) {
+ _addr[i] = addr[i];
+ _len[i] = len[i];
+@@ -128,7 +143,7 @@
+ _addr[num_elem] = pw;
+ _len[num_elem] = pwlen;
+
+- if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
++ if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v))
+ return -1;
+
+ iv = out;
+@@ -140,17 +155,18 @@
+ /* zero out 63rd and 31st bits of ctr (from right) */
+ v[8] &= 0x7f;
+ v[12] &= 0x7f;
+- return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
++ return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen);
+ }
+
+
+-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
++int aes_siv_decrypt(const u8 *key, size_t key_len,
++ const u8 *iv_crypt, size_t iv_c_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *out)
+ {
+ const u8 *_addr[6];
+ size_t _len[6];
+- const u8 *k1 = key, *k2 = key + 16;
++ const u8 *k1, *k2;
+ size_t crypt_len;
+ size_t i;
+ int ret;
+@@ -157,9 +173,13 @@
+ u8 iv[AES_BLOCK_SIZE];
+ u8 check[AES_BLOCK_SIZE];
+
+- if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
++ if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 ||
++ (key_len != 32 && key_len != 48 && key_len != 64))
+ return -1;
+ crypt_len = iv_c_len - AES_BLOCK_SIZE;
++ key_len /= 2;
++ k1 = key;
++ k2 = key + key_len;
+
+ for (i = 0; i < num_elem; i++) {
+ _addr[i] = addr[i];
+@@ -174,11 +194,11 @@
+ iv[8] &= 0x7f;
+ iv[12] &= 0x7f;
+
+- ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
++ ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len);
+ if (ret)
+ return ret;
+
+- ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
++ ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check);
+ if (ret)
+ return ret;
+ if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
+--- contrib/wpa/src/crypto/aes.h.orig
++++ contrib/wpa/src/crypto/aes.h
+@@ -12,10 +12,10 @@
+ #define AES_BLOCK_SIZE 16
+
+ void * aes_encrypt_init(const u8 *key, size_t len);
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+ void aes_encrypt_deinit(void *ctx);
+ void * aes_decrypt_init(const u8 *key, size_t len);
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+ void aes_decrypt_deinit(void *ctx);
+
+ #endif /* AES_H */
+--- contrib/wpa/src/crypto/aes_siv.h.orig
++++ contrib/wpa/src/crypto/aes_siv.h
+@@ -9,11 +9,13 @@
+ #ifndef AES_SIV_H
+ #define AES_SIV_H
+
+-int aes_siv_encrypt(const u8 *key, const u8 *pw,
+- size_t pwlen, size_t num_elem,
+- const u8 *addr[], const size_t *len, u8 *out);
+-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
++int aes_siv_encrypt(const u8 *key, size_t key_len,
++ const u8 *pw, size_t pwlen,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *out);
++int aes_siv_decrypt(const u8 *key, size_t key_len,
++ const u8 *iv_crypt, size_t iv_c_len,
++ size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *out);
+
+ #endif /* AES_SIV_H */
+--- contrib/wpa/src/crypto/aes_wrap.h.orig
++++ contrib/wpa/src/crypto/aes_wrap.h
+@@ -3,7 +3,7 @@
+ *
+ * - AES Key Wrap Algorithm (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
+- * - AES-128 CTR mode encryption
++ * - AES-128/192/256 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ * - AES-GCM
+@@ -33,6 +33,8 @@
+ int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+ u8 *mac);
+ int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
++int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
++ u8 *data, size_t data_len);
+ int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len);
+ int __must_check aes_128_eax_encrypt(const u8 *key,
+--- contrib/wpa/src/crypto/crypto.h.orig
++++ contrib/wpa/src/crypto/crypto.h
+@@ -1,6 +1,6 @@
+ /*
+ * Wrapper functions for crypto libraries
+- * Copyright (c) 2004-2013, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -80,12 +80,35 @@
+ u8 *mac);
+
+ /**
++ * sha384_vector - SHA384 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 on failure
++ */
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac);
++
++/**
++ * sha512_vector - SHA512 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 on failure
++ */
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac);
++
++/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
++ * Returns: 0 on success, -1 on failure
+ */
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+ /**
+ * aes_encrypt_init - Initialize AES for encryption
+@@ -100,8 +123,9 @@
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
++ * Returns: 0 on success, -1 on failure
+ */
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+ /**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+@@ -122,8 +146,9 @@
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
++ * Returns: 0 on success, -1 on failure
+ */
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+ /**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+@@ -135,7 +160,8 @@
+ enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+- CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
++ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
++ CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
+ };
+
+ struct crypto_hash;
+@@ -391,6 +417,14 @@
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey);
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len);
++
+ /**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+@@ -503,6 +537,14 @@
+ u8 *buf, size_t buflen, size_t padlen);
+
+ /**
++ * crypto_bignum_rand - Create a random number in range of modulus
++ * @r: Bignum; set to a random value
++ * @m: Bignum; modulus
++ * Returns: 0 on success, -1 on failure
++ */
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
++
++/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+@@ -584,6 +626,16 @@
+ struct crypto_bignum *d);
+
+ /**
++ * crypto_bignum_rshift - r = a >> n
++ * @a: Bignum
++ * @n: Number of bits
++ * @r: Bignum; used to store the result of a >> n
++ * Returns: 0 on success, -1 on failure
++ */
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++ struct crypto_bignum *r);
++
++/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+@@ -614,6 +666,13 @@
+ int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+ /**
++ * crypto_bignum_is_odd - Is the given bignum odd
++ * @a: Bignum
++ * Returns: 1 if @a is odd or 0 if not
++ */
++int crypto_bignum_is_odd(const struct crypto_bignum *a);
++
++/**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+@@ -659,6 +718,13 @@
+ size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+ /**
++ * crypto_ec_order_len - Get length of the order in octets
++ * @e: EC context from crypto_ec_init()
++ * Returns: Length of the order defining the group
++ */
++size_t crypto_ec_order_len(struct crypto_ec *e);
++
++/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+@@ -695,6 +761,16 @@
+ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+ /**
++ * crypto_ec_point_x - Copies the x-ordinate point into big number
++ * @e: EC context from crypto_ec_init()
++ * @p: EC point data
++ * @x: Big number to set to the copy of x-ordinate
++ * Returns: 0 on success, -1 on failure
++ */
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++ struct crypto_bignum *x);
++
++/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+@@ -723,7 +799,7 @@
+ const u8 *val);
+
+ /**
+- * crypto_bignum_add - c = a + b
++ * crypto_ec_point_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+@@ -735,7 +811,7 @@
+ struct crypto_ec_point *c);
+
+ /**
+- * crypto_bignum_mul - res = b * p
++ * crypto_ec_point_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+@@ -806,4 +882,12 @@
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b);
+
++struct crypto_ecdh;
++
++struct crypto_ecdh * crypto_ecdh_init(int group);
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++ const u8 *key, size_t len);
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
++
+ #endif /* CRYPTO_H */
+--- contrib/wpa/src/crypto/crypto_gnutls.c.orig
++++ contrib/wpa/src/crypto/crypto_gnutls.c
+@@ -1,6 +1,6 @@
+ /*
+ * WPA Supplicant / wrapper functions for libgcrypt
+- * Copyright (c) 2004-2009, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -10,28 +10,43 @@
+ #include
+
+ #include "common.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
+ #include "crypto.h"
+
+-int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++static int gnutls_digest_vector(int algo, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
+ {
+ gcry_md_hd_t hd;
+ unsigned char *p;
+ size_t i;
+
+- if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
++ if (TEST_FAIL())
+ return -1;
++
++ if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR)
++ return -1;
+ for (i = 0; i < num_elem; i++)
+ gcry_md_write(hd, addr[i], len[i]);
+- p = gcry_md_read(hd, GCRY_MD_MD4);
++ p = gcry_md_read(hd, algo);
+ if (p)
+- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
++ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
+ gcry_md_close(hd);
+ return 0;
+ }
+
+
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+ {
++ return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac);
++}
++
++
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
+ gcry_cipher_hd_t hd;
+ u8 pkey[8], next, tmp;
+ int i;
+@@ -49,49 +64,161 @@
+ gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
+ gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
+ gcry_cipher_close(hd);
++ return 0;
+ }
+
+
+ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+ {
+- gcry_md_hd_t hd;
+- unsigned char *p;
+- size_t i;
+-
+- if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
+- return -1;
+- for (i = 0; i < num_elem; i++)
+- gcry_md_write(hd, addr[i], len[i]);
+- p = gcry_md_read(hd, GCRY_MD_MD5);
+- if (p)
+- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
+- gcry_md_close(hd);
+- return 0;
++ return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac);
+ }
+
+
+ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+ {
++ return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac);
++}
++
++
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac);
++}
++
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac);
++}
++
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac);
++}
++
++
++static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len,
++ size_t num_elem, const u8 *addr[],
++ const size_t *len, u8 *mac)
++{
+ gcry_md_hd_t hd;
+ unsigned char *p;
+ size_t i;
+
+- if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
++ if (TEST_FAIL())
+ return -1;
++
++ if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
++ return -1;
++ if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) {
++ gcry_md_close(hd);
++ return -1;
++ }
+ for (i = 0; i < num_elem; i++)
+ gcry_md_write(hd, addr[i], len[i]);
+- p = gcry_md_read(hd, GCRY_MD_SHA1);
++ p = gcry_md_read(hd, algo);
+ if (p)
+- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
++ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
+ gcry_md_close(hd);
+ return 0;
+ }
+
+
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr,
++ len, mac);
++}
++
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr,
++ len, mac);
++}
++
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++#ifdef CONFIG_SHA256
++
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr,
++ len, mac);
++}
++
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA256 */
++
++
++#ifdef CONFIG_SHA384
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr,
++ len, mac);
++}
++
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA384 */
++
++
++#ifdef CONFIG_SHA512
++
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr,
++ len, mac);
++}
++
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA512 */
++
++
+ void * aes_encrypt_init(const u8 *key, size_t len)
+ {
+ gcry_cipher_hd_t hd;
+
++ if (TEST_FAIL())
++ return NULL;
++
+ if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+ GPG_ERR_NO_ERROR) {
+ printf("cipher open failed\n");
+@@ -107,10 +234,11 @@
+ }
+
+
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+ {
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
++ return 0;
+ }
+
+
+@@ -125,6 +253,9 @@
+ {
+ gcry_cipher_hd_t hd;
+
++ if (TEST_FAIL())
++ return NULL;
++
+ if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+ GPG_ERR_NO_ERROR)
+ return NULL;
+@@ -137,10 +268,11 @@
+ }
+
+
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+ {
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
++ return 0;
+ }
+
+
+@@ -151,6 +283,81 @@
+ }
+
+
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++}
++
++
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ gcry_mpi_t pub = NULL;
++ int res = -1;
++
++ if (pubkey_len > prime_len ||
++ (pubkey_len == prime_len &&
++ os_memcmp(pubkey, prime, prime_len) >= 0))
++ return -1;
++
++ if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) !=
++ GPG_ERR_NO_ERROR ||
++ gcry_mpi_cmp_ui(pub, 1) <= 0)
++ goto fail;
++
++ if (order) {
++ gcry_mpi_t p = NULL, q = NULL, tmp;
++ int failed;
++
++ /* verify: pubkey^q == 1 mod p */
++ tmp = gcry_mpi_new(prime_len * 8);
++ failed = !tmp ||
++ gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len,
++ NULL) != GPG_ERR_NO_ERROR ||
++ gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len,
++ NULL) != GPG_ERR_NO_ERROR;
++ if (!failed) {
++ gcry_mpi_powm(tmp, pub, q, p);
++ failed = gcry_mpi_cmp_ui(tmp, 1) != 0;
++ }
++ gcry_mpi_release(p);
++ gcry_mpi_release(q);
++ gcry_mpi_release(tmp);
++ if (failed)
++ goto fail;
++ }
++
++ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len);
++fail:
++ gcry_mpi_release(pub);
++ return res;
++}
++
++
+ int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+--- contrib/wpa/src/crypto/crypto_internal-modexp.c.orig
++++ contrib/wpa/src/crypto/crypto_internal-modexp.c
+@@ -13,6 +13,79 @@
+ #include "crypto.h"
+
+
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++}
++
++
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ struct bignum *pub;
++ int res = -1;
++
++ if (pubkey_len > prime_len ||
++ (pubkey_len == prime_len &&
++ os_memcmp(pubkey, prime, prime_len) >= 0))
++ return -1;
++
++ pub = bignum_init();
++ if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 ||
++ bignum_cmp_d(pub, 1) <= 0)
++ goto fail;
++
++ if (order) {
++ struct bignum *p, *q, *tmp;
++ int failed;
++
++ /* verify: pubkey^q == 1 mod p */
++ p = bignum_init();
++ q = bignum_init();
++ tmp = bignum_init();
++ failed = !p || !q || !tmp ||
++ bignum_set_unsigned_bin(p, prime, prime_len) < 0 ||
++ bignum_set_unsigned_bin(q, order, order_len) < 0 ||
++ bignum_exptmod(pub, q, p, tmp) < 0 ||
++ bignum_cmp_d(tmp, 1) != 0;
++ bignum_deinit(p);
++ bignum_deinit(q);
++ bignum_deinit(tmp);
++ if (failed)
++ goto fail;
++ }
++
++ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len);
++fail:
++ bignum_deinit(pub);
++ return res;
++}
++
++
+ int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+--- contrib/wpa/src/crypto/crypto_internal.c.orig
++++ contrib/wpa/src/crypto/crypto_internal.c
+@@ -11,6 +11,8 @@
+ #include "common.h"
+ #include "crypto.h"
+ #include "sha256_i.h"
++#include "sha384_i.h"
++#include "sha512_i.h"
+ #include "sha1_i.h"
+ #include "md5_i.h"
+
+@@ -22,6 +24,12 @@
+ #ifdef CONFIG_SHA256
+ struct sha256_state sha256;
+ #endif /* CONFIG_SHA256 */
++#ifdef CONFIG_INTERNAL_SHA384
++ struct sha384_state sha384;
++#endif /* CONFIG_INTERNAL_SHA384 */
++#ifdef CONFIG_INTERNAL_SHA512
++ struct sha512_state sha512;
++#endif /* CONFIG_INTERNAL_SHA512 */
+ } u;
+ u8 key[64];
+ size_t key_len;
+@@ -54,6 +62,16 @@
+ sha256_init(&ctx->u.sha256);
+ break;
+ #endif /* CONFIG_SHA256 */
++#ifdef CONFIG_INTERNAL_SHA384
++ case CRYPTO_HASH_ALG_SHA384:
++ sha384_init(&ctx->u.sha384);
++ break;
++#endif /* CONFIG_INTERNAL_SHA384 */
++#ifdef CONFIG_INTERNAL_SHA512
++ case CRYPTO_HASH_ALG_SHA512:
++ sha512_init(&ctx->u.sha512);
++ break;
++#endif /* CONFIG_INTERNAL_SHA512 */
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (key_len > sizeof(k_pad)) {
+ MD5Init(&ctx->u.md5);
+@@ -142,6 +160,16 @@
+ sha256_process(&ctx->u.sha256, data, len);
+ break;
+ #endif /* CONFIG_SHA256 */
++#ifdef CONFIG_INTERNAL_SHA384
++ case CRYPTO_HASH_ALG_SHA384:
++ sha384_process(&ctx->u.sha384, data, len);
++ break;
++#endif /* CONFIG_INTERNAL_SHA384 */
++#ifdef CONFIG_INTERNAL_SHA512
++ case CRYPTO_HASH_ALG_SHA512:
++ sha512_process(&ctx->u.sha512, data, len);
++ break;
++#endif /* CONFIG_INTERNAL_SHA512 */
+ default:
+ break;
+ }
+@@ -191,6 +219,28 @@
+ sha256_done(&ctx->u.sha256, mac);
+ break;
+ #endif /* CONFIG_SHA256 */
++#ifdef CONFIG_INTERNAL_SHA384
++ case CRYPTO_HASH_ALG_SHA384:
++ if (*len < 48) {
++ *len = 48;
++ os_free(ctx);
++ return -1;
++ }
++ *len = 48;
++ sha384_done(&ctx->u.sha384, mac);
++ break;
++#endif /* CONFIG_INTERNAL_SHA384 */
++#ifdef CONFIG_INTERNAL_SHA512
++ case CRYPTO_HASH_ALG_SHA512:
++ if (*len < 64) {
++ *len = 64;
++ os_free(ctx);
++ return -1;
++ }
++ *len = 64;
++ sha512_done(&ctx->u.sha512, mac);
++ break;
++#endif /* CONFIG_INTERNAL_SHA512 */
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (*len < 16) {
+ *len = 16;
+@@ -260,6 +310,9 @@
+
+ os_free(ctx);
+
++ if (TEST_FAIL())
++ return -1;
++
+ return 0;
+ }
+
+--- contrib/wpa/src/crypto/crypto_libtomcrypt.c.orig
++++ contrib/wpa/src/crypto/crypto_libtomcrypt.c
+@@ -35,7 +35,7 @@
+ }
+
+
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+ {
+ u8 pkey[8], next, tmp;
+ int i;
+@@ -53,6 +53,7 @@
+ des_setup(pkey, 8, 0, &skey);
+ des_ecb_encrypt(clear, cypher, &skey);
+ des_done(&skey);
++ return 0;
+ }
+
+
+@@ -96,10 +97,10 @@
+ }
+
+
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+ {
+ symmetric_key *skey = ctx;
+- aes_ecb_encrypt(plain, crypt, skey);
++ return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1;
+ }
+
+
+@@ -125,10 +126,10 @@
+ }
+
+
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+ {
+ symmetric_key *skey = ctx;
+- aes_ecb_encrypt(plain, (u8 *) crypt, skey);
++ return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1;
+ }
+
+
+@@ -277,6 +278,9 @@
+
+ os_free(ctx);
+
++ if (TEST_FAIL())
++ return -1;
++
+ return ret;
+ }
+
+@@ -297,7 +301,7 @@
+ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+-{
++{
+ struct crypto_cipher *ctx;
+ int idx, res, rc4 = 0;
+
+@@ -693,6 +697,44 @@
+
+ #ifdef CONFIG_MODEXP
+
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++}
++
++
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ /* TODO: check pubkey */
++ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len);
++}
++
++
+ int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+--- contrib/wpa/src/crypto/crypto_linux.c.orig
++++ contrib/wpa/src/crypto/crypto_linux.c
+@@ -0,0 +1,1009 @@
++/*
++ * Crypto wrapper for Linux kernel AF_ALG
++ * Copyright (c) 2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++#include
++
++#include "common.h"
++#include "crypto.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "aes.h"
++
++
++#ifndef SOL_ALG
++#define SOL_ALG 279
++#endif /* SOL_ALG */
++
++
++static int linux_af_alg_socket(const char *type, const char *name)
++{
++ struct sockaddr_alg sa;
++ int s;
++
++ if (TEST_FAIL())
++ return -1;
++
++ s = socket(AF_ALG, SOCK_SEQPACKET, 0);
++ if (s < 0) {
++ wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++
++ os_memset(&sa, 0, sizeof(sa));
++ sa.salg_family = AF_ALG;
++ os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
++ os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
++ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
++ wpa_printf(MSG_ERROR,
++ "%s: Failed to bind AF_ALG socket(%s,%s): %s",
++ __func__, type, name, strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ return s;
++}
++
++
++static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
++ size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len,
++ u8 *mac, size_t mac_len)
++{
++ int s, t;
++ size_t i;
++ ssize_t res;
++ int ret = -1;
++
++ s = linux_af_alg_socket("hash", alg);
++ if (s < 0)
++ return -1;
++
++ if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
++ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
++ __func__, strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ t = accept(s, NULL, NULL);
++ if (t < 0) {
++ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ for (i = 0; i < num_elem; i++) {
++ res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR,
++ "%s: send on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++ if ((size_t) res < len[i]) {
++ wpa_printf(MSG_ERROR,
++ "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
++ __func__, (int) res, (int) len[i]);
++ goto fail;
++ }
++ }
++
++ res = recv(t, mac, mac_len, 0);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR,
++ "%s: recv on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++ if ((size_t) res < mac_len) {
++ wpa_printf(MSG_ERROR,
++ "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
++ __func__, (int) res, (int) mac_len);
++ goto fail;
++ }
++
++ ret = 0;
++fail:
++ close(t);
++ close(s);
++
++ return ret;
++}
++
++
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
++ mac, 16);
++}
++
++
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
++ mac, MD5_MAC_LEN);
++}
++
++
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
++ mac, SHA1_MAC_LEN);
++}
++
++
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
++ mac, SHA256_MAC_LEN);
++}
++
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
++ mac, SHA384_MAC_LEN);
++}
++
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
++ mac, 64);
++}
++
++
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
++ addr, len, mac, 16);
++}
++
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
++ addr, len, mac, SHA1_MAC_LEN);
++}
++
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
++ addr, len, mac, SHA256_MAC_LEN);
++}
++
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
++ addr, len, mac, SHA384_MAC_LEN);
++}
++
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++struct crypto_hash {
++ int s;
++ int t;
++ size_t mac_len;
++ int failed;
++};
++
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++ size_t key_len)
++{
++ struct crypto_hash *ctx;
++ const char *name;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ switch (alg) {
++ case CRYPTO_HASH_ALG_MD5:
++ name = "md5";
++ ctx->mac_len = MD5_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_SHA1:
++ name = "sha1";
++ ctx->mac_len = SHA1_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_HMAC_MD5:
++ name = "hmac(md5)";
++ ctx->mac_len = MD5_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_HMAC_SHA1:
++ name = "hmac(sha1)";
++ ctx->mac_len = SHA1_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_SHA256:
++ name = "sha256";
++ ctx->mac_len = SHA256_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_HMAC_SHA256:
++ name = "hmac(sha256)";
++ ctx->mac_len = SHA256_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_SHA384:
++ name = "sha384";
++ ctx->mac_len = SHA384_MAC_LEN;
++ break;
++ case CRYPTO_HASH_ALG_SHA512:
++ name = "sha512";
++ ctx->mac_len = 64;
++ break;
++ default:
++ os_free(ctx);
++ return NULL;
++ }
++
++ ctx->s = linux_af_alg_socket("hash", name);
++ if (ctx->s < 0) {
++ os_free(ctx);
++ return NULL;
++ }
++
++ if (key && key_len &&
++ setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
++ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
++ __func__, strerror(errno));
++ close(ctx->s);
++ os_free(ctx);
++ return NULL;
++ }
++
++ ctx->t = accept(ctx->s, NULL, NULL);
++ if (ctx->t < 0) {
++ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ close(ctx->s);
++ os_free(ctx);
++ return NULL;
++ }
++
++ return ctx;
++}
++
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++ ssize_t res;
++
++ if (!ctx)
++ return;
++
++ res = send(ctx->t, data, len, MSG_MORE);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR,
++ "%s: send on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ ctx->failed = 1;
++ return;
++ }
++ if ((size_t) res < len) {
++ wpa_printf(MSG_ERROR,
++ "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
++ __func__, (int) res, (int) len);
++ ctx->failed = 1;
++ return;
++ }
++}
++
++
++static void crypto_hash_deinit(struct crypto_hash *ctx)
++{
++ close(ctx->s);
++ close(ctx->t);
++ os_free(ctx);
++}
++
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++ ssize_t res;
++
++ if (!ctx)
++ return -2;
++
++ if (!mac || !len) {
++ crypto_hash_deinit(ctx);
++ return 0;
++ }
++
++ if (ctx->failed) {
++ crypto_hash_deinit(ctx);
++ return -2;
++ }
++
++ if (*len < ctx->mac_len) {
++ crypto_hash_deinit(ctx);
++ *len = ctx->mac_len;
++ return -1;
++ }
++ *len = ctx->mac_len;
++
++ res = recv(ctx->t, mac, ctx->mac_len, 0);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR,
++ "%s: recv on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ crypto_hash_deinit(ctx);
++ return -2;
++ }
++ if ((size_t) res < ctx->mac_len) {
++ wpa_printf(MSG_ERROR,
++ "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
++ __func__, (int) res, (int) ctx->mac_len);
++ crypto_hash_deinit(ctx);
++ return -2;
++ }
++
++ crypto_hash_deinit(ctx);
++
++ if (TEST_FAIL())
++ return -1;
++ return 0;
++}
++
++
++struct linux_af_alg_skcipher {
++ int s;
++ int t;
++};
++
++
++static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
++{
++ if (!skcipher)
++ return;
++ if (skcipher->s >= 0)
++ close(skcipher->s);
++ if (skcipher->t >= 0)
++ close(skcipher->t);
++ os_free(skcipher);
++}
++
++
++static struct linux_af_alg_skcipher *
++linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
++{
++ struct linux_af_alg_skcipher *skcipher;
++
++ skcipher = os_zalloc(sizeof(*skcipher));
++ if (!skcipher)
++ goto fail;
++ skcipher->t = -1;
++
++ skcipher->s = linux_af_alg_socket("skcipher", alg);
++ if (skcipher->s < 0)
++ goto fail;
++
++ if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
++ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++
++ skcipher->t = accept(skcipher->s, NULL, NULL);
++ if (skcipher->t < 0) {
++ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++
++ return skcipher;
++fail:
++ linux_af_alg_skcipher_deinit(skcipher);
++ return NULL;
++}
++
++
++static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
++ int enc, const u8 *in, u8 *out)
++{
++ char buf[CMSG_SPACE(sizeof(u32))];
++ struct iovec io[1];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++
++ io[0].iov_base = (void *) in;
++ io[0].iov_len = AES_BLOCK_SIZE;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
++ msg.msg_iov = io;
++ msg.msg_iovlen = 1;
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
++
++ ret = sendmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++
++ ret = read(skcipher->t, out, AES_BLOCK_SIZE);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: read failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++ if (ret < AES_BLOCK_SIZE) {
++ wpa_printf(MSG_ERROR,
++ "%s: read did not return full data (%d/%d)",
++ __func__, (int) ret, AES_BLOCK_SIZE);
++ return -1;
++ }
++
++ return 0;
++}
++
++
++void * aes_encrypt_init(const u8 *key, size_t len)
++{
++ return linux_af_alg_skcipher("ecb(aes)", key, len);
++}
++
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++ struct linux_af_alg_skcipher *skcipher = ctx;
++
++ return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
++}
++
++
++void aes_encrypt_deinit(void *ctx)
++{
++ linux_af_alg_skcipher_deinit(ctx);
++}
++
++
++void * aes_decrypt_init(const u8 *key, size_t len)
++{
++ return linux_af_alg_skcipher("ecb(aes)", key, len);
++}
++
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++ struct linux_af_alg_skcipher *skcipher = ctx;
++
++ return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
++}
++
++
++void aes_decrypt_deinit(void *ctx)
++{
++ linux_af_alg_skcipher_deinit(ctx);
++}
++
++
++int rc4_skip(const u8 *key, size_t keylen, size_t skip,
++ u8 *data, size_t data_len)
++{
++ struct linux_af_alg_skcipher *skcipher;
++ u8 *skip_buf;
++ char buf[CMSG_SPACE(sizeof(u32))];
++ struct iovec io[2];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++
++ skip_buf = os_zalloc(skip + 1);
++ if (!skip_buf)
++ return -1;
++ skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
++ if (!skcipher) {
++ os_free(skip_buf);
++ return -1;
++ }
++
++ io[0].iov_base = skip_buf;
++ io[0].iov_len = skip;
++ io[1].iov_base = data;
++ io[1].iov_len = data_len;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
++ msg.msg_iov = io;
++ msg.msg_iovlen = 2;
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = ALG_OP_ENCRYPT;
++
++ ret = sendmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ os_free(skip_buf);
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++ os_free(skip_buf);
++
++ msg.msg_control = NULL;
++ msg.msg_controllen = 0;
++ ret = recvmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
++ __func__, strerror(errno));
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++ linux_af_alg_skcipher_deinit(skcipher);
++
++ if ((size_t) ret < skip + data_len) {
++ wpa_printf(MSG_ERROR,
++ "%s: recvmsg did not return full data (%d/%d)",
++ __func__, (int) ret, (int) (skip + data_len));
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++ u8 pkey[8], next, tmp;
++ int i;
++ struct linux_af_alg_skcipher *skcipher;
++ char buf[CMSG_SPACE(sizeof(u32))];
++ struct iovec io[1];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++ int res = -1;
++
++ /* Add parity bits to the key */
++ next = 0;
++ for (i = 0; i < 7; i++) {
++ tmp = key[i];
++ pkey[i] = (tmp >> i) | next | 1;
++ next = tmp << (7 - i);
++ }
++ pkey[i] = next | 1;
++
++ skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
++ if (!skcipher)
++ goto fail;
++
++ io[0].iov_base = (void *) clear;
++ io[0].iov_len = 8;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
++ msg.msg_iov = io;
++ msg.msg_iovlen = 1;
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = ALG_OP_ENCRYPT;
++
++ ret = sendmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++
++ ret = read(skcipher->t, cypher, 8);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: read failed: %s",
++ __func__, strerror(errno));
++ goto fail;
++ }
++ if (ret < 8) {
++ wpa_printf(MSG_ERROR,
++ "%s: read did not return full data (%d/8)",
++ __func__, (int) ret);
++ goto fail;
++ }
++
++ res = 0;
++fail:
++ linux_af_alg_skcipher_deinit(skcipher);
++ return res;
++}
++
++
++static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
++ u8 *data, size_t data_len)
++{
++ struct linux_af_alg_skcipher *skcipher;
++ char buf[100];
++ struct iovec io[1];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++ struct af_alg_iv *alg_iv;
++ size_t iv_len = AES_BLOCK_SIZE;
++
++ skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
++ if (!skcipher)
++ return -1;
++
++ io[0].iov_base = (void *) data;
++ io[0].iov_len = data_len;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
++ CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ msg.msg_iov = io;
++ msg.msg_iovlen = 1;
++
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
++
++ hdr = CMSG_NXTHDR(&msg, hdr);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_IV;
++ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
++ alg_iv->ivlen = iv_len;
++ os_memcpy(alg_iv->iv, iv, iv_len);
++
++ ret = sendmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++
++ ret = recvmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
++ __func__, strerror(errno));
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++ if ((size_t) ret < data_len) {
++ wpa_printf(MSG_ERROR,
++ "%s: recvmsg not return full data (%d/%d)",
++ __func__, (int) ret, (int) data_len);
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++
++ linux_af_alg_skcipher_deinit(skcipher);
++ return 0;
++}
++
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ return aes_128_cbc_oper(key, 1, iv, data, data_len);
++}
++
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ return aes_128_cbc_oper(key, 0, iv, data, data_len);
++}
++
++
++int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
++ addr, len, mac, AES_BLOCK_SIZE);
++}
++
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
++}
++
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
++ u8 *plain)
++{
++ struct linux_af_alg_skcipher *skcipher;
++ char buf[100];
++ struct iovec io[1];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++ struct af_alg_iv *alg_iv;
++ size_t iv_len = 8;
++
++ skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
++ if (!skcipher)
++ return -1;
++
++ io[0].iov_base = (void *) (cipher + iv_len);
++ io[0].iov_len = n * 8;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
++ CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ msg.msg_iov = io;
++ msg.msg_iovlen = 1;
++
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = ALG_OP_DECRYPT;
++
++ hdr = CMSG_NXTHDR(&msg, hdr);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_IV;
++ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
++ alg_iv->ivlen = iv_len;
++ os_memcpy(alg_iv->iv, cipher, iv_len);
++
++ ret = sendmsg(skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++
++ ret = read(skcipher->t, plain, n * 8);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: read failed: %s",
++ __func__, strerror(errno));
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++ if (ret < n * 8) {
++ wpa_printf(MSG_ERROR,
++ "%s: read not return full data (%d/%d)",
++ __func__, (int) ret, n * 8);
++ linux_af_alg_skcipher_deinit(skcipher);
++ return -1;
++ }
++
++ linux_af_alg_skcipher_deinit(skcipher);
++ return 0;
++}
++
++
++struct crypto_cipher {
++ struct linux_af_alg_skcipher *skcipher;
++};
++
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++ const u8 *iv, const u8 *key,
++ size_t key_len)
++{
++ struct crypto_cipher *ctx;
++ const char *name;
++ struct af_alg_iv *alg_iv;
++ size_t iv_len = 0;
++ char buf[100];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ switch (alg) {
++ case CRYPTO_CIPHER_ALG_RC4:
++ name = "ecb(arc4)";
++ break;
++ case CRYPTO_CIPHER_ALG_AES:
++ name = "cbc(aes)";
++ iv_len = AES_BLOCK_SIZE;
++ break;
++ case CRYPTO_CIPHER_ALG_3DES:
++ name = "cbc(des3_ede)";
++ iv_len = 8;
++ break;
++ case CRYPTO_CIPHER_ALG_DES:
++ name = "cbc(des)";
++ iv_len = 8;
++ break;
++ default:
++ os_free(ctx);
++ return NULL;
++ }
++
++ ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
++ if (!ctx->skcipher) {
++ os_free(ctx);
++ return NULL;
++ }
++
++ if (iv && iv_len) {
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_IV;
++ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
++ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
++ alg_iv->ivlen = iv_len;
++ os_memcpy(alg_iv->iv, iv, iv_len);
++
++ ret = sendmsg(ctx->skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ linux_af_alg_skcipher_deinit(ctx->skcipher);
++ os_free(ctx);
++ return NULL;
++ }
++ }
++
++ return ctx;
++}
++
++
++static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
++ u8 *out, size_t len)
++{
++ char buf[CMSG_SPACE(sizeof(u32))];
++ struct iovec io[1];
++ struct msghdr msg;
++ struct cmsghdr *hdr;
++ ssize_t ret;
++ u32 *op;
++
++ io[0].iov_base = (void *) in;
++ io[0].iov_len = len;
++ os_memset(&msg, 0, sizeof(msg));
++ os_memset(buf, 0, sizeof(buf));
++ msg.msg_control = buf;
++ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
++ msg.msg_iov = io;
++ msg.msg_iovlen = 1;
++ hdr = CMSG_FIRSTHDR(&msg);
++ hdr->cmsg_level = SOL_ALG;
++ hdr->cmsg_type = ALG_SET_OP;
++ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
++ op = (u32 *) CMSG_DATA(hdr);
++ *op = type;
++
++ ret = sendmsg(ctx->skcipher->t, &msg, 0);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++
++ ret = read(ctx->skcipher->t, out, len);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, "%s: read failed: %s",
++ __func__, strerror(errno));
++ return -1;
++ }
++ if (ret < (ssize_t) len) {
++ wpa_printf(MSG_ERROR,
++ "%s: read did not return full data (%d/%d)",
++ __func__, (int) ret, (int) len);
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
++ u8 *crypt, size_t len)
++{
++ return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
++}
++
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
++ u8 *plain, size_t len)
++{
++ return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
++}
++
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++ if (ctx) {
++ linux_af_alg_skcipher_deinit(ctx->skcipher);
++ os_free(ctx);
++ }
++}
++
++
++int crypto_global_init(void)
++{
++ return 0;
++}
++
++
++void crypto_global_deinit(void)
++{
++}
+--- contrib/wpa/src/crypto/crypto_module_tests.c.orig
++++ contrib/wpa/src/crypto/crypto_module_tests.c
+@@ -9,6 +9,7 @@
+ #include "utils/includes.h"
+
+ #include "utils/common.h"
++#include "utils/module_tests.h"
+ #include "crypto/aes_siv.h"
+ #include "crypto/aes_wrap.h"
+ #include "crypto/aes.h"
+@@ -16,6 +17,7 @@
+ #include "crypto/crypto.h"
+ #include "crypto/sha1.h"
+ #include "crypto/sha256.h"
++#include "crypto/sha384.h"
+
+
+ static int test_siv(void)
+@@ -91,7 +93,7 @@
+ addr[0] = ad;
+ len[0] = sizeof(ad);
+
+- if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
++ if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext),
+ 1, addr, len, out)) {
+ wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+ return 1;
+@@ -102,7 +104,8 @@
+ return 1;
+ }
+
+- if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
++ if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c),
++ 1, addr, len, out)) {
+ wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+ return 1;
+ }
+@@ -120,7 +123,8 @@
+ addr[2] = nonce_2;
+ len[2] = sizeof(nonce_2);
+
+- if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
++ if (aes_siv_encrypt(key_2, sizeof(key_2),
++ plaintext_2, sizeof(plaintext_2),
+ 3, addr, len, out)) {
+ wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+ return 1;
+@@ -131,7 +135,8 @@
+ return 1;
+ }
+
+- if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
++ if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2),
++ 3, addr, len, out)) {
+ wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+ return 1;
+ }
+@@ -1266,7 +1271,7 @@
+ }
+
+
+-const struct {
++static const struct {
+ char *data;
+ u8 hash[32];
+ } tests[] = {
+@@ -1290,14 +1295,15 @@
+ }
+ };
+
+-const struct hmac_test {
+- u8 key[80];
++static const struct hmac_test {
++ u8 key[150];
+ size_t key_len;
+- u8 data[128];
++ u8 data[160];
+ size_t data_len;
+- u8 hash[32];
++ u8 hash[32]; /* HMAC-SHA-256 */
++ u8 hash384[48]; /* HMAC-SHA-384 */
+ } hmac_tests[] = {
+- /* draft-ietf-ipsec-ciph-sha-256-01.txt */
++ /* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */
+ {
+ {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+@@ -1312,7 +1318,8 @@
+ 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
+ 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
+ 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
+- }
++ },
++ { }
+ },
+ {
+ {
+@@ -1329,7 +1336,8 @@
+ 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
+ 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
+ 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
+- }
++ },
++ { }
+ },
+ {
+ {
+@@ -1347,7 +1355,8 @@
+ 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
+ 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
+ 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
+- }
++ },
++ { }
+ },
+ {
+ {
+@@ -1364,9 +1373,34 @@
+ 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
+ 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
+ 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
++ },
++ { }
++ },
++ { /* RFC 4231 - Test Case 1 */
++ {
++ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
++ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
++ 0x0b, 0x0b, 0x0b, 0x0b
++ },
++ 20,
++ "Hi There",
++ 8,
++ {
++ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
++ 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
++ 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
++ 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7
++ },
++ {
++ 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62,
++ 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f,
++ 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
++ 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c,
++ 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f,
++ 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6
+ }
+ },
+- {
++ { /* RFC 4231 - Test Case 2 */
+ "Jefe",
+ 4,
+ "what do ya want for nothing?",
+@@ -1376,6 +1410,14 @@
+ 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+ 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+ 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
++ },
++ {
++ 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31,
++ 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b,
++ 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
++ 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e,
++ 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7,
++ 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49
+ }
+ },
+ {
+@@ -1401,6 +1443,39 @@
+ 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
+ 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
+ 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
++ },
++ { }
++ },
++ { /* RFC 4231 - Test Case 3 */
++ {
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa
++ },
++ 20,
++ {
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
++ 0xdd, 0xdd
++ },
++ 50,
++ {
++ 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
++ 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7,
++ 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
++ 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe
++ },
++ {
++ 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a,
++ 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f,
++ 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
++ 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b,
++ 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9,
++ 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27
+ }
+ },
+ {
+@@ -1427,6 +1502,40 @@
+ 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
+ 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
+ 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
++ },
++ { }
++ },
++ { /* RFC 4231 - Test Case 4 */
++ {
++ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
++ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
++ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
++ 0x19,
++ },
++ 25,
++ {
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
++ 0xcd, 0xcd
++ },
++ 50,
++ {
++ 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e,
++ 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a,
++ 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07,
++ 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b
++ },
++ {
++ 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85,
++ 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7,
++ 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
++ 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e,
++ 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79,
++ 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb
+ }
+ },
+ {
+@@ -1444,7 +1553,8 @@
+ 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
+ 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
+ 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
+- }
++ },
++ { }
+ },
+ {
+ {
+@@ -1467,6 +1577,45 @@
+ 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
+ 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
+ 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
++ },
++ { }
++ },
++ { /* RFC 4231 - Test Case 6 */
++ {
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa
++ },
++ 131,
++ "Test Using Larger Than Block-Size Key - Hash Key First",
++ 54,
++ {
++ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f,
++ 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f,
++ 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
++ 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
++ },
++ {
++ 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90,
++ 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4,
++ 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
++ 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6,
++ 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82,
++ 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52
+ }
+ },
+ {
+@@ -1491,6 +1640,45 @@
+ 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
+ 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
+ 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
++ },
++ { }
++ },
++ { /* RFC 4231 - Test Case 7 */
++ {
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
++ 0xaa, 0xaa, 0xaa
++ },
++ 131,
++ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
++ 152,
++ {
++ 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb,
++ 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44,
++ 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93,
++ 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2
++ },
++ {
++ 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d,
++ 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c,
++ 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
++ 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5,
++ 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d,
++ 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e
+ }
+ }
+ };
+@@ -1503,6 +1691,7 @@
+ const u8 *addr[2];
+ size_t len[2];
+ int errors = 0;
++ u8 *key;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
+@@ -1573,6 +1762,31 @@
+ hash, sizeof(hash));
+ /* TODO: add proper test case for this */
+
++ key = os_malloc(8161);
++ if (key) {
++#ifdef CONFIG_HMAC_SHA256_KDF
++ int res;
++
++ res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
++ (u8 *) "seed", 4, key, 8160);
++ if (res) {
++ wpa_printf(MSG_INFO,
++ "Unexpected hmac_sha256_kdf(outlen=8160) failure");
++ errors++;
++ }
++
++ res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
++ (u8 *) "seed", 4, key, 8161);
++ if (res == 0) {
++ wpa_printf(MSG_INFO,
++ "Unexpected hmac_sha256_kdf(outlen=8161) success");
++ errors++;
++ }
++#endif /* CONFIG_HMAC_SHA256_KDF */
++
++ os_free(key);
++ }
++
+ if (!errors)
+ wpa_printf(MSG_INFO, "SHA256 test cases passed");
+ return errors;
+@@ -1579,6 +1793,254 @@
+ }
+
+
++static int test_sha384(void)
++{
++#ifdef CONFIG_SHA384
++ unsigned int i;
++ u8 hash[48];
++ const u8 *addr[2];
++ size_t len[2];
++ int errors = 0;
++ const char *data = "hello";
++ const u8 hash_res[] = {
++ 0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69,
++ 0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb,
++ 0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3,
++ 0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90,
++ 0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd,
++ 0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f
++ };
++
++ addr[0] = (const u8 *) data;
++ len[0] = 5;
++ if (sha384_vector(1, addr, len, hash) < 0 ||
++ os_memcmp(hash, hash_res, 48) != 0) {
++ wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL");
++ errors++;
++ } else {
++ wpa_printf(MSG_INFO, "SHA384 test case 1: OK");
++ }
++
++ addr[0] = (const u8 *) data;
++ len[0] = 4;
++ addr[1] = (const u8 *) data + 4;
++ len[1] = 1;
++ if (sha384_vector(2, addr, len, hash) < 0 ||
++ os_memcmp(hash, hash_res, 48) != 0) {
++ wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL");
++ errors++;
++ } else {
++ wpa_printf(MSG_INFO, "SHA384 test case 2: OK");
++ }
++
++ for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
++ const struct hmac_test *t = &hmac_tests[i];
++
++ if (t->hash384[0] == 0 && t->hash384[1] == 0 &&
++ t->hash384[2] == 0 && t->hash384[3] == 0)
++ continue;
++ wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1);
++
++ if (hmac_sha384(t->key, t->key_len, t->data, t->data_len,
++ hash) < 0 ||
++ os_memcmp(hash, t->hash384, 48) != 0) {
++ wpa_printf(MSG_INFO, " FAIL");
++ errors++;
++ } else
++ wpa_printf(MSG_INFO, " OK");
++
++ addr[0] = t->data;
++ len[0] = t->data_len;
++ if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len,
++ hash) < 0 ||
++ os_memcmp(hash, t->hash384, 48) != 0) {
++ wpa_printf(MSG_INFO, " FAIL");
++ errors++;
++ } else
++ wpa_printf(MSG_INFO, " OK");
++
++ if (len[0]) {
++ addr[0] = t->data;
++ len[0] = 1;
++ addr[1] = t->data + 1;
++ len[1] = t->data_len - 1;
++ if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len,
++ hash) < 0 ||
++ os_memcmp(hash, t->hash384, 48) != 0) {
++ wpa_printf(MSG_INFO, " FAIL");
++ errors++;
++ } else
++ wpa_printf(MSG_INFO, " OK");
++ }
++ }
++
++ if (!errors)
++ wpa_printf(MSG_INFO, "SHA384 test cases passed");
++ return errors;
++#else /* CONFIG_SHA384 */
++ return 0;
++#endif /* CONFIG_SHA384 */
++}
++
++
++static int test_fips186_2_prf(void)
++{
++ /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
++ u8 xkey[] = {
++ 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
++ 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
++ 0xeb, 0x5a, 0x38, 0xb6
++ };
++ u8 w[] = {
++ 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
++ 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
++ 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
++ 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
++ 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
++ };
++ u8 buf[40];
++
++ wpa_printf(MSG_INFO,
++ "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)");
++ if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 ||
++ os_memcmp(w, buf, sizeof(w)) != 0) {
++ wpa_printf(MSG_INFO, "fips186_2_prf failed");
++ return 1;
++ }
++
++ return 0;
++}
++
++
++static int test_extract_expand_hkdf(void)
++{
++ u8 prk[SHA256_MAC_LEN];
++ u8 okm[82];
++
++ /* RFC 5869, A.1 */
++ u8 ikm1[22] = {
++ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
++ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
++ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
++ };
++ u8 salt1[13] = {
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0a, 0x0b, 0x0c
++ };
++ u8 info1[10] = {
++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
++ 0xf8, 0xf9
++ };
++ u8 prk1[32] = {
++ 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
++ 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
++ 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
++ 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
++ };
++ u8 okm1[42] = {
++ 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
++ 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
++ 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
++ 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
++ 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
++ 0x58, 0x65
++ };
++
++ /* RFC 5869, A.2 */
++ u8 ikm2[80] = {
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
++ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
++ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
++ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
++ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
++ };
++ u8 salt2[80] = {
++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
++ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
++ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
++ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
++ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
++ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
++ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
++ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
++ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
++ };
++ u8 info2[80] = {
++ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
++ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
++ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
++ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
++ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
++ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
++ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
++ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
++ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
++ };
++ u8 prk2[32] = {
++ 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
++ 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
++ 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
++ 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44
++ };
++ u8 okm2[82] = {
++ 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
++ 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
++ 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
++ 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
++ 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
++ 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
++ 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
++ 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
++ 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
++ 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
++ 0x1d, 0x87
++ };
++
++ wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)");
++
++ wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1");
++ if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0)
++ return -1;
++ if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) {
++ wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
++ return -1;
++ }
++ if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1),
++ okm, sizeof(okm1)) < 0)
++ return -1;
++ if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) {
++ wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
++ return -1;
++ }
++
++ wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2");
++ if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0)
++ return -1;
++ if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) {
++ wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
++ return -1;
++ }
++ if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2),
++ okm, sizeof(okm2)) < 0)
++ return -1;
++ if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) {
++ wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
++ return -1;
++ }
++
++ wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed");
++
++ return 0;
++}
++
++
+ static int test_ms_funcs(void)
+ {
+ #ifndef CONFIG_FIPS
+@@ -1695,6 +2157,9 @@
+ test_md5() ||
+ test_sha1() ||
+ test_sha256() ||
++ test_sha384() ||
++ test_fips186_2_prf() ||
++ test_extract_expand_hkdf() ||
+ test_ms_funcs())
+ ret = -1;
+
+--- contrib/wpa/src/crypto/crypto_nettle.c.orig
++++ contrib/wpa/src/crypto/crypto_nettle.c
+@@ -0,0 +1,469 @@
++/*
++ * Wrapper functions for libnettle and libgmp
++ * Copyright (c) 2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++#include
++#include
++#undef des_encrypt
++#include
++#include
++#undef aes_encrypt
++#undef aes_decrypt
++#include
++#include
++
++#include "common.h"
++#include "md5.h"
++#include "sha1.h"
++#include "sha256.h"
++#include "sha384.h"
++#include "sha512.h"
++#include "crypto.h"
++
++
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++ struct des_ctx ctx;
++ u8 pkey[8], next, tmp;
++ int i;
++
++ /* Add parity bits to the key */
++ next = 0;
++ for (i = 0; i < 7; i++) {
++ tmp = key[i];
++ pkey[i] = (tmp >> i) | next | 1;
++ next = tmp << (7 - i);
++ }
++ pkey[i] = next | 1;
++
++ nettle_des_set_key(&ctx, pkey);
++ nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ void *ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ctx = os_malloc(alg->context_size);
++ if (!ctx)
++ return -1;
++ alg->init(ctx);
++ for (i = 0; i < num_elem; i++)
++ alg->update(ctx, len[i], addr[i]);
++ alg->digest(ctx, alg->digest_size, mac);
++ bin_clear_free(ctx, alg->context_size);
++ return 0;
++}
++
++
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac);
++}
++
++
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac);
++}
++
++
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac);
++}
++
++
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac);
++}
++
++
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac);
++}
++
++
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac);
++}
++
++
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ struct hmac_md5_ctx ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ hmac_md5_set_key(&ctx, key_len, key);
++ for (i = 0; i < num_elem; i++)
++ hmac_md5_update(&ctx, len[i], addr[i]);
++ hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ struct hmac_sha1_ctx ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ hmac_sha1_set_key(&ctx, key_len, key);
++ for (i = 0; i < num_elem; i++)
++ hmac_sha1_update(&ctx, len[i], addr[i]);
++ hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++#ifdef CONFIG_SHA256
++
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ struct hmac_sha256_ctx ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ hmac_sha256_set_key(&ctx, key_len, key);
++ for (i = 0; i < num_elem; i++)
++ hmac_sha256_update(&ctx, len[i], addr[i]);
++ hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA256 */
++
++
++#ifdef CONFIG_SHA384
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ struct hmac_sha384_ctx ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ hmac_sha384_set_key(&ctx, key_len, key);
++ for (i = 0; i < num_elem; i++)
++ hmac_sha384_update(&ctx, len[i], addr[i]);
++ hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA384 */
++
++
++#ifdef CONFIG_SHA512
++
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ struct hmac_sha512_ctx ctx;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ hmac_sha512_set_key(&ctx, key_len, key);
++ for (i = 0; i < num_elem; i++)
++ hmac_sha512_update(&ctx, len[i], addr[i]);
++ hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac);
++ os_memset(&ctx, 0, sizeof(ctx));
++ return 0;
++}
++
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA512 */
++
++
++void * aes_encrypt_init(const u8 *key, size_t len)
++{
++ struct aes_ctx *ctx;
++
++ if (TEST_FAIL())
++ return NULL;
++ ctx = os_malloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ nettle_aes_set_encrypt_key(ctx, len, key);
++
++ return ctx;
++}
++
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++ struct aes_ctx *actx = ctx;
++ nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain);
++ return 0;
++}
++
++
++void aes_encrypt_deinit(void *ctx)
++{
++ struct aes_ctx *actx = ctx;
++ bin_clear_free(actx, sizeof(*actx));
++}
++
++
++void * aes_decrypt_init(const u8 *key, size_t len)
++{
++ struct aes_ctx *ctx;
++
++ if (TEST_FAIL())
++ return NULL;
++ ctx = os_malloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ nettle_aes_set_decrypt_key(ctx, len, key);
++
++ return ctx;
++}
++
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++ struct aes_ctx *actx = ctx;
++ nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt);
++ return 0;
++}
++
++
++void aes_decrypt_deinit(void *ctx)
++{
++ struct aes_ctx *actx = ctx;
++ bin_clear_free(actx, sizeof(*actx));
++}
++
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
++ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
++
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
++ return -1;
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
++
++ return 0;
++}
++
++
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ mpz_t pub;
++ int res = -1;
++
++ if (pubkey_len > prime_len ||
++ (pubkey_len == prime_len &&
++ os_memcmp(pubkey, prime, prime_len) >= 0))
++ return -1;
++
++ mpz_init(pub);
++ mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey);
++ if (mpz_cmp_d(pub, 1) <= 0)
++ goto fail;
++
++ if (order) {
++ mpz_t p, q, tmp;
++ int failed;
++
++ /* verify: pubkey^q == 1 mod p */
++ mpz_inits(p, q, tmp, NULL);
++ mpz_import(p, prime_len, 1, 1, 1, 0, prime);
++ mpz_import(q, order_len, 1, 1, 1, 0, order);
++ mpz_powm(tmp, pub, q, p);
++ failed = mpz_cmp_d(tmp, 1) != 0;
++ mpz_clears(p, q, tmp, NULL);
++ if (failed)
++ goto fail;
++ }
++
++ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len);
++fail:
++ mpz_clear(pub);
++ return res;
++}
++
++
++int crypto_mod_exp(const u8 *base, size_t base_len,
++ const u8 *power, size_t power_len,
++ const u8 *modulus, size_t modulus_len,
++ u8 *result, size_t *result_len)
++{
++ mpz_t bn_base, bn_exp, bn_modulus, bn_result;
++ int ret = -1;
++ size_t len;
++
++ mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL);
++ mpz_import(bn_base, base_len, 1, 1, 1, 0, base);
++ mpz_import(bn_exp, power_len, 1, 1, 1, 0, power);
++ mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus);
++
++ mpz_powm(bn_result, bn_base, bn_exp, bn_modulus);
++ len = mpz_sizeinbase(bn_result, 2);
++ len = (len + 7) / 8;
++ if (*result_len < len)
++ goto error;
++ mpz_export(result, result_len, 1, 1, 1, 0, bn_result);
++ ret = 0;
++
++error:
++ mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL);
++ return ret;
++}
++
++
++struct crypto_cipher {
++ enum crypto_cipher_alg alg;
++ union {
++ struct arcfour_ctx arcfour_ctx;
++ } u;
++};
++
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++ const u8 *iv, const u8 *key,
++ size_t key_len)
++{
++ struct crypto_cipher *ctx;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ ctx->alg = alg;
++
++ switch (alg) {
++ case CRYPTO_CIPHER_ALG_RC4:
++ nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key);
++ break;
++ default:
++ os_free(ctx);
++ return NULL;
++ }
++
++ return ctx;
++}
++
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
++ u8 *crypt, size_t len)
++{
++ switch (ctx->alg) {
++ case CRYPTO_CIPHER_ALG_RC4:
++ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain);
++ break;
++ default:
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
++ u8 *plain, size_t len)
++{
++ switch (ctx->alg) {
++ case CRYPTO_CIPHER_ALG_RC4:
++ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt);
++ break;
++ default:
++ return -1;
++ }
++
++ return 0;
++}
++
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++ bin_clear_free(ctx, sizeof(*ctx));
++}
+--- contrib/wpa/src/crypto/crypto_none.c.orig
++++ contrib/wpa/src/crypto/crypto_none.c
+@@ -18,6 +18,7 @@
+ }
+
+
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+ {
++ return 0;
+ }
+--- contrib/wpa/src/crypto/crypto_openssl.c.orig
++++ contrib/wpa/src/crypto/crypto_openssl.c
+@@ -1,6 +1,6 @@
+ /*
+ * Wrapper functions for OpenSSL libcrypto
+- * Copyright (c) 2004-2015, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -24,16 +24,72 @@
+ #endif /* CONFIG_ECC */
+
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "wpabuf.h"
+ #include "dh_group5.h"
+ #include "sha1.h"
+ #include "sha256.h"
+ #include "sha384.h"
++#include "sha512.h"
++#include "md5.h"
++#include "aes_wrap.h"
+ #include "crypto.h"
+
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
++/* Compatibility wrappers for older versions. */
++
++static HMAC_CTX * HMAC_CTX_new(void)
++{
++ HMAC_CTX *ctx;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (ctx)
++ HMAC_CTX_init(ctx);
++ return ctx;
++}
++
++
++static void HMAC_CTX_free(HMAC_CTX *ctx)
++{
++ if (!ctx)
++ return;
++ HMAC_CTX_cleanup(ctx);
++ bin_clear_free(ctx, sizeof(*ctx));
++}
++
++
++static EVP_MD_CTX * EVP_MD_CTX_new(void)
++{
++ EVP_MD_CTX *ctx;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (ctx)
++ EVP_MD_CTX_init(ctx);
++ return ctx;
++}
++
++
++static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
++{
++ if (!ctx)
++ return;
++ EVP_MD_CTX_cleanup(ctx);
++ bin_clear_free(ctx, sizeof(*ctx));
++}
++
++#endif /* OpenSSL version < 1.1.0 */
++
+ static BIGNUM * get_group5_prime(void)
+ {
+-#ifdef OPENSSL_IS_BORINGSSL
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
++ !(defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
++ return BN_get_rfc3526_prime_1536(NULL);
++#elif !defined(OPENSSL_IS_BORINGSSL)
++ return get_rfc3526_prime_1536(NULL);
++#else
+ static const unsigned char RFC3526_PRIME_1536[] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+ 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+@@ -53,41 +109,76 @@
+ 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ };
+ return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+-#else /* OPENSSL_IS_BORINGSSL */
+- return get_rfc3526_prime_1536(NULL);
+-#endif /* OPENSSL_IS_BORINGSSL */
++#endif
+ }
+
++
++static BIGNUM * get_group5_order(void)
++{
++ static const unsigned char RFC3526_ORDER_1536[] = {
++ 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51,
++ 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68,
++ 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53,
++ 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E,
++ 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36,
++ 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22,
++ 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74,
++ 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6,
++ 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08,
++ 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E,
++ 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B,
++ 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF,
++ 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB,
++ 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36,
++ 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04,
++ 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
++ };
++ return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
++}
++
++
+ #ifdef OPENSSL_NO_SHA256
+ #define NO_SHA256_WRAPPER
+ #endif
++#ifdef OPENSSL_NO_SHA512
++#define NO_SHA384_WRAPPER
++#endif
+
+ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+ {
+- EVP_MD_CTX ctx;
++ EVP_MD_CTX *ctx;
+ size_t i;
+ unsigned int mac_len;
+
+- EVP_MD_CTX_init(&ctx);
+- if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
++ if (TEST_FAIL())
++ return -1;
++
++ ctx = EVP_MD_CTX_new();
++ if (!ctx)
++ return -1;
++ if (!EVP_DigestInit_ex(ctx, type, NULL)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
++ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+ for (i = 0; i < num_elem; i++) {
+- if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
++ if (!EVP_DigestUpdate(ctx, addr[i], len[i])) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+ "failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
++ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+ }
+- if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
++ if (!EVP_DigestFinal(ctx, mac, &mac_len)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
++ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
++ EVP_MD_CTX_free(ctx);
+
+ return 0;
+ }
+@@ -101,7 +192,7 @@
+ #endif /* CONFIG_FIPS */
+
+
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+ {
+ u8 pkey[8], next, tmp;
+ int i;
+@@ -119,6 +210,7 @@
+ DES_set_key((DES_cblock *) &pkey, &ks);
+ DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+ DES_ENCRYPT);
++ return 0;
+ }
+
+
+@@ -129,16 +221,17 @@
+ #ifdef OPENSSL_NO_RC4
+ return -1;
+ #else /* OPENSSL_NO_RC4 */
+- EVP_CIPHER_CTX ctx;
++ EVP_CIPHER_CTX *ctx;
+ int outl;
+ int res = -1;
+ unsigned char skip_buf[16];
+
+- EVP_CIPHER_CTX_init(&ctx);
+- if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+- !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+- !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
+- !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
++ ctx = EVP_CIPHER_CTX_new();
++ if (!ctx ||
++ !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
++ !EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
++ !EVP_CIPHER_CTX_set_key_length(ctx, keylen) ||
++ !EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1))
+ goto out;
+
+ while (skip >= sizeof(skip_buf)) {
+@@ -145,16 +238,17 @@
+ size_t len = skip;
+ if (len > sizeof(skip_buf))
+ len = sizeof(skip_buf);
+- if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
++ if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len))
+ goto out;
+ skip -= len;
+ }
+
+- if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
++ if (EVP_CipherUpdate(ctx, data, &outl, data, data_len))
+ res = 0;
+
+ out:
+- EVP_CIPHER_CTX_cleanup(&ctx);
++ if (ctx)
++ EVP_CIPHER_CTX_free(ctx);
+ return res;
+ #endif /* OPENSSL_NO_RC4 */
+ }
+@@ -184,15 +278,31 @@
+ #endif /* NO_SHA256_WRAPPER */
+
+
++#ifndef NO_SHA384_WRAPPER
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac);
++}
++#endif /* NO_SHA384_WRAPPER */
++
++
++#ifndef NO_SHA512_WRAPPER
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
++}
++#endif /* NO_SHA512_WRAPPER */
++
++
+ static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+ {
+ switch (keylen) {
+ case 16:
+ return EVP_aes_128_ecb();
+-#ifndef OPENSSL_IS_BORINGSSL
+ case 24:
+ return EVP_aes_192_ecb();
+-#endif /* OPENSSL_IS_BORINGSSL */
+ case 32:
+ return EVP_aes_256_ecb();
+ }
+@@ -206,14 +316,19 @@
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
++ if (TEST_FAIL())
++ return NULL;
++
+ type = aes_get_evp_cipher(len);
+- if (type == NULL)
++ if (!type) {
++ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
++ __func__, (unsigned int) len);
+ return NULL;
++ }
+
+- ctx = os_malloc(sizeof(*ctx));
++ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ return NULL;
+- EVP_CIPHER_CTX_init(ctx);
+ if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ os_free(ctx);
+ return NULL;
+@@ -223,7 +338,7 @@
+ }
+
+
+-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+ {
+ EVP_CIPHER_CTX *c = ctx;
+ int clen = 16;
+@@ -230,7 +345,9 @@
+ if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
++ return -1;
+ }
++ return 0;
+ }
+
+
+@@ -247,8 +364,7 @@
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES encrypt", len);
+ }
+- EVP_CIPHER_CTX_cleanup(c);
+- bin_clear_free(c, sizeof(*c));
++ EVP_CIPHER_CTX_free(c);
+ }
+
+
+@@ -257,16 +373,21 @@
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
++ if (TEST_FAIL())
++ return NULL;
++
+ type = aes_get_evp_cipher(len);
+- if (type == NULL)
++ if (!type) {
++ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
++ __func__, (unsigned int) len);
+ return NULL;
++ }
+
+- ctx = os_malloc(sizeof(*ctx));
++ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ return NULL;
+- EVP_CIPHER_CTX_init(ctx);
+ if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+- os_free(ctx);
++ EVP_CIPHER_CTX_free(ctx);
+ return NULL;
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+@@ -274,7 +395,7 @@
+ }
+
+
+-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+ {
+ EVP_CIPHER_CTX *c = ctx;
+ int plen = 16;
+@@ -281,7 +402,9 @@
+ if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
++ return -1;
+ }
++ return 0;
+ }
+
+
+@@ -298,8 +421,7 @@
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES decrypt", len);
+ }
+- EVP_CIPHER_CTX_cleanup(c);
+- bin_clear_free(c, sizeof(*c));
++ EVP_CIPHER_CTX_free(c);
+ }
+
+
+@@ -311,6 +433,8 @@
+ AES_KEY actx;
+ int res;
+
++ if (TEST_FAIL())
++ return -1;
+ if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+@@ -325,6 +449,8 @@
+ AES_KEY actx;
+ int res;
+
++ if (TEST_FAIL())
++ return -1;
+ if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+@@ -338,54 +464,128 @@
+
+ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+ {
+- EVP_CIPHER_CTX ctx;
++ EVP_CIPHER_CTX *ctx;
+ int clen, len;
+ u8 buf[16];
++ int res = -1;
+
+- EVP_CIPHER_CTX_init(&ctx);
+- if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
++ if (TEST_FAIL())
+ return -1;
+- EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
++ ctx = EVP_CIPHER_CTX_new();
++ if (!ctx)
++ return -1;
+ clen = data_len;
+- if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 ||
+- clen != (int) data_len)
+- return -1;
+-
+ len = sizeof(buf);
+- if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+- return -1;
+- EVP_CIPHER_CTX_cleanup(&ctx);
++ if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
++ EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
++ EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 &&
++ clen == (int) data_len &&
++ EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
++ res = 0;
++ EVP_CIPHER_CTX_free(ctx);
+
+- return 0;
++ return res;
+ }
+
+
+ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+ {
+- EVP_CIPHER_CTX ctx;
++ EVP_CIPHER_CTX *ctx;
+ int plen, len;
+ u8 buf[16];
++ int res = -1;
+
+- EVP_CIPHER_CTX_init(&ctx);
+- if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
++ if (TEST_FAIL())
+ return -1;
+- EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
++ ctx = EVP_CIPHER_CTX_new();
++ if (!ctx)
++ return -1;
+ plen = data_len;
+- if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 ||
+- plen != (int) data_len)
++ len = sizeof(buf);
++ if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
++ EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
++ EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 &&
++ plen == (int) data_len &&
++ EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
++ res = 0;
++ EVP_CIPHER_CTX_free(ctx);
++
++ return res;
++
++}
++
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ size_t pubkey_len, pad;
++
++ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
++ if (os_memcmp(privkey, prime, prime_len) > 0) {
++ /* Make sure private value is smaller than prime */
++ privkey[0] = 0;
++ }
+
+- len = sizeof(buf);
+- if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
++ pubkey_len = prime_len;
++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
++ pubkey, &pubkey_len) < 0)
+ return -1;
+- EVP_CIPHER_CTX_cleanup(&ctx);
++ if (pubkey_len < prime_len) {
++ pad = prime_len - pubkey_len;
++ os_memmove(pubkey + pad, pubkey, pubkey_len);
++ os_memset(pubkey, 0, pad);
++ }
+
+ return 0;
+ }
+
+
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ BIGNUM *pub, *p;
++ int res = -1;
++
++ pub = BN_bin2bn(pubkey, pubkey_len, NULL);
++ p = BN_bin2bn(prime, prime_len, NULL);
++ if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) ||
++ BN_cmp(pub, p) >= 0)
++ goto fail;
++
++ if (order) {
++ BN_CTX *ctx;
++ BIGNUM *q, *tmp;
++ int failed;
++
++ /* verify: pubkey^q == 1 mod p */
++ q = BN_bin2bn(order, order_len, NULL);
++ ctx = BN_CTX_new();
++ tmp = BN_new();
++ failed = !q || !ctx || !tmp ||
++ !BN_mod_exp(tmp, pub, q, p, ctx) ||
++ !BN_is_one(tmp);
++ BN_clear(q);
++ BN_clear(tmp);
++ BN_CTX_free(ctx);
++ if (failed)
++ goto fail;
++ }
++
++ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
++ prime, prime_len, secret, len);
++fail:
++ BN_clear(pub);
++ BN_clear(p);
++ return res;
++}
++
++
+ int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+@@ -408,7 +608,8 @@
+ bn_result == NULL)
+ goto error;
+
+- if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
++ if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
++ ctx, NULL) != 1)
+ goto error;
+
+ *result_len = BN_bn2bin(bn_result, result);
+@@ -425,8 +626,8 @@
+
+
+ struct crypto_cipher {
+- EVP_CIPHER_CTX enc;
+- EVP_CIPHER_CTX dec;
++ EVP_CIPHER_CTX *enc;
++ EVP_CIPHER_CTX *dec;
+ };
+
+
+@@ -487,23 +688,25 @@
+ return NULL;
+ }
+
+- EVP_CIPHER_CTX_init(&ctx->enc);
+- EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
+- if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
+- !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
+- !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
+- EVP_CIPHER_CTX_cleanup(&ctx->enc);
++ if (!(ctx->enc = EVP_CIPHER_CTX_new()) ||
++ !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) ||
++ !EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) ||
++ !EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) ||
++ !EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) {
++ if (ctx->enc)
++ EVP_CIPHER_CTX_free(ctx->enc);
+ os_free(ctx);
+ return NULL;
+ }
+
+- EVP_CIPHER_CTX_init(&ctx->dec);
+- EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
+- if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
+- !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
+- !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
+- EVP_CIPHER_CTX_cleanup(&ctx->enc);
+- EVP_CIPHER_CTX_cleanup(&ctx->dec);
++ if (!(ctx->dec = EVP_CIPHER_CTX_new()) ||
++ !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) ||
++ !EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) ||
++ !EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) ||
++ !EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) {
++ EVP_CIPHER_CTX_free(ctx->enc);
++ if (ctx->dec)
++ EVP_CIPHER_CTX_free(ctx->dec);
+ os_free(ctx);
+ return NULL;
+ }
+@@ -516,7 +719,7 @@
+ u8 *crypt, size_t len)
+ {
+ int outl;
+- if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
++ if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len))
+ return -1;
+ return 0;
+ }
+@@ -527,7 +730,7 @@
+ {
+ int outl;
+ outl = len;
+- if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
++ if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len))
+ return -1;
+ return 0;
+ }
+@@ -535,8 +738,8 @@
+
+ void crypto_cipher_deinit(struct crypto_cipher *ctx)
+ {
+- EVP_CIPHER_CTX_cleanup(&ctx->enc);
+- EVP_CIPHER_CTX_cleanup(&ctx->dec);
++ EVP_CIPHER_CTX_free(ctx->enc);
++ EVP_CIPHER_CTX_free(ctx->dec);
+ os_free(ctx);
+ }
+
+@@ -543,11 +746,15 @@
+
+ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+ {
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ DH *dh;
+ struct wpabuf *pubkey = NULL, *privkey = NULL;
+ size_t publen, privlen;
+
+ *priv = NULL;
++ wpabuf_free(*publ);
+ *publ = NULL;
+
+ dh = DH_new();
+@@ -562,6 +769,10 @@
+ if (dh->p == NULL)
+ goto err;
+
++ dh->q = get_group5_order();
++ if (!dh->q)
++ goto err;
++
+ if (DH_generate_key(dh) != 1)
+ goto err;
+
+@@ -586,11 +797,68 @@
+ wpabuf_clear_free(privkey);
+ DH_free(dh);
+ return NULL;
++#else
++ DH *dh;
++ struct wpabuf *pubkey = NULL, *privkey = NULL;
++ size_t publen, privlen;
++ BIGNUM *p, *g, *q;
++ const BIGNUM *priv_key = NULL, *pub_key = NULL;
++
++ *priv = NULL;
++ wpabuf_free(*publ);
++ *publ = NULL;
++
++ dh = DH_new();
++ if (dh == NULL)
++ return NULL;
++
++ g = BN_new();
++ p = get_group5_prime();
++ q = get_group5_order();
++ if (!g || BN_set_word(g, 2) != 1 || !p || !q ||
++ DH_set0_pqg(dh, p, q, g) != 1)
++ goto err;
++ p = NULL;
++ q = NULL;
++ g = NULL;
++
++ if (DH_generate_key(dh) != 1)
++ goto err;
++
++ DH_get0_key(dh, &pub_key, &priv_key);
++ publen = BN_num_bytes(pub_key);
++ pubkey = wpabuf_alloc(publen);
++ if (!pubkey)
++ goto err;
++ privlen = BN_num_bytes(priv_key);
++ privkey = wpabuf_alloc(privlen);
++ if (!privkey)
++ goto err;
++
++ BN_bn2bin(pub_key, wpabuf_put(pubkey, publen));
++ BN_bn2bin(priv_key, wpabuf_put(privkey, privlen));
++
++ *priv = privkey;
++ *publ = pubkey;
++ return dh;
++
++err:
++ BN_free(p);
++ BN_free(q);
++ BN_free(g);
++ wpabuf_clear_free(pubkey);
++ wpabuf_clear_free(privkey);
++ DH_free(dh);
++ return NULL;
++#endif
+ }
+
+
+ void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+ {
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ DH *dh;
+
+ dh = DH_new();
+@@ -621,6 +889,42 @@
+ err:
+ DH_free(dh);
+ return NULL;
++#else
++ DH *dh;
++ BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL;
++
++ dh = DH_new();
++ if (dh == NULL)
++ return NULL;
++
++ g = BN_new();
++ p = get_group5_prime();
++ if (!g || BN_set_word(g, 2) != 1 || !p ||
++ DH_set0_pqg(dh, p, NULL, g) != 1)
++ goto err;
++ p = NULL;
++ g = NULL;
++
++ priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
++ pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
++ if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1)
++ goto err;
++ pub_key = NULL;
++ priv_key = NULL;
++
++ if (DH_generate_key(dh) != 1)
++ goto err;
++
++ return dh;
++
++err:
++ BN_free(p);
++ BN_free(g);
++ BN_free(pub_key);
++ BN_clear_free(priv_key);
++ DH_free(dh);
++ return NULL;
++#endif
+ }
+
+
+@@ -672,7 +976,7 @@
+
+
+ struct crypto_hash {
+- HMAC_CTX ctx;
++ HMAC_CTX *ctx;
+ };
+
+
+@@ -707,16 +1011,17 @@
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+- HMAC_CTX_init(&ctx->ctx);
++ ctx->ctx = HMAC_CTX_new();
++ if (!ctx->ctx) {
++ os_free(ctx);
++ return NULL;
++ }
+
+-#if OPENSSL_VERSION_NUMBER < 0x00909000
+- HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+-#else /* openssl < 0.9.9 */
+- if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
++ if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) {
++ HMAC_CTX_free(ctx->ctx);
+ bin_clear_free(ctx, sizeof(*ctx));
+ return NULL;
+ }
+-#endif /* openssl < 0.9.9 */
+
+ return ctx;
+ }
+@@ -726,7 +1031,7 @@
+ {
+ if (ctx == NULL)
+ return;
+- HMAC_Update(&ctx->ctx, data, len);
++ HMAC_Update(ctx->ctx, data, len);
+ }
+
+
+@@ -739,20 +1044,19 @@
+ return -2;
+
+ if (mac == NULL || len == NULL) {
++ HMAC_CTX_free(ctx->ctx);
+ bin_clear_free(ctx, sizeof(*ctx));
+ return 0;
+ }
+
+ mdlen = *len;
+-#if OPENSSL_VERSION_NUMBER < 0x00909000
+- HMAC_Final(&ctx->ctx, mac, &mdlen);
+- res = 1;
+-#else /* openssl < 0.9.9 */
+- res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+-#endif /* openssl < 0.9.9 */
+- HMAC_CTX_cleanup(&ctx->ctx);
++ res = HMAC_Final(ctx->ctx, mac, &mdlen);
++ HMAC_CTX_free(ctx->ctx);
+ bin_clear_free(ctx, sizeof(*ctx));
+
++ if (TEST_FAIL())
++ return -1;
++
+ if (res == 1) {
+ *len = mdlen;
+ return 0;
+@@ -767,28 +1071,26 @@
+ const u8 *addr[], const size_t *len, u8 *mac,
+ unsigned int mdlen)
+ {
+- HMAC_CTX ctx;
++ HMAC_CTX *ctx;
+ size_t i;
+ int res;
+
+- HMAC_CTX_init(&ctx);
+-#if OPENSSL_VERSION_NUMBER < 0x00909000
+- HMAC_Init_ex(&ctx, key, key_len, type, NULL);
+-#else /* openssl < 0.9.9 */
+- if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
++ if (TEST_FAIL())
+ return -1;
+-#endif /* openssl < 0.9.9 */
+
++ ctx = HMAC_CTX_new();
++ if (!ctx)
++ return -1;
++ res = HMAC_Init_ex(ctx, key, key_len, type, NULL);
++ if (res != 1)
++ goto done;
++
+ for (i = 0; i < num_elem; i++)
+- HMAC_Update(&ctx, addr[i], len[i]);
++ HMAC_Update(ctx, addr[i], len[i]);
+
+-#if OPENSSL_VERSION_NUMBER < 0x00909000
+- HMAC_Final(&ctx, mac, &mdlen);
+- res = 1;
+-#else /* openssl < 0.9.9 */
+- res = HMAC_Final(&ctx, mac, &mdlen);
+-#endif /* openssl < 0.9.9 */
+- HMAC_CTX_cleanup(&ctx);
++ res = HMAC_Final(ctx, mac, &mdlen);
++done:
++ HMAC_CTX_free(ctx);
+
+ return res == 1 ? 0 : -1;
+ }
+@@ -863,7 +1165,7 @@
+ const u8 *addr[], const size_t *len, u8 *mac)
+ {
+ return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+- len, mac, 32);
++ len, mac, 48);
+ }
+
+
+@@ -876,6 +1178,25 @@
+ #endif /* CONFIG_SHA384 */
+
+
++#ifdef CONFIG_SHA512
++
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
++ len, mac, 64);
++}
++
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA512 */
++
++
+ int crypto_get_random(void *buf, size_t len)
+ {
+ if (RAND_bytes(buf, len) != 1)
+@@ -892,6 +1213,9 @@
+ int ret = -1;
+ size_t outlen, i;
+
++ if (TEST_FAIL())
++ return -1;
++
+ ctx = CMAC_CTX_new();
+ if (ctx == NULL)
+ return -1;
+@@ -941,6 +1265,8 @@
+
+ struct crypto_bignum * crypto_bignum_init(void)
+ {
++ if (TEST_FAIL())
++ return NULL;
+ return (struct crypto_bignum *) BN_new();
+ }
+
+@@ -947,7 +1273,12 @@
+
+ struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+ {
+- BIGNUM *bn = BN_bin2bn(buf, len, NULL);
++ BIGNUM *bn;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ bn = BN_bin2bn(buf, len, NULL);
+ return (struct crypto_bignum *) bn;
+ }
+
+@@ -966,6 +1297,9 @@
+ {
+ int num_bytes, offset;
+
++ if (TEST_FAIL())
++ return -1;
++
+ if (padlen > buflen)
+ return -1;
+
+@@ -984,6 +1318,14 @@
+ }
+
+
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++ if (TEST_FAIL())
++ return -1;
++ return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
++}
++
++
+ int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+@@ -1019,11 +1361,15 @@
+ int res;
+ BN_CTX *bnctx;
+
++ if (TEST_FAIL())
++ return -1;
++
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+- res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+- (const BIGNUM *) c, bnctx);
++ res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
++ (const BIGNUM *) b, (const BIGNUM *) c,
++ bnctx, NULL);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+@@ -1037,9 +1383,16 @@
+ BIGNUM *res;
+ BN_CTX *bnctx;
+
++ if (TEST_FAIL())
++ return -1;
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
++#ifdef OPENSSL_IS_BORINGSSL
++ /* TODO: use BN_mod_inverse_blinded() ? */
++#else /* OPENSSL_IS_BORINGSSL */
++ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+@@ -1052,6 +1405,8 @@
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+ {
++ if (TEST_FAIL())
++ return -1;
+ return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+ 0 : -1;
+ }
+@@ -1065,9 +1420,15 @@
+
+ BN_CTX *bnctx;
+
++ if (TEST_FAIL())
++ return -1;
++
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
++#ifndef OPENSSL_IS_BORINGSSL
++ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+@@ -1085,6 +1446,9 @@
+
+ BN_CTX *bnctx;
+
++ if (TEST_FAIL())
++ return -1;
++
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+@@ -1096,6 +1460,15 @@
+ }
+
+
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++ struct crypto_bignum *r)
++{
++ /* Note: BN_rshift() does not modify the first argument even though it
++ * has not been marked const. */
++ return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
++}
++
++
+ int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b)
+ {
+@@ -1121,6 +1494,12 @@
+ }
+
+
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++ return BN_is_odd((const BIGNUM *) a);
++}
++
++
+ int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p)
+ {
+@@ -1127,7 +1506,11 @@
+ BN_CTX *bnctx;
+ BIGNUM *exp = NULL, *tmp = NULL;
+ int res = -2;
++ unsigned int mask;
+
++ if (TEST_FAIL())
++ return -2;
++
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -2;
+@@ -1138,16 +1521,17 @@
+ /* exp = (p-1) / 2 */
+ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+ !BN_rshift1(exp, exp) ||
+- !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+- bnctx))
++ !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
++ (const BIGNUM *) p, bnctx, NULL))
+ goto fail;
+
+- if (BN_is_word(tmp, 1))
+- res = 1;
+- else if (BN_is_zero(tmp))
+- res = 0;
+- else
+- res = -1;
++ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
++ * constant time selection to avoid branches here. */
++ res = -1;
++ mask = const_time_eq(BN_is_word(tmp, 1), 1);
++ res = const_time_select_int(mask, 1, res);
++ mask = const_time_eq(BN_is_zero(tmp), 1);
++ res = const_time_select_int(mask, 0, res);
+
+ fail:
+ BN_clear_free(tmp);
+@@ -1161,6 +1545,7 @@
+
+ struct crypto_ec {
+ EC_GROUP *group;
++ int nid;
+ BN_CTX *bnctx;
+ BIGNUM *prime;
+ BIGNUM *order;
+@@ -1218,6 +1603,7 @@
+ if (e == NULL)
+ return NULL;
+
++ e->nid = nid;
+ e->bnctx = BN_CTX_new();
+ e->group = EC_GROUP_new_by_curve_name(nid);
+ e->prime = BN_new();
+@@ -1252,6 +1638,8 @@
+
+ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+ {
++ if (TEST_FAIL())
++ return NULL;
+ if (e == NULL)
+ return NULL;
+ return (struct crypto_ec_point *) EC_POINT_new(e->group);
+@@ -1270,6 +1658,12 @@
+ }
+
+
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++ return BN_num_bytes(e->order);
++}
++
++
+ const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+ {
+ return (const struct crypto_bignum *) e->prime;
+@@ -1291,6 +1685,16 @@
+ }
+
+
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++ struct crypto_bignum *x)
++{
++ return EC_POINT_get_affine_coordinates_GFp(e->group,
++ (const EC_POINT *) p,
++ (BIGNUM *) x, NULL,
++ e->bnctx) == 1 ? 0 : -1;
++}
++
++
+ int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y)
+ {
+@@ -1298,6 +1702,9 @@
+ int ret = -1;
+ int len = BN_num_bytes(e->prime);
+
++ if (TEST_FAIL())
++ return -1;
++
+ x_bn = BN_new();
+ y_bn = BN_new();
+
+@@ -1328,6 +1735,9 @@
+ EC_POINT *elem;
+ int len = BN_num_bytes(e->prime);
+
++ if (TEST_FAIL())
++ return NULL;
++
+ x = BN_bin2bn(val, len, NULL);
+ y = BN_bin2bn(val + len, len, NULL);
+ elem = EC_POINT_new(e->group);
+@@ -1355,6 +1765,8 @@
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c)
+ {
++ if (TEST_FAIL())
++ return -1;
+ return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+ (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+ }
+@@ -1364,6 +1776,8 @@
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res)
+ {
++ if (TEST_FAIL())
++ return -1;
+ return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+ (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+ ? 0 : -1;
+@@ -1372,6 +1786,8 @@
+
+ int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+ {
++ if (TEST_FAIL())
++ return -1;
+ return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+ }
+
+@@ -1380,6 +1796,8 @@
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+ {
++ if (TEST_FAIL())
++ return -1;
+ if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+ (const BIGNUM *) x, y_bit,
+ e->bnctx) ||
+@@ -1395,6 +1813,9 @@
+ {
+ BIGNUM *tmp, *tmp2, *y_sqr = NULL;
+
++ if (TEST_FAIL())
++ return NULL;
++
+ tmp = BN_new();
+ tmp2 = BN_new();
+
+@@ -1439,4 +1860,228 @@
+ (const EC_POINT *) b, e->bnctx);
+ }
+
++
++struct crypto_ecdh {
++ struct crypto_ec *ec;
++ EVP_PKEY *pkey;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++ struct crypto_ecdh *ecdh;
++ EVP_PKEY *params = NULL;
++ EC_KEY *ec_params;
++ EVP_PKEY_CTX *kctx = NULL;
++
++ ecdh = os_zalloc(sizeof(*ecdh));
++ if (!ecdh)
++ goto fail;
++
++ ecdh->ec = crypto_ec_init(group);
++ if (!ecdh->ec)
++ goto fail;
++
++ ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
++ if (!ec_params) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Failed to generate EC_KEY parameters");
++ goto fail;
++ }
++ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
++ params = EVP_PKEY_new();
++ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Failed to generate EVP_PKEY parameters");
++ goto fail;
++ }
++
++ kctx = EVP_PKEY_CTX_new(params, NULL);
++ if (!kctx)
++ goto fail;
++
++ if (EVP_PKEY_keygen_init(kctx) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EVP_PKEY_keygen_init failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
++ wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++done:
++ EVP_PKEY_free(params);
++ EVP_PKEY_CTX_free(kctx);
++
++ return ecdh;
++fail:
++ crypto_ecdh_deinit(ecdh);
++ ecdh = NULL;
++ goto done;
++}
++
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++ struct wpabuf *buf = NULL;
++ EC_KEY *eckey;
++ const EC_POINT *pubkey;
++ BIGNUM *x, *y = NULL;
++ int len = BN_num_bytes(ecdh->ec->prime);
++ int res;
++
++ eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
++ if (!eckey)
++ return NULL;
++
++ pubkey = EC_KEY_get0_public_key(eckey);
++ if (!pubkey)
++ return NULL;
++
++ x = BN_new();
++ if (inc_y) {
++ y = BN_new();
++ if (!y)
++ goto fail;
++ }
++ buf = wpabuf_alloc(inc_y ? 2 * len : len);
++ if (!x || !buf)
++ goto fail;
++
++ if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
++ x, y, ecdh->ec->bnctx) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ res = crypto_bignum_to_bin((struct crypto_bignum *) x,
++ wpabuf_put(buf, len), len, len);
++ if (res < 0)
++ goto fail;
++
++ if (inc_y) {
++ res = crypto_bignum_to_bin((struct crypto_bignum *) y,
++ wpabuf_put(buf, len), len, len);
++ if (res < 0)
++ goto fail;
++ }
++
++done:
++ BN_clear_free(x);
++ BN_clear_free(y);
++ EC_KEY_free(eckey);
++
++ return buf;
++fail:
++ wpabuf_free(buf);
++ buf = NULL;
++ goto done;
++}
++
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++ const u8 *key, size_t len)
++{
++ BIGNUM *x, *y = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ EVP_PKEY *peerkey = NULL;
++ struct wpabuf *secret = NULL;
++ size_t secret_len;
++ EC_POINT *pub;
++ EC_KEY *eckey = NULL;
++
++ x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
++ pub = EC_POINT_new(ecdh->ec->group);
++ if (!x || !pub)
++ goto fail;
++
++ if (inc_y) {
++ y = BN_bin2bn(key + len / 2, len / 2, NULL);
++ if (!y)
++ goto fail;
++ if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
++ x, y,
++ ecdh->ec->bnctx)) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++ } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
++ pub, x, 0,
++ ecdh->ec->bnctx)) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: ECDH peer public key is not on curve");
++ goto fail;
++ }
++
++ eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
++ if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EC_KEY_set_public_key failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ peerkey = EVP_PKEY_new();
++ if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
++ goto fail;
++
++ ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
++ if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
++ EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
++ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EVP_PKEY_derive(1) failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++ secret = wpabuf_alloc(secret_len);
++ if (!secret)
++ goto fail;
++ if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len),
++ &secret_len) != 1) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EVP_PKEY_derive(2) failed: %s",
++ ERR_error_string(ERR_get_error(), NULL));
++ goto fail;
++ }
++
++done:
++ BN_free(x);
++ BN_free(y);
++ EC_KEY_free(eckey);
++ EC_POINT_free(pub);
++ EVP_PKEY_CTX_free(ctx);
++ EVP_PKEY_free(peerkey);
++ return secret;
++fail:
++ wpabuf_free(secret);
++ secret = NULL;
++ goto done;
++}
++
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++ if (ecdh) {
++ crypto_ec_deinit(ecdh->ec);
++ EVP_PKEY_free(ecdh->pkey);
++ os_free(ecdh);
++ }
++}
++
+ #endif /* CONFIG_ECC */
+--- contrib/wpa/src/crypto/crypto_wolfssl.c.orig
++++ contrib/wpa/src/crypto/crypto_wolfssl.c
+@@ -0,0 +1,1786 @@
++/*
++ * Wrapper functions for libwolfssl
++ * Copyright (c) 2004-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "crypto.h"
++
++/* wolfSSL headers */
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++
++#ifndef CONFIG_FIPS
++
++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ Md4 md4;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitMd4(&md4);
++
++ for (i = 0; i < num_elem; i++)
++ wc_Md4Update(&md4, addr[i], len[i]);
++
++ wc_Md4Final(&md4, mac);
++
++ return 0;
++}
++
++
++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ wc_Md5 md5;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitMd5(&md5);
++
++ for (i = 0; i < num_elem; i++)
++ wc_Md5Update(&md5, addr[i], len[i]);
++
++ wc_Md5Final(&md5, mac);
++
++ return 0;
++}
++
++#endif /* CONFIG_FIPS */
++
++
++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
++{
++ wc_Sha sha;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitSha(&sha);
++
++ for (i = 0; i < num_elem; i++)
++ wc_ShaUpdate(&sha, addr[i], len[i]);
++
++ wc_ShaFinal(&sha, mac);
++
++ return 0;
++}
++
++
++#ifndef NO_SHA256_WRAPPER
++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ wc_Sha256 sha256;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitSha256(&sha256);
++
++ for (i = 0; i < num_elem; i++)
++ wc_Sha256Update(&sha256, addr[i], len[i]);
++
++ wc_Sha256Final(&sha256, mac);
++
++ return 0;
++}
++#endif /* NO_SHA256_WRAPPER */
++
++
++#ifdef CONFIG_SHA384
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ wc_Sha384 sha384;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitSha384(&sha384);
++
++ for (i = 0; i < num_elem; i++)
++ wc_Sha384Update(&sha384, addr[i], len[i]);
++
++ wc_Sha384Final(&sha384, mac);
++
++ return 0;
++}
++#endif /* CONFIG_SHA384 */
++
++
++#ifdef CONFIG_SHA512
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ wc_Sha512 sha512;
++ size_t i;
++
++ if (TEST_FAIL())
++ return -1;
++
++ wc_InitSha512(&sha512);
++
++ for (i = 0; i < num_elem; i++)
++ wc_Sha512Update(&sha512, addr[i], len[i]);
++
++ wc_Sha512Final(&sha512, mac);
++
++ return 0;
++}
++#endif /* CONFIG_SHA512 */
++
++
++static int wolfssl_hmac_vector(int type, const u8 *key,
++ size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac,
++ unsigned int mdlen)
++{
++ Hmac hmac;
++ size_t i;
++
++ (void) mdlen;
++
++ if (TEST_FAIL())
++ return -1;
++
++ if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
++ return -1;
++ for (i = 0; i < num_elem; i++)
++ if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
++ return -1;
++ if (wc_HmacFinal(&hmac, mac) != 0)
++ return -1;
++ return 0;
++}
++
++
++#ifndef CONFIG_FIPS
++
++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
++ mac, 16);
++}
++
++
++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_FIPS */
++
++
++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
++ mac, 20);
++}
++
++
++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
++ u8 *mac)
++{
++ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++
++#ifdef CONFIG_SHA256
++
++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
++ mac, 32);
++}
++
++
++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA256 */
++
++
++#ifdef CONFIG_SHA384
++
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
++ mac, 48);
++}
++
++
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA384 */
++
++
++#ifdef CONFIG_SHA512
++
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
++ mac, 64);
++}
++
++
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
++}
++
++#endif /* CONFIG_SHA512 */
++
++
++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
++ int iterations, u8 *buf, size_t buflen)
++{
++ if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
++ ssid_len, iterations, buflen, WC_SHA) != 0)
++ return -1;
++ return 0;
++}
++
++
++#ifdef CONFIG_DES
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++{
++ Des des;
++ u8 pkey[8], next, tmp;
++ int i;
++
++ /* Add parity bits to the key */
++ next = 0;
++ for (i = 0; i < 7; i++) {
++ tmp = key[i];
++ pkey[i] = (tmp >> i) | next | 1;
++ next = tmp << (7 - i);
++ }
++ pkey[i] = next | 1;
++
++ wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
++ wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
++
++ return 0;
++}
++#endif /* CONFIG_DES */
++
++
++void * aes_encrypt_init(const u8 *key, size_t len)
++{
++ Aes *aes;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ aes = os_malloc(sizeof(Aes));
++ if (!aes)
++ return NULL;
++
++ if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
++ os_free(aes);
++ return NULL;
++ }
++
++ return aes;
++}
++
++
++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
++{
++ wc_AesEncryptDirect(ctx, crypt, plain);
++ return 0;
++}
++
++
++void aes_encrypt_deinit(void *ctx)
++{
++ os_free(ctx);
++}
++
++
++void * aes_decrypt_init(const u8 *key, size_t len)
++{
++ Aes *aes;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ aes = os_malloc(sizeof(Aes));
++ if (!aes)
++ return NULL;
++
++ if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
++ os_free(aes);
++ return NULL;
++ }
++
++ return aes;
++}
++
++
++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
++{
++ wc_AesDecryptDirect(ctx, plain, crypt);
++ return 0;
++}
++
++
++void aes_decrypt_deinit(void *ctx)
++{
++ os_free(ctx);
++}
++
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ Aes aes;
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
++ if (ret != 0)
++ return -1;
++
++ ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
++ if (ret != 0)
++ return -1;
++ return 0;
++}
++
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++ Aes aes;
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
++ if (ret != 0)
++ return -1;
++
++ ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
++ if (ret != 0)
++ return -1;
++ return 0;
++}
++
++
++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
++{
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
++ NULL);
++ return ret != (n + 1) * 8 ? -1 : 0;
++}
++
++
++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
++ u8 *plain)
++{
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
++ NULL);
++ return ret != n * 8 ? -1 : 0;
++}
++
++
++#ifndef CONFIG_NO_RC4
++int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
++ size_t data_len)
++{
++#ifndef NO_RC4
++ Arc4 arc4;
++ unsigned char skip_buf[16];
++
++ wc_Arc4SetKey(&arc4, key, keylen);
++
++ while (skip >= sizeof(skip_buf)) {
++ size_t len = skip;
++
++ if (len > sizeof(skip_buf))
++ len = sizeof(skip_buf);
++ wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
++ skip -= len;
++ }
++
++ wc_Arc4Process(&arc4, data, data, data_len);
++
++ return 0;
++#else /* NO_RC4 */
++ return -1;
++#endif /* NO_RC4 */
++}
++#endif /* CONFIG_NO_RC4 */
++
++
++#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
++ || defined(EAP_SERVER_IKEV2)
++union wolfssl_cipher {
++ Aes aes;
++ Des3 des3;
++ Arc4 arc4;
++};
++
++struct crypto_cipher {
++ enum crypto_cipher_alg alg;
++ union wolfssl_cipher enc;
++ union wolfssl_cipher dec;
++};
++
++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
++ const u8 *iv, const u8 *key,
++ size_t key_len)
++{
++ struct crypto_cipher *ctx;
++
++ ctx = os_zalloc(sizeof(*ctx));
++ if (!ctx)
++ return NULL;
++
++ switch (alg) {
++#ifndef CONFIG_NO_RC4
++#ifndef NO_RC4
++ case CRYPTO_CIPHER_ALG_RC4:
++ wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
++ wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
++ break;
++#endif /* NO_RC4 */
++#endif /* CONFIG_NO_RC4 */
++#ifndef NO_AES
++ case CRYPTO_CIPHER_ALG_AES:
++ switch (key_len) {
++ case 16:
++ case 24:
++ case 32:
++ break;
++ default:
++ os_free(ctx);
++ return NULL;
++ }
++ if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
++ AES_ENCRYPTION) ||
++ wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
++ AES_DECRYPTION)) {
++ os_free(ctx);
++ return NULL;
++ }
++ break;
++#endif /* NO_AES */
++#ifndef NO_DES3
++ case CRYPTO_CIPHER_ALG_3DES:
++ if (key_len != DES3_KEYLEN ||
++ wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
++ wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
++ os_free(ctx);
++ return NULL;
++ }
++ break;
++#endif /* NO_DES3 */
++ case CRYPTO_CIPHER_ALG_RC2:
++ case CRYPTO_CIPHER_ALG_DES:
++ default:
++ os_free(ctx);
++ return NULL;
++ }
++
++ ctx->alg = alg;
++
++ return ctx;
++}
++
++
++int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
++ u8 *crypt, size_t len)
++{
++ switch (ctx->alg) {
++#ifndef CONFIG_NO_RC4
++#ifndef NO_RC4
++ case CRYPTO_CIPHER_ALG_RC4:
++ wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
++ return 0;
++#endif /* NO_RC4 */
++#endif /* CONFIG_NO_RC4 */
++#ifndef NO_AES
++ case CRYPTO_CIPHER_ALG_AES:
++ if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
++ return -1;
++ return 0;
++#endif /* NO_AES */
++#ifndef NO_DES3
++ case CRYPTO_CIPHER_ALG_3DES:
++ if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
++ return -1;
++ return 0;
++#endif /* NO_DES3 */
++ default:
++ return -1;
++ }
++ return -1;
++}
++
++
++int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
++ u8 *plain, size_t len)
++{
++ switch (ctx->alg) {
++#ifndef CONFIG_NO_RC4
++#ifndef NO_RC4
++ case CRYPTO_CIPHER_ALG_RC4:
++ wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
++ return 0;
++#endif /* NO_RC4 */
++#endif /* CONFIG_NO_RC4 */
++#ifndef NO_AES
++ case CRYPTO_CIPHER_ALG_AES:
++ if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
++ return -1;
++ return 0;
++#endif /* NO_AES */
++#ifndef NO_DES3
++ case CRYPTO_CIPHER_ALG_3DES:
++ if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
++ return -1;
++ return 0;
++#endif /* NO_DES3 */
++ default:
++ return -1;
++ }
++ return -1;
++}
++
++
++void crypto_cipher_deinit(struct crypto_cipher *ctx)
++{
++ os_free(ctx);
++}
++
++#endif
++
++
++#ifdef CONFIG_WPS_NFC
++
++static const unsigned char RFC3526_PRIME_1536[] = {
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
++ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
++ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
++ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
++ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
++ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
++ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
++ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
++ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
++ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
++ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
++ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
++ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
++ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
++ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
++ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static const unsigned char RFC3526_GENERATOR_1536[] = {
++ 0x02
++};
++
++#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
++
++
++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
++{
++ WC_RNG rng;
++ DhKey *ret = NULL;
++ DhKey *dh = NULL;
++ struct wpabuf *privkey = NULL;
++ struct wpabuf *pubkey = NULL;
++ word32 priv_sz, pub_sz;
++
++ *priv = NULL;
++ wpabuf_free(*publ);
++ *publ = NULL;
++
++ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ if (!dh)
++ return NULL;
++ wc_InitDhKey(dh);
++
++ if (wc_InitRng(&rng) != 0) {
++ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ return NULL;
++ }
++
++ privkey = wpabuf_alloc(RFC3526_LEN);
++ pubkey = wpabuf_alloc(RFC3526_LEN);
++ if (!privkey || !pubkey)
++ goto done;
++
++ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
++ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
++ != 0)
++ goto done;
++
++ if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
++ wpabuf_mhead(pubkey), &pub_sz) != 0)
++ goto done;
++
++ wpabuf_put(privkey, priv_sz);
++ wpabuf_put(pubkey, pub_sz);
++
++ ret = dh;
++ *priv = privkey;
++ *publ = pubkey;
++ dh = NULL;
++ privkey = NULL;
++ pubkey = NULL;
++done:
++ wpabuf_clear_free(pubkey);
++ wpabuf_clear_free(privkey);
++ if (dh) {
++ wc_FreeDhKey(dh);
++ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ }
++ wc_FreeRng(&rng);
++ return ret;
++}
++
++
++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
++{
++ DhKey *ret = NULL;
++ DhKey *dh;
++ byte *secret;
++ word32 secret_sz;
++
++ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ if (!dh)
++ return NULL;
++ wc_InitDhKey(dh);
++
++ secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ if (!secret)
++ goto done;
++
++ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
++ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
++ != 0)
++ goto done;
++
++ if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
++ wpabuf_len(priv), RFC3526_GENERATOR_1536,
++ sizeof(RFC3526_GENERATOR_1536)) != 0)
++ goto done;
++
++ if (secret_sz != wpabuf_len(publ) ||
++ os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
++ goto done;
++
++ ret = dh;
++ dh = NULL;
++done:
++ if (dh) {
++ wc_FreeDhKey(dh);
++ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ }
++ XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ return ret;
++}
++
++
++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
++ const struct wpabuf *own_private)
++{
++ struct wpabuf *ret = NULL;
++ struct wpabuf *secret;
++ word32 secret_sz;
++
++ secret = wpabuf_alloc(RFC3526_LEN);
++ if (!secret)
++ goto done;
++
++ if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
++ wpabuf_head(own_private), wpabuf_len(own_private),
++ wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
++ goto done;
++
++ wpabuf_put(secret, secret_sz);
++
++ ret = secret;
++ secret = NULL;
++done:
++ wpabuf_clear_free(secret);
++ return ret;
++}
++
++
++void dh5_free(void *ctx)
++{
++ if (!ctx)
++ return;
++
++ wc_FreeDhKey(ctx);
++ XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++}
++
++#endif /* CONFIG_WPS_NFC */
++
++
++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
++ u8 *pubkey)
++{
++ int ret = -1;
++ WC_RNG rng;
++ DhKey *dh = NULL;
++ word32 priv_sz, pub_sz;
++
++ if (TEST_FAIL())
++ return -1;
++
++ dh = os_malloc(sizeof(DhKey));
++ if (!dh)
++ return -1;
++ wc_InitDhKey(dh);
++
++ if (wc_InitRng(&rng) != 0) {
++ os_free(dh);
++ return -1;
++ }
++
++ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
++ goto done;
++
++ if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
++ != 0)
++ goto done;
++
++ if (priv_sz < prime_len) {
++ size_t pad_sz = prime_len - priv_sz;
++
++ os_memmove(privkey + pad_sz, privkey, priv_sz);
++ os_memset(privkey, 0, pad_sz);
++ }
++
++ if (pub_sz < prime_len) {
++ size_t pad_sz = prime_len - pub_sz;
++
++ os_memmove(pubkey + pad_sz, pubkey, pub_sz);
++ os_memset(pubkey, 0, pad_sz);
++ }
++ ret = 0;
++done:
++ wc_FreeDhKey(dh);
++ os_free(dh);
++ wc_FreeRng(&rng);
++ return ret;
++}
++
++
++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
++ const u8 *order, size_t order_len,
++ const u8 *privkey, size_t privkey_len,
++ const u8 *pubkey, size_t pubkey_len,
++ u8 *secret, size_t *len)
++{
++ int ret = -1;
++ DhKey *dh;
++ word32 secret_sz;
++
++ dh = os_malloc(sizeof(DhKey));
++ if (!dh)
++ return -1;
++ wc_InitDhKey(dh);
++
++ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
++ goto done;
++
++ if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
++ pubkey_len) != 0)
++ goto done;
++
++ *len = secret_sz;
++ ret = 0;
++done:
++ wc_FreeDhKey(dh);
++ os_free(dh);
++ return ret;
++}
++
++
++#ifdef CONFIG_FIPS
++int crypto_get_random(void *buf, size_t len)
++{
++ int ret = 0;
++ WC_RNG rng;
++
++ if (wc_InitRng(&rng) != 0)
++ return -1;
++ if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
++ ret = -1;
++ wc_FreeRng(&rng);
++ return ret;
++}
++#endif /* CONFIG_FIPS */
++
++
++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
++struct crypto_hash {
++ Hmac hmac;
++ int size;
++};
++
++
++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
++ size_t key_len)
++{
++ struct crypto_hash *ret = NULL;
++ struct crypto_hash *hash;
++ int type;
++
++ hash = os_zalloc(sizeof(*hash));
++ if (!hash)
++ goto done;
++
++ switch (alg) {
++#ifndef NO_MD5
++ case CRYPTO_HASH_ALG_HMAC_MD5:
++ hash->size = 16;
++ type = WC_MD5;
++ break;
++#endif /* NO_MD5 */
++#ifndef NO_SHA
++ case CRYPTO_HASH_ALG_HMAC_SHA1:
++ type = WC_SHA;
++ hash->size = 20;
++ break;
++#endif /* NO_SHA */
++#ifdef CONFIG_SHA256
++#ifndef NO_SHA256
++ case CRYPTO_HASH_ALG_HMAC_SHA256:
++ type = WC_SHA256;
++ hash->size = 32;
++ break;
++#endif /* NO_SHA256 */
++#endif /* CONFIG_SHA256 */
++ default:
++ goto done;
++ }
++
++ if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
++ goto done;
++
++ ret = hash;
++ hash = NULL;
++done:
++ os_free(hash);
++ return ret;
++}
++
++
++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
++{
++ if (!ctx)
++ return;
++ wc_HmacUpdate(&ctx->hmac, data, len);
++}
++
++
++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
++{
++ int ret = 0;
++
++ if (!ctx)
++ return -2;
++
++ if (!mac || !len)
++ goto done;
++
++ if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
++ ret = -1;
++ goto done;
++ }
++
++ *len = ctx->size;
++ ret = 0;
++done:
++ bin_clear_free(ctx, sizeof(*ctx));
++ if (TEST_FAIL())
++ return -1;
++ return ret;
++}
++
++#endif
++
++
++int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ Cmac cmac;
++ size_t i;
++ word32 sz;
++
++ if (TEST_FAIL())
++ return -1;
++
++ if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
++ return -1;
++
++ for (i = 0; i < num_elem; i++)
++ if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
++ return -1;
++
++ sz = AES_BLOCK_SIZE;
++ if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
++ return -1;
++
++ return 0;
++}
++
++
++int omac1_aes_128_vector(const u8 *key, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
++}
++
++
++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
++}
++
++
++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
++{
++ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
++}
++
++
++struct crypto_bignum * crypto_bignum_init(void)
++{
++ mp_int *a;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ a = os_malloc(sizeof(*a));
++ if (!a || mp_init(a) != MP_OKAY) {
++ os_free(a);
++ a = NULL;
++ }
++
++ return (struct crypto_bignum *) a;
++}
++
++
++struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
++{
++ mp_int *a;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ a = (mp_int *) crypto_bignum_init();
++ if (!a)
++ return NULL;
++
++ if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
++ os_free(a);
++ a = NULL;
++ }
++
++ return (struct crypto_bignum *) a;
++}
++
++
++void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
++{
++ if (!n)
++ return;
++
++ if (clear)
++ mp_forcezero((mp_int *) n);
++ mp_clear((mp_int *) n);
++ os_free((mp_int *) n);
++}
++
++
++int crypto_bignum_to_bin(const struct crypto_bignum *a,
++ u8 *buf, size_t buflen, size_t padlen)
++{
++ int num_bytes, offset;
++
++ if (TEST_FAIL())
++ return -1;
++
++ if (padlen > buflen)
++ return -1;
++
++ num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
++ if ((size_t) num_bytes > buflen)
++ return -1;
++ if (padlen > (size_t) num_bytes)
++ offset = padlen - num_bytes;
++ else
++ offset = 0;
++
++ os_memset(buf, 0, offset);
++ mp_to_unsigned_bin((mp_int *) a, buf + offset);
++
++ return num_bytes + offset;
++}
++
++
++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
++{
++ int ret = 0;
++ WC_RNG rng;
++
++ if (TEST_FAIL())
++ return -1;
++ if (wc_InitRng(&rng) != 0)
++ return -1;
++ if (mp_rand_prime((mp_int *) r,
++ (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
++ &rng, NULL) != 0)
++ ret = -1;
++ if (ret == 0 &&
++ mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
++ ret = -1;
++ wc_FreeRng(&rng);
++ return ret;
++}
++
++
++int crypto_bignum_add(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *r)
++{
++ return mp_add((mp_int *) a, (mp_int *) b,
++ (mp_int *) r) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_mod(const struct crypto_bignum *a,
++ const struct crypto_bignum *m,
++ struct crypto_bignum *r)
++{
++ return mp_mod((mp_int *) a, (mp_int *) m,
++ (mp_int *) r) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_exptmod(const struct crypto_bignum *b,
++ const struct crypto_bignum *e,
++ const struct crypto_bignum *m,
++ struct crypto_bignum *r)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
++ (mp_int *) r) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_inverse(const struct crypto_bignum *a,
++ const struct crypto_bignum *m,
++ struct crypto_bignum *r)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mp_invmod((mp_int *) a, (mp_int *) m,
++ (mp_int *) r) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_sub(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *r)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mp_add((mp_int *) a, (mp_int *) b,
++ (mp_int *) r) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_div(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
++ NULL) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_mulmod(const struct crypto_bignum *a,
++ const struct crypto_bignum *b,
++ const struct crypto_bignum *m,
++ struct crypto_bignum *d)
++{
++ if (TEST_FAIL())
++ return -1;
++
++ return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
++ (mp_int *) d) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
++ struct crypto_bignum *r)
++{
++ if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
++ return -1;
++ mp_rshb((mp_int *) r, n);
++ return 0;
++}
++
++
++int crypto_bignum_cmp(const struct crypto_bignum *a,
++ const struct crypto_bignum *b)
++{
++ return mp_cmp((mp_int *) a, (mp_int *) b);
++}
++
++
++int crypto_bignum_bits(const struct crypto_bignum *a)
++{
++ return mp_count_bits((mp_int *) a);
++}
++
++
++int crypto_bignum_is_zero(const struct crypto_bignum *a)
++{
++ return mp_iszero((mp_int *) a);
++}
++
++
++int crypto_bignum_is_one(const struct crypto_bignum *a)
++{
++ return mp_isone((const mp_int *) a);
++}
++
++int crypto_bignum_is_odd(const struct crypto_bignum *a)
++{
++ return mp_isodd((mp_int *) a);
++}
++
++
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++ const struct crypto_bignum *p)
++{
++ mp_int t;
++ int ret;
++ int res = -2;
++
++ if (TEST_FAIL())
++ return -2;
++
++ if (mp_init(&t) != MP_OKAY)
++ return -2;
++
++ /* t = (p-1) / 2 */
++ ret = mp_sub_d((mp_int *) p, 1, &t);
++ if (ret == MP_OKAY)
++ mp_rshb(&t, 1);
++ if (ret == MP_OKAY)
++ ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
++ if (ret == MP_OKAY) {
++ if (mp_isone(&t))
++ res = 1;
++ else if (mp_iszero(&t))
++ res = 0;
++ else
++ res = -1;
++ }
++
++ mp_clear(&t);
++ return res;
++}
++
++
++#ifdef CONFIG_ECC
++
++int ecc_map(ecc_point *, mp_int *, mp_digit);
++int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
++ mp_int *a, mp_int *modulus, mp_digit mp);
++
++struct crypto_ec {
++ ecc_key key;
++ mp_int a;
++ mp_int prime;
++ mp_int order;
++ mp_digit mont_b;
++ mp_int b;
++};
++
++
++struct crypto_ec * crypto_ec_init(int group)
++{
++ int built = 0;
++ struct crypto_ec *e;
++ int curve_id;
++
++ /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
++ switch (group) {
++ case 19:
++ curve_id = ECC_SECP256R1;
++ break;
++ case 20:
++ curve_id = ECC_SECP384R1;
++ break;
++ case 21:
++ curve_id = ECC_SECP521R1;
++ break;
++ case 25:
++ curve_id = ECC_SECP192R1;
++ break;
++ case 26:
++ curve_id = ECC_SECP224R1;
++ break;
++#ifdef HAVE_ECC_BRAINPOOL
++ case 27:
++ curve_id = ECC_BRAINPOOLP224R1;
++ break;
++ case 28:
++ curve_id = ECC_BRAINPOOLP256R1;
++ break;
++ case 29:
++ curve_id = ECC_BRAINPOOLP384R1;
++ break;
++ case 30:
++ curve_id = ECC_BRAINPOOLP512R1;
++ break;
++#endif /* HAVE_ECC_BRAINPOOL */
++ default:
++ return NULL;
++ }
++
++ e = os_zalloc(sizeof(*e));
++ if (!e)
++ return NULL;
++
++ if (wc_ecc_init(&e->key) != 0 ||
++ wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
++ mp_init(&e->a) != MP_OKAY ||
++ mp_init(&e->prime) != MP_OKAY ||
++ mp_init(&e->order) != MP_OKAY ||
++ mp_init(&e->b) != MP_OKAY ||
++ mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
++ mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
++ mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
++ mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
++ mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
++ goto done;
++
++ built = 1;
++done:
++ if (!built) {
++ crypto_ec_deinit(e);
++ e = NULL;
++ }
++ return e;
++}
++
++
++void crypto_ec_deinit(struct crypto_ec* e)
++{
++ if (!e)
++ return;
++
++ mp_clear(&e->b);
++ mp_clear(&e->order);
++ mp_clear(&e->prime);
++ mp_clear(&e->a);
++ wc_ecc_free(&e->key);
++ os_free(e);
++}
++
++
++struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
++{
++ if (TEST_FAIL())
++ return NULL;
++ if (!e)
++ return NULL;
++ return (struct crypto_ec_point *) wc_ecc_new_point();
++}
++
++
++size_t crypto_ec_prime_len(struct crypto_ec *e)
++{
++ return (mp_count_bits(&e->prime) + 7) / 8;
++}
++
++
++size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
++{
++ return mp_count_bits(&e->prime);
++}
++
++
++size_t crypto_ec_order_len(struct crypto_ec *e)
++{
++ return (mp_count_bits(&e->order) + 7) / 8;
++}
++
++
++const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *) &e->prime;
++}
++
++
++const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
++{
++ return (const struct crypto_bignum *) &e->order;
++}
++
++
++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
++{
++ ecc_point *point = (ecc_point *) p;
++
++ if (!p)
++ return;
++
++ if (clear) {
++ mp_forcezero(point->x);
++ mp_forcezero(point->y);
++ mp_forcezero(point->z);
++ }
++ wc_ecc_del_point(point);
++}
++
++
++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
++ struct crypto_bignum *x)
++{
++ return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
++}
++
++
++int crypto_ec_point_to_bin(struct crypto_ec *e,
++ const struct crypto_ec_point *point, u8 *x, u8 *y)
++{
++ ecc_point *p = (ecc_point *) point;
++
++ if (TEST_FAIL())
++ return -1;
++
++ if (!mp_isone(p->z)) {
++ if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
++ return -1;
++ }
++
++ if (x) {
++ if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
++ e->key.dp->size,
++ e->key.dp->size) <= 0)
++ return -1;
++ }
++
++ if (y) {
++ if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
++ e->key.dp->size,
++ e->key.dp->size) <= 0)
++ return -1;
++ }
++
++ return 0;
++}
++
++
++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
++ const u8 *val)
++{
++ ecc_point *point = NULL;
++ int loaded = 0;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ point = wc_ecc_new_point();
++ if (!point)
++ goto done;
++
++ if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
++ goto done;
++ val += e->key.dp->size;
++ if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
++ goto done;
++ mp_set(point->z, 1);
++
++ loaded = 1;
++done:
++ if (!loaded) {
++ wc_ecc_del_point(point);
++ point = NULL;
++ }
++ return (struct crypto_ec_point *) point;
++}
++
++
++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b,
++ struct crypto_ec_point *c)
++{
++ mp_int mu;
++ ecc_point *ta = NULL, *tb = NULL;
++ ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
++ mp_int *modulus = &e->prime;
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = mp_init(&mu);
++ if (ret != MP_OKAY)
++ return -1;
++
++ ret = mp_montgomery_calc_normalization(&mu, modulus);
++ if (ret != MP_OKAY) {
++ mp_clear(&mu);
++ return -1;
++ }
++
++ if (!mp_isone(&mu)) {
++ ta = wc_ecc_new_point();
++ if (!ta) {
++ mp_clear(&mu);
++ return -1;
++ }
++ tb = wc_ecc_new_point();
++ if (!tb) {
++ wc_ecc_del_point(ta);
++ mp_clear(&mu);
++ return -1;
++ }
++
++ if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
++ mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
++ mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
++ mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
++ mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
++ mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
++ ret = -1;
++ goto end;
++ }
++ pa = ta;
++ pb = tb;
++ }
++
++ ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
++ &e->prime, e->mont_b);
++ if (ret != 0) {
++ ret = -1;
++ goto end;
++ }
++
++ if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
++ ret = -1;
++ else
++ ret = 0;
++end:
++ wc_ecc_del_point(tb);
++ wc_ecc_del_point(ta);
++ mp_clear(&mu);
++ return ret;
++}
++
++
++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
++ const struct crypto_bignum *b,
++ struct crypto_ec_point *res)
++{
++ int ret;
++
++ if (TEST_FAIL())
++ return -1;
++
++ ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
++ &e->a, &e->prime, 1);
++ return ret == 0 ? 0 : -1;
++}
++
++
++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
++{
++ ecc_point *point = (ecc_point *) p;
++
++ if (TEST_FAIL())
++ return -1;
++
++ if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
++ return -1;
++
++ return 0;
++}
++
++
++int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
++ struct crypto_ec_point *p,
++ const struct crypto_bignum *x, int y_bit)
++{
++ byte buf[1 + 2 * MAX_ECC_BYTES];
++ int ret;
++ int prime_len = crypto_ec_prime_len(e);
++
++ if (TEST_FAIL())
++ return -1;
++
++ buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
++ ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
++ if (ret <= 0)
++ return -1;
++ ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
++ (ecc_point *) p);
++ if (ret != 0)
++ return -1;
++
++ return 0;
++}
++
++
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++ const struct crypto_bignum *x)
++{
++ mp_int *y2 = NULL;
++ mp_int t;
++ int calced = 0;
++
++ if (TEST_FAIL())
++ return NULL;
++
++ if (mp_init(&t) != MP_OKAY)
++ return NULL;
++
++ y2 = (mp_int *) crypto_bignum_init();
++ if (!y2)
++ goto done;
++
++ if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
++ mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
++ mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
++ mp_addmod(y2, &t, &e->prime, y2) != 0 ||
++ mp_addmod(y2, &e->b, &e->prime, y2) != 0)
++ goto done;
++
++ calced = 1;
++done:
++ if (!calced) {
++ if (y2) {
++ mp_clear(y2);
++ os_free(y2);
++ }
++ mp_clear(&t);
++ }
++
++ return (struct crypto_bignum *) y2;
++}
++
++
++int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ return wc_ecc_point_is_at_infinity((ecc_point *) p);
++}
++
++
++int crypto_ec_point_is_on_curve(struct crypto_ec *e,
++ const struct crypto_ec_point *p)
++{
++ return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
++ MP_OKAY;
++}
++
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++ const struct crypto_ec_point *a,
++ const struct crypto_ec_point *b)
++{
++ return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
++}
++
++
++struct crypto_ecdh {
++ struct crypto_ec *ec;
++};
++
++struct crypto_ecdh * crypto_ecdh_init(int group)
++{
++ struct crypto_ecdh *ecdh = NULL;
++ WC_RNG rng;
++ int ret;
++
++ if (wc_InitRng(&rng) != 0)
++ goto fail;
++
++ ecdh = os_zalloc(sizeof(*ecdh));
++ if (!ecdh)
++ goto fail;
++
++ ecdh->ec = crypto_ec_init(group);
++ if (!ecdh->ec)
++ goto fail;
++
++ ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
++ ecdh->ec->key.dp->id);
++ if (ret < 0)
++ goto fail;
++
++done:
++ wc_FreeRng(&rng);
++
++ return ecdh;
++fail:
++ crypto_ecdh_deinit(ecdh);
++ ecdh = NULL;
++ goto done;
++}
++
++
++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
++{
++ if (ecdh) {
++ crypto_ec_deinit(ecdh->ec);
++ os_free(ecdh);
++ }
++}
++
++
++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
++{
++ struct wpabuf *buf = NULL;
++ int ret;
++ int len = ecdh->ec->key.dp->size;
++
++ buf = wpabuf_alloc(inc_y ? 2 * len : len);
++ if (!buf)
++ goto fail;
++
++ ret = crypto_bignum_to_bin((struct crypto_bignum *)
++ ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
++ len, len);
++ if (ret < 0)
++ goto fail;
++ if (inc_y) {
++ ret = crypto_bignum_to_bin((struct crypto_bignum *)
++ ecdh->ec->key.pubkey.y,
++ wpabuf_put(buf, len), len, len);
++ if (ret < 0)
++ goto fail;
++ }
++
++done:
++ return buf;
++fail:
++ wpabuf_free(buf);
++ buf = NULL;
++ goto done;
++}
++
++
++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
++ const u8 *key, size_t len)
++{
++ int ret;
++ struct wpabuf *pubkey = NULL;
++ struct wpabuf *secret = NULL;
++ word32 key_len = ecdh->ec->key.dp->size;
++ ecc_point *point = NULL;
++ size_t need_key_len = inc_y ? 2 * key_len : key_len;
++
++ if (len < need_key_len)
++ goto fail;
++ pubkey = wpabuf_alloc(1 + 2 * key_len);
++ if (!pubkey)
++ goto fail;
++ wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
++ wpabuf_put_data(pubkey, key, need_key_len);
++
++ point = wc_ecc_new_point();
++ if (!point)
++ goto fail;
++
++ ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
++ ecdh->ec->key.idx, point);
++ if (ret != MP_OKAY)
++ goto fail;
++
++ secret = wpabuf_alloc(key_len);
++ if (!secret)
++ goto fail;
++
++ ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
++ wpabuf_put(secret, key_len), &key_len);
++ if (ret != MP_OKAY)
++ goto fail;
++
++done:
++ wc_ecc_del_point(point);
++ wpabuf_free(pubkey);
++ return secret;
++fail:
++ wpabuf_free(secret);
++ secret = NULL;
++ goto done;
++}
++
++#endif /* CONFIG_ECC */
+--- contrib/wpa/src/crypto/des-internal.c.orig
++++ contrib/wpa/src/crypto/des-internal.c
+@@ -48,7 +48,7 @@
+
+ static const u32 bytebit[8] =
+ {
+- 0200, 0100, 040, 020, 010, 04, 02, 01
++ 0200, 0100, 040, 020, 010, 04, 02, 01
+ };
+
+ static const u32 bigbyte[24] =
+@@ -58,22 +58,22 @@
+ 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL,
+ 0x800UL, 0x400UL, 0x200UL, 0x100UL,
+ 0x80UL, 0x40UL, 0x20UL, 0x10UL,
+- 0x8UL, 0x4UL, 0x2UL, 0x1L
++ 0x8UL, 0x4UL, 0x2UL, 0x1L
+ };
+
+ /* Use the key schedule specific in the standard (ANSI X3.92-1981) */
+
+ static const u8 pc1[56] = {
+- 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+- 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
++ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
++ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+- 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
++ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ };
+
+ static const u8 totrot[16] = {
+ 1, 2, 4, 6,
+- 8, 10, 12, 14,
+- 15, 17, 19, 21,
++ 8, 10, 12, 14,
++ 15, 17, 19, 21,
+ 23, 25, 27, 28
+ };
+
+@@ -396,7 +396,7 @@
+
+ /* wpa_supplicant/hostapd specific wrapper */
+
+-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+ {
+ u8 pkey[8], next, tmp;
+ int i;
+@@ -421,6 +421,7 @@
+
+ os_memset(pkey, 0, sizeof(pkey));
+ os_memset(ek, 0, sizeof(ek));
++ return 0;
+ }
+
+
+--- contrib/wpa/src/crypto/dh_group5.c.orig
++++ contrib/wpa/src/crypto/dh_group5.c
+@@ -15,6 +15,7 @@
+
+ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+ {
++ wpabuf_free(*publ);
+ *publ = dh_init(dh_groups_get(5), priv);
+ if (*publ == NULL)
+ return NULL;
+--- contrib/wpa/src/crypto/dh_groups.c.orig
++++ contrib/wpa/src/crypto/dh_groups.c
+@@ -1151,8 +1151,8 @@
+ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
+ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
+-
+
++
+ static const struct dh_group dh_groups[] = {
+ DH_GROUP(5, 1),
+ #ifdef ALL_DH_GROUPS
+@@ -1203,32 +1203,24 @@
+ if (*priv == NULL)
+ return NULL;
+
+- if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
+- {
++ pv_len = dh->prime_len;
++ pv = wpabuf_alloc(pv_len);
++ if (pv == NULL) {
+ wpabuf_clear_free(*priv);
+ *priv = NULL;
+ return NULL;
+ }
+-
+- if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
+- /* Make sure private value is smaller than prime */
+- *(wpabuf_mhead_u8(*priv)) = 0;
+- }
+- wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+-
+- pv_len = dh->prime_len;
+- pv = wpabuf_alloc(pv_len);
+- if (pv == NULL)
+- return NULL;
+- if (crypto_mod_exp(dh->generator, dh->generator_len,
+- wpabuf_head(*priv), wpabuf_len(*priv),
+- dh->prime, dh->prime_len, wpabuf_mhead(pv),
+- &pv_len) < 0) {
++ if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
++ wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
+ wpabuf_clear_free(pv);
+- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
++ wpa_printf(MSG_INFO, "DH: crypto_dh_init failed");
++ wpabuf_clear_free(*priv);
++ *priv = NULL;
+ return NULL;
+ }
+- wpabuf_put(pv, pv_len);
++ wpabuf_put(*priv, dh->prime_len);
++ wpabuf_put(pv, dh->prime_len);
++ wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+ wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
+
+ return pv;
+@@ -1256,12 +1248,15 @@
+ shared = wpabuf_alloc(shared_len);
+ if (shared == NULL)
+ return NULL;
+- if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
+- wpabuf_head(own_private), wpabuf_len(own_private),
+- dh->prime, dh->prime_len,
+- wpabuf_mhead(shared), &shared_len) < 0) {
++ if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
++ dh->order, dh->order_len,
++ wpabuf_head(own_private),
++ wpabuf_len(own_private),
++ wpabuf_head(peer_public),
++ wpabuf_len(peer_public),
++ wpabuf_mhead(shared), &shared_len) < 0) {
+ wpabuf_clear_free(shared);
+- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
++ wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
+ return NULL;
+ }
+ wpabuf_put(shared, shared_len);
+--- contrib/wpa/src/crypto/fips_prf_openssl.c.orig
++++ contrib/wpa/src/crypto/fips_prf_openssl.c
+@@ -17,6 +17,19 @@
+ {
+ SHA_CTX context;
+ os_memset(&context, 0, sizeof(context));
++#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID)
++ context.h[0] = state[0];
++ context.h[1] = state[1];
++ context.h[2] = state[2];
++ context.h[3] = state[3];
++ context.h[4] = state[4];
++ SHA1_Transform(&context, data);
++ state[0] = context.h[0];
++ state[1] = context.h[1];
++ state[2] = context.h[2];
++ state[3] = context.h[3];
++ state[4] = context.h[4];
++#else
+ context.h0 = state[0];
+ context.h1 = state[1];
+ context.h2 = state[2];
+@@ -28,6 +41,7 @@
+ state[2] = context.h2;
+ state[3] = context.h3;
+ state[4] = context.h4;
++#endif
+ }
+
+
+@@ -62,12 +76,11 @@
+ /* w_i = G(t, XVAL) */
+ os_memcpy(_t, t, 20);
+ sha1_transform(_t, xkey);
+- _t[0] = host_to_be32(_t[0]);
+- _t[1] = host_to_be32(_t[1]);
+- _t[2] = host_to_be32(_t[2]);
+- _t[3] = host_to_be32(_t[3]);
+- _t[4] = host_to_be32(_t[4]);
+- os_memcpy(xpos, _t, 20);
++ WPA_PUT_BE32(xpos, _t[0]);
++ WPA_PUT_BE32(xpos + 4, _t[1]);
++ WPA_PUT_BE32(xpos + 8, _t[2]);
++ WPA_PUT_BE32(xpos + 12, _t[3]);
++ WPA_PUT_BE32(xpos + 16, _t[4]);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+--- contrib/wpa/src/crypto/fips_prf_wolfssl.c.orig
++++ contrib/wpa/src/crypto/fips_prf_wolfssl.c
+@@ -0,0 +1,87 @@
++/*
++ * FIPS 186-2 PRF for libcrypto
++ * Copyright (c) 2004-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++#include
++#include
++
++#include "common.h"
++#include "crypto.h"
++
++
++static void sha1_transform(u32 *state, const u8 data[64])
++{
++ wc_Sha sha;
++
++ os_memset(&sha, 0, sizeof(sha));
++ sha.digest[0] = state[0];
++ sha.digest[1] = state[1];
++ sha.digest[2] = state[2];
++ sha.digest[3] = state[3];
++ sha.digest[4] = state[4];
++ wc_ShaUpdate(&sha, data, 64);
++ state[0] = sha.digest[0];
++ state[1] = sha.digest[1];
++ state[2] = sha.digest[2];
++ state[3] = sha.digest[3];
++ state[4] = sha.digest[4];
++}
++
++
++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
++{
++ u8 xkey[64];
++ u32 t[5], _t[5];
++ int i, j, m, k;
++ u8 *xpos = x;
++ u32 carry;
++
++ if (seed_len < sizeof(xkey))
++ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
++ else
++ seed_len = sizeof(xkey);
++
++ /* FIPS 186-2 + change notice 1 */
++
++ os_memcpy(xkey, seed, seed_len);
++ t[0] = 0x67452301;
++ t[1] = 0xEFCDAB89;
++ t[2] = 0x98BADCFE;
++ t[3] = 0x10325476;
++ t[4] = 0xC3D2E1F0;
++
++ m = xlen / 40;
++ for (j = 0; j < m; j++) {
++ /* XSEED_j = 0 */
++ for (i = 0; i < 2; i++) {
++ /* XVAL = (XKEY + XSEED_j) mod 2^b */
++
++ /* w_i = G(t, XVAL) */
++ os_memcpy(_t, t, 20);
++ sha1_transform(_t, xkey);
++ WPA_PUT_BE32(xpos, _t[0]);
++ WPA_PUT_BE32(xpos + 4, _t[1]);
++ WPA_PUT_BE32(xpos + 8, _t[2]);
++ WPA_PUT_BE32(xpos + 12, _t[3]);
++ WPA_PUT_BE32(xpos + 16, _t[4]);
++
++ /* XKEY = (1 + XKEY + w_i) mod 2^b */
++ carry = 1;
++ for (k = 19; k >= 0; k--) {
++ carry += xkey[k] + xpos[k];
++ xkey[k] = carry & 0xff;
++ carry >>= 8;
++ }
++
++ xpos += 20;
++ }
++ /* x_j = w_0|w_1 */
++ }
++
++ return 0;
++}
+--- contrib/wpa/src/crypto/md4-internal.c.orig
++++ contrib/wpa/src/crypto/md4-internal.c
+@@ -31,6 +31,9 @@
+ MD4_CTX ctx;
+ size_t i;
+
++ if (TEST_FAIL())
++ return -1;
++
+ MD4Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ MD4Update(&ctx, addr[i], len[i]);
+@@ -82,7 +85,7 @@
+ (cp)[1] = (value) >> 8; \
+ (cp)[0] = (value); } while (0)
+
+-static u8 PADDING[MD4_BLOCK_LENGTH] = {
++static const u8 PADDING[MD4_BLOCK_LENGTH] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+--- contrib/wpa/src/crypto/md5-internal.c.orig
++++ contrib/wpa/src/crypto/md5-internal.c
+@@ -33,6 +33,9 @@
+ MD5_CTX ctx;
+ size_t i;
+
++ if (TEST_FAIL())
++ return -1;
++
+ MD5Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ MD5Update(&ctx, addr[i], len[i]);
+--- contrib/wpa/src/crypto/ms_funcs.c.orig
++++ contrib/wpa/src/crypto/ms_funcs.c
+@@ -48,7 +48,7 @@
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0x1F) << 6) | (c2 & 0x3F));
+ j += 2;
+- } else if (i == utf8_string_len ||
++ } else if (i == utf8_string_len - 1 ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+@@ -140,17 +140,20 @@
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
++ * Returns: 0 on success, -1 on failure
+ */
+-void challenge_response(const u8 *challenge, const u8 *password_hash,
+- u8 *response)
++int challenge_response(const u8 *challenge, const u8 *password_hash,
++ u8 *response)
+ {
+ u8 zpwd[7];
+- des_encrypt(challenge, password_hash, response);
+- des_encrypt(challenge, password_hash + 7, response + 8);
++
++ if (des_encrypt(challenge, password_hash, response) < 0 ||
++ des_encrypt(challenge, password_hash + 7, response + 8) < 0)
++ return -1;
+ zpwd[0] = password_hash[14];
+ zpwd[1] = password_hash[15];
+ os_memset(zpwd + 2, 0, 5);
+- des_encrypt(challenge, zpwd, response + 16);
++ return des_encrypt(challenge, zpwd, response + 16);
+ }
+
+
+@@ -175,9 +178,9 @@
+
+ if (challenge_hash(peer_challenge, auth_challenge, username,
+ username_len, challenge) ||
+- nt_password_hash(password, password_len, password_hash))
++ nt_password_hash(password, password_len, password_hash) ||
++ challenge_response(challenge, password_hash, response))
+ return -1;
+- challenge_response(challenge, password_hash, response);
+ return 0;
+ }
+
+@@ -202,9 +205,9 @@
+
+ if (challenge_hash(peer_challenge, auth_challenge,
+ username, username_len,
+- challenge))
++ challenge) ||
++ challenge_response(challenge, password_hash, response))
+ return -1;
+- challenge_response(challenge, password_hash, response);
+ return 0;
+ }
+
+@@ -304,9 +307,10 @@
+ size_t password_len, u8 *response)
+ {
+ u8 password_hash[16];
+- if (nt_password_hash(password, password_len, password_hash))
++
++ if (nt_password_hash(password, password_len, password_hash) ||
++ challenge_response(challenge, password_hash, response))
+ return -1;
+- challenge_response(challenge, password_hash, response);
+ return 0;
+ }
+
+@@ -487,12 +491,15 @@
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
++ * Returns: 0 on success, -1 on failure
+ */
+-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+- const u8 *block, u8 *cypher)
++int nt_password_hash_encrypted_with_block(const u8 *password_hash,
++ const u8 *block, u8 *cypher)
+ {
+- des_encrypt(password_hash, block, cypher);
+- des_encrypt(password_hash + 8, block + 7, cypher + 8);
++ if (des_encrypt(password_hash, block, cypher) < 0 ||
++ des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
++ return -1;
++ return 0;
+ }
+
+
+@@ -515,10 +522,10 @@
+ if (nt_password_hash(old_password, old_password_len,
+ old_password_hash) ||
+ nt_password_hash(new_password, new_password_len,
+- new_password_hash))
++ new_password_hash) ||
++ nt_password_hash_encrypted_with_block(old_password_hash,
++ new_password_hash,
++ encrypted_password_hash))
+ return -1;
+- nt_password_hash_encrypted_with_block(old_password_hash,
+- new_password_hash,
+- encrypted_password_hash);
+ return 0;
+ }
+--- contrib/wpa/src/crypto/ms_funcs.h.orig
++++ contrib/wpa/src/crypto/ms_funcs.h
+@@ -31,8 +31,8 @@
+ int nt_challenge_response(const u8 *challenge, const u8 *password,
+ size_t password_len, u8 *response);
+
+-void challenge_response(const u8 *challenge, const u8 *password_hash,
+- u8 *response);
++int challenge_response(const u8 *challenge, const u8 *password_hash,
++ u8 *response);
+ int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len, u8 *challenge);
+ int nt_password_hash(const u8 *password, size_t password_len,
+@@ -50,8 +50,8 @@
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_pw_block);
+-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+- const u8 *block, u8 *cypher);
++int nt_password_hash_encrypted_with_block(const u8 *password_hash,
++ const u8 *block, u8 *cypher);
+ int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+--- contrib/wpa/src/crypto/random.c.orig
++++ contrib/wpa/src/crypto/random.c
+@@ -25,6 +25,9 @@
+ #include "utils/includes.h"
+ #ifdef __linux__
+ #include
++#ifdef CONFIG_GETRANDOM
++#include
++#endif /* CONFIG_GETRANDOM */
+ #endif /* __linux__ */
+
+ #include "utils/common.h"
+@@ -54,7 +57,6 @@
+ static unsigned int own_pool_ready = 0;
+ #define RANDOM_ENTROPY_SIZE 20
+ static char *random_entropy_file = NULL;
+-static int random_entropy_file_read = 0;
+
+ #define MIN_COLLECT_ENTROPY 1000
+ static unsigned int entropy = 0;
+@@ -66,6 +68,9 @@
+
+ static u32 __ROL32(u32 x, u32 y)
+ {
++ if (y == 0)
++ return x;
++
+ return (x << (y & 31)) | (x >> (32 - (y & 31)));
+ }
+
+@@ -226,30 +231,52 @@
+ return 1; /* Already initialized - good to continue */
+
+ /*
+- * Try to fetch some more data from the kernel high quality
+- * /dev/random. There may not be enough data available at this point,
++ * Try to fetch some more data from the kernel high quality RNG.
++ * There may not be enough data available at this point,
+ * so use non-blocking read to avoid blocking the application
+ * completely.
+ */
+- fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+- if (fd < 0) {
+- wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+- strerror(errno));
+- return -1;
+- }
+
+- res = read(fd, dummy_key + dummy_key_avail,
+- sizeof(dummy_key) - dummy_key_avail);
++#ifdef CONFIG_GETRANDOM
++ res = getrandom(dummy_key + dummy_key_avail,
++ sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK);
+ if (res < 0) {
+- wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+- "%s", strerror(errno));
+- res = 0;
++ if (errno == ENOSYS) {
++ wpa_printf(MSG_DEBUG,
++ "random: getrandom() not supported, falling back to /dev/random");
++ } else {
++ wpa_printf(MSG_INFO,
++ "random: no data from getrandom(): %s",
++ strerror(errno));
++ res = 0;
++ }
+ }
+- wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
+- "/dev/random", (unsigned) res,
++#else /* CONFIG_GETRANDOM */
++ res = -1;
++#endif /* CONFIG_GETRANDOM */
++ if (res < 0) {
++ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
++ if (fd < 0) {
++ wpa_printf(MSG_ERROR,
++ "random: Cannot open /dev/random: %s",
++ strerror(errno));
++ return -1;
++ }
++
++ res = read(fd, dummy_key + dummy_key_avail,
++ sizeof(dummy_key) - dummy_key_avail);
++ if (res < 0) {
++ wpa_printf(MSG_ERROR,
++ "random: Cannot read from /dev/random: %s",
++ strerror(errno));
++ res = 0;
++ }
++ close(fd);
++ }
++
++ wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+- close(fd);
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ if (own_pool_ready < MIN_READY_MARK)
+@@ -259,7 +286,7 @@
+ }
+
+ wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+- "random data available from /dev/random",
++ "random data available",
+ (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+
+ if (own_pool_ready >= MIN_READY_MARK ||
+@@ -354,7 +381,6 @@
+
+ own_pool_ready = (u8) buf[0];
+ random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+- random_entropy_file_read = 1;
+ os_free(buf);
+ wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+ "(own_pool_ready=%u)",
+@@ -412,6 +438,19 @@
+ if (random_fd >= 0)
+ return;
+
++#ifdef CONFIG_GETRANDOM
++ {
++ u8 dummy;
++
++ if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 ||
++ errno != ENOSYS) {
++ wpa_printf(MSG_DEBUG,
++ "random: getrandom() support available");
++ return;
++ }
++ }
++#endif /* CONFIG_GETRANDOM */
++
+ random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (random_fd < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+--- contrib/wpa/src/crypto/sha1-internal.c.orig
++++ contrib/wpa/src/crypto/sha1-internal.c
+@@ -33,6 +33,9 @@
+ SHA1_CTX ctx;
+ size_t i;
+
++ if (TEST_FAIL())
++ return -1;
++
+ SHA1Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ SHA1Update(&ctx, addr[i], len[i]);
+@@ -50,7 +53,7 @@
+ 100% Public Domain
+
+ -----------------
+-Modified 7/98
++Modified 7/98
+ By James H. Brown
+ Still 100% Public Domain
+
+@@ -72,7 +75,7 @@
+ be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+ "a"s).
+
+-I also changed the declaration of variables i & j in SHA1Update to
++I also changed the declaration of variables i & j in SHA1Update to
+ unsigned long from unsigned int for the same reason.
+
+ These changes should make no difference to any 32 bit implementations since
+@@ -99,7 +102,7 @@
+ Modified 4/01
+ By Saul Kravitz
+ Still 100% PD
+-Modified to run on Compaq Alpha hardware.
++Modified to run on Compaq Alpha hardware.
+
+ -----------------
+ Modified 4/01
+@@ -159,7 +162,7 @@
+ {
+ printf("%s (%d,%d) %x %x %x %x %x\n",
+ msg,
+- context->count[0], context->count[1],
++ context->count[0], context->count[1],
+ context->state[0],
+ context->state[1],
+ context->state[2],
+@@ -294,7 +297,6 @@
+ 255);
+ }
+ /* Wipe variables */
+- i = 0;
+ os_memset(context->buffer, 0, 64);
+ os_memset(context->state, 0, 20);
+ os_memset(context->count, 0, 8);
+--- contrib/wpa/src/crypto/sha1-tlsprf.c.orig
++++ contrib/wpa/src/crypto/sha1-tlsprf.c
+@@ -40,9 +40,6 @@
+ const unsigned char *SHA1_addr[3];
+ size_t SHA1_len[3];
+
+- if (secret_len & 1)
+- return -1;
+-
+ MD5_addr[0] = A_MD5;
+ MD5_len[0] = MD5_MAC_LEN;
+ MD5_addr[1] = (unsigned char *) label;
+--- contrib/wpa/src/crypto/sha256-internal.c.orig
++++ contrib/wpa/src/crypto/sha256-internal.c
+@@ -28,6 +28,9 @@
+ struct sha256_state ctx;
+ size_t i;
+
++ if (TEST_FAIL())
++ return -1;
++
+ sha256_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha256_process(&ctx, addr[i], len[i]))
+@@ -66,7 +69,7 @@
+ ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+ #define Ch(x,y,z) (z ^ (x & (y ^ z)))
+-#define Maj(x,y,z) (((x | y) & z) | (x & y))
++#define Maj(x,y,z) (((x | y) & z) | (x & y))
+ #define S(x, n) RORc((x), (n))
+ #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+ #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+@@ -97,7 +100,7 @@
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+- }
++ }
+
+ /* Compress */
+ #define RND(a,b,c,d,e,f,g,h,i) \
+@@ -108,7 +111,7 @@
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+- t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
++ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+--- contrib/wpa/src/crypto/sha256-kdf.c.orig
++++ contrib/wpa/src/crypto/sha256-kdf.c
+@@ -1,6 +1,6 @@
+ /*
+- * HMAC-SHA256 KDF (RFC 5295)
+- * Copyright (c) 2014, Jouni Malinen
++ * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
++ * Copyright (c) 2014-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -16,7 +16,8 @@
+ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+- * @label: A unique label for each purpose of the KDF
++ * @label: A unique label for each purpose of the KDF or %NULL to select
++ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+@@ -24,7 +25,9 @@
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+- * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
++ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
++ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
++ * RFC 5869, Chapter 2.3.
+ */
+ int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+@@ -38,8 +41,13 @@
+
+ addr[0] = T;
+ len[0] = SHA256_MAC_LEN;
+- addr[1] = (const unsigned char *) label;
+- len[1] = os_strlen(label) + 1;
++ if (label) {
++ addr[1] = (const unsigned char *) label;
++ len[1] = os_strlen(label) + 1;
++ } else {
++ addr[1] = (const u8 *) "";
++ len[1] = 0;
++ }
+ addr[2] = seed;
+ len[2] = seed_len;
+ addr[3] = &iter;
+--- contrib/wpa/src/crypto/sha256-prf.c.orig
++++ contrib/wpa/src/crypto/sha256-prf.c
+@@ -1,6 +1,6 @@
+ /*
+ * SHA256-based PRF (IEEE 802.11r)
+- * Copyright (c) 2003-2013, Jouni Malinen
++ * Copyright (c) 2003-2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -22,14 +22,16 @@
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
++ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+-void sha256_prf(const u8 *key, size_t key_len, const char *label,
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+ {
+- sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
++ return sha256_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8);
+ }
+
+
+@@ -42,6 +44,7 @@
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
++ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+@@ -48,9 +51,9 @@
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+-void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf,
+- size_t buf_len_bits)
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits)
+ {
+ u16 counter = 1;
+ size_t pos, plen;
+@@ -75,11 +78,14 @@
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+- hmac_sha256_vector(key, key_len, 4, addr, len,
+- &buf[pos]);
++ if (hmac_sha256_vector(key, key_len, 4, addr, len,
++ &buf[pos]) < 0)
++ return -1;
+ pos += SHA256_MAC_LEN;
+ } else {
+- hmac_sha256_vector(key, key_len, 4, addr, len, hash);
++ if (hmac_sha256_vector(key, key_len, 4, addr, len,
++ hash) < 0)
++ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ break;
+@@ -97,4 +103,6 @@
+ }
+
+ os_memset(hash, 0, sizeof(hash));
++
++ return 0;
+ }
+--- contrib/wpa/src/crypto/sha256.h.orig
++++ contrib/wpa/src/crypto/sha256.h
+@@ -1,6 +1,6 @@
+ /*
+ * SHA256 hash implementation and interface functions
+- * Copyright (c) 2003-2014, Jouni Malinen
++ * Copyright (c) 2003-2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -15,11 +15,11 @@
+ const u8 *addr[], const size_t *len, u8 *mac);
+ int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+-void sha256_prf(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+-void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf,
+- size_t buf_len_bits);
++int sha256_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits);
+ void tls_prf_sha256(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+--- contrib/wpa/src/crypto/sha384-internal.c.orig
++++ contrib/wpa/src/crypto/sha384-internal.c
+@@ -0,0 +1,92 @@
++/*
++ * SHA-384 hash implementation and interface functions
++ * Copyright (c) 2015, Pali Rohár
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha384_i.h"
++#include "crypto.h"
++
++
++/**
++ * sha384_vector - SHA384 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 of failure
++ */
++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ struct sha384_state ctx;
++ size_t i;
++
++ sha384_init(&ctx);
++ for (i = 0; i < num_elem; i++)
++ if (sha384_process(&ctx, addr[i], len[i]))
++ return -1;
++ if (sha384_done(&ctx, mac))
++ return -1;
++ return 0;
++}
++
++
++/* ===== start - public domain SHA384 implementation ===== */
++
++/* This is based on SHA384 implementation in LibTomCrypt that was released into
++ * public domain by Tom St Denis. */
++
++#define CONST64(n) n ## ULL
++
++/**
++ Initialize the hash state
++ @param md The hash state you wish to initialize
++ @return CRYPT_OK if successful
++*/
++void sha384_init(struct sha384_state *md)
++{
++ md->curlen = 0;
++ md->length = 0;
++ md->state[0] = CONST64(0xcbbb9d5dc1059ed8);
++ md->state[1] = CONST64(0x629a292a367cd507);
++ md->state[2] = CONST64(0x9159015a3070dd17);
++ md->state[3] = CONST64(0x152fecd8f70e5939);
++ md->state[4] = CONST64(0x67332667ffc00b31);
++ md->state[5] = CONST64(0x8eb44a8768581511);
++ md->state[6] = CONST64(0xdb0c2e0d64f98fa7);
++ md->state[7] = CONST64(0x47b5481dbefa4fa4);
++}
++
++int sha384_process(struct sha384_state *md, const unsigned char *in,
++ unsigned long inlen)
++{
++ return sha512_process(md, in, inlen);
++}
++
++/**
++ Terminate the hash to get the digest
++ @param md The hash state
++ @param out [out] The destination of the hash (48 bytes)
++ @return CRYPT_OK if successful
++*/
++int sha384_done(struct sha384_state *md, unsigned char *out)
++{
++ unsigned char buf[64];
++
++ if (md->curlen >= sizeof(md->buf))
++ return -1;
++
++ if (sha512_done(md, buf) != 0)
++ return -1;
++
++ os_memcpy(out, buf, 48);
++ return 0;
++}
++
++/* ===== end - public domain SHA384 implementation ===== */
+--- contrib/wpa/src/crypto/sha384-kdf.c.orig
++++ contrib/wpa/src/crypto/sha384-kdf.c
+@@ -0,0 +1,87 @@
++/*
++ * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869)
++ * Copyright (c) 2014-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha384.h"
++
++
++/**
++ * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295)
++ * @secret: Key for KDF
++ * @secret_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the KDF or %NULL to select
++ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
++ * @seed: Seed value to bind into the key
++ * @seed_len: Length of the seed
++ * @out: Buffer for the generated pseudo-random key
++ * @outlen: Number of bytes of key to generate
++ * Returns: 0 on success, -1 on failure.
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
++ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
++ * RFC 5869, Chapter 2.3.
++ */
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ u8 T[SHA384_MAC_LEN];
++ u8 iter = 1;
++ const unsigned char *addr[4];
++ size_t len[4];
++ size_t pos, clen;
++
++ addr[0] = T;
++ len[0] = SHA384_MAC_LEN;
++ if (label) {
++ addr[1] = (const unsigned char *) label;
++ len[1] = os_strlen(label) + 1;
++ } else {
++ addr[1] = (const u8 *) "";
++ len[1] = 0;
++ }
++ addr[2] = seed;
++ len[2] = seed_len;
++ addr[3] = &iter;
++ len[3] = 1;
++
++ if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
++ return -1;
++
++ pos = 0;
++ for (;;) {
++ clen = outlen - pos;
++ if (clen > SHA384_MAC_LEN)
++ clen = SHA384_MAC_LEN;
++ os_memcpy(out + pos, T, clen);
++ pos += clen;
++
++ if (pos == outlen)
++ break;
++
++ if (iter == 255) {
++ os_memset(out, 0, outlen);
++ os_memset(T, 0, SHA384_MAC_LEN);
++ return -1;
++ }
++ iter++;
++
++ if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
++ {
++ os_memset(out, 0, outlen);
++ os_memset(T, 0, SHA384_MAC_LEN);
++ return -1;
++ }
++ }
++
++ os_memset(T, 0, SHA384_MAC_LEN);
++ return 0;
++}
+--- contrib/wpa/src/crypto/sha384-prf.c.orig
++++ contrib/wpa/src/crypto/sha384-prf.c
+@@ -1,6 +1,6 @@
+ /*
+ * SHA384-based KDF (IEEE 802.11ac)
+- * Copyright (c) 2003-2015, Jouni Malinen
++ * Copyright (c) 2003-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -22,14 +22,16 @@
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
++ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+-void sha384_prf(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+ {
+- sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
++ return sha384_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8);
+ }
+
+
+@@ -42,6 +44,7 @@
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
++ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+@@ -48,9 +51,9 @@
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf,
+- size_t buf_len_bits)
++int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits)
+ {
+ u16 counter = 1;
+ size_t pos, plen;
+@@ -75,11 +78,14 @@
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA384_MAC_LEN) {
+- hmac_sha384_vector(key, key_len, 4, addr, len,
+- &buf[pos]);
++ if (hmac_sha384_vector(key, key_len, 4, addr, len,
++ &buf[pos]) < 0)
++ return -1;
+ pos += SHA384_MAC_LEN;
+ } else {
+- hmac_sha384_vector(key, key_len, 4, addr, len, hash);
++ if (hmac_sha384_vector(key, key_len, 4, addr, len,
++ hash) < 0)
++ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ break;
+@@ -97,4 +103,6 @@
+ }
+
+ os_memset(hash, 0, sizeof(hash));
++
++ return 0;
+ }
+--- contrib/wpa/src/crypto/sha384.c.orig
++++ contrib/wpa/src/crypto/sha384.c
+@@ -0,0 +1,104 @@
++/*
++ * SHA-384 hash implementation and interface functions
++ * Copyright (c) 2003-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha384.h"
++#include "crypto.h"
++
++
++/**
++ * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104)
++ * @key: Key for HMAC operations
++ * @key_len: Length of the key in bytes
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash (48 bytes)
++ * Returns: 0 on success, -1 on failure
++ */
++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
++ unsigned char tk[48];
++ const u8 *_addr[6];
++ size_t _len[6], i;
++
++ if (num_elem > 5) {
++ /*
++ * Fixed limit on the number of fragments to avoid having to
++ * allocate memory (which could fail).
++ */
++ return -1;
++ }
++
++ /* if key is longer than 128 bytes reset it to key = SHA384(key) */
++ if (key_len > 128) {
++ if (sha384_vector(1, &key, &key_len, tk) < 0)
++ return -1;
++ key = tk;
++ key_len = 48;
++ }
++
++ /* the HMAC_SHA384 transform looks like:
++ *
++ * SHA384(K XOR opad, SHA384(K XOR ipad, text))
++ *
++ * where K is an n byte key
++ * ipad is the byte 0x36 repeated 128 times
++ * opad is the byte 0x5c repeated 128 times
++ * and text is the data being protected */
++
++ /* start out by storing key in ipad */
++ os_memset(k_pad, 0, sizeof(k_pad));
++ os_memcpy(k_pad, key, key_len);
++ /* XOR key with ipad values */
++ for (i = 0; i < 128; i++)
++ k_pad[i] ^= 0x36;
++
++ /* perform inner SHA384 */
++ _addr[0] = k_pad;
++ _len[0] = 128;
++ for (i = 0; i < num_elem; i++) {
++ _addr[i + 1] = addr[i];
++ _len[i + 1] = len[i];
++ }
++ if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0)
++ return -1;
++
++ os_memset(k_pad, 0, sizeof(k_pad));
++ os_memcpy(k_pad, key, key_len);
++ /* XOR key with opad values */
++ for (i = 0; i < 128; i++)
++ k_pad[i] ^= 0x5c;
++
++ /* perform outer SHA384 */
++ _addr[0] = k_pad;
++ _len[0] = 128;
++ _addr[1] = mac;
++ _len[1] = SHA384_MAC_LEN;
++ return sha384_vector(2, _addr, _len, mac);
++}
++
++
++/**
++ * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104)
++ * @key: Key for HMAC operations
++ * @key_len: Length of the key in bytes
++ * @data: Pointers to the data area
++ * @data_len: Length of the data area
++ * @mac: Buffer for the hash (48 bytes)
++ * Returns: 0 on success, -1 on failure
++ */
++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
++}
+--- contrib/wpa/src/crypto/sha384.h.orig
++++ contrib/wpa/src/crypto/sha384.h
+@@ -1,6 +1,6 @@
+ /*
+ * SHA384 hash implementation and interface functions
+- * Copyright (c) 2015, Jouni Malinen
++ * Copyright (c) 2015-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -15,10 +15,13 @@
+ const u8 *addr[], const size_t *len, u8 *mac);
+ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+-void sha384_prf(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+- const u8 *data, size_t data_len, u8 *buf,
+- size_t buf_len_bits);
++int sha384_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
++int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits);
++int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen);
+
+ #endif /* SHA384_H */
+--- contrib/wpa/src/crypto/sha384_i.h.orig
++++ contrib/wpa/src/crypto/sha384_i.h
+@@ -0,0 +1,23 @@
++/*
++ * SHA-384 internal definitions
++ * Copyright (c) 2015, Pali Rohár
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef SHA384_I_H
++#define SHA384_I_H
++
++#include "sha512_i.h"
++
++#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
++
++#define sha384_state sha512_state
++
++void sha384_init(struct sha384_state *md);
++int sha384_process(struct sha384_state *md, const unsigned char *in,
++ unsigned long inlen);
++int sha384_done(struct sha384_state *md, unsigned char *out);
++
++#endif /* SHA384_I_H */
+--- contrib/wpa/src/crypto/sha512-internal.c.orig
++++ contrib/wpa/src/crypto/sha512-internal.c
+@@ -0,0 +1,270 @@
++/*
++ * SHA-512 hash implementation and interface functions
++ * Copyright (c) 2015, Pali Rohár
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha512_i.h"
++#include "crypto.h"
++
++
++/**
++ * sha512_vector - SHA512 hash for data vector
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash
++ * Returns: 0 on success, -1 of failure
++ */
++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
++ u8 *mac)
++{
++ struct sha512_state ctx;
++ size_t i;
++
++ sha512_init(&ctx);
++ for (i = 0; i < num_elem; i++)
++ if (sha512_process(&ctx, addr[i], len[i]))
++ return -1;
++ if (sha512_done(&ctx, mac))
++ return -1;
++ return 0;
++}
++
++
++/* ===== start - public domain SHA512 implementation ===== */
++
++/* This is based on SHA512 implementation in LibTomCrypt that was released into
++ * public domain by Tom St Denis. */
++
++#define CONST64(n) n ## ULL
++
++/* the K array */
++static const u64 K[80] = {
++ CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
++ CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
++ CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
++ CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
++ CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
++ CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
++ CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
++ CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
++ CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
++ CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
++ CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
++ CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
++ CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
++ CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
++ CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
++ CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
++ CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
++ CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
++ CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
++ CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
++ CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
++ CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
++ CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
++ CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
++ CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
++ CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
++ CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
++ CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
++ CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
++ CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
++ CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
++ CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
++ CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
++ CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
++ CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
++ CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
++ CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
++ CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
++ CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
++ CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
++};
++
++/* Various logical functions */
++#define Ch(x,y,z) (z ^ (x & (y ^ z)))
++#define Maj(x,y,z) (((x | y) & z) | (x & y))
++#define S(x, n) ROR64c(x, n)
++#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n))
++#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
++#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
++#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
++#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
++#ifndef MIN
++#define MIN(x, y) (((x) < (y)) ? (x) : (y))
++#endif
++
++#define ROR64c(x, y) \
++ ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
++ ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \
++ CONST64(0xFFFFFFFFFFFFFFFF))
++
++/* compress 1024-bits */
++static int sha512_compress(struct sha512_state *md, unsigned char *buf)
++{
++ u64 S[8], t0, t1;
++ u64 *W;
++ int i;
++
++ W = os_malloc(80 * sizeof(u64));
++ if (!W)
++ return -1;
++
++ /* copy state into S */
++ for (i = 0; i < 8; i++) {
++ S[i] = md->state[i];
++ }
++
++ /* copy the state into 1024-bits into W[0..15] */
++ for (i = 0; i < 16; i++)
++ W[i] = WPA_GET_BE64(buf + (8 * i));
++
++ /* fill W[16..79] */
++ for (i = 16; i < 80; i++) {
++ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
++ W[i - 16];
++ }
++
++ /* Compress */
++ for (i = 0; i < 80; i++) {
++ t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
++ t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
++ S[7] = S[6];
++ S[6] = S[5];
++ S[5] = S[4];
++ S[4] = S[3] + t0;
++ S[3] = S[2];
++ S[2] = S[1];
++ S[1] = S[0];
++ S[0] = t0 + t1;
++ }
++
++ /* feedback */
++ for (i = 0; i < 8; i++) {
++ md->state[i] = md->state[i] + S[i];
++ }
++
++ os_free(W);
++ return 0;
++}
++
++
++/**
++ Initialize the hash state
++ @param md The hash state you wish to initialize
++ @return CRYPT_OK if successful
++*/
++void sha512_init(struct sha512_state *md)
++{
++ md->curlen = 0;
++ md->length = 0;
++ md->state[0] = CONST64(0x6a09e667f3bcc908);
++ md->state[1] = CONST64(0xbb67ae8584caa73b);
++ md->state[2] = CONST64(0x3c6ef372fe94f82b);
++ md->state[3] = CONST64(0xa54ff53a5f1d36f1);
++ md->state[4] = CONST64(0x510e527fade682d1);
++ md->state[5] = CONST64(0x9b05688c2b3e6c1f);
++ md->state[6] = CONST64(0x1f83d9abfb41bd6b);
++ md->state[7] = CONST64(0x5be0cd19137e2179);
++}
++
++
++/**
++ Process a block of memory though the hash
++ @param md The hash state
++ @param in The data to hash
++ @param inlen The length of the data (octets)
++ @return CRYPT_OK if successful
++*/
++int sha512_process(struct sha512_state *md, const unsigned char *in,
++ unsigned long inlen)
++{
++ unsigned long n;
++
++ if (md->curlen >= sizeof(md->buf))
++ return -1;
++
++ while (inlen > 0) {
++ if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) {
++ if (sha512_compress(md, (unsigned char *) in) < 0)
++ return -1;
++ md->length += SHA512_BLOCK_SIZE * 8;
++ in += SHA512_BLOCK_SIZE;
++ inlen -= SHA512_BLOCK_SIZE;
++ } else {
++ n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen));
++ os_memcpy(md->buf + md->curlen, in, n);
++ md->curlen += n;
++ in += n;
++ inlen -= n;
++ if (md->curlen == SHA512_BLOCK_SIZE) {
++ if (sha512_compress(md, md->buf) < 0)
++ return -1;
++ md->length += 8 * SHA512_BLOCK_SIZE;
++ md->curlen = 0;
++ }
++ }
++ }
++
++ return 0;
++}
++
++
++/**
++ Terminate the hash to get the digest
++ @param md The hash state
++ @param out [out] The destination of the hash (64 bytes)
++ @return CRYPT_OK if successful
++*/
++int sha512_done(struct sha512_state *md, unsigned char *out)
++{
++ int i;
++
++ if (md->curlen >= sizeof(md->buf))
++ return -1;
++
++ /* increase the length of the message */
++ md->length += md->curlen * CONST64(8);
++
++ /* append the '1' bit */
++ md->buf[md->curlen++] = (unsigned char) 0x80;
++
++ /* if the length is currently above 112 bytes we append zeros
++ * then compress. Then we can fall back to padding zeros and length
++ * encoding like normal.
++ */
++ if (md->curlen > 112) {
++ while (md->curlen < 128) {
++ md->buf[md->curlen++] = (unsigned char) 0;
++ }
++ sha512_compress(md, md->buf);
++ md->curlen = 0;
++ }
++
++ /* pad up to 120 bytes of zeroes
++ * note: that from 112 to 120 is the 64 MSB of the length. We assume
++ * that you won't hash > 2^64 bits of data... :-)
++ */
++ while (md->curlen < 120) {
++ md->buf[md->curlen++] = (unsigned char) 0;
++ }
++
++ /* store length */
++ WPA_PUT_BE64(md->buf + 120, md->length);
++ sha512_compress(md, md->buf);
++
++ /* copy output */
++ for (i = 0; i < 8; i++)
++ WPA_PUT_BE64(out + (8 * i), md->state[i]);
++
++ return 0;
++}
++
++/* ===== end - public domain SHA512 implementation ===== */
+--- contrib/wpa/src/crypto/sha512-kdf.c.orig
++++ contrib/wpa/src/crypto/sha512-kdf.c
+@@ -0,0 +1,87 @@
++/*
++ * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869)
++ * Copyright (c) 2014-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha512.h"
++
++
++/**
++ * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295)
++ * @secret: Key for KDF
++ * @secret_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the KDF or %NULL to select
++ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
++ * @seed: Seed value to bind into the key
++ * @seed_len: Length of the seed
++ * @out: Buffer for the generated pseudo-random key
++ * @outlen: Number of bytes of key to generate
++ * Returns: 0 on success, -1 on failure.
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
++ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
++ * RFC 5869, Chapter 2.3.
++ */
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen)
++{
++ u8 T[SHA512_MAC_LEN];
++ u8 iter = 1;
++ const unsigned char *addr[4];
++ size_t len[4];
++ size_t pos, clen;
++
++ addr[0] = T;
++ len[0] = SHA512_MAC_LEN;
++ if (label) {
++ addr[1] = (const unsigned char *) label;
++ len[1] = os_strlen(label) + 1;
++ } else {
++ addr[1] = (const u8 *) "";
++ len[1] = 0;
++ }
++ addr[2] = seed;
++ len[2] = seed_len;
++ addr[3] = &iter;
++ len[3] = 1;
++
++ if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
++ return -1;
++
++ pos = 0;
++ for (;;) {
++ clen = outlen - pos;
++ if (clen > SHA512_MAC_LEN)
++ clen = SHA512_MAC_LEN;
++ os_memcpy(out + pos, T, clen);
++ pos += clen;
++
++ if (pos == outlen)
++ break;
++
++ if (iter == 255) {
++ os_memset(out, 0, outlen);
++ os_memset(T, 0, SHA512_MAC_LEN);
++ return -1;
++ }
++ iter++;
++
++ if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
++ {
++ os_memset(out, 0, outlen);
++ os_memset(T, 0, SHA512_MAC_LEN);
++ return -1;
++ }
++ }
++
++ os_memset(T, 0, SHA512_MAC_LEN);
++ return 0;
++}
+--- contrib/wpa/src/crypto/sha512-prf.c.orig
++++ contrib/wpa/src/crypto/sha512-prf.c
+@@ -0,0 +1,108 @@
++/*
++ * SHA512-based KDF (IEEE 802.11ac)
++ * Copyright (c) 2003-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha512.h"
++#include "crypto.h"
++
++
++/**
++ * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
++ * @key: Key for KDF
++ * @key_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the PRF
++ * @data: Extra data to bind into the key
++ * @data_len: Length of the data
++ * @buf: Buffer for the generated pseudo-random key
++ * @buf_len: Number of bytes of key to generate
++ * Returns: 0 on success, -1 on failure
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key.
++ */
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++ return sha512_prf_bits(key, key_len, label, data, data_len, buf,
++ buf_len * 8);
++}
++
++
++/**
++ * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
++ * @key: Key for KDF
++ * @key_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the PRF
++ * @data: Extra data to bind into the key
++ * @data_len: Length of the data
++ * @buf: Buffer for the generated pseudo-random key
++ * @buf_len: Number of bits of key to generate
++ * Returns: 0 on success, -1 on failure
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key. If the requested buf_len is not divisible by eight, the least
++ * significant 1-7 bits of the last octet in the output are not part of the
++ * requested output.
++ */
++int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits)
++{
++ u16 counter = 1;
++ size_t pos, plen;
++ u8 hash[SHA512_MAC_LEN];
++ const u8 *addr[4];
++ size_t len[4];
++ u8 counter_le[2], length_le[2];
++ size_t buf_len = (buf_len_bits + 7) / 8;
++
++ addr[0] = counter_le;
++ len[0] = 2;
++ addr[1] = (u8 *) label;
++ len[1] = os_strlen(label);
++ addr[2] = data;
++ len[2] = data_len;
++ addr[3] = length_le;
++ len[3] = sizeof(length_le);
++
++ WPA_PUT_LE16(length_le, buf_len_bits);
++ pos = 0;
++ while (pos < buf_len) {
++ plen = buf_len - pos;
++ WPA_PUT_LE16(counter_le, counter);
++ if (plen >= SHA512_MAC_LEN) {
++ if (hmac_sha512_vector(key, key_len, 4, addr, len,
++ &buf[pos]) < 0)
++ return -1;
++ pos += SHA512_MAC_LEN;
++ } else {
++ if (hmac_sha512_vector(key, key_len, 4, addr, len,
++ hash) < 0)
++ return -1;
++ os_memcpy(&buf[pos], hash, plen);
++ pos += plen;
++ break;
++ }
++ counter++;
++ }
++
++ /*
++ * Mask out unused bits in the last octet if it does not use all the
++ * bits.
++ */
++ if (buf_len_bits % 8) {
++ u8 mask = 0xff << (8 - buf_len_bits % 8);
++ buf[pos - 1] &= mask;
++ }
++
++ os_memset(hash, 0, sizeof(hash));
++
++ return 0;
++}
+--- contrib/wpa/src/crypto/sha512.c.orig
++++ contrib/wpa/src/crypto/sha512.c
+@@ -0,0 +1,104 @@
++/*
++ * SHA-512 hash implementation and interface functions
++ * Copyright (c) 2003-2018, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "sha512.h"
++#include "crypto.h"
++
++
++/**
++ * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104)
++ * @key: Key for HMAC operations
++ * @key_len: Length of the key in bytes
++ * @num_elem: Number of elements in the data vector
++ * @addr: Pointers to the data areas
++ * @len: Lengths of the data blocks
++ * @mac: Buffer for the hash (64 bytes)
++ * Returns: 0 on success, -1 on failure
++ */
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac)
++{
++ unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
++ unsigned char tk[64];
++ const u8 *_addr[6];
++ size_t _len[6], i;
++
++ if (num_elem > 5) {
++ /*
++ * Fixed limit on the number of fragments to avoid having to
++ * allocate memory (which could fail).
++ */
++ return -1;
++ }
++
++ /* if key is longer than 128 bytes reset it to key = SHA512(key) */
++ if (key_len > 128) {
++ if (sha512_vector(1, &key, &key_len, tk) < 0)
++ return -1;
++ key = tk;
++ key_len = 64;
++ }
++
++ /* the HMAC_SHA512 transform looks like:
++ *
++ * SHA512(K XOR opad, SHA512(K XOR ipad, text))
++ *
++ * where K is an n byte key
++ * ipad is the byte 0x36 repeated 128 times
++ * opad is the byte 0x5c repeated 128 times
++ * and text is the data being protected */
++
++ /* start out by storing key in ipad */
++ os_memset(k_pad, 0, sizeof(k_pad));
++ os_memcpy(k_pad, key, key_len);
++ /* XOR key with ipad values */
++ for (i = 0; i < 128; i++)
++ k_pad[i] ^= 0x36;
++
++ /* perform inner SHA512 */
++ _addr[0] = k_pad;
++ _len[0] = 128;
++ for (i = 0; i < num_elem; i++) {
++ _addr[i + 1] = addr[i];
++ _len[i + 1] = len[i];
++ }
++ if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0)
++ return -1;
++
++ os_memset(k_pad, 0, sizeof(k_pad));
++ os_memcpy(k_pad, key, key_len);
++ /* XOR key with opad values */
++ for (i = 0; i < 128; i++)
++ k_pad[i] ^= 0x5c;
++
++ /* perform outer SHA512 */
++ _addr[0] = k_pad;
++ _len[0] = 128;
++ _addr[1] = mac;
++ _len[1] = SHA512_MAC_LEN;
++ return sha512_vector(2, _addr, _len, mac);
++}
++
++
++/**
++ * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104)
++ * @key: Key for HMAC operations
++ * @key_len: Length of the key in bytes
++ * @data: Pointers to the data area
++ * @data_len: Length of the data area
++ * @mac: Buffer for the hash (64 bytes)
++ * Returns: 0 on success, -1 on failure
++ */
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac)
++{
++ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
++}
+--- contrib/wpa/src/crypto/sha512.h.orig
++++ contrib/wpa/src/crypto/sha512.h
+@@ -0,0 +1,27 @@
++/*
++ * SHA512 hash implementation and interface functions
++ * Copyright (c) 2015-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef SHA512_H
++#define SHA512_H
++
++#define SHA512_MAC_LEN 64
++
++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
++ const u8 *addr[], const size_t *len, u8 *mac);
++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
++ size_t data_len, u8 *mac);
++int sha512_prf(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
++int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
++ const u8 *data, size_t data_len, u8 *buf,
++ size_t buf_len_bits);
++int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
++ const char *label, const u8 *seed, size_t seed_len,
++ u8 *out, size_t outlen);
++
++#endif /* SHA512_H */
+--- contrib/wpa/src/crypto/sha512_i.h.orig
++++ contrib/wpa/src/crypto/sha512_i.h
+@@ -0,0 +1,25 @@
++/*
++ * SHA-512 internal definitions
++ * Copyright (c) 2015, Pali Rohár
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef SHA512_I_H
++#define SHA512_I_H
++
++#define SHA512_BLOCK_SIZE 128
++
++struct sha512_state {
++ u64 length, state[8];
++ u32 curlen;
++ u8 buf[SHA512_BLOCK_SIZE];
++};
++
++void sha512_init(struct sha512_state *md);
++int sha512_process(struct sha512_state *md, const unsigned char *in,
++ unsigned long inlen);
++int sha512_done(struct sha512_state *md, unsigned char *out);
++
++#endif /* SHA512_I_H */
+--- contrib/wpa/src/crypto/tls.h.orig
++++ contrib/wpa/src/crypto/tls.h
+@@ -41,6 +41,8 @@
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+ TLS_FAIL_DOMAIN_MISMATCH = 10,
++ TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
++ TLS_FAIL_DN_MISMATCH = 12,
+ };
+
+
+@@ -63,6 +65,7 @@
+ size_t hash_len;
+ const char *altsubject[TLS_MAX_ALT_SUBJECT];
+ int num_altsubject;
++ const char *serial_num;
+ } peer_cert;
+
+ struct {
+@@ -80,6 +83,8 @@
+ int cert_in_cb;
+ const char *openssl_ciphers;
+ unsigned int tls_session_lifetime;
++ unsigned int crl_reload_interval;
++ unsigned int tls_flags;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+@@ -95,6 +100,14 @@
+ #define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+ #define TLS_CONN_EAP_FAST BIT(7)
+ #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
++#define TLS_CONN_EXT_CERT_CHECK BIT(9)
++#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
++#define TLS_CONN_SUITEB BIT(11)
++#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
++#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
++#define TLS_CONN_ENABLE_TLSv1_0 BIT(14)
++#define TLS_CONN_ENABLE_TLSv1_1 BIT(15)
++#define TLS_CONN_ENABLE_TLSv1_2 BIT(16)
+
+ /**
+ * struct tls_connection_params - Parameters for TLS connection
+@@ -107,12 +120,19 @@
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+- * @suffix_match: String to suffix match in the dNSName or CN of the peer
+- * certificate or %NULL to allow all domain names. This may allow subdomains an
+- * wildcard certificates. Each domain name label must have a full match.
++ * @suffix_match: Semicolon deliminated string of values to suffix match against
++ * the dNSName or CN of the peer certificate or %NULL to allow all domain names.
++ * This may allow subdomains and wildcard certificates. Each domain name label
++ * must have a full case-insensitive match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
++ *
++ * More than one match string can be provided by using semicolons to
++ * separate the strings (e.g., example.org;example.com). When multiple
++ * strings are specified, a match with any one of the values is
++ * considered a sufficient match for the certificate, i.e., the
++ * conditions are ORed together.
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+@@ -136,9 +156,15 @@
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
++ * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if
++ * supported, empty string to disable, or a colon-separated curve list.
+ * @flags: Parameter options (TLS_CONN_*)
+ * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
+ * or %NULL if OCSP is not enabled
++ * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
++ * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
++ * ocsp_multi is not enabled
++ * @check_cert_subject: Client certificate subject name matching string
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+@@ -176,9 +202,12 @@
+ const char *cert_id;
+ const char *ca_cert_id;
+ const char *openssl_ciphers;
++ const char *openssl_ecdh_curves;
+
+ unsigned int flags;
+ const char *ocsp_stapling_response;
++ const char *ocsp_stapling_response_multi;
++ const char *check_cert_subject;
+ };
+
+
+@@ -242,6 +271,18 @@
+ int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+ /**
++ * tls_connection_peer_serial_num - Fetch peer certificate serial number
++ * @tls_ctx: TLS context data from tls_init()
++ * @conn: Connection context data from tls_connection_init()
++ * Returns: Allocated string buffer containing the peer certificate serial
++ * number or %NULL on error.
++ *
++ * The caller is responsible for freeing the returned buffer with os_free().
++ */
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn);
++
++/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+@@ -297,9 +338,11 @@
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
++ * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors
+ * Returns: 0 on success, -1 on failure
+ */
+-int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
++int __must_check tls_global_set_verify(void *tls_ctx, int check_crl,
++ int strict);
+
+ /**
+ * tls_connection_set_verify - Set certificate verification options
+@@ -330,31 +373,44 @@
+ struct tls_random *data);
+
+ /**
+- * tls_connection_prf - Use TLS-PRF to derive keying material
++ * tls_connection_export_key - Derive keying material from a TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
+- * @server_random_first: seed is 0 = client_random|server_random,
+- * 1 = server_random|client_random
+- * @skip_keyblock: Skip TLS key block from the beginning of PRF output
++ * @context: Optional extra upper-layer context (max len 2^16)
++ * @context_len: The length of the context value
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+- * tls_connection_prf() is required so that further keying material can be
+- * derived from the master secret. Example implementation of this function is in
+- * tls_prf_sha1_md5() when it is called with seed set to
+- * client_random|server_random (or server_random|client_random). For TLSv1.2 and
+- * newer, a different PRF is needed, though.
++ * Exports keying material using the mechanism described in RFC 5705. If
++ * context is %NULL, context is not provided; otherwise, context is provided
++ * (including the case of empty context with context_len == 0).
+ */
+-int __must_check tls_connection_prf(void *tls_ctx,
+- struct tls_connection *conn,
+- const char *label,
+- int server_random_first,
+- int skip_keyblock,
+- u8 *out, size_t out_len);
++int __must_check tls_connection_export_key(void *tls_ctx,
++ struct tls_connection *conn,
++ const char *label,
++ const u8 *context,
++ size_t context_len,
++ u8 *out, size_t out_len);
+
+ /**
++ * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
++ * @tls_ctx: TLS context data from tls_init()
++ * @conn: Connection context data from tls_connection_init()
++ * @out: Buffer for output data from TLS-PRF
++ * @out_len: Length of the output buffer
++ * Returns: 0 on success, -1 on failure
++ *
++ * Exports key material after the normal TLS key block for use with
++ * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
++ * uses a different legacy mechanism.
++ */
++int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
++ struct tls_connection *conn,
++ u8 *out, size_t out_len);
++
++/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+@@ -455,7 +511,9 @@
+ TLS_CIPHER_RC4_SHA /* 0x0005 */,
+ TLS_CIPHER_AES128_SHA /* 0x002f */,
+ TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+- TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
++ TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
++ TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
++ TLS_CIPHER_AES256_SHA /* 0x0035 */,
+ };
+
+ /**
+--- contrib/wpa/src/crypto/tls_gnutls.c.orig
++++ contrib/wpa/src/crypto/tls_gnutls.c
+@@ -1,6 +1,6 @@
+ /*
+ * SSL/TLS interface functions for GnuTLS
+- * Copyright (c) 2004-2011, Jouni Malinen
++ * Copyright (c) 2004-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -37,6 +37,8 @@
+ union tls_event_data *data);
+ void *cb_ctx;
+ int cert_in_cb;
++
++ char *ocsp_stapling_response;
+ };
+
+ struct tls_connection {
+@@ -133,6 +135,7 @@
+ if (global->params_set)
+ gnutls_certificate_free_credentials(global->xcred);
+ os_free(global->session_data);
++ os_free(global->ocsp_stapling_response);
+ os_free(global);
+ }
+
+@@ -292,6 +295,14 @@
+ }
+
+
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* TODO */
++ return NULL;
++}
++
++
+ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+ {
+ struct tls_global *global = ssl_ctx;
+@@ -343,10 +354,25 @@
+ const struct tls_connection_params *params)
+ {
+ int ret;
++ const char *err;
++ char prio_buf[100];
++ const char *prio = NULL;
+
+ if (conn == NULL || params == NULL)
+ return -1;
+
++ if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++ wpa_printf(MSG_INFO,
++ "GnuTLS: ocsp=3 not supported");
++ return -1;
++ }
++
++ if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
++ wpa_printf(MSG_INFO,
++ "GnuTLS: tls_ext_cert_check=1 not supported");
++ return -1;
++ }
++
+ if (params->subject_match) {
+ wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+ return -1;
+@@ -382,12 +408,66 @@
+
+ conn->flags = params->flags;
+
++ if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
++ TLS_CONN_DISABLE_TLSv1_1 |
++ TLS_CONN_DISABLE_TLSv1_2)) {
++ os_snprintf(prio_buf, sizeof(prio_buf),
++ "NORMAL:-VERS-SSL3.0%s%s%s",
++ params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
++ ":-VERS-TLS1.0" : "",
++ params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
++ ":-VERS-TLS1.1" : "",
++ params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
++ ":-VERS-TLS1.2" : "");
++ prio = prio_buf;
++ }
++
+ if (params->openssl_ciphers) {
+- wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
++ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
++ prio = "SUITEB128";
++ } else if (os_strcmp(params->openssl_ciphers,
++ "SUITEB192") == 0) {
++ prio = "SUITEB192";
++ } else if ((params->flags & TLS_CONN_SUITEB) &&
++ os_strcmp(params->openssl_ciphers,
++ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
++ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
++ } else if (os_strcmp(params->openssl_ciphers,
++ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
++ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
++ } else if (os_strcmp(params->openssl_ciphers,
++ "DHE-RSA-AES256-GCM-SHA384") == 0) {
++ prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
++ } else if (os_strcmp(params->openssl_ciphers,
++ "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) {
++ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
++ } else {
++ wpa_printf(MSG_INFO,
++ "GnuTLS: openssl_ciphers not supported");
++ return -1;
++ }
++ } else if (params->flags & TLS_CONN_SUITEB) {
++ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
++ }
++
++ if (prio) {
++ wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio);
++ ret = gnutls_priority_set_direct(conn->session, prio, &err);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ "GnuTLS: Priority string failure at '%s'",
++ err);
++ return -1;
++ }
++ }
++
++ if (params->openssl_ecdh_curves) {
++ wpa_printf(MSG_INFO,
++ "GnuTLS: openssl_ecdh_curves not supported");
+ return -1;
+ }
+
+- /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
++ /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
+ * to force peer validation(?) */
+
+ if (params->ca_cert) {
+@@ -410,6 +490,13 @@
+ gnutls_strerror(ret));
+ return -1;
+ }
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Successfully read CA cert '%s' in PEM format",
++ params->ca_cert);
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Successfully read CA cert '%s' in DER format",
++ params->ca_cert);
+ }
+ } else if (params->ca_cert_blob) {
+ gnutls_datum_t ca;
+@@ -457,6 +544,9 @@
+ }
+
+ if (params->client_cert && params->private_key) {
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format",
++ params->client_cert, params->private_key);
+ #if GNUTLS_VERSION_NUMBER >= 0x03010b
+ ret = gnutls_certificate_set_x509_key_file2(
+ conn->xcred, params->client_cert, params->private_key,
+@@ -468,8 +558,9 @@
+ GNUTLS_X509_FMT_DER);
+ #endif
+ if (ret < 0) {
+- wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+- "in DER format: %s", gnutls_strerror(ret));
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format",
++ gnutls_strerror(ret));
+ #if GNUTLS_VERSION_NUMBER >= 0x03010b
+ ret = gnutls_certificate_set_x509_key_file2(
+ conn->xcred, params->client_cert,
+@@ -486,11 +577,19 @@
+ gnutls_strerror(ret));
+ return ret;
+ }
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Successfully read client cert/key in PEM format");
++ } else {
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Successfully read client cert/key in DER format");
+ }
+ } else if (params->private_key) {
+ int pkcs12_ok = 0;
+ #ifdef PKCS12_FUNCS
+ /* Try to load in PKCS#12 format */
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format",
++ params->private_key);
+ ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+ conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
+ params->private_key_passwd);
+@@ -596,6 +695,44 @@
+ }
+
+
++#if GNUTLS_VERSION_NUMBER >= 0x030103
++static int server_ocsp_status_req(gnutls_session_t session, void *ptr,
++ gnutls_datum_t *resp)
++{
++ struct tls_global *global = ptr;
++ char *cached;
++ size_t len;
++
++ if (!global->ocsp_stapling_response) {
++ wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured");
++ return GNUTLS_E_NO_CERTIFICATE_STATUS;
++ }
++
++ cached = os_readfile(global->ocsp_stapling_response, &len);
++ if (!cached) {
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: OCSP status callback - could not read response file (%s)",
++ global->ocsp_stapling_response);
++ return GNUTLS_E_NO_CERTIFICATE_STATUS;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "GnuTLS: OCSP status callback - send cached response");
++ resp->data = gnutls_malloc(len);
++ if (!resp->data) {
++ os_free(resp);
++ return GNUTLS_E_MEMORY_ERROR;
++ }
++
++ os_memcpy(resp->data, cached, len);
++ resp->size = len;
++ os_free(cached);
++
++ return GNUTLS_E_SUCCESS;
++}
++#endif /* 3.1.3 */
++
++
+ int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+ {
+@@ -602,6 +739,9 @@
+ struct tls_global *global = tls_ctx;
+ int ret;
+
++ if (params->check_cert_subject)
++ return -1; /* not yet supported */
++
+ /* Currently, global parameters are only set when running in server
+ * mode. */
+ global->server = 1;
+@@ -690,6 +830,17 @@
+ }
+ }
+
++#if GNUTLS_VERSION_NUMBER >= 0x030103
++ os_free(global->ocsp_stapling_response);
++ if (params->ocsp_stapling_response)
++ global->ocsp_stapling_response =
++ os_strdup(params->ocsp_stapling_response);
++ else
++ global->ocsp_stapling_response = NULL;
++ gnutls_certificate_set_ocsp_status_request_function(
++ global->xcred, server_ocsp_status_req, global);
++#endif /* 3.1.3 */
++
+ global->params_set = 1;
+
+ return 0;
+@@ -700,7 +851,7 @@
+ }
+
+
+-int tls_global_set_verify(void *ssl_ctx, int check_crl)
++int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
+ {
+ /* TODO */
+ return 0;
+@@ -746,18 +897,34 @@
+ }
+
+
+-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+- const char *label, int server_random_first,
+- int skip_keyblock, u8 *out, size_t out_len)
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
+ {
+- if (conn == NULL || conn->session == NULL || skip_keyblock)
++ if (conn == NULL || conn->session == NULL)
+ return -1;
+
++#if GNUTLS_VERSION_NUMBER >= 0x030404
++ return gnutls_prf_rfc5705(conn->session, os_strlen(label), label,
++ context_len, (const char *) context,
++ out_len, (char *) out);
++#else /* 3.4.4 */
++ if (context)
++ return -1;
+ return gnutls_prf(conn->session, os_strlen(label), label,
+- server_random_first, 0, NULL, out_len, (char *) out);
++ 0 /* client_random first */, 0, NULL, out_len,
++ (char *) out);
++#endif /* 3.4.4 */
+ }
+
+
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ return -1;
++}
++
++
+ static void gnutls_tls_fail_event(struct tls_connection *conn,
+ const gnutls_datum_t *cert, int depth,
+ const char *subject, const char *err_str,
+@@ -919,6 +1086,52 @@
+ }
+
+
++static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match,
++ int full)
++{
++ int res = -1;
++
++#if GNUTLS_VERSION_NUMBER >= 0x030300
++ if (full)
++ res = gnutls_x509_crt_check_hostname2(
++ cert, match,
++ GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
++#endif /* >= 3.3.0 */
++ if (res == -1)
++ res = gnutls_x509_crt_check_hostname(cert, match);
++
++ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d",
++ full ? "": "suffix ", match, res);
++ return res;
++}
++
++
++static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match,
++ int full)
++{
++ char *values, *token, *context = NULL;
++ int ret = 0;
++
++ if (!os_strchr(match, ';'))
++ return tls_match_suffix_helper(cert, match, full);
++
++ values = os_strdup(match);
++ if (!values)
++ return 0;
++
++ /* Process each match alternative separately until a match is found */
++ while ((token = str_token(values, ";", &context))) {
++ if (tls_match_suffix_helper(cert, token, full)) {
++ ret = 1;
++ break;
++ }
++ }
++
++ os_free(values);
++ return ret;
++}
++
++
+ static int tls_connection_verify_peer(gnutls_session_t session)
+ {
+ struct tls_connection *conn;
+@@ -1114,8 +1327,7 @@
+
+ if (i == 0) {
+ if (conn->suffix_match &&
+- !gnutls_x509_crt_check_hostname(
+- cert, conn->suffix_match)) {
++ !tls_match_suffix(cert, conn->suffix_match, 0)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Domain suffix match '%s' not found",
+ conn->suffix_match);
+@@ -1131,9 +1343,7 @@
+
+ #if GNUTLS_VERSION_NUMBER >= 0x030300
+ if (conn->domain_match &&
+- !gnutls_x509_crt_check_hostname2(
+- cert, conn->domain_match,
+- GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
++ !tls_match_suffix(cert, conn->domain_match, 1)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Domain match '%s' not found",
+ conn->domain_match);
+@@ -1262,6 +1472,7 @@
+ ret = gnutls_handshake(conn->session);
+ if (ret < 0) {
+ gnutls_alert_description_t alert;
++ union tls_event_data ev;
+
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+@@ -1272,6 +1483,23 @@
+ conn->push_buf = wpabuf_alloc(0);
+ }
+ break;
++ case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
++ wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime");
++ if (conn->global->event_cb) {
++ os_memset(&ev, 0, sizeof(ev));
++ ev.alert.is_local = 1;
++ ev.alert.type = "fatal";
++ ev.alert.description = "insufficient security";
++ conn->global->event_cb(conn->global->cb_ctx,
++ TLS_ALERT, &ev);
++ }
++ /*
++ * Could send a TLS Alert to the server, but for now,
++ * simply terminate handshake.
++ */
++ conn->failed++;
++ conn->write_alerts++;
++ break;
+ case GNUTLS_E_FATAL_ALERT_RECEIVED:
+ alert = gnutls_alert_get(conn->session);
+ wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
+@@ -1278,8 +1506,6 @@
+ __func__, gnutls_alert_get_name(alert));
+ conn->read_alerts++;
+ if (conn->global->event_cb != NULL) {
+- union tls_event_data ev;
+-
+ os_memset(&ev, 0, sizeof(ev));
+ ev.alert.is_local = 0;
+ ev.alert.type = gnutls_alert_get_name(alert);
+@@ -1430,8 +1656,18 @@
+ int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+ {
+- /* TODO */
+- return -1;
++ gnutls_protocol_t ver;
++
++ ver = gnutls_protocol_get_version(conn->session);
++ if (ver == GNUTLS_TLS1_0)
++ os_strlcpy(buf, "TLSv1", buflen);
++ else if (ver == GNUTLS_TLS1_1)
++ os_strlcpy(buf, "TLSv1.1", buflen);
++ else if (ver == GNUTLS_TLS1_2)
++ os_strlcpy(buf, "TLSv1.2", buflen);
++ else
++ return -1;
++ return 0;
+ }
+
+
+@@ -1438,8 +1674,35 @@
+ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+ {
+- /* TODO */
+- buf[0] = '\0';
++ gnutls_cipher_algorithm_t cipher;
++ gnutls_kx_algorithm_t kx;
++ gnutls_mac_algorithm_t mac;
++ const char *kx_str, *cipher_str, *mac_str;
++ int res;
++
++ cipher = gnutls_cipher_get(conn->session);
++ cipher_str = gnutls_cipher_get_name(cipher);
++ if (!cipher_str)
++ cipher_str = "";
++
++ kx = gnutls_kx_get(conn->session);
++ kx_str = gnutls_kx_get_name(kx);
++ if (!kx_str)
++ kx_str = "";
++
++ mac = gnutls_mac_get(conn->session);
++ mac_str = gnutls_mac_get_name(mac);
++ if (!mac_str)
++ mac_str = "";
++
++ if (kx == GNUTLS_KX_RSA)
++ res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str);
++ else
++ res = os_snprintf(buf, buflen, "%s-%s-%s",
++ kx_str, cipher_str, mac_str);
++ if (os_snprintf_error(buflen, res))
++ return -1;
++
+ return 0;
+ }
+
+--- contrib/wpa/src/crypto/tls_internal.c.orig
++++ contrib/wpa/src/crypto/tls_internal.c
+@@ -1,6 +1,6 @@
+ /*
+ * TLS interface functions and an internal TLS implementation
+- * Copyright (c) 2004-2011, Jouni Malinen
++ * Copyright (c) 2004-2019, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -23,6 +23,11 @@
+ int server;
+ struct tlsv1_credentials *server_cred;
+ int check_crl;
++
++ void (*event_cb)(void *ctx, enum tls_event ev,
++ union tls_event_data *data);
++ void *cb_ctx;
++ int cert_in_cb;
+ };
+
+ struct tls_connection {
+@@ -51,6 +56,11 @@
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
++ if (conf) {
++ global->event_cb = conf->event_cb;
++ global->cb_ctx = conf->cb_ctx;
++ global->cert_in_cb = conf->cert_in_cb;
++ }
+
+ return global;
+ }
+@@ -64,10 +74,12 @@
+ tlsv1_client_global_deinit();
+ #endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ #ifdef CONFIG_TLS_INTERNAL_SERVER
+- tlsv1_cred_free(global->server_cred);
+ tlsv1_server_global_deinit();
+ #endif /* CONFIG_TLS_INTERNAL_SERVER */
+ }
++#ifdef CONFIG_TLS_INTERNAL_SERVER
++ tlsv1_cred_free(global->server_cred);
++#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ os_free(global);
+ }
+
+@@ -95,6 +107,8 @@
+ os_free(conn);
+ return NULL;
+ }
++ tlsv1_client_set_cb(conn->client, global->event_cb,
++ global->cb_ctx, global->cert_in_cb);
+ }
+ #endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ #ifdef CONFIG_TLS_INTERNAL_SERVER
+@@ -163,6 +177,14 @@
+ }
+
+
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* TODO */
++ return NULL;
++}
++
++
+ int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+ {
+ #ifdef CONFIG_TLS_INTERNAL_CLIENT
+@@ -186,6 +208,12 @@
+ if (conn->client == NULL)
+ return -1;
+
++ if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
++ wpa_printf(MSG_INFO,
++ "TLS: tls_ext_cert_check=1 not supported");
++ return -1;
++ }
++
+ cred = tlsv1_cred_alloc();
+ if (cred == NULL)
+ return -1;
+@@ -220,6 +248,12 @@
+ return -1;
+ }
+
++ if (params->openssl_ecdh_curves) {
++ wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported");
++ tlsv1_cred_free(cred);
++ return -1;
++ }
++
+ if (tlsv1_set_ca_cert(cred, params->ca_cert,
+ params->ca_cert_blob, params->ca_cert_blob_len,
+ params->ca_path)) {
+@@ -259,8 +293,7 @@
+ return -1;
+ }
+
+- tlsv1_client_set_time_checks(
+- conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
++ tlsv1_client_set_flags(conn->client, params->flags);
+
+ return 0;
+ #else /* CONFIG_TLS_INTERNAL_CLIENT */
+@@ -276,6 +309,9 @@
+ struct tls_global *global = tls_ctx;
+ struct tlsv1_credentials *cred;
+
++ if (params->check_cert_subject)
++ return -1; /* not yet supported */
++
+ /* Currently, global parameters are only set when running in server
+ * mode. */
+ global->server = 1;
+@@ -312,6 +348,13 @@
+ return -1;
+ }
+
++ if (params->ocsp_stapling_response)
++ cred->ocsp_stapling_response =
++ os_strdup(params->ocsp_stapling_response);
++ if (params->ocsp_stapling_response_multi)
++ cred->ocsp_stapling_response_multi =
++ os_strdup(params->ocsp_stapling_response_multi);
++
+ return 0;
+ #else /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+@@ -319,7 +362,7 @@
+ }
+
+
+-int tls_global_set_verify(void *tls_ctx, int check_crl)
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+ {
+ struct tls_global *global = tls_ctx;
+ global->check_crl = check_crl;
+@@ -368,9 +411,10 @@
+ }
+
+
+-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+- const char *label, int server_random_first,
+- int skip_keyblock, u8 *out, size_t out_len)
++static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, int server_random_first,
++ int skip_keyblock, u8 *out, size_t out_len)
+ {
+ int ret = -1, skip = 0;
+ u8 *tmp_out = NULL;
+@@ -388,16 +432,16 @@
+
+ #ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+- ret = tlsv1_client_prf(conn->client, label,
+- server_random_first,
+- _out, out_len);
++ ret = tlsv1_client_prf(conn->client, label, context,
++ context_len, server_random_first,
++ _out, skip + out_len);
+ }
+ #endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ #ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+- ret = tlsv1_server_prf(conn->server, label,
+- server_random_first,
+- _out, out_len);
++ ret = tlsv1_server_prf(conn->server, label, context,
++ context_len, server_random_first,
++ _out, skip + out_len);
+ }
+ #endif /* CONFIG_TLS_INTERNAL_SERVER */
+ if (ret == 0 && skip_keyblock)
+@@ -408,6 +452,23 @@
+ }
+
+
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
++{
++ return tls_connection_prf(tls_ctx, conn, label, context, context_len,
++ 0, 0, out, out_len);
++}
++
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
++ 1, 1, out, out_len);
++}
++
++
+ struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+@@ -621,7 +682,12 @@
+ int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+ {
+- /* TODO */
++ if (conn == NULL)
++ return -1;
++#ifdef CONFIG_TLS_INTERNAL_CLIENT
++ if (conn->client)
++ return tlsv1_client_get_version(conn->client, buf, buflen);
++#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+ }
+
+@@ -666,6 +732,10 @@
+
+ int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+ {
++#ifdef CONFIG_TLS_INTERNAL_SERVER
++ if (conn->server)
++ return tlsv1_server_get_failed(conn->server);
++#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return 0;
+ }
+
+@@ -672,6 +742,10 @@
+
+ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+ {
++#ifdef CONFIG_TLS_INTERNAL_SERVER
++ if (conn->server)
++ return tlsv1_server_get_read_alerts(conn->server);
++#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return 0;
+ }
+
+@@ -679,6 +753,10 @@
+ int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+ {
++#ifdef CONFIG_TLS_INTERNAL_SERVER
++ if (conn->server)
++ return tlsv1_server_get_write_alerts(conn->server);
++#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return 0;
+ }
+
+--- contrib/wpa/src/crypto/tls_none.c.orig
++++ contrib/wpa/src/crypto/tls_none.c
+@@ -45,6 +45,13 @@
+ }
+
+
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ return NULL;
++}
++
++
+ int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+ {
+ return -1;
+@@ -65,7 +72,7 @@
+ }
+
+
+-int tls_global_set_verify(void *tls_ctx, int check_crl)
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+ {
+ return -1;
+ }
+@@ -86,14 +93,21 @@
+ }
+
+
+-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+- const char *label, int server_random_first,
+- int skip_keyblock, u8 *out, size_t out_len)
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
+ {
+ return -1;
+ }
+
+
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ return -1;
++}
++
++
+ struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+--- contrib/wpa/src/crypto/tls_openssl.c.orig
++++ contrib/wpa/src/crypto/tls_openssl.c
+@@ -18,6 +18,7 @@
+
+ #include
+ #include
++#include
+ #include
+ #include
+ #ifndef OPENSSL_NO_ENGINE
+@@ -35,12 +36,12 @@
+ #include "sha1.h"
+ #include "sha256.h"
+ #include "tls.h"
++#include "tls_openssl.h"
+
+-#if OPENSSL_VERSION_NUMBER < 0x10000000L
+-/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
+- * deprecated. However, OpenSSL 0.9.8 doesn't include
+- * ERR_remove_thread_state. */
+-#define ERR_remove_thread_state(tid) ERR_remove_state(0)
++#if !defined(CONFIG_FIPS) && \
++ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
++ defined(EAP_SERVER_FAST))
++#define OPENSSL_NEED_EAP_FAST_PRF
+ #endif
+
+ #if defined(OPENSSL_IS_BORINGSSL)
+@@ -57,6 +58,69 @@
+ #endif /* OPENSSL_NO_TLSEXT */
+ #endif /* SSL_set_tlsext_status_type */
+
++#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
++ !defined(BORINGSSL_API_VERSION)
++/*
++ * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
++ * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
++ * older versions.
++ */
++
++static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
++ size_t outlen)
++{
++ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
++ return 0;
++ os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
++ return SSL3_RANDOM_SIZE;
++}
++
++
++static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
++ size_t outlen)
++{
++ if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
++ return 0;
++ os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
++ return SSL3_RANDOM_SIZE;
++}
++
++
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
++static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
++ unsigned char *out, size_t outlen)
++{
++ if (!session || session->master_key_length < 0 ||
++ (size_t) session->master_key_length > outlen)
++ return 0;
++ if ((size_t) session->master_key_length < outlen)
++ outlen = session->master_key_length;
++ os_memcpy(out, session->master_key, outlen);
++ return outlen;
++}
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
++
++#endif
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
++#ifdef CONFIG_SUITEB
++static int RSA_bits(const RSA *r)
++{
++ return BN_num_bits(r->n);
++}
++#endif /* CONFIG_SUITEB */
++
++
++static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
++{
++ return ASN1_STRING_data((ASN1_STRING *) x);
++}
++#endif
++
+ #ifdef ANDROID
+ #include
+ #include
+@@ -71,6 +135,66 @@
+ free(value);
+ return bio;
+ }
++
++
++static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
++{
++ BIO *bio = BIO_from_keystore(key_alias);
++ STACK_OF(X509_INFO) *stack = NULL;
++ stack_index_t i;
++
++ if (bio) {
++ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
++ BIO_free(bio);
++ }
++
++ if (!stack) {
++ wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
++ key_alias);
++ return -1;
++ }
++
++ for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
++ X509_INFO *info = sk_X509_INFO_value(stack, i);
++
++ if (info->x509)
++ X509_STORE_add_cert(ctx, info->x509);
++ if (info->crl)
++ X509_STORE_add_crl(ctx, info->crl);
++ }
++
++ sk_X509_INFO_pop_free(stack, X509_INFO_free);
++
++ return 0;
++}
++
++
++static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
++ const char *encoded_key_alias)
++{
++ int rc = -1;
++ int len = os_strlen(encoded_key_alias);
++ unsigned char *decoded_alias;
++
++ if (len & 1) {
++ wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
++ encoded_key_alias);
++ return rc;
++ }
++
++ decoded_alias = os_malloc(len / 2 + 1);
++ if (decoded_alias) {
++ if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
++ decoded_alias[len / 2] = '\0';
++ rc = tls_add_ca_from_keystore(
++ ctx, (const char *) decoded_alias);
++ }
++ os_free(decoded_alias);
++ }
++
++ return rc;
++}
++
+ #endif /* ANDROID */
+
+ static int tls_openssl_ref_count = 0;
+@@ -90,18 +214,26 @@
+ struct tls_data {
+ SSL_CTX *ssl;
+ unsigned int tls_session_lifetime;
++ int check_crl;
++ int check_crl_strict;
++ char *ca_cert;
++ unsigned int crl_reload_interval;
++ struct os_reltime crl_last_reload;
++ char *check_cert_subject;
+ };
+
+ struct tls_connection {
+ struct tls_context *context;
++ struct tls_data *data;
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ BIO *ssl_in, *ssl_out;
+-#ifndef OPENSSL_NO_ENGINE
++#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+ ENGINE *engine; /* functional reference to the engine */
+ EVP_PKEY *private_key; /* the private key if using engine */
+ #endif /* OPENSSL_NO_ENGINE */
+ char *subject_match, *altsubject_match, *suffix_match, *domain_match;
++ char *check_cert_subject;
+ int read_alerts, write_alerts, failed;
+
+ tls_session_ticket_cb session_ticket_cb;
+@@ -116,6 +248,8 @@
+ unsigned int server_cert_only:1;
+ unsigned int invalid_hb_used:1;
+ unsigned int success_data:1;
++ unsigned int client_hello_generated:1;
++ unsigned int server:1;
+
+ u8 srv_cert_hash[32];
+
+@@ -125,10 +259,11 @@
+ X509 *peer_issuer;
+ X509 *peer_issuer_issuer;
+
+-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char server_random[SSL3_RANDOM_SIZE];
+-#endif
++
++ u16 cipher_suite;
++ int server_dh_prime_len;
+ };
+
+
+@@ -176,6 +311,36 @@
+ #endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
++static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl)
++{
++ int flags;
++ X509_STORE *store;
++
++ store = X509_STORE_new();
++ if (!store) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: %s - failed to allocate new certificate store",
++ __func__);
++ return NULL;
++ }
++
++ if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) {
++ tls_show_errors(MSG_WARNING, __func__,
++ "Failed to load root certificates");
++ X509_STORE_free(store);
++ return NULL;
++ }
++
++ flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0;
++ if (check_crl == 2)
++ flags |= X509_V_FLAG_CRL_CHECK_ALL;
++
++ X509_STORE_set_flags(store, flags);
++
++ return store;
++}
++
++
+ #ifdef CONFIG_NATIVE_WINDOWS
+
+ /* Windows CryptoAPI and access to certificate stores */
+@@ -526,7 +691,8 @@
+ wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+ "system certificate store: subject='%s'", buf);
+
+- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
++ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
++ cert)) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add ca_cert to OpenSSL "
+ "certificate store");
+@@ -624,10 +790,16 @@
+
+ engine = ENGINE_by_id(id);
+ if (engine) {
+- ENGINE_free(engine);
+ wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+ "available", id);
+- return 0;
++ /*
++ * If it was auto-loaded by ENGINE_by_id() we might still
++ * need to tell it which PKCS#11 module to use in legacy
++ * (non-p11-kit) environments. Do so now; even if it was
++ * properly initialised before, setting it again will be
++ * harmless.
++ */
++ goto found;
+ }
+ ERR_clear_error();
+
+@@ -664,7 +836,7 @@
+ id, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+-
++ found:
+ while (post && post[0]) {
+ wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+ if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+@@ -808,6 +980,9 @@
+ }
+ #endif /* OPENSSL_FIPS */
+ #endif /* CONFIG_FIPS */
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ SSL_load_error_strings();
+ SSL_library_init();
+ #ifndef OPENSSL_NO_SHA256
+@@ -829,6 +1004,7 @@
+ #endif /* OPENSSL_NO_RC2 */
+ PKCS12_PBE_add();
+ #endif /* PKCS12_FUNCS */
++#endif /* < 1.1.0 */
+ } else {
+ context = tls_context_new(conf);
+ if (context == NULL)
+@@ -849,15 +1025,26 @@
+ os_free(tls_global);
+ tls_global = NULL;
+ }
++ os_free(data);
+ return NULL;
+ }
+ data->ssl = ssl;
+- if (conf)
++ if (conf) {
+ data->tls_session_lifetime = conf->tls_session_lifetime;
++ data->crl_reload_interval = conf->crl_reload_interval;
++ }
+
+ SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+
++#ifdef SSL_MODE_NO_AUTO_CHAIN
++ /* Number of deployed use cases assume the default OpenSSL behavior of
++ * auto chaining the local certificate is in use. BoringSSL removed this
++ * functionality by default, so we need to restore it here to avoid
++ * breaking existing use cases. */
++ SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
++#endif /* SSL_MODE_NO_AUTO_CHAIN */
++
+ SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+ SSL_CTX_set_app_data(ssl, context);
+ if (data->tls_session_lifetime > 0) {
+@@ -885,8 +1072,10 @@
+
+ #ifndef OPENSSL_NO_ENGINE
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ERR_load_ENGINE_strings();
+ ENGINE_load_dynamic();
++#endif /* OPENSSL_VERSION_NUMBER */
+
+ if (conf &&
+ (conf->opensc_engine_path || conf->pkcs11_engine_path ||
+@@ -903,7 +1092,7 @@
+ if (conf && conf->openssl_ciphers)
+ ciphers = conf->openssl_ciphers;
+ else
+- ciphers = "DEFAULT:!EXP:!LOW";
++ ciphers = TLS_DEFAULT_CIPHERS;
+ if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to set cipher string '%s'",
+@@ -925,10 +1114,14 @@
+ os_free(context);
+ if (data->tls_session_lifetime > 0)
+ SSL_CTX_flush_sessions(ssl, 0);
++ os_free(data->ca_cert);
+ SSL_CTX_free(ssl);
+
+ tls_openssl_ref_count--;
+ if (tls_openssl_ref_count == 0) {
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ #ifndef OPENSSL_NO_ENGINE
+ ENGINE_cleanup();
+ #endif /* OPENSSL_NO_ENGINE */
+@@ -936,6 +1129,7 @@
+ ERR_remove_thread_state(NULL);
+ ERR_free_strings();
+ EVP_cleanup();
++#endif /* < 1.1.0 */
+ os_free(tls_global->ocsp_stapling_response);
+ tls_global->ocsp_stapling_response = NULL;
+ os_free(tls_global);
+@@ -942,6 +1136,7 @@
+ tls_global = NULL;
+ }
+
++ os_free(data->check_cert_subject);
+ os_free(data);
+ }
+
+@@ -967,10 +1162,32 @@
+ #endif /* OPENSSL_NO_ENGINE */
+
+
++#ifdef ANDROID
++/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
++EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
++#endif /* ANDROID */
++
+ static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+ const char *pin, const char *key_id,
+ const char *cert_id, const char *ca_cert_id)
+ {
++#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL)
++#if !defined(OPENSSL_NO_ENGINE)
++#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
++#endif
++ if (!key_id)
++ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++ conn->engine = NULL;
++ conn->private_key = EVP_PKEY_from_keystore(key_id);
++ if (!conn->private_key) {
++ wpa_printf(MSG_ERROR,
++ "ENGINE: cannot load private key with id '%s' [%s]",
++ key_id,
++ ERR_error_string(ERR_get_error(), NULL));
++ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++ }
++#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
++
+ #ifndef OPENSSL_NO_ENGINE
+ int ret = -1;
+ if (engine_id == NULL) {
+@@ -1068,7 +1285,7 @@
+
+ static void tls_engine_deinit(struct tls_connection *conn)
+ {
+-#ifndef OPENSSL_NO_ENGINE
++#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+ wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+ if (conn->private_key) {
+ EVP_PKEY_free(conn->private_key);
+@@ -1075,10 +1292,12 @@
+ conn->private_key = NULL;
+ }
+ if (conn->engine) {
++#if !defined(OPENSSL_IS_BORINGSSL)
+ ENGINE_finish(conn->engine);
++#endif /* !OPENSSL_IS_BORINGSSL */
+ conn->engine = NULL;
+ }
+-#endif /* OPENSSL_NO_ENGINE */
++#endif /* ANDROID || !OPENSSL_NO_ENGINE */
+ }
+
+
+@@ -1097,6 +1316,168 @@
+ }
+
+
++static const char * openssl_content_type(int content_type)
++{
++ switch (content_type) {
++ case 20:
++ return "change cipher spec";
++ case 21:
++ return "alert";
++ case 22:
++ return "handshake";
++ case 23:
++ return "application data";
++ case 24:
++ return "heartbeat";
++ case 256:
++ return "TLS header info"; /* pseudo content type */
++ default:
++ return "?";
++ }
++}
++
++
++static const char * openssl_handshake_type(int content_type, const u8 *buf,
++ size_t len)
++{
++ if (content_type != 22 || !buf || len == 0)
++ return "";
++ switch (buf[0]) {
++ case 0:
++ return "hello request";
++ case 1:
++ return "client hello";
++ case 2:
++ return "server hello";
++ case 3:
++ return "hello verify request";
++ case 4:
++ return "new session ticket";
++ case 5:
++ return "end of early data";
++ case 6:
++ return "hello retry request";
++ case 8:
++ return "encrypted extensions";
++ case 11:
++ return "certificate";
++ case 12:
++ return "server key exchange";
++ case 13:
++ return "certificate request";
++ case 14:
++ return "server hello done";
++ case 15:
++ return "certificate verify";
++ case 16:
++ return "client key exchange";
++ case 20:
++ return "finished";
++ case 21:
++ return "certificate url";
++ case 22:
++ return "certificate status";
++ case 23:
++ return "supplemental data";
++ case 24:
++ return "key update";
++ case 254:
++ return "message hash";
++ default:
++ return "?";
++ }
++}
++
++
++#ifdef CONFIG_SUITEB
++
++static void check_server_hello(struct tls_connection *conn,
++ const u8 *pos, const u8 *end)
++{
++ size_t payload_len, id_len;
++
++ /*
++ * Parse ServerHello to get the selected cipher suite since OpenSSL does
++ * not make it cleanly available during handshake and we need to know
++ * whether DHE was selected.
++ */
++
++ if (end - pos < 3)
++ return;
++ payload_len = WPA_GET_BE24(pos);
++ pos += 3;
++
++ if ((size_t) (end - pos) < payload_len)
++ return;
++ end = pos + payload_len;
++
++ /* Skip Version and Random */
++ if (end - pos < 2 + SSL3_RANDOM_SIZE)
++ return;
++ pos += 2 + SSL3_RANDOM_SIZE;
++
++ /* Skip Session ID */
++ if (end - pos < 1)
++ return;
++ id_len = *pos++;
++ if ((size_t) (end - pos) < id_len)
++ return;
++ pos += id_len;
++
++ if (end - pos < 2)
++ return;
++ conn->cipher_suite = WPA_GET_BE16(pos);
++ wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
++ conn->cipher_suite);
++}
++
++
++static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
++ const u8 *pos, const u8 *end)
++{
++ size_t payload_len;
++ u16 dh_len;
++ BIGNUM *p;
++ int bits;
++
++ if (!(conn->flags & TLS_CONN_SUITEB))
++ return;
++
++ /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
++ if (conn->cipher_suite != 0x9f)
++ return;
++
++ if (end - pos < 3)
++ return;
++ payload_len = WPA_GET_BE24(pos);
++ pos += 3;
++
++ if ((size_t) (end - pos) < payload_len)
++ return;
++ end = pos + payload_len;
++
++ if (end - pos < 2)
++ return;
++ dh_len = WPA_GET_BE16(pos);
++ pos += 2;
++
++ if ((size_t) (end - pos) < dh_len)
++ return;
++ p = BN_bin2bn(pos, dh_len, NULL);
++ if (!p)
++ return;
++
++ bits = BN_num_bits(p);
++ BN_free(p);
++
++ conn->server_dh_prime_len = bits;
++ wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
++ conn->server_dh_prime_len);
++}
++
++#endif /* CONFIG_SUITEB */
++
++
+ static void tls_msg_cb(int write_p, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl, void *arg)
+ {
+@@ -1103,8 +1484,18 @@
+ struct tls_connection *conn = arg;
+ const u8 *pos = buf;
+
+- wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
+- write_p ? "TX" : "RX", version, content_type);
++ if (write_p == 2) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: session ver=0x%x content_type=%d",
++ version, content_type);
++ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
++ return;
++ }
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
++ write_p ? "TX" : "RX", version, content_type,
++ openssl_content_type(content_type),
++ openssl_handshake_type(content_type, buf, len));
+ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
+ if (content_type == 24 && len >= 3 && pos[0] == 1) {
+ size_t payload_len = WPA_GET_BE16(pos + 1);
+@@ -1113,6 +1504,18 @@
+ conn->invalid_hb_used = 1;
+ }
+ }
++
++#ifdef CONFIG_SUITEB
++ /*
++ * Need to parse these handshake messages to be able to check DH prime
++ * length since OpenSSL does not expose the new cipher suite and DH
++ * parameters during handshake (e.g., for cert_cb() callback).
++ */
++ if (content_type == 22 && pos && len > 0 && pos[0] == 2)
++ check_server_hello(conn, pos + 1, pos + len);
++ if (content_type == 22 && pos && len > 0 && pos[0] == 12)
++ check_server_key_exchange(ssl, conn, pos + 1, pos + len);
++#endif /* CONFIG_SUITEB */
+ }
+
+
+@@ -1122,11 +1525,32 @@
+ SSL_CTX *ssl = data->ssl;
+ struct tls_connection *conn;
+ long options;
++ X509_STORE *new_cert_store;
++ struct os_reltime now;
+ struct tls_context *context = SSL_CTX_get_app_data(ssl);
+
++ /* Replace X509 store if it is time to update CRL. */
++ if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 &&
++ os_reltime_expired(&now, &data->crl_last_reload,
++ data->crl_reload_interval)) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Flushing X509 store with ca_cert file");
++ new_cert_store = tls_crl_cert_reload(data->ca_cert,
++ data->check_crl);
++ if (!new_cert_store) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Error replacing X509 store with ca_cert file");
++ } else {
++ /* Replace old store */
++ SSL_CTX_set_cert_store(ssl, new_cert_store);
++ data->crl_last_reload = now;
++ }
++ }
++
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
++ conn->data = data;
+ conn->ssl_ctx = ssl;
+ conn->ssl = SSL_new(ssl);
+ if (conn->ssl == NULL) {
+@@ -1190,6 +1614,7 @@
+ os_free(conn->altsubject_match);
+ os_free(conn->suffix_match);
+ os_free(conn->domain_match);
++ os_free(conn->check_cert_subject);
+ os_free(conn->session_ticket);
+ os_free(conn);
+ }
+@@ -1201,6 +1626,31 @@
+ }
+
+
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ ASN1_INTEGER *ser;
++ char *serial_num;
++ size_t len;
++
++ if (!conn->peer_cert)
++ return NULL;
++
++ ser = X509_get_serialNumber(conn->peer_cert);
++ if (!ser)
++ return NULL;
++
++ len = ASN1_STRING_length(ser) * 2 + 1;
++ serial_num = os_malloc(len);
++ if (!serial_num)
++ return NULL;
++ wpa_snprintf_hex_uppercase(serial_num, len,
++ ASN1_STRING_get0_data(ser),
++ ASN1_STRING_length(ser));
++ return serial_num;
++}
++
++
+ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+ {
+ if (conn == NULL)
+@@ -1234,6 +1684,8 @@
+ found++;
+ }
+
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
++
+ return found;
+ }
+
+@@ -1283,9 +1735,9 @@
+
+ #ifndef CONFIG_NATIVE_WINDOWS
+ static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+- int full)
++ size_t match_len, int full)
+ {
+- size_t i, match_len;
++ size_t i;
+
+ /* Check for embedded nuls that could mess up suffix matching */
+ for (i = 0; i < len; i++) {
+@@ -1295,7 +1747,6 @@
+ }
+ }
+
+- match_len = os_strlen(match);
+ if (match_len > len || (full && match_len != len))
+ return 0;
+
+@@ -1315,12 +1766,223 @@
+ #endif /* CONFIG_NATIVE_WINDOWS */
+
+
+-static int tls_match_suffix(X509 *cert, const char *match, int full)
++struct tls_dn_field_order_cnt {
++ u8 cn;
++ u8 c;
++ u8 l;
++ u8 st;
++ u8 o;
++ u8 ou;
++ u8 email;
++};
++
++
++static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt,
++ int nid)
+ {
+-#ifdef CONFIG_NATIVE_WINDOWS
+- /* wincrypt.h has conflicting X509_NAME definition */
+- return -1;
+-#else /* CONFIG_NATIVE_WINDOWS */
++ switch (nid) {
++ case NID_commonName:
++ return dn_cnt->cn;
++ case NID_countryName:
++ return dn_cnt->c;
++ case NID_localityName:
++ return dn_cnt->l;
++ case NID_stateOrProvinceName:
++ return dn_cnt->st;
++ case NID_organizationName:
++ return dn_cnt->o;
++ case NID_organizationalUnitName:
++ return dn_cnt->ou;
++ case NID_pkcs9_emailAddress:
++ return dn_cnt->email;
++ default:
++ wpa_printf(MSG_ERROR,
++ "TLS: Unknown NID '%d' in check_cert_subject",
++ nid);
++ return -1;
++ }
++}
++
++
++/**
++ * match_dn_field - Match configuration DN field against Certificate DN field
++ * @cert: Certificate
++ * @nid: NID of DN field
++ * @field: Field name
++ * @value DN field value which is passed from configuration
++ * e.g., if configuration have C=US and this argument will point to US.
++ * @dn_cnt: DN matching context
++ * Returns: 1 on success and 0 on failure
++ */
++static int match_dn_field(const X509 *cert, int nid, const char *field,
++ const char *value,
++ const struct tls_dn_field_order_cnt *dn_cnt)
++{
++ int i, ret = 0, len, config_dn_field_index, match_index = 0;
++ X509_NAME *name;
++
++ len = os_strlen(value);
++ name = X509_get_subject_name((X509 *) cert);
++
++ /* Assign incremented cnt for every field of DN to check DN field in
++ * right order */
++ config_dn_field_index = get_dn_field_index(dn_cnt, nid);
++ if (config_dn_field_index < 0)
++ return 0;
++
++ /* Fetch value based on NID */
++ for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
++ X509_NAME_ENTRY *e;
++ ASN1_STRING *cn;
++
++ e = X509_NAME_get_entry(name, i);
++ if (!e)
++ continue;
++
++ cn = X509_NAME_ENTRY_get_data(e);
++ if (!cn)
++ continue;
++
++ match_index++;
++
++ /* check for more than one DN field with same name */
++ if (match_index != config_dn_field_index)
++ continue;
++
++ /* Check wildcard at the right end side */
++ /* E.g., if OU=develop* mentioned in configuration, allow 'OU'
++ * of the subject in the client certificate to start with
++ * 'develop' */
++ if (len > 0 && value[len - 1] == '*') {
++ /* Compare actual certificate DN field value with
++ * configuration DN field value up to the specified
++ * length. */
++ ret = ASN1_STRING_length(cn) >= len - 1 &&
++ os_memcmp(ASN1_STRING_get0_data(cn), value,
++ len - 1) == 0;
++ } else {
++ /* Compare actual certificate DN field value with
++ * configuration DN field value */
++ ret = ASN1_STRING_length(cn) == len &&
++ os_memcmp(ASN1_STRING_get0_data(cn), value,
++ len) == 0;
++ }
++ if (!ret) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'",
++ field, value, ASN1_STRING_get0_data(cn));
++ }
++ break;
++ }
++
++ return ret;
++}
++
++
++/**
++ * get_value_from_field - Get value from DN field
++ * @cert: Certificate
++ * @field_str: DN field string which is passed from configuration file (e.g.,
++ * C=US)
++ * @dn_cnt: DN matching context
++ * Returns: 1 on success and 0 on failure
++ */
++static int get_value_from_field(const X509 *cert, char *field_str,
++ struct tls_dn_field_order_cnt *dn_cnt)
++{
++ int nid;
++ char *context = NULL, *name, *value;
++
++ if (os_strcmp(field_str, "*") == 0)
++ return 1; /* wildcard matches everything */
++
++ name = str_token(field_str, "=", &context);
++ if (!name)
++ return 0;
++
++ /* Compare all configured DN fields and assign nid based on that to
++ * fetch correct value from certificate subject */
++ if (os_strcmp(name, "CN") == 0) {
++ nid = NID_commonName;
++ dn_cnt->cn++;
++ } else if(os_strcmp(name, "C") == 0) {
++ nid = NID_countryName;
++ dn_cnt->c++;
++ } else if (os_strcmp(name, "L") == 0) {
++ nid = NID_localityName;
++ dn_cnt->l++;
++ } else if (os_strcmp(name, "ST") == 0) {
++ nid = NID_stateOrProvinceName;
++ dn_cnt->st++;
++ } else if (os_strcmp(name, "O") == 0) {
++ nid = NID_organizationName;
++ dn_cnt->o++;
++ } else if (os_strcmp(name, "OU") == 0) {
++ nid = NID_organizationalUnitName;
++ dn_cnt->ou++;
++ } else if (os_strcmp(name, "emailAddress") == 0) {
++ nid = NID_pkcs9_emailAddress;
++ dn_cnt->email++;
++ } else {
++ wpa_printf(MSG_ERROR,
++ "TLS: Unknown field '%s' in check_cert_subject", name);
++ return 0;
++ }
++
++ value = str_token(field_str, "=", &context);
++ if (!value) {
++ wpa_printf(MSG_ERROR,
++ "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject",
++ name);
++ return 0;
++ }
++
++ return match_dn_field(cert, nid, name, value, dn_cnt);
++}
++
++
++/**
++ * tls_match_dn_field - Match subject DN field with check_cert_subject
++ * @cert: Certificate
++ * @match: check_cert_subject string
++ * Returns: Return 1 on success and 0 on failure
++*/
++static int tls_match_dn_field(X509 *cert, const char *match)
++{
++ const char *token, *last = NULL;
++ char field[256];
++ struct tls_dn_field_order_cnt dn_cnt;
++
++ os_memset(&dn_cnt, 0, sizeof(dn_cnt));
++
++ /* Maximum length of each DN field is 255 characters */
++
++ /* Process each '/' delimited field */
++ while ((token = cstr_token(match, "/", &last))) {
++ if (last - token >= (int) sizeof(field)) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Too long DN matching field value in '%s'",
++ match);
++ return 0;
++ }
++ os_memcpy(field, token, last - token);
++ field[last - token] = '\0';
++
++ if (!get_value_from_field(cert, field, &dn_cnt)) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'",
++ field);
++ return 0;
++ }
++ }
++
++ return 1;
++}
++
++
++#ifndef CONFIG_NATIVE_WINDOWS
++static int tls_match_suffix_helper(X509 *cert, const char *match,
++ size_t match_len, int full)
++{
+ GENERAL_NAME *gen;
+ void *ext;
+ int i;
+@@ -1342,13 +2004,15 @@
+ gen->d.dNSName->data,
+ gen->d.dNSName->length);
+ if (domain_suffix_match(gen->d.dNSName->data,
+- gen->d.dNSName->length, match, full) ==
+- 1) {
++ gen->d.dNSName->length,
++ match, match_len, full) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+ full ? "Match" : "Suffix match");
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+ return 1;
+ }
+ }
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+ if (dns_name) {
+ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+@@ -1372,8 +2036,8 @@
+ continue;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+ cn->data, cn->length);
+- if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+- {
++ if (domain_suffix_match(cn->data, cn->length,
++ match, match_len, full) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+ full ? "Match" : "Suffix match");
+ return 1;
+@@ -1383,7 +2047,26 @@
+ wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+ full ? "": "suffix ");
+ return 0;
++}
+ #endif /* CONFIG_NATIVE_WINDOWS */
++
++
++static int tls_match_suffix(X509 *cert, const char *match, int full)
++{
++#ifdef CONFIG_NATIVE_WINDOWS
++ /* wincrypt.h has conflicting X509_NAME definition */
++ return -1;
++#else /* CONFIG_NATIVE_WINDOWS */
++ const char *token, *last = NULL;
++
++ /* Process each match alternative separately until a match is found */
++ while ((token = cstr_token(match, ";", &last))) {
++ if (tls_match_suffix_helper(cert, token, last - token, full))
++ return 1;
++ }
++
++ return 0;
++#endif /* CONFIG_NATIVE_WINDOWS */
+ }
+
+
+@@ -1481,6 +2164,8 @@
+ GENERAL_NAME *gen;
+ void *ext;
+ stack_index_t i;
++ ASN1_INTEGER *ser;
++ char serial_num[128];
+ #ifdef CONFIG_SHA256
+ u8 hash[32];
+ #endif /* CONFIG_SHA256 */
+@@ -1489,7 +2174,8 @@
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+- if (conn->cert_probe || context->cert_in_cb) {
++ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
++ context->cert_in_cb) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+@@ -1508,6 +2194,14 @@
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
+
++ ser = X509_get_serialNumber(err_cert);
++ if (ser) {
++ wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
++ ASN1_STRING_get0_data(ser),
++ ASN1_STRING_length(ser));
++ ev.peer_cert.serial_num = serial_num;
++ }
++
+ ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ char *pos;
+@@ -1544,6 +2238,7 @@
+ pos += gen->d.ia5->length;
+ *pos = '\0';
+ }
++ sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+ for (alt = 0; alt < num_altsubject; alt++)
+ ev.peer_cert.altsubject[alt] = altsubject[alt];
+@@ -1565,6 +2260,7 @@
+ struct tls_connection *conn;
+ struct tls_context *context;
+ char *match, *altmatch, *suffix_match, *domain_match;
++ const char *check_cert_subject;
+ const char *err_str;
+
+ err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+@@ -1605,6 +2301,13 @@
+ "time mismatch");
+ preverify_ok = 1;
+ }
++ if (!preverify_ok && !conn->data->check_crl_strict &&
++ (err == X509_V_ERR_CRL_HAS_EXPIRED ||
++ err == X509_V_ERR_CRL_NOT_YET_VALID)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Ignore certificate validity CRL time mismatch");
++ preverify_ok = 1;
++ }
+
+ err_str = X509_verify_cert_error_string(err);
+
+@@ -1658,6 +2361,18 @@
+ "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+ preverify_ok, err, err_str,
+ conn->ca_cert_verify, depth, buf);
++ check_cert_subject = conn->check_cert_subject;
++ if (!check_cert_subject)
++ check_cert_subject = conn->data->check_cert_subject;
++ if (check_cert_subject) {
++ if (depth == 0 &&
++ !tls_match_dn_field(err_cert, check_cert_subject)) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Distinguished Name",
++ TLS_FAIL_DN_MISMATCH);
++ }
++ }
+ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+ "match with '%s'", buf, match);
+@@ -1701,7 +2416,64 @@
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
+
+- if (preverify_ok && context->event_cb != NULL)
++#ifdef CONFIG_SUITEB
++ if (conn->flags & TLS_CONN_SUITEB) {
++ EVP_PKEY *pk;
++ RSA *rsa;
++ int len = -1;
++
++ pk = X509_get_pubkey(err_cert);
++ if (pk) {
++ rsa = EVP_PKEY_get1_RSA(pk);
++ if (rsa) {
++ len = RSA_bits(rsa);
++ RSA_free(rsa);
++ }
++ EVP_PKEY_free(pk);
++ }
++
++ if (len >= 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: RSA modulus size: %d bits", len);
++ if (len < 3072) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(
++ conn, err_cert, err,
++ depth, buf,
++ "Insufficient RSA modulus size",
++ TLS_FAIL_INSUFFICIENT_KEY_LEN);
++ }
++ }
++ }
++#endif /* CONFIG_SUITEB */
++
++#ifdef OPENSSL_IS_BORINGSSL
++ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
++ preverify_ok) {
++ enum ocsp_result res;
++
++ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
++ conn->peer_issuer,
++ conn->peer_issuer_issuer);
++ if (res == OCSP_REVOKED) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "certificate revoked",
++ TLS_FAIL_REVOKED);
++ if (err == X509_V_OK)
++ X509_STORE_CTX_set_error(
++ x509_ctx, X509_V_ERR_CERT_REVOKED);
++ } else if (res != OCSP_GOOD &&
++ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
++ preverify_ok = 0;
++ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "bad certificate status response",
++ TLS_FAIL_UNSPECIFIED);
++ }
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++
++ if (depth == 0 && preverify_ok && context->event_cb != NULL)
+ context->event_cb(context->cb_ctx,
+ TLS_CERT_CHAIN_SUCCESS, NULL);
+
+@@ -1837,30 +2609,40 @@
+ }
+
+ #ifdef ANDROID
++ /* Single alias */
+ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
+- BIO *bio = BIO_from_keystore(&ca_cert[11]);
+- STACK_OF(X509_INFO) *stack = NULL;
+- stack_index_t i;
++ if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
++ &ca_cert[11]) < 0)
++ return -1;
++ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
++ return 0;
++ }
+
+- if (bio) {
+- stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+- BIO_free(bio);
+- }
+- if (!stack)
++ /* Multiple aliases separated by space */
++ if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
++ char *aliases = os_strdup(&ca_cert[12]);
++ const char *delim = " ";
++ int rc = 0;
++ char *savedptr;
++ char *alias;
++
++ if (!aliases)
+ return -1;
+-
+- for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+- X509_INFO *info = sk_X509_INFO_value(stack, i);
+- if (info->x509) {
+- X509_STORE_add_cert(ssl_ctx->cert_store,
+- info->x509);
++ alias = strtok_r(aliases, delim, &savedptr);
++ for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
++ if (tls_add_ca_from_keystore_encoded(
++ SSL_CTX_get_cert_store(ssl_ctx), alias)) {
++ wpa_printf(MSG_WARNING,
++ "OpenSSL: %s - Failed to add ca_cert %s from keystore",
++ __func__, alias);
++ rc = -1;
++ break;
+ }
+- if (info->crl) {
+- X509_STORE_add_crl(ssl_ctx->cert_store,
+- info->crl);
+- }
+ }
+- sk_X509_INFO_pop_free(stack, X509_INFO_free);
++ os_free(aliases);
++ if (rc)
++ return rc;
++
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ return 0;
+ }
+@@ -1928,6 +2710,9 @@
+ SSL_CTX_set_client_CA_list(ssl_ctx,
+ SSL_load_client_CA_file(ca_cert));
+ #endif /* OPENSSL_NO_STDIO */
++
++ os_free(data->ca_cert);
++ data->ca_cert = os_strdup(ca_cert);
+ }
+
+ return 0;
+@@ -1934,7 +2719,7 @@
+ }
+
+
+-int tls_global_set_verify(void *ssl_ctx, int check_crl)
++int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
+ {
+ int flags;
+
+@@ -1951,6 +2736,10 @@
+ if (check_crl == 2)
+ flags |= X509_V_FLAG_CRL_CHECK_ALL;
+ X509_STORE_set_flags(cs, flags);
++
++ data->check_crl = check_crl;
++ data->check_crl_strict = strict;
++ os_get_reltime(&data->crl_last_reload);
+ }
+ return 0;
+ }
+@@ -1960,7 +2749,8 @@
+ const char *subject_match,
+ const char *altsubject_match,
+ const char *suffix_match,
+- const char *domain_match)
++ const char *domain_match,
++ const char *check_cert_subject)
+ {
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+@@ -1994,19 +2784,60 @@
+ return -1;
+ }
+
++ os_free(conn->check_cert_subject);
++ conn->check_cert_subject = NULL;
++ if (check_cert_subject) {
++ conn->check_cert_subject = os_strdup(check_cert_subject);
++ if (!conn->check_cert_subject)
++ return -1;
++ }
++
+ return 0;
+ }
+
+
+-static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
++#ifdef CONFIG_SUITEB
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++static int suiteb_cert_cb(SSL *ssl, void *arg)
+ {
++ struct tls_connection *conn = arg;
++
++ /*
++ * This cert_cb() is not really the best location for doing a
++ * constraint check for the ServerKeyExchange message, but this seems to
++ * be the only place where the current OpenSSL sequence can be
++ * terminated cleanly with an TLS alert going out to the server.
++ */
++
++ if (!(conn->flags & TLS_CONN_SUITEB))
++ return 1;
++
++ /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
++ if (conn->cipher_suite != 0x9f)
++ return 1;
++
++ if (conn->server_dh_prime_len >= 3072)
++ return 1;
++
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
++ conn->server_dh_prime_len);
++ return 0;
++}
++#endif /* OPENSSL_VERSION_NUMBER */
++#endif /* CONFIG_SUITEB */
++
++
++static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
++ const char *openssl_ciphers)
++{
++ SSL *ssl = conn->ssl;
++
+ #ifdef SSL_OP_NO_TICKET
+ if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
+ SSL_set_options(ssl, SSL_OP_NO_TICKET);
+-#ifdef SSL_clear_options
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+-#endif /* SSL_clear_options */
+ #endif /* SSL_OP_NO_TICKET */
+
+ #ifdef SSL_OP_NO_TLSv1
+@@ -2027,6 +2858,165 @@
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
+ #endif /* SSL_OP_NO_TLSv1_2 */
++#ifdef SSL_OP_NO_TLSv1_3
++ if (flags & TLS_CONN_DISABLE_TLSv1_3)
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
++ else
++ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
++#endif /* SSL_OP_NO_TLSv1_3 */
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ if (flags & (TLS_CONN_ENABLE_TLSv1_0 |
++ TLS_CONN_ENABLE_TLSv1_1 |
++ TLS_CONN_ENABLE_TLSv1_2)) {
++ int version = 0;
++
++ /* Explicit request to enable TLS versions even if needing to
++ * override systemwide policies. */
++ if (flags & TLS_CONN_ENABLE_TLSv1_0) {
++ version = TLS1_VERSION;
++ } else if (flags & TLS_CONN_ENABLE_TLSv1_1) {
++ if (!(flags & TLS_CONN_DISABLE_TLSv1_0))
++ version = TLS1_1_VERSION;
++ } else if (flags & TLS_CONN_ENABLE_TLSv1_2) {
++ if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 |
++ TLS_CONN_DISABLE_TLSv1_1)))
++ version = TLS1_2_VERSION;
++ }
++ if (!version) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Invalid TLS version configuration");
++ return -1;
++ }
++
++ if (SSL_set_min_proto_version(ssl, version) != 1) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Failed to set minimum TLS version");
++ return -1;
++ }
++ }
++#endif /* >= 1.1.0 */
++
++#ifdef CONFIG_SUITEB
++#ifdef OPENSSL_IS_BORINGSSL
++ /* Start with defaults from BoringSSL */
++ SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
++#endif /* OPENSSL_IS_BORINGSSL */
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++ if (flags & TLS_CONN_SUITEB_NO_ECDH) {
++ const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
++
++ if (openssl_ciphers) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Override ciphers for Suite B (no ECDH): %s",
++ openssl_ciphers);
++ ciphers = openssl_ciphers;
++ }
++ if (SSL_set_cipher_list(ssl, ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B ciphers");
++ return -1;
++ }
++ } else if (flags & TLS_CONN_SUITEB) {
++ EC_KEY *ecdh;
++ const char *ciphers =
++ "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
++ int nid[1] = { NID_secp384r1 };
++
++ if (openssl_ciphers) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Override ciphers for Suite B: %s",
++ openssl_ciphers);
++ ciphers = openssl_ciphers;
++ }
++ if (SSL_set_cipher_list(ssl, ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B ciphers");
++ return -1;
++ }
++
++ if (SSL_set1_curves(ssl, nid, 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B curves");
++ return -1;
++ }
++
++ ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
++ if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
++ EC_KEY_free(ecdh);
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set ECDH parameter");
++ return -1;
++ }
++ EC_KEY_free(ecdh);
++ }
++ if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
++#ifdef OPENSSL_IS_BORINGSSL
++ uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
++
++ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
++ 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B sigalgs");
++ return -1;
++ }
++#else /* OPENSSL_IS_BORINGSSL */
++ /* ECDSA+SHA384 if need to add EC support here */
++ if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B sigalgs");
++ return -1;
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1);
++ SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
++ SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
++ }
++#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
++ if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: Suite B RSA case not supported with this OpenSSL version");
++ return -1;
++ }
++#endif /* OPENSSL_VERSION_NUMBER */
++
++#ifdef OPENSSL_IS_BORINGSSL
++ if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
++ uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
++ int nid[1] = { NID_secp384r1 };
++
++ if (SSL_set1_curves(ssl, nid, 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B curves");
++ return -1;
++ }
++
++ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
++ 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set Suite B sigalgs");
++ return -1;
++ }
++ }
++#else /* OPENSSL_IS_BORINGSSL */
++ if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) &&
++ openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set openssl_ciphers '%s'",
++ openssl_ciphers);
++ return -1;
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++#else /* CONFIG_SUITEB */
++ if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set openssl_ciphers '%s'",
++ openssl_ciphers);
++ return -1;
++ }
++#endif /* CONFIG_SUITEB */
++
++ return 0;
+ }
+
+
+@@ -2050,7 +3040,8 @@
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+- tls_set_conn_flags(conn->ssl, flags);
++ if (tls_set_conn_flags(conn, flags, NULL) < 0)
++ return -1;
+ conn->flags = flags;
+
+ SSL_set_accept_state(conn->ssl);
+@@ -2082,6 +3073,17 @@
+ if (client_cert == NULL && client_cert_blob == NULL)
+ return 0;
+
++#ifdef PKCS12_FUNCS
++#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
++ /*
++ * Clear previously set extra chain certificates, if any, from PKCS#12
++ * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
++ * chain properly.
++ */
++ SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
++#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
++#endif /* PKCS12_FUNCS */
++
+ if (client_cert_blob &&
+ SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+ client_cert_blob_len) == 1) {
+@@ -2103,7 +3105,6 @@
+ int ret = -1;
+ if (bio) {
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+- BIO_free(bio);
+ }
+ if (x509) {
+ if (SSL_use_certificate(conn->ssl, x509) == 1)
+@@ -2110,6 +3111,18 @@
+ ret = 0;
+ X509_free(x509);
+ }
++
++ /* Read additional certificates into the chain. */
++ while (bio) {
++ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
++ if (x509) {
++ /* Takes ownership of x509 */
++ SSL_add0_chain_cert(conn->ssl, x509);
++ } else {
++ BIO_free(bio);
++ bio = NULL;
++ }
++ }
+ return ret;
+ }
+ #endif /* ANDROID */
+@@ -2122,6 +3135,15 @@
+ return 0;
+ }
+
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
++ !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
++ if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) {
++ ERR_clear_error();
++ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file"
++ " --> OK");
++ return 0;
++ }
++#else
+ if (SSL_use_certificate_file(conn->ssl, client_cert,
+ SSL_FILETYPE_PEM) == 1) {
+ ERR_clear_error();
+@@ -2129,6 +3151,7 @@
+ " --> OK");
+ return 0;
+ }
++#endif
+
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_file failed");
+@@ -2168,16 +3191,6 @@
+ }
+
+
+-static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+-{
+- if (password == NULL) {
+- return 0;
+- }
+- os_strlcpy(buf, (char *) password, size);
+- return os_strlen(buf);
+-}
+-
+-
+ #ifdef PKCS12_FUNCS
+ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
+ const char *passwd)
+@@ -2229,28 +3242,42 @@
+ }
+
+ if (certs) {
+-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+- SSL_clear_chain_certs(ssl);
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
++ if (ssl)
++ SSL_clear_chain_certs(ssl);
++ else
++ SSL_CTX_clear_chain_certs(data->ssl);
+ while ((cert = sk_X509_pop(certs)) != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+ " from PKCS12: subject='%s'", buf);
+- if (SSL_add1_chain_cert(ssl, cert) != 1) {
++ if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) ||
++ (!ssl && SSL_CTX_add1_chain_cert(data->ssl,
++ cert) != 1)) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to add additional certificate");
+ res = -1;
++ X509_free(cert);
+ break;
+ }
++ X509_free(cert);
+ }
+ if (!res) {
+ /* Try to continue anyway */
+ }
+- sk_X509_free(certs);
++ sk_X509_pop_free(certs, X509_free);
+ #ifndef OPENSSL_IS_BORINGSSL
+- res = SSL_build_cert_chain(ssl,
+- SSL_BUILD_CHAIN_FLAG_CHECK |
+- SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++ if (ssl)
++ res = SSL_build_cert_chain(
++ ssl,
++ SSL_BUILD_CHAIN_FLAG_CHECK |
++ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++ else
++ res = SSL_CTX_build_cert_chain(
++ data->ssl,
++ SSL_BUILD_CHAIN_FLAG_CHECK |
++ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+ if (!res) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to build certificate chain");
+@@ -2265,9 +3292,7 @@
+ */
+ res = 0;
+ #else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_clear_extra_chain_certs(data->ssl);
+-#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
+ while ((cert = sk_X509_pop(certs)) != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+@@ -2279,11 +3304,12 @@
+ */
+ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+ {
++ X509_free(cert);
+ res = -1;
+ break;
+ }
+ }
+- sk_X509_free(certs);
++ sk_X509_pop_free(certs, X509_free);
+ #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+ }
+
+@@ -2463,7 +3489,7 @@
+
+ static int tls_connection_engine_private_key(struct tls_connection *conn)
+ {
+-#ifndef OPENSSL_NO_ENGINE
++#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+ if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "ENGINE: cannot use private key for TLS");
+@@ -2483,6 +3509,64 @@
+ }
+
+
++#ifndef OPENSSL_NO_STDIO
++static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
++{
++ if (!password)
++ return 0;
++ os_strlcpy(buf, (const char *) password, size);
++ return os_strlen(buf);
++}
++#endif /* OPENSSL_NO_STDIO */
++
++
++static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
++ const char *private_key,
++ const char *private_key_passwd)
++{
++#ifndef OPENSSL_NO_STDIO
++ BIO *bio;
++ EVP_PKEY *pkey;
++ int ret;
++
++ /* First try ASN.1 (DER). */
++ bio = BIO_new_file(private_key, "r");
++ if (!bio)
++ return -1;
++ pkey = d2i_PrivateKey_bio(bio, NULL);
++ BIO_free(bio);
++
++ if (pkey) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
++ } else {
++ /* Try PEM with the provided password. */
++ bio = BIO_new_file(private_key, "r");
++ if (!bio)
++ return -1;
++ pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
++ (void *) private_key_passwd);
++ BIO_free(bio);
++ if (!pkey)
++ return -1;
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
++ /* Clear errors from the previous failed load. */
++ ERR_clear_error();
++ }
++
++ if (ssl)
++ ret = SSL_use_PrivateKey(ssl, pkey);
++ else
++ ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
++
++ EVP_PKEY_free(pkey);
++ return ret == 1 ? 0 : -1;
++#else /* OPENSSL_NO_STDIO */
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
++ return -1;
++#endif /* OPENSSL_NO_STDIO */
++}
++
++
+ static int tls_connection_private_key(struct tls_data *data,
+ struct tls_connection *conn,
+ const char *private_key,
+@@ -2490,23 +3574,11 @@
+ const u8 *private_key_blob,
+ size_t private_key_blob_len)
+ {
+- SSL_CTX *ssl_ctx = data->ssl;
+- char *passwd;
+ int ok;
+
+ if (private_key == NULL && private_key_blob == NULL)
+ return 0;
+
+- if (private_key_passwd) {
+- passwd = os_strdup(private_key_passwd);
+- if (passwd == NULL)
+- return -1;
+- } else
+- passwd = NULL;
+-
+- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+-
+ ok = 0;
+ while (private_key_blob) {
+ if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+@@ -2537,7 +3609,8 @@
+ }
+
+ if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
+- private_key_blob_len, passwd) == 0) {
++ private_key_blob_len,
++ private_key_passwd) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
+ "OK");
+ ok = 1;
+@@ -2548,29 +3621,14 @@
+ }
+
+ while (!ok && private_key) {
+-#ifndef OPENSSL_NO_STDIO
+- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+- SSL_FILETYPE_ASN1) == 1) {
+- wpa_printf(MSG_DEBUG, "OpenSSL: "
+- "SSL_use_PrivateKey_File (DER) --> OK");
++ if (tls_use_private_key_file(data, conn->ssl, private_key,
++ private_key_passwd) == 0) {
+ ok = 1;
+ break;
+ }
+
+- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+- SSL_FILETYPE_PEM) == 1) {
+- wpa_printf(MSG_DEBUG, "OpenSSL: "
+- "SSL_use_PrivateKey_File (PEM) --> OK");
+- ok = 1;
+- break;
+- }
+-#else /* OPENSSL_NO_STDIO */
+- wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+- __func__);
+-#endif /* OPENSSL_NO_STDIO */
+-
+- if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
+- == 0) {
++ if (tls_read_pkcs12(data, conn->ssl, private_key,
++ private_key_passwd) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
+ "--> OK");
+ ok = 1;
+@@ -2590,12 +3648,9 @@
+ if (!ok) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load private key");
+- os_free(passwd);
+ return -1;
+ }
+ ERR_clear_error();
+- SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+- os_free(passwd);
+
+ if (!SSL_check_private_key(conn->ssl)) {
+ tls_show_errors(MSG_INFO, __func__, "Private key failed "
+@@ -2613,37 +3668,19 @@
+ const char *private_key_passwd)
+ {
+ SSL_CTX *ssl_ctx = data->ssl;
+- char *passwd;
+
+ if (private_key == NULL)
+ return 0;
+
+- if (private_key_passwd) {
+- passwd = os_strdup(private_key_passwd);
+- if (passwd == NULL)
+- return -1;
+- } else
+- passwd = NULL;
+-
+- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+- if (
+-#ifndef OPENSSL_NO_STDIO
+- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+- SSL_FILETYPE_ASN1) != 1 &&
+- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+- SSL_FILETYPE_PEM) != 1 &&
+-#endif /* OPENSSL_NO_STDIO */
+- tls_read_pkcs12(data, NULL, private_key, passwd)) {
++ if (tls_use_private_key_file(data, NULL, private_key,
++ private_key_passwd) &&
++ tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load private key");
+- os_free(passwd);
+ ERR_clear_error();
+ return -1;
+ }
+- os_free(passwd);
+ ERR_clear_error();
+- SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ tls_show_errors(MSG_INFO, __func__,
+@@ -2812,16 +3849,6 @@
+ if (conn == NULL || keys == NULL)
+ return -1;
+ ssl = conn->ssl;
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+- if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+- return -1;
+-
+- os_memset(keys, 0, sizeof(*keys));
+- keys->client_random = ssl->s3->client_random;
+- keys->client_random_len = SSL3_RANDOM_SIZE;
+- keys->server_random = ssl->s3->server_random;
+- keys->server_random_len = SSL3_RANDOM_SIZE;
+-#else
+ if (ssl == NULL)
+ return -1;
+
+@@ -2832,16 +3859,17 @@
+ keys->server_random = conn->server_random;
+ keys->server_random_len = SSL_get_server_random(
+ ssl, conn->server_random, sizeof(conn->server_random));
+-#endif
+
+ return 0;
+ }
+
+
+-#ifndef CONFIG_FIPS
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
+ static int openssl_get_keyblock_size(SSL *ssl)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ const EVP_CIPHER *c;
+ const EVP_MD *h;
+ int md_size;
+@@ -2851,17 +3879,11 @@
+ return -1;
+
+ c = ssl->enc_read_ctx->cipher;
+-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+ h = EVP_MD_CTX_md(ssl->read_hash);
+-#else
+- h = ssl->read_hash;
+-#endif
+ if (h)
+ md_size = EVP_MD_size(h);
+-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ else if (ssl->s3)
+ md_size = ssl->s3->tmp.new_mac_secret_size;
+-#endif
+ else
+ return -1;
+
+@@ -2899,86 +3921,26 @@
+ EVP_CIPHER_iv_length(c));
+ #endif
+ }
+-#endif /* CONFIG_FIPS */
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+
+
+-static int openssl_tls_prf(struct tls_connection *conn,
+- const char *label, int server_random_first,
+- int skip_keyblock, u8 *out, size_t out_len)
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
+ {
+-#ifdef CONFIG_FIPS
+- wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
+- "mode");
+- return -1;
+-#else /* CONFIG_FIPS */
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+- SSL *ssl;
+- u8 *rnd;
+- int ret = -1;
+- int skip = 0;
+- u8 *tmp_out = NULL;
+- u8 *_out = out;
+- const char *ver;
+-
+- /*
+- * TLS library did not support key generation, so get the needed TLS
+- * session parameters and use an internal implementation of TLS PRF to
+- * derive the key.
+- */
+-
+- if (conn == NULL)
++ if (!conn ||
++ SSL_export_keying_material(conn->ssl, out, out_len, label,
++ os_strlen(label), context, context_len,
++ context != NULL) != 1)
+ return -1;
+- ssl = conn->ssl;
+- if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
+- ssl->session->master_key_length <= 0)
+- return -1;
+- ver = SSL_get_version(ssl);
++ return 0;
++}
+
+- if (skip_keyblock) {
+- skip = openssl_get_keyblock_size(ssl);
+- if (skip < 0)
+- return -1;
+- tmp_out = os_malloc(skip + out_len);
+- if (!tmp_out)
+- return -1;
+- _out = tmp_out;
+- }
+
+- rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+- if (!rnd) {
+- os_free(tmp_out);
+- return -1;
+- }
+-
+- if (server_random_first) {
+- os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+- os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
+- SSL3_RANDOM_SIZE);
+- } else {
+- os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+- os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
+- SSL3_RANDOM_SIZE);
+- }
+-
+- if (os_strcmp(ver, "TLSv1.2") == 0) {
+- tls_prf_sha256(ssl->session->master_key,
+- ssl->session->master_key_length,
+- label, rnd, 2 * SSL3_RANDOM_SIZE,
+- _out, skip + out_len);
+- ret = 0;
+- } else if (tls_prf_sha1_md5(ssl->session->master_key,
+- ssl->session->master_key_length,
+- label, rnd, 2 * SSL3_RANDOM_SIZE,
+- _out, skip + out_len) == 0) {
+- ret = 0;
+- }
+- os_free(rnd);
+- if (ret == 0 && skip_keyblock)
+- os_memcpy(out, _out + skip, out_len);
+- bin_clear_free(tmp_out, skip);
+-
+- return ret;
+-#else
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++#ifdef OPENSSL_NEED_EAP_FAST_PRF
+ SSL *ssl;
+ SSL_SESSION *sess;
+ u8 *rnd;
+@@ -2993,9 +3955,9 @@
+ const char *ver;
+
+ /*
+- * TLS library did not support key generation, so get the needed TLS
+- * session parameters and use an internal implementation of TLS PRF to
+- * derive the key.
++ * TLS library did not support EAP-FAST key generation, so get the
++ * needed TLS session parameters and use an internal implementation of
++ * TLS PRF to derive the key.
+ */
+
+ if (conn == NULL)
+@@ -3008,15 +3970,13 @@
+ if (!ver || !sess)
+ return -1;
+
+- if (skip_keyblock) {
+- skip = openssl_get_keyblock_size(ssl);
+- if (skip < 0)
+- return -1;
+- tmp_out = os_malloc(skip + out_len);
+- if (!tmp_out)
+- return -1;
+- _out = tmp_out;
+- }
++ skip = openssl_get_keyblock_size(ssl);
++ if (skip < 0)
++ return -1;
++ tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++ _out = tmp_out;
+
+ rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+ if (!rnd) {
+@@ -3029,65 +3989,36 @@
+ master_key_len = SSL_SESSION_get_master_key(sess, master_key,
+ sizeof(master_key));
+
+- if (server_random_first) {
+- os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+- os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
+- SSL3_RANDOM_SIZE);
+- } else {
+- os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
+- os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
+- SSL3_RANDOM_SIZE);
+- }
++ os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
++ os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
+
+ if (os_strcmp(ver, "TLSv1.2") == 0) {
+ tls_prf_sha256(master_key, master_key_len,
+- label, rnd, 2 * SSL3_RANDOM_SIZE,
++ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len);
+ ret = 0;
+ } else if (tls_prf_sha1_md5(master_key, master_key_len,
+- label, rnd, 2 * SSL3_RANDOM_SIZE,
++ "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len) == 0) {
+ ret = 0;
+ }
+ os_memset(master_key, 0, sizeof(master_key));
+ os_free(rnd);
+- if (ret == 0 && skip_keyblock)
++ if (ret == 0)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip);
+
+ return ret;
+-#endif
+-#endif /* CONFIG_FIPS */
++#else /* OPENSSL_NEED_EAP_FAST_PRF */
++ wpa_printf(MSG_ERROR,
++ "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
++ return -1;
++#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+ }
+
+
+-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+- const char *label, int server_random_first,
+- int skip_keyblock, u8 *out, size_t out_len)
+-{
+-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+- SSL *ssl;
+- if (conn == NULL)
+- return -1;
+- if (server_random_first || skip_keyblock)
+- return openssl_tls_prf(conn, label,
+- server_random_first, skip_keyblock,
+- out, out_len);
+- ssl = conn->ssl;
+- if (SSL_export_keying_material(ssl, out, out_len, label,
+- os_strlen(label), NULL, 0, 0) == 1) {
+- wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
+- return 0;
+- }
+-#endif
+- return openssl_tls_prf(conn, label, server_random_first,
+- skip_keyblock, out, out_len);
+-}
+-
+-
+ static struct wpabuf *
+-openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+- int server)
++openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
+ {
+ int res;
+ struct wpabuf *out_data;
+@@ -3105,7 +4036,7 @@
+ }
+
+ /* Initiate TLS handshake or continue the existing handshake */
+- if (server)
++ if (conn->server)
+ res = SSL_accept(conn->ssl);
+ else
+ res = SSL_connect(conn->ssl);
+@@ -3120,9 +4051,58 @@
+ else {
+ tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+ conn->failed++;
++ if (!conn->server && !conn->client_hello_generated) {
++ /* The server would not understand TLS Alert
++ * before ClientHello, so simply terminate
++ * handshake on this type of error case caused
++ * by a likely internal error like no ciphers
++ * available. */
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not generate ClientHello");
++ conn->write_alerts++;
++ return NULL;
++ }
+ }
+ }
+
++ if (!conn->server && !conn->failed)
++ conn->client_hello_generated = 1;
++
++#ifdef CONFIG_SUITEB
++ if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
++ os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
++ conn->server_dh_prime_len < 3072) {
++ struct tls_context *context = conn->context;
++
++ /*
++ * This should not be reached since earlier cert_cb should have
++ * terminated the handshake. Keep this check here for extra
++ * protection if anything goes wrong with the more low-level
++ * checks based on having to parse the TLS handshake messages.
++ */
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Server DH prime length: %d bits",
++ conn->server_dh_prime_len);
++
++ if (context->event_cb) {
++ union tls_event_data ev;
++
++ os_memset(&ev, 0, sizeof(ev));
++ ev.alert.is_local = 1;
++ ev.alert.type = "fatal";
++ ev.alert.description = "insufficient security";
++ context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
++ }
++ /*
++ * Could send a TLS Alert to the server, but for now, simply
++ * terminate handshake.
++ */
++ conn->failed++;
++ conn->write_alerts++;
++ return NULL;
++ }
++#endif /* CONFIG_SUITEB */
++
+ /* Get the TLS handshake data to be sent to the server */
+ res = BIO_ctrl_pending(conn->ssl_out);
+ wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+@@ -3192,7 +4172,7 @@
+ static struct wpabuf *
+ openssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+- struct wpabuf **appl_data, int server)
++ struct wpabuf **appl_data)
+ {
+ struct wpabuf *out_data;
+
+@@ -3199,7 +4179,7 @@
+ if (appl_data)
+ *appl_data = NULL;
+
+- out_data = openssl_handshake(conn, in_data, server);
++ out_data = openssl_handshake(conn, in_data);
+ if (out_data == NULL)
+ return NULL;
+ if (conn->invalid_hb_used) {
+@@ -3236,7 +4216,7 @@
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+ {
+- return openssl_connection_handshake(conn, in_data, appl_data, 0);
++ return openssl_connection_handshake(conn, in_data, appl_data);
+ }
+
+
+@@ -3245,7 +4225,8 @@
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+ {
+- return openssl_connection_handshake(conn, in_data, appl_data, 1);
++ conn->server = 1;
++ return openssl_connection_handshake(conn, in_data, appl_data);
+ }
+
+
+@@ -3340,11 +4321,7 @@
+
+ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+ {
+-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+- return conn ? SSL_cache_hit(conn->ssl) : 0;
+-#else
+- return conn ? conn->ssl->hit : 0;
+-#endif
++ return conn ? SSL_session_reused(conn->ssl) : 0;
+ }
+
+
+@@ -3351,7 +4328,7 @@
+ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+ {
+- char buf[100], *pos, *end;
++ char buf[500], *pos, *end;
+ u8 *c;
+ int ret;
+
+@@ -3379,6 +4356,12 @@
+ case TLS_CIPHER_ANON_DH_AES128_SHA:
+ suite = "ADH-AES128-SHA";
+ break;
++ case TLS_CIPHER_RSA_DHE_AES256_SHA:
++ suite = "DHE-RSA-AES256-SHA";
++ break;
++ case TLS_CIPHER_AES256_SHA:
++ suite = "AES256-SHA";
++ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+ "cipher selection: %d", *c);
+@@ -3394,7 +4377,7 @@
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
+-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ if (os_strstr(buf, ":ADH-")) {
+ /*
+@@ -3579,7 +4562,7 @@
+ {
+ struct tls_connection *conn = arg;
+ const unsigned char *p;
+- int len, status, reason;
++ int len, status, reason, res;
+ OCSP_RESPONSE *rsp;
+ OCSP_BASICRESP *basic;
+ OCSP_CERTID *id;
+@@ -3674,23 +4657,42 @@
+ return 0;
+ }
+
+- id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
++ id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
+ if (!id) {
+- wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ return 0;
+ }
+
+- if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+- &this_update, &next_update)) {
++ res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
++ &this_update, &next_update);
++ if (!res) {
++ id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
++ if (!id) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
++ OCSP_BASICRESP_free(basic);
++ OCSP_RESPONSE_free(rsp);
++ return 0;
++ }
++
++ res = OCSP_resp_find_status(basic, id, &status, &reason,
++ &produced_at, &this_update,
++ &next_update);
++ }
++
++ if (!res) {
+ wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+ (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
+ " (OCSP not required)");
++ OCSP_CERTID_free(id);
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
+ }
++ OCSP_CERTID_free(id);
+
+ if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+ tls_show_errors(MSG_INFO, __func__,
+@@ -3765,10 +4767,17 @@
+ const char *cert_id = params->cert_id;
+ const char *ca_cert_id = params->ca_cert_id;
+ const char *engine_id = params->engine ? params->engine_id : NULL;
++ const char *ciphers;
+
+ if (conn == NULL)
+ return -1;
+
++ if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: ocsp=3 not supported");
++ return -1;
++ }
++
+ /*
+ * If the engine isn't explicitly configured, and any of the
+ * cert/key fields are actually PKCS#11 URIs, then automatically
+@@ -3800,7 +4809,7 @@
+ engine_id = "pkcs11";
+
+ #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ if (params->flags & TLS_CONN_EAP_FAST) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Use TLSv1_method() for EAP-FAST");
+@@ -3811,6 +4820,17 @@
+ }
+ }
+ #endif
++#if OPENSSL_VERSION_NUMBER >= 0x10101000L
++#ifdef SSL_OP_NO_TLSv1_3
++ if (params->flags & TLS_CONN_EAP_FAST) {
++ /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
++ * refuses to start the handshake with the modified ciphersuite
++ * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
++ wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
++ SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
++ }
++#endif /* SSL_OP_NO_TLSv1_3 */
++#endif
+ #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+ while ((err = ERR_get_error())) {
+@@ -3829,7 +4849,8 @@
+ params->subject_match,
+ params->altsubject_match,
+ params->suffix_match,
+- params->domain_match))
++ params->domain_match,
++ params->check_cert_subject))
+ return -1;
+
+ if (engine_id && ca_cert_id) {
+@@ -3869,16 +4890,67 @@
+ return -1;
+ }
+
+- if (params->openssl_ciphers &&
+- SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
++ ciphers = params->openssl_ciphers;
++#ifdef CONFIG_SUITEB
++#ifdef OPENSSL_IS_BORINGSSL
++ if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
++ /* BoringSSL removed support for SUITEB192, so need to handle
++ * this with hardcoded ciphersuite and additional checks for
++ * other parameters. */
++ ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
++ }
++#endif /* OPENSSL_IS_BORINGSSL */
++#endif /* CONFIG_SUITEB */
++ if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set cipher string '%s'",
+- params->openssl_ciphers);
++ ciphers);
+ return -1;
+ }
+
+- tls_set_conn_flags(conn->ssl, params->flags);
++ if (!params->openssl_ecdh_curves) {
++#ifndef OPENSSL_IS_BORINGSSL
++#ifndef OPENSSL_NO_EC
++#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
++ (OPENSSL_VERSION_NUMBER < 0x10100000L)
++ if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set ECDH curves to auto");
++ return -1;
++ }
++#endif /* >= 1.0.2 && < 1.1.0 */
++#endif /* OPENSSL_NO_EC */
++#endif /* OPENSSL_IS_BORINGSSL */
++ } else if (params->openssl_ecdh_curves[0]) {
++#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
++ wpa_printf(MSG_INFO,
++ "OpenSSL: ECDH configuration nnot supported");
++ return -1;
++#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
++#ifndef OPENSSL_NO_EC
++ if (SSL_set1_curves_list(conn->ssl,
++ params->openssl_ecdh_curves) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set ECDH curves '%s'",
++ params->openssl_ecdh_curves);
++ return -1;
++ }
++#else /* OPENSSL_NO_EC */
++ wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
++ return -1;
++#endif /* OPENSSL_NO_EC */
++#endif /* OPENSSL_IS_BORINGSSL */
++ }
+
++ if (tls_set_conn_flags(conn, params->flags,
++ params->openssl_ciphers) < 0)
++ return -1;
++
++#ifdef OPENSSL_IS_BORINGSSL
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ SSL_enable_ocsp_stapling(conn->ssl);
++ }
++#else /* OPENSSL_IS_BORINGSSL */
+ #ifdef HAVE_OCSP
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ SSL_CTX *ssl_ctx = data->ssl;
+@@ -3897,6 +4969,7 @@
+ "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
+ }
+ #endif /* HAVE_OCSP */
++#endif /* OPENSSL_IS_BORINGSSL */
+
+ conn->flags = params->flags;
+
+@@ -3918,6 +4991,15 @@
+ __func__, ERR_error_string(err, NULL));
+ }
+
++ os_free(data->check_cert_subject);
++ data->check_cert_subject = NULL;
++ if (params->check_cert_subject) {
++ data->check_cert_subject =
++ os_strdup(params->check_cert_subject);
++ if (!data->check_cert_subject)
++ return -1;
++ }
++
+ if (tls_global_ca_cert(data, params->ca_cert) ||
+ tls_global_client_cert(data, params->client_cert) ||
+ tls_global_private_key(data, params->private_key,
+@@ -3935,13 +5017,49 @@
+ return -1;
+ }
+
++ if (!params->openssl_ecdh_curves) {
++#ifndef OPENSSL_IS_BORINGSSL
++#ifndef OPENSSL_NO_EC
++#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
++ (OPENSSL_VERSION_NUMBER < 0x10100000L)
++ if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set ECDH curves to auto");
++ return -1;
++ }
++#endif /* >= 1.0.2 && < 1.1.0 */
++#endif /* OPENSSL_NO_EC */
++#endif /* OPENSSL_IS_BORINGSSL */
++ } else if (params->openssl_ecdh_curves[0]) {
++#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
++ wpa_printf(MSG_INFO,
++ "OpenSSL: ECDH configuration nnot supported");
++ return -1;
++#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
++#ifndef OPENSSL_NO_EC
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
++#endif
++ if (SSL_CTX_set1_curves_list(ssl_ctx,
++ params->openssl_ecdh_curves) !=
++ 1) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Failed to set ECDH curves '%s'",
++ params->openssl_ecdh_curves);
++ return -1;
++ }
++#else /* OPENSSL_NO_EC */
++ wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
++ return -1;
++#endif /* OPENSSL_NO_EC */
++#endif /* OPENSSL_IS_BORINGSSL */
++ }
++
+ #ifdef SSL_OP_NO_TICKET
+ if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+-#ifdef SSL_CTX_clear_options
+ else
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
+-#endif /* SSL_clear_options */
+ #endif /* SSL_OP_NO_TICKET */
+
+ #ifdef HAVE_OCSP
+@@ -3964,7 +5082,7 @@
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
+-#ifdef OPENSSL_IS_BORINGSSL
++#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
+ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ const SSL_CIPHER **cipher, void *arg)
+@@ -3977,7 +5095,9 @@
+ struct tls_connection *conn = arg;
+ int ret;
+
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && \
++ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+@@ -4030,11 +5150,10 @@
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+ "extension", data, len);
+
+- conn->session_ticket = os_malloc(len);
++ conn->session_ticket = os_memdup(data, len);
+ if (conn->session_ticket == NULL)
+ return 0;
+
+- os_memcpy(conn->session_ticket, data, len);
+ conn->session_ticket_len = len;
+
+ return 1;
+@@ -4072,9 +5191,15 @@
+
+ int tls_get_library_version(char *buf, size_t buf_len)
+ {
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+ OPENSSL_VERSION_TEXT,
++ OpenSSL_version(OPENSSL_VERSION));
++#else
++ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
++ OPENSSL_VERSION_TEXT,
+ SSLeay_version(SSLEAY_VERSION));
++#endif
+ }
+
+
+--- contrib/wpa/src/crypto/tls_openssl.h.orig
++++ contrib/wpa/src/crypto/tls_openssl.h
+@@ -0,0 +1,19 @@
++/*
++ * SSL/TLS interface functions for OpenSSL
++ * Copyright (c) 2004-2015, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#ifndef TLS_OPENSSL_H
++#define TLS_OPENSSL_H
++
++enum ocsp_result {
++ OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
++};
++
++enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
++ X509 *issuer, X509 *issuer_issuer);
++
++#endif /* TLS_OPENSSL_H */
+--- contrib/wpa/src/crypto/tls_openssl_ocsp.c.orig
++++ contrib/wpa/src/crypto/tls_openssl_ocsp.c
+@@ -0,0 +1,846 @@
++/*
++ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
++ * Copyright (c) 2004-2015, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include
++#include
++#include
++#ifdef OPENSSL_IS_BORINGSSL
++#include
++#include
++#endif /* OPENSSL_IS_BORINGSSL */
++
++#include "common.h"
++#include "tls_openssl.h"
++
++
++#ifdef OPENSSL_IS_BORINGSSL
++
++static void tls_show_errors(int level, const char *func, const char *txt)
++{
++ unsigned long err;
++
++ wpa_printf(level, "OpenSSL: %s - %s %s",
++ func, txt, ERR_error_string(ERR_get_error(), NULL));
++
++ while ((err = ERR_get_error())) {
++ wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
++ ERR_error_string(err, NULL));
++ }
++}
++
++
++/*
++ * CertID ::= SEQUENCE {
++ * hashAlgorithm AlgorithmIdentifier,
++ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
++ * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key
++ * serialNumber CertificateSerialNumber }
++ */
++typedef struct {
++ X509_ALGOR *hashAlgorithm;
++ ASN1_OCTET_STRING *issuerNameHash;
++ ASN1_OCTET_STRING *issuerKeyHash;
++ ASN1_INTEGER *serialNumber;
++} CertID;
++
++/*
++ * ResponseBytes ::= SEQUENCE {
++ * responseType OBJECT IDENTIFIER,
++ * response OCTET STRING }
++ */
++typedef struct {
++ ASN1_OBJECT *responseType;
++ ASN1_OCTET_STRING *response;
++} ResponseBytes;
++
++/*
++ * OCSPResponse ::= SEQUENCE {
++ * responseStatus OCSPResponseStatus,
++ * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
++ */
++typedef struct {
++ ASN1_ENUMERATED *responseStatus;
++ ResponseBytes *responseBytes;
++} OCSPResponse;
++
++ASN1_SEQUENCE(ResponseBytes) = {
++ ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
++ ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
++} ASN1_SEQUENCE_END(ResponseBytes);
++
++ASN1_SEQUENCE(OCSPResponse) = {
++ ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
++ ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
++} ASN1_SEQUENCE_END(OCSPResponse);
++
++IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
++
++/*
++ * ResponderID ::= CHOICE {
++ * byName [1] Name,
++ * byKey [2] KeyHash }
++ */
++typedef struct {
++ int type;
++ union {
++ X509_NAME *byName;
++ ASN1_OCTET_STRING *byKey;
++ } value;
++} ResponderID;
++
++/*
++ * RevokedInfo ::= SEQUENCE {
++ * revocationTime GeneralizedTime,
++ * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
++ */
++typedef struct {
++ ASN1_GENERALIZEDTIME *revocationTime;
++ ASN1_ENUMERATED *revocationReason;
++} RevokedInfo;
++
++/*
++ * CertStatus ::= CHOICE {
++ * good [0] IMPLICIT NULL,
++ * revoked [1] IMPLICIT RevokedInfo,
++ * unknown [2] IMPLICIT UnknownInfo }
++ */
++typedef struct {
++ int type;
++ union {
++ ASN1_NULL *good;
++ RevokedInfo *revoked;
++ ASN1_NULL *unknown;
++ } value;
++} CertStatus;
++
++/*
++ * SingleResponse ::= SEQUENCE {
++ * certID CertID,
++ * certStatus CertStatus,
++ * thisUpdate GeneralizedTime,
++ * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
++ * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
++ */
++typedef struct {
++ CertID *certID;
++ CertStatus *certStatus;
++ ASN1_GENERALIZEDTIME *thisUpdate;
++ ASN1_GENERALIZEDTIME *nextUpdate;
++ STACK_OF(X509_EXTENSION) *singleExtensions;
++} SingleResponse;
++
++/*
++ * ResponseData ::= SEQUENCE {
++ * version [0] EXPLICIT Version DEFAULT v1,
++ * responderID ResponderID,
++ * producedAt GeneralizedTime,
++ * responses SEQUENCE OF SingleResponse,
++ * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
++ */
++typedef struct {
++ ASN1_INTEGER *version;
++ ResponderID *responderID;
++ ASN1_GENERALIZEDTIME *producedAt;
++ STACK_OF(SingleResponse) *responses;
++ STACK_OF(X509_EXTENSION) *responseExtensions;
++} ResponseData;
++
++/*
++ * BasicOCSPResponse ::= SEQUENCE {
++ * tbsResponseData ResponseData,
++ * signatureAlgorithm AlgorithmIdentifier,
++ * signature BIT STRING,
++ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
++ */
++typedef struct {
++ ResponseData *tbsResponseData;
++ X509_ALGOR *signatureAlgorithm;
++ ASN1_BIT_STRING *signature;
++ STACK_OF(X509) *certs;
++} BasicOCSPResponse;
++
++ASN1_SEQUENCE(CertID) = {
++ ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
++ ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
++ ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
++ ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
++} ASN1_SEQUENCE_END(CertID);
++
++ASN1_CHOICE(ResponderID) = {
++ ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
++ ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
++} ASN1_CHOICE_END(ResponderID);
++
++ASN1_SEQUENCE(RevokedInfo) = {
++ ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
++ ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
++} ASN1_SEQUENCE_END(RevokedInfo);
++
++ASN1_CHOICE(CertStatus) = {
++ ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
++ ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
++ ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
++} ASN1_CHOICE_END(CertStatus);
++
++ASN1_SEQUENCE(SingleResponse) = {
++ ASN1_SIMPLE(SingleResponse, certID, CertID),
++ ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
++ ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
++ ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
++ ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
++ X509_EXTENSION, 1)
++} ASN1_SEQUENCE_END(SingleResponse);
++
++ASN1_SEQUENCE(ResponseData) = {
++ ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
++ ASN1_SIMPLE(ResponseData, responderID, ResponderID),
++ ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
++ ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
++ ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
++ X509_EXTENSION, 1)
++} ASN1_SEQUENCE_END(ResponseData);
++
++ASN1_SEQUENCE(BasicOCSPResponse) = {
++ ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
++ ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
++ ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
++ ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
++} ASN1_SEQUENCE_END(BasicOCSPResponse);
++
++IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
++
++#define sk_SingleResponse_num(sk) \
++sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
++
++#define sk_SingleResponse_value(sk, i) \
++ ((SingleResponse *) \
++ sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
++
++
++static char * mem_bio_to_str(BIO *out)
++{
++ char *txt;
++ size_t rlen;
++ int res;
++
++ rlen = BIO_ctrl_pending(out);
++ txt = os_malloc(rlen + 1);
++ if (!txt) {
++ BIO_free(out);
++ return NULL;
++ }
++
++ res = BIO_read(out, txt, rlen);
++ BIO_free(out);
++ if (res < 0) {
++ os_free(txt);
++ return NULL;
++ }
++
++ txt[res] = '\0';
++ return txt;
++}
++
++
++static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
++{
++ BIO *out;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ if (!ASN1_GENERALIZEDTIME_print(out, t)) {
++ BIO_free(out);
++ return NULL;
++ }
++
++ return mem_bio_to_str(out);
++}
++
++
++static char * responderid_str(ResponderID *rid)
++{
++ BIO *out;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ switch (rid->type) {
++ case 0:
++ X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
++ break;
++ case 1:
++ i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
++ break;
++ default:
++ BIO_free(out);
++ return NULL;
++ }
++
++ return mem_bio_to_str(out);
++}
++
++
++static char * octet_string_str(ASN1_OCTET_STRING *o)
++{
++ BIO *out;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
++ return mem_bio_to_str(out);
++}
++
++
++static char * integer_str(ASN1_INTEGER *i)
++{
++ BIO *out;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ i2a_ASN1_INTEGER(out, i);
++ return mem_bio_to_str(out);
++}
++
++
++static char * algor_str(X509_ALGOR *alg)
++{
++ BIO *out;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ i2a_ASN1_OBJECT(out, alg->algorithm);
++ return mem_bio_to_str(out);
++}
++
++
++static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
++{
++ BIO *out;
++
++ if (!ext)
++ return NULL;
++
++ out = BIO_new(BIO_s_mem());
++ if (!out)
++ return NULL;
++
++ if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
++ BIO_free(out);
++ return NULL;
++ }
++ return mem_bio_to_str(out);
++}
++
++
++static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
++ ASN1_GENERALIZEDTIME *nextupd)
++{
++ time_t now, tmp;
++
++ if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Invalid OCSP response thisUpdate");
++ return 0;
++ }
++
++ time(&now);
++ tmp = now + 5 * 60; /* allow five minute clock difference */
++ if (X509_cmp_time(thisupd, &tmp) > 0) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
++ return 0;
++ }
++
++ if (!nextupd)
++ return 1; /* OK - no limit on response age */
++
++ if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Invalid OCSP response nextUpdate");
++ return 0;
++ }
++
++ tmp = now - 5 * 60; /* allow five minute clock difference */
++ if (X509_cmp_time(nextupd, &tmp) < 0) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
++ return 0;
++ }
++
++ if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP response nextUpdate before thisUpdate");
++ return 0;
++ }
++
++ /* Both thisUpdate and nextUpdate are valid */
++ return -1;
++}
++
++
++static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
++{
++ X509_NAME *iname;
++ ASN1_BIT_STRING *ikey;
++ const EVP_MD *dgst;
++ unsigned int len;
++ unsigned char md[EVP_MAX_MD_SIZE];
++ ASN1_OCTET_STRING *hash;
++ char *txt;
++
++ dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
++ if (!dgst) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not find matching hash algorithm for OCSP");
++ return -1;
++ }
++
++ iname = X509_get_issuer_name(cert);
++ if (!X509_NAME_digest(iname, dgst, md, &len))
++ return -1;
++ hash = ASN1_OCTET_STRING_new();
++ if (!hash)
++ return -1;
++ if (!ASN1_OCTET_STRING_set(hash, md, len)) {
++ ASN1_OCTET_STRING_free(hash);
++ return -1;
++ }
++
++ txt = octet_string_str(hash);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
++ txt);
++ os_free(txt);
++ }
++
++ if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
++ ASN1_OCTET_STRING_free(hash);
++ return -1;
++ }
++
++ ikey = X509_get0_pubkey_bitstr(issuer);
++ if (!ikey ||
++ !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
++ !ASN1_OCTET_STRING_set(hash, md, len)) {
++ ASN1_OCTET_STRING_free(hash);
++ return -1;
++ }
++
++ txt = octet_string_str(hash);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
++ txt);
++ os_free(txt);
++ }
++
++ if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
++ ASN1_OCTET_STRING_free(hash);
++ return -1;
++ }
++
++ ASN1_OCTET_STRING_free(hash);
++ return 0;
++}
++
++
++static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
++{
++ unsigned int i;
++ unsigned char hash[SHA_DIGEST_LENGTH];
++
++ if (rid->type == 0) {
++ /* byName */
++ return X509_find_by_subject(certs, rid->value.byName);
++ }
++
++ /* byKey */
++ if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
++ return NULL;
++ for (i = 0; i < sk_X509_num(certs); i++) {
++ X509 *x = sk_X509_value(certs, i);
++
++ X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
++ if (os_memcmp(rid->value.byKey->data, hash,
++ SHA_DIGEST_LENGTH) == 0)
++ return x;
++ }
++
++ return NULL;
++}
++
++
++enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
++ X509 *issuer, X509 *issuer_issuer)
++{
++ const uint8_t *resp_data;
++ size_t resp_len;
++ OCSPResponse *resp;
++ int status;
++ ResponseBytes *bytes;
++ const u8 *basic_data;
++ size_t basic_len;
++ BasicOCSPResponse *basic;
++ ResponseData *rd;
++ char *txt;
++ int i, num;
++ unsigned int j, num_resp;
++ SingleResponse *matching_resp = NULL, *cmp_sresp;
++ enum ocsp_result result = OCSP_INVALID;
++ X509_STORE *store;
++ STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
++ X509_STORE_CTX ctx;
++ X509 *signer, *tmp_cert;
++ int signer_trusted = 0;
++ EVP_PKEY *skey;
++ int ret;
++ char buf[256];
++
++ txt = integer_str(X509_get_serialNumber(cert));
++ if (txt) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
++ os_free(txt);
++ }
++
++ SSL_get0_ocsp_response(ssl, &resp_data, &resp_len);
++ if (resp_data == NULL || resp_len == 0) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
++ return OCSP_NO_RESPONSE;
++ }
++
++ wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
++
++ resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
++ if (!resp) {
++ wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
++ return OCSP_INVALID;
++ }
++
++ status = ASN1_ENUMERATED_get(resp->responseStatus);
++ if (status != 0) {
++ wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
++ status);
++ return OCSP_INVALID;
++ }
++
++ bytes = resp->responseBytes;
++
++ if (!bytes ||
++ OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Could not find BasicOCSPResponse");
++ return OCSP_INVALID;
++ }
++
++ basic_data = ASN1_STRING_data(bytes->response);
++ basic_len = ASN1_STRING_length(bytes->response);
++ wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
++ basic_data, basic_len);
++
++ basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
++ if (!basic) {
++ wpa_printf(MSG_INFO,
++ "OpenSSL: Could not parse BasicOCSPResponse");
++ OCSPResponse_free(resp);
++ return OCSP_INVALID;
++ }
++
++ rd = basic->tbsResponseData;
++
++ if (basic->certs) {
++ untrusted = sk_X509_dup(basic->certs);
++ if (!untrusted)
++ goto fail;
++
++ num = sk_X509_num(basic->certs);
++ for (i = 0; i < num; i++) {
++ X509 *extra_cert;
++
++ extra_cert = sk_X509_value(basic->certs, i);
++ X509_NAME_oneline(X509_get_subject_name(extra_cert),
++ buf, sizeof(buf));
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: BasicOCSPResponse cert %s", buf);
++
++ if (!sk_X509_push(untrusted, extra_cert)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not add certificate to the untrusted stack");
++ }
++ }
++ }
++
++ store = SSL_CTX_get_cert_store(ssl_ctx);
++ if (issuer) {
++ if (X509_STORE_add_cert(store, issuer) != 1) {
++ tls_show_errors(MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer to certificate store");
++ }
++ certs = sk_X509_new_null();
++ if (certs) {
++ tmp_cert = X509_dup(issuer);
++ if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
++ tls_show_errors(
++ MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer to OCSP responder trust store");
++ X509_free(tmp_cert);
++ sk_X509_free(certs);
++ certs = NULL;
++ }
++ if (certs && issuer_issuer) {
++ tmp_cert = X509_dup(issuer_issuer);
++ if (tmp_cert &&
++ !sk_X509_push(certs, tmp_cert)) {
++ tls_show_errors(
++ MSG_INFO, __func__,
++ "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
++ X509_free(tmp_cert);
++ }
++ }
++ }
++ }
++
++ signer = ocsp_find_signer(certs, rd->responderID);
++ if (!signer)
++ signer = ocsp_find_signer(untrusted, rd->responderID);
++ else
++ signer_trusted = 1;
++ if (!signer) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not find OCSP signer certificate");
++ goto fail;
++ }
++
++ skey = X509_get_pubkey(signer);
++ if (!skey) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not get OCSP signer public key");
++ goto fail;
++ }
++ if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
++ basic->signatureAlgorithm, basic->signature,
++ basic->tbsResponseData, skey) <= 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: BasicOCSPResponse signature is invalid");
++ goto fail;
++ }
++
++ X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
++ buf);
++
++ if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted))
++ goto fail;
++ X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
++ ret = X509_verify_cert(&ctx);
++ chain = X509_STORE_CTX_get1_chain(&ctx);
++ X509_STORE_CTX_cleanup(&ctx);
++ if (ret <= 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not validate OCSP signer certificate");
++ goto fail;
++ }
++
++ if (!chain || sk_X509_num(chain) <= 0) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
++ goto fail;
++ }
++
++ if (!signer_trusted) {
++ X509_check_purpose(signer, -1, 0);
++ if ((signer->ex_flags & EXFLAG_XKUSAGE) &&
++ (signer->ex_xkusage & XKU_OCSP_SIGN)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP signer certificate delegation OK");
++ } else {
++ tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
++ if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
++ X509_TRUST_TRUSTED) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP signer certificate not trusted");
++ result = OCSP_NO_RESPONSE;
++ goto fail;
++ }
++ }
++ }
++
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
++ ASN1_INTEGER_get(rd->version));
++
++ txt = responderid_str(rd->responderID);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
++ txt);
++ os_free(txt);
++ }
++
++ txt = generalizedtime_str(rd->producedAt);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
++ txt);
++ os_free(txt);
++ }
++
++ num_resp = sk_SingleResponse_num(rd->responses);
++ if (num_resp == 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
++ result = OCSP_NO_RESPONSE;
++ goto fail;
++ }
++ cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
++ for (j = 0; j < num_resp; j++) {
++ SingleResponse *sresp;
++ CertID *cid1, *cid2;
++
++ sresp = sk_SingleResponse_value(rd->responses, j);
++ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
++ j + 1, num_resp);
++
++ txt = algor_str(sresp->certID->hashAlgorithm);
++ if (txt) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: certID hashAlgorithm: %s", txt);
++ os_free(txt);
++ }
++
++ txt = octet_string_str(sresp->certID->issuerNameHash);
++ if (txt) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: certID issuerNameHash: %s", txt);
++ os_free(txt);
++ }
++
++ txt = octet_string_str(sresp->certID->issuerKeyHash);
++ if (txt) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: certID issuerKeyHash: %s", txt);
++ os_free(txt);
++ }
++
++ txt = integer_str(sresp->certID->serialNumber);
++ if (txt) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: certID serialNumber: %s", txt);
++ os_free(txt);
++ }
++
++ switch (sresp->certStatus->type) {
++ case 0:
++ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
++ break;
++ case 1:
++ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
++ break;
++ default:
++ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
++ break;
++ }
++
++ txt = generalizedtime_str(sresp->thisUpdate);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
++ os_free(txt);
++ }
++
++ if (sresp->nextUpdate) {
++ txt = generalizedtime_str(sresp->nextUpdate);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
++ txt);
++ os_free(txt);
++ }
++ }
++
++ txt = extensions_str("singleExtensions",
++ sresp->singleExtensions);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
++ os_free(txt);
++ }
++
++ cid1 = cmp_sresp->certID;
++ cid2 = sresp->certID;
++ if (j > 0 &&
++ (OBJ_cmp(cid1->hashAlgorithm->algorithm,
++ cid2->hashAlgorithm->algorithm) != 0 ||
++ ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
++ cid2->issuerNameHash) != 0 ||
++ ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
++ cid2->issuerKeyHash) != 0)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
++ goto fail;
++ }
++
++ if (!matching_resp && issuer &&
++ ASN1_INTEGER_cmp(sresp->certID->serialNumber,
++ X509_get_serialNumber(cert)) == 0 &&
++ issuer_match(cert, issuer, sresp->certID) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: This response matches peer certificate");
++ matching_resp = sresp;
++ }
++ }
++
++ txt = extensions_str("responseExtensions", rd->responseExtensions);
++ if (txt) {
++ wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
++ os_free(txt);
++ }
++
++ if (!matching_resp) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: Could not find OCSP response that matches the peer certificate");
++ result = OCSP_NO_RESPONSE;
++ goto fail;
++ }
++
++ if (!ocsp_resp_valid(matching_resp->thisUpdate,
++ matching_resp->nextUpdate)) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP response not valid at this time");
++ goto fail;
++ }
++
++ if (matching_resp->certStatus->type == 1) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
++ result = OCSP_REVOKED;
++ goto fail;
++ }
++
++ if (matching_resp->certStatus->type != 0) {
++ wpa_printf(MSG_DEBUG,
++ "OpenSSL: OCSP response did not indicate good status");
++ result = OCSP_NO_RESPONSE;
++ goto fail;
++ }
++
++ /* OCSP response indicated the certificate is good. */
++ result = OCSP_GOOD;
++fail:
++ sk_X509_pop_free(chain, X509_free);
++ sk_X509_free(untrusted);
++ sk_X509_pop_free(certs, X509_free);
++ BasicOCSPResponse_free(basic);
++ OCSPResponse_free(resp);
++
++ return result;
++}
++
++#endif /* OPENSSL_IS_BORINGSSL */
+--- contrib/wpa/src/crypto/tls_wolfssl.c.orig
++++ contrib/wpa/src/crypto/tls_wolfssl.c
+@@ -0,0 +1,2201 @@
++/*
++ * SSL/TLS interface functions for wolfSSL TLS case
++ * Copyright (c) 2004-2017, Jouni Malinen
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++
++#include "common.h"
++#include "crypto.h"
++#include "crypto/sha1.h"
++#include "crypto/sha256.h"
++#include "tls.h"
++
++/* wolfSSL includes */
++#include
++#include
++#include
++#include
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#define HAVE_AESGCM
++#include
++#endif
++
++#if !defined(CONFIG_FIPS) && \
++ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
++ defined(EAP_SERVER_FAST))
++#define WOLFSSL_NEED_EAP_FAST_PRF
++#endif
++
++#define SECRET_LEN 48
++#define RAN_LEN 32
++#define SESSION_TICKET_LEN 256
++
++static int tls_ref_count = 0;
++
++static int tls_ex_idx_session = 0;
++
++
++/* tls input data for wolfSSL Read Callback */
++struct tls_in_data {
++ const struct wpabuf *in_data;
++ size_t consumed; /* how many bytes have we used already */
++};
++
++/* tls output data for wolfSSL Write Callback */
++struct tls_out_data {
++ struct wpabuf *out_data;
++};
++
++struct tls_context {
++ void (*event_cb)(void *ctx, enum tls_event ev,
++ union tls_event_data *data);
++ void *cb_ctx;
++ int cert_in_cb;
++ char *ocsp_stapling_response;
++};
++
++static struct tls_context *tls_global = NULL;
++
++/* wolfssl tls_connection */
++struct tls_connection {
++ struct tls_context *context;
++ WOLFSSL *ssl;
++ int read_alerts;
++ int write_alerts;
++ int failed;
++ struct tls_in_data input;
++ struct tls_out_data output;
++ char *subject_match;
++ char *alt_subject_match;
++ char *suffix_match;
++ char *domain_match;
++
++ u8 srv_cert_hash[32];
++
++ unsigned char client_random[RAN_LEN];
++ unsigned char server_random[RAN_LEN];
++ unsigned int flags;
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++ tls_session_ticket_cb session_ticket_cb;
++ void *session_ticket_cb_ctx;
++ byte session_ticket[SESSION_TICKET_LEN];
++#endif
++ unsigned int ca_cert_verify:1;
++ unsigned int cert_probe:1;
++ unsigned int server_cert_only:1;
++ unsigned int success_data:1;
++
++ WOLFSSL_X509 *peer_cert;
++ WOLFSSL_X509 *peer_issuer;
++ WOLFSSL_X509 *peer_issuer_issuer;
++};
++
++
++static struct tls_context * tls_context_new(const struct tls_config *conf)
++{
++ struct tls_context *context = os_zalloc(sizeof(*context));
++
++ if (!context)
++ return NULL;
++
++ if (conf) {
++ context->event_cb = conf->event_cb;
++ context->cb_ctx = conf->cb_ctx;
++ context->cert_in_cb = conf->cert_in_cb;
++ }
++
++ return context;
++}
++
++
++static void wolfssl_reset_in_data(struct tls_in_data *in,
++ const struct wpabuf *buf)
++{
++ /* old one not owned by us so don't free */
++ in->in_data = buf;
++ in->consumed = 0;
++}
++
++
++static void wolfssl_reset_out_data(struct tls_out_data *out)
++{
++ /* old one not owned by us so don't free */
++ out->out_data = wpabuf_alloc_copy("", 0);
++}
++
++
++/* wolfSSL I/O Receive CallBack */
++static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
++{
++ size_t get = sz;
++ struct tls_in_data *data = ctx;
++
++ if (!data)
++ return -1;
++
++ if (get > (wpabuf_len(data->in_data) - data->consumed))
++ get = wpabuf_len(data->in_data) - data->consumed;
++
++ os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get);
++ data->consumed += get;
++
++ if (get == 0)
++ return -2; /* WANT_READ */
++
++ return (int) get;
++}
++
++
++/* wolfSSL I/O Send CallBack */
++static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
++{
++ struct wpabuf *tmp;
++ struct tls_out_data *data = ctx;
++
++ if (!data)
++ return -1;
++
++ wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz);
++
++ tmp = wpabuf_alloc_copy(buf, sz);
++ if (!tmp)
++ return -1;
++ data->out_data = wpabuf_concat(data->out_data, tmp);
++ if (!data->out_data)
++ return -1;
++
++ return sz;
++}
++
++
++static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
++{
++ struct wpabuf *buf;
++
++ buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++ if (!buf)
++ return;
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Free application session data %p (sess %p)",
++ buf, sess);
++ wpabuf_free(buf);
++
++ wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
++}
++
++
++void * tls_init(const struct tls_config *conf)
++{
++ WOLFSSL_CTX *ssl_ctx;
++ struct tls_context *context;
++ const char *ciphers;
++
++#ifdef DEBUG_WOLFSSL
++ wolfSSL_Debugging_ON();
++#endif /* DEBUG_WOLFSSL */
++
++ context = tls_context_new(conf);
++ if (!context)
++ return NULL;
++
++ if (tls_ref_count == 0) {
++ tls_global = context;
++
++ if (wolfSSL_Init() < 0)
++ return NULL;
++ /* wolfSSL_Debugging_ON(); */
++ }
++
++ tls_ref_count++;
++
++ /* start as client */
++ ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
++ if (!ssl_ctx) {
++ tls_ref_count--;
++ if (context != tls_global)
++ os_free(context);
++ if (tls_ref_count == 0) {
++ os_free(tls_global);
++ tls_global = NULL;
++ }
++ }
++ wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
++ wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
++ wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
++
++ if (conf->tls_session_lifetime > 0) {
++ wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
++ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
++ SSL_SESS_CACHE_SERVER);
++ wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
++ wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
++ } else {
++ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
++ SSL_SESS_CACHE_CLIENT);
++ }
++
++ if (conf && conf->openssl_ciphers)
++ ciphers = conf->openssl_ciphers;
++ else
++ ciphers = "ALL";
++ if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
++ wpa_printf(MSG_ERROR,
++ "wolfSSL: Failed to set cipher string '%s'",
++ ciphers);
++ tls_deinit(ssl_ctx);
++ return NULL;
++ }
++
++ return ssl_ctx;
++}
++
++
++void tls_deinit(void *ssl_ctx)
++{
++ struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
++
++ if (context != tls_global)
++ os_free(context);
++
++ wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
++
++ tls_ref_count--;
++ if (tls_ref_count == 0) {
++ wolfSSL_Cleanup();
++ os_free(tls_global);
++ tls_global = NULL;
++ }
++}
++
++
++int tls_get_errors(void *tls_ctx)
++{
++#ifdef DEBUG_WOLFSSL
++#if 0
++ unsigned long err;
++
++ err = wolfSSL_ERR_peek_last_error_line(NULL, NULL);
++ if (err != 0) {
++ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
++ wolfSSL_ERR_error_string(err, NULL));
++ return 1;
++ }
++#endif
++#endif /* DEBUG_WOLFSSL */
++ return 0;
++}
++
++
++struct tls_connection * tls_connection_init(void *tls_ctx)
++{
++ WOLFSSL_CTX *ssl_ctx = tls_ctx;
++ struct tls_connection *conn;
++
++ wpa_printf(MSG_DEBUG, "SSL: connection init");
++
++ conn = os_zalloc(sizeof(*conn));
++ if (!conn)
++ return NULL;
++ conn->ssl = wolfSSL_new(ssl_ctx);
++ if (!conn->ssl) {
++ os_free(conn);
++ return NULL;
++ }
++
++ wolfSSL_SetIOReadCtx(conn->ssl, &conn->input);
++ wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
++ wolfSSL_set_ex_data(conn->ssl, 0, conn);
++ conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
++
++ /* Need randoms post-hanshake for EAP-FAST, export key and deriving
++ * session ID in EAP methods. */
++ wolfSSL_KeepArrays(conn->ssl);
++ wolfSSL_KeepHandshakeResources(conn->ssl);
++ wolfSSL_UseClientSuites(conn->ssl);
++
++ return conn;
++}
++
++
++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
++{
++ if (!conn)
++ return;
++
++ wpa_printf(MSG_DEBUG, "SSL: connection deinit");
++
++ /* parts */
++ wolfSSL_free(conn->ssl);
++ os_free(conn->subject_match);
++ os_free(conn->alt_subject_match);
++ os_free(conn->suffix_match);
++ os_free(conn->domain_match);
++
++ /* self */
++ os_free(conn);
++}
++
++
++int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? wolfSSL_is_init_finished(conn->ssl) : 0;
++}
++
++
++char * tls_connection_peer_serial_num(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* TODO */
++ return NULL;
++}
++
++
++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
++{
++ WOLFSSL_SESSION *session;
++
++ if (!conn)
++ return -1;
++
++ wpa_printf(MSG_DEBUG, "SSL: connection shutdown");
++
++ /* Set quiet as OpenSSL does */
++ wolfSSL_set_quiet_shutdown(conn->ssl, 1);
++ wolfSSL_shutdown(conn->ssl);
++
++ session = wolfSSL_get_session(conn->ssl);
++ if (wolfSSL_clear(conn->ssl) != 1)
++ return -1;
++ wolfSSL_set_session(conn->ssl, session);
++
++ return 0;
++}
++
++
++static int tls_connection_set_subject_match(struct tls_connection *conn,
++ const char *subject_match,
++ const char *alt_subject_match,
++ const char *suffix_match,
++ const char *domain_match)
++{
++ os_free(conn->subject_match);
++ conn->subject_match = NULL;
++ if (subject_match) {
++ conn->subject_match = os_strdup(subject_match);
++ if (!conn->subject_match)
++ return -1;
++ }
++
++ os_free(conn->alt_subject_match);
++ conn->alt_subject_match = NULL;
++ if (alt_subject_match) {
++ conn->alt_subject_match = os_strdup(alt_subject_match);
++ if (!conn->alt_subject_match)
++ return -1;
++ }
++
++ os_free(conn->suffix_match);
++ conn->suffix_match = NULL;
++ if (suffix_match) {
++ conn->suffix_match = os_strdup(suffix_match);
++ if (!conn->suffix_match)
++ return -1;
++ }
++
++ os_free(conn->domain_match);
++ conn->domain_match = NULL;
++ if (domain_match) {
++ conn->domain_match = os_strdup(domain_match);
++ if (!conn->domain_match)
++ return -1;
++ }
++
++ return 0;
++}
++
++
++static int tls_connection_dh(struct tls_connection *conn, const char *dh_file,
++ const u8 *dh_blob, size_t blob_len)
++{
++ if (!dh_file && !dh_blob)
++ return 0;
++
++ wolfSSL_set_accept_state(conn->ssl);
++
++ if (dh_blob) {
++ if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO, "SSL: use DH DER blob failed");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG, "SSL: use DH blob OK");
++ return 0;
++ }
++
++ if (dh_file) {
++ wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file);
++ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
++ SSL_FILETYPE_PEM) < 0) {
++ wpa_printf(MSG_INFO, "SSL: use DH PEM file failed");
++ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use DH DER file failed");
++ return -1;
++ }
++ }
++ wpa_printf(MSG_DEBUG, "SSL: use DH file OK");
++ return 0;
++ }
++
++ return 0;
++}
++
++
++static int tls_connection_client_cert(struct tls_connection *conn,
++ const char *client_cert,
++ const u8 *client_cert_blob,
++ size_t blob_len)
++{
++ if (!client_cert && !client_cert_blob)
++ return 0;
++
++ if (client_cert_blob) {
++ if (wolfSSL_use_certificate_chain_buffer_format(
++ conn->ssl, client_cert_blob, blob_len,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use client cert DER blob failed");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
++ return 0;
++ }
++
++ if (client_cert) {
++ if (wolfSSL_use_certificate_chain_file(conn->ssl,
++ client_cert) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use client cert PEM file failed");
++ if (wolfSSL_use_certificate_chain_file_format(
++ conn->ssl, client_cert,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use client cert DER file failed");
++ return -1;
++ }
++ }
++ wpa_printf(MSG_DEBUG, "SSL: use client cert file OK");
++ return 0;
++ }
++
++ return 0;
++}
++
++
++static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
++{
++ if (!password)
++ return 0;
++ os_strlcpy(buf, (char *) password, size);
++ return os_strlen(buf);
++}
++
++
++static int tls_connection_private_key(void *tls_ctx,
++ struct tls_connection *conn,
++ const char *private_key,
++ const char *private_key_passwd,
++ const u8 *private_key_blob,
++ size_t blob_len)
++{
++ WOLFSSL_CTX *ctx = tls_ctx;
++ char *passwd = NULL;
++ int ok = 0;
++
++ if (!private_key && !private_key_blob)
++ return 0;
++
++ if (private_key_passwd) {
++ passwd = os_strdup(private_key_passwd);
++ if (!passwd)
++ return -1;
++ }
++
++ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
++ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
++
++ if (private_key_blob) {
++ if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
++ private_key_blob, blob_len,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use private DER blob failed");
++ } else {
++ wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
++ ok = 1;
++ }
++ }
++
++ if (!ok && private_key) {
++ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
++ SSL_FILETYPE_PEM) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: use private key PEM file failed");
++ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
++ SSL_FILETYPE_ASN1) < 0)
++ {
++ wpa_printf(MSG_INFO,
++ "SSL: use private key DER file failed");
++ } else {
++ ok = 1;
++ }
++ } else {
++ ok = 1;
++ }
++
++ if (ok)
++ wpa_printf(MSG_DEBUG, "SSL: use private key file OK");
++ }
++
++ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
++ os_free(passwd);
++
++ if (!ok)
++ return -1;
++
++ return 0;
++}
++
++
++static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
++ const char *value, size_t len)
++{
++ WOLFSSL_ASN1_OBJECT *gen;
++ void *ext;
++ int found = 0;
++ int i;
++
++ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
++
++ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
++ gen = wolfSSL_sk_value(ext, i);
++ if (gen->type != type)
++ continue;
++ if (os_strlen((char *) gen->obj) == len &&
++ os_memcmp(value, gen->obj, len) == 0)
++ found++;
++ }
++
++ wolfSSL_sk_ASN1_OBJECT_free(ext);
++
++ return found;
++}
++
++
++static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
++{
++ int type;
++ const char *pos, *end;
++ size_t len;
++
++ pos = match;
++ do {
++ if (os_strncmp(pos, "EMAIL:", 6) == 0) {
++ type = GEN_EMAIL;
++ pos += 6;
++ } else if (os_strncmp(pos, "DNS:", 4) == 0) {
++ type = GEN_DNS;
++ pos += 4;
++ } else if (os_strncmp(pos, "URI:", 4) == 0) {
++ type = GEN_URI;
++ pos += 4;
++ } else {
++ wpa_printf(MSG_INFO,
++ "TLS: Invalid altSubjectName match '%s'",
++ pos);
++ return 0;
++ }
++ end = os_strchr(pos, ';');
++ while (end) {
++ if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
++ os_strncmp(end + 1, "DNS:", 4) == 0 ||
++ os_strncmp(end + 1, "URI:", 4) == 0)
++ break;
++ end = os_strchr(end + 1, ';');
++ }
++ if (end)
++ len = end - pos;
++ else
++ len = os_strlen(pos);
++ if (tls_match_alt_subject_component(cert, type, pos, len) > 0)
++ return 1;
++ pos = end + 1;
++ } while (end);
++
++ return 0;
++}
++
++
++static int domain_suffix_match(const char *val, size_t len, const char *match,
++ size_t match_len, int full)
++{
++ size_t i;
++
++ /* Check for embedded nuls that could mess up suffix matching */
++ for (i = 0; i < len; i++) {
++ if (val[i] == '\0') {
++ wpa_printf(MSG_DEBUG,
++ "TLS: Embedded null in a string - reject");
++ return 0;
++ }
++ }
++
++ if (match_len > len || (full && match_len != len))
++ return 0;
++
++ if (os_strncasecmp(val + len - match_len, match, match_len) != 0)
++ return 0; /* no match */
++
++ if (match_len == len)
++ return 1; /* exact match */
++
++ if (val[len - match_len - 1] == '.')
++ return 1; /* full label match completes suffix match */
++
++ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
++ return 0;
++}
++
++
++static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
++ size_t match_len, int full)
++{
++ WOLFSSL_ASN1_OBJECT *gen;
++ void *ext;
++ int i;
++ int j;
++ int dns_name = 0;
++ WOLFSSL_X509_NAME *name;
++
++ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
++ full ? "" : "suffix ", match);
++
++ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
++
++ for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
++ gen = wolfSSL_sk_value(ext, j);
++ if (gen->type != ASN_DNS_TYPE)
++ continue;
++ dns_name++;
++ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
++ gen->obj, os_strlen((char *)gen->obj));
++ if (domain_suffix_match((const char *) gen->obj,
++ os_strlen((char *) gen->obj), match,
++ match_len, full) == 1) {
++ wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
++ full ? "Match" : "Suffix match");
++ wolfSSL_sk_ASN1_OBJECT_free(ext);
++ return 1;
++ }
++ }
++ wolfSSL_sk_ASN1_OBJECT_free(ext);
++
++ if (dns_name) {
++ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
++ return 0;
++ }
++
++ name = wolfSSL_X509_get_subject_name(cert);
++ i = -1;
++ for (;;) {
++ WOLFSSL_X509_NAME_ENTRY *e;
++ WOLFSSL_ASN1_STRING *cn;
++
++ i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME,
++ i);
++ if (i == -1)
++ break;
++ e = wolfSSL_X509_NAME_get_entry(name, i);
++ if (!e)
++ continue;
++ cn = wolfSSL_X509_NAME_ENTRY_get_data(e);
++ if (!cn)
++ continue;
++ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
++ cn->data, cn->length);
++ if (domain_suffix_match(cn->data, cn->length,
++ match, match_len, full) == 1) {
++ wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
++ full ? "Match" : "Suffix match");
++ return 1;
++ }
++ }
++
++ wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
++ full ? "" : "suffix ");
++ return 0;
++}
++
++
++static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
++{
++ const char *token, *last = NULL;
++
++ /* Process each match alternative separately until a match is found */
++ while ((token = cstr_token(match, ";", &last))) {
++ if (tls_match_suffix_helper(cert, token, last - token, full))
++ return 1;
++ }
++
++ return 0;
++}
++
++
++static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
++{
++ switch (err) {
++ case X509_V_ERR_CERT_REVOKED:
++ return TLS_FAIL_REVOKED;
++ case ASN_BEFORE_DATE_E:
++ case X509_V_ERR_CERT_NOT_YET_VALID:
++ case X509_V_ERR_CRL_NOT_YET_VALID:
++ return TLS_FAIL_NOT_YET_VALID;
++ case ASN_AFTER_DATE_E:
++ case X509_V_ERR_CERT_HAS_EXPIRED:
++ case X509_V_ERR_CRL_HAS_EXPIRED:
++ return TLS_FAIL_EXPIRED;
++ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
++ case X509_V_ERR_UNABLE_TO_GET_CRL:
++ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
++ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
++ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
++ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
++ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
++ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
++ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
++ case X509_V_ERR_INVALID_CA:
++ return TLS_FAIL_UNTRUSTED;
++ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
++ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
++ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
++ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
++ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
++ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
++ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
++ case X509_V_ERR_CERT_UNTRUSTED:
++ case X509_V_ERR_CERT_REJECTED:
++ return TLS_FAIL_BAD_CERTIFICATE;
++ default:
++ return TLS_FAIL_UNSPECIFIED;
++ }
++}
++
++
++static const char * wolfssl_tls_err_string(int err, const char *err_str)
++{
++ switch (err) {
++ case ASN_BEFORE_DATE_E:
++ return "certificate is not yet valid";
++ case ASN_AFTER_DATE_E:
++ return "certificate has expired";
++ default:
++ return err_str;
++ }
++}
++
++
++static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
++{
++ struct wpabuf *buf = NULL;
++ const u8 *data;
++ int cert_len;
++
++ data = wolfSSL_X509_get_der(cert, &cert_len);
++ if (!data)
++ buf = wpabuf_alloc_copy(data, cert_len);
++
++ return buf;
++}
++
++
++static void wolfssl_tls_fail_event(struct tls_connection *conn,
++ WOLFSSL_X509 *err_cert, int err, int depth,
++ const char *subject, const char *err_str,
++ enum tls_fail_reason reason)
++{
++ union tls_event_data ev;
++ struct wpabuf *cert = NULL;
++ struct tls_context *context = conn->context;
++
++ if (!context->event_cb)
++ return;
++
++ cert = get_x509_cert(err_cert);
++ os_memset(&ev, 0, sizeof(ev));
++ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
++ reason : wolfssl_tls_fail_reason(err);
++ ev.cert_fail.depth = depth;
++ ev.cert_fail.subject = subject;
++ ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str);
++ ev.cert_fail.cert = cert;
++ context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
++ wpabuf_free(cert);
++}
++
++
++static void wolfssl_tls_cert_event(struct tls_connection *conn,
++ WOLFSSL_X509 *err_cert, int depth,
++ const char *subject)
++{
++ struct wpabuf *cert = NULL;
++ union tls_event_data ev;
++ struct tls_context *context = conn->context;
++ char *alt_subject[TLS_MAX_ALT_SUBJECT];
++ int alt, num_alt_subject = 0;
++ WOLFSSL_ASN1_OBJECT *gen;
++ void *ext;
++ int i;
++#ifdef CONFIG_SHA256
++ u8 hash[32];
++#endif /* CONFIG_SHA256 */
++
++ if (!context->event_cb)
++ return;
++
++ os_memset(&ev, 0, sizeof(ev));
++ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
++ context->cert_in_cb) {
++ cert = get_x509_cert(err_cert);
++ ev.peer_cert.cert = cert;
++ }
++
++#ifdef CONFIG_SHA256
++ if (cert) {
++ const u8 *addr[1];
++ size_t len[1];
++
++ addr[0] = wpabuf_head(cert);
++ len[0] = wpabuf_len(cert);
++ if (sha256_vector(1, addr, len, hash) == 0) {
++ ev.peer_cert.hash = hash;
++ ev.peer_cert.hash_len = sizeof(hash);
++ }
++ }
++#endif /* CONFIG_SHA256 */
++
++ ev.peer_cert.depth = depth;
++ ev.peer_cert.subject = subject;
++
++ ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL);
++ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
++ char *pos;
++
++ if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
++ break;
++ gen = wolfSSL_sk_value((void *) ext, i);
++ if (gen->type != GEN_EMAIL &&
++ gen->type != GEN_DNS &&
++ gen->type != GEN_URI)
++ continue;
++
++ pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
++ if (!pos)
++ break;
++ alt_subject[num_alt_subject++] = pos;
++
++ switch (gen->type) {
++ case GEN_EMAIL:
++ os_memcpy(pos, "EMAIL:", 6);
++ pos += 6;
++ break;
++ case GEN_DNS:
++ os_memcpy(pos, "DNS:", 4);
++ pos += 4;
++ break;
++ case GEN_URI:
++ os_memcpy(pos, "URI:", 4);
++ pos += 4;
++ break;
++ }
++
++ os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
++ pos += os_strlen((char *)gen->obj);
++ *pos = '\0';
++ }
++ wolfSSL_sk_ASN1_OBJECT_free(ext);
++
++ for (alt = 0; alt < num_alt_subject; alt++)
++ ev.peer_cert.altsubject[alt] = alt_subject[alt];
++ ev.peer_cert.num_altsubject = num_alt_subject;
++
++ context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
++ wpabuf_free(cert);
++ for (alt = 0; alt < num_alt_subject; alt++)
++ os_free(alt_subject[alt]);
++}
++
++
++static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
++{
++ char buf[256];
++ WOLFSSL_X509 *err_cert;
++ int err, depth;
++ WOLFSSL *ssl;
++ struct tls_connection *conn;
++ struct tls_context *context;
++ char *match, *altmatch, *suffix_match, *domain_match;
++ const char *err_str;
++
++ err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
++ if (!err_cert) {
++ wpa_printf(MSG_DEBUG, "wolfSSL: No Cert");
++ return 0;
++ }
++
++ err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
++ depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
++ ssl = wolfSSL_X509_STORE_CTX_get_ex_data(
++ x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx());
++ wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
++ sizeof(buf));
++
++ conn = wolfSSL_get_ex_data(ssl, 0);
++ if (!conn) {
++ wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
++ return 0;
++ }
++
++ if (depth == 0)
++ conn->peer_cert = err_cert;
++ else if (depth == 1)
++ conn->peer_issuer = err_cert;
++ else if (depth == 2)
++ conn->peer_issuer_issuer = err_cert;
++
++ context = conn->context;
++ match = conn->subject_match;
++ altmatch = conn->alt_subject_match;
++ suffix_match = conn->suffix_match;
++ domain_match = conn->domain_match;
++
++ if (!preverify_ok && !conn->ca_cert_verify)
++ preverify_ok = 1;
++ if (!preverify_ok && depth > 0 && conn->server_cert_only)
++ preverify_ok = 1;
++ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
++ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
++ err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E ||
++ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Ignore certificate validity time mismatch");
++ preverify_ok = 1;
++ }
++
++ err_str = wolfSSL_X509_verify_cert_error_string(err);
++
++#ifdef CONFIG_SHA256
++ /*
++ * Do not require preverify_ok so we can explicity allow otherwise
++ * invalid pinned server certificates.
++ */
++ if (depth == 0 && conn->server_cert_only) {
++ struct wpabuf *cert;
++
++ cert = get_x509_cert(err_cert);
++ if (!cert) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Could not fetch server certificate data");
++ preverify_ok = 0;
++ } else {
++ u8 hash[32];
++ const u8 *addr[1];
++ size_t len[1];
++
++ addr[0] = wpabuf_head(cert);
++ len[0] = wpabuf_len(cert);
++ if (sha256_vector(1, addr, len, hash) < 0 ||
++ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
++ err_str = "Server certificate mismatch";
++ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
++ preverify_ok = 0;
++ } else if (!preverify_ok) {
++ /*
++ * Certificate matches pinned certificate, allow
++ * regardless of other problems.
++ */
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Ignore validation issues for a pinned server certificate");
++ preverify_ok = 1;
++ }
++ wpabuf_free(cert);
++ }
++ }
++#endif /* CONFIG_SHA256 */
++
++ if (!preverify_ok) {
++ wpa_printf(MSG_WARNING,
++ "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
++ err, err_str, depth, buf);
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ err_str, TLS_FAIL_UNSPECIFIED);
++ return preverify_ok;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
++ __func__, preverify_ok, err, err_str,
++ conn->ca_cert_verify, depth, buf);
++ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
++ wpa_printf(MSG_WARNING,
++ "TLS: Subject '%s' did not match with '%s'",
++ buf, match);
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Subject mismatch",
++ TLS_FAIL_SUBJECT_MISMATCH);
++ } else if (depth == 0 && altmatch &&
++ !tls_match_alt_subject(err_cert, altmatch)) {
++ wpa_printf(MSG_WARNING,
++ "TLS: altSubjectName match '%s' not found",
++ altmatch);
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "AltSubject mismatch",
++ TLS_FAIL_ALTSUBJECT_MISMATCH);
++ } else if (depth == 0 && suffix_match &&
++ !tls_match_suffix(err_cert, suffix_match, 0)) {
++ wpa_printf(MSG_WARNING,
++ "TLS: Domain suffix match '%s' not found",
++ suffix_match);
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Domain suffix mismatch",
++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
++ } else if (depth == 0 && domain_match &&
++ !tls_match_suffix(err_cert, domain_match, 1)) {
++ wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
++ domain_match);
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Domain mismatch",
++ TLS_FAIL_DOMAIN_MISMATCH);
++ } else {
++ wolfssl_tls_cert_event(conn, err_cert, depth, buf);
++ }
++
++ if (conn->cert_probe && preverify_ok && depth == 0) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Reject server certificate on probe-only run");
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "Server certificate chain probe",
++ TLS_FAIL_SERVER_CHAIN_PROBE);
++ }
++
++#ifdef HAVE_OCSP_WOLFSSL
++ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
++ preverify_ok) {
++ enum ocsp_result res;
++
++ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
++ conn->peer_issuer,
++ conn->peer_issuer_issuer);
++ if (res == OCSP_REVOKED) {
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "certificate revoked",
++ TLS_FAIL_REVOKED);
++ if (err == X509_V_OK)
++ X509_STORE_CTX_set_error(
++ x509_ctx, X509_V_ERR_CERT_REVOKED);
++ } else if (res != OCSP_GOOD &&
++ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
++ preverify_ok = 0;
++ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
++ "bad certificate status response",
++ TLS_FAIL_UNSPECIFIED);
++ }
++ }
++#endif /* HAVE_OCSP_WOLFSSL */
++ if (depth == 0 && preverify_ok && context->event_cb != NULL)
++ context->event_cb(context->cb_ctx,
++ TLS_CERT_CHAIN_SUCCESS, NULL);
++
++ return preverify_ok;
++}
++
++
++static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
++ const char *ca_cert,
++ const u8 *ca_cert_blob, size_t blob_len,
++ const char *ca_path)
++{
++ WOLFSSL_CTX *ctx = tls_ctx;
++
++ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
++ conn->ca_cert_verify = 1;
++
++ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Probe for server certificate chain");
++ conn->cert_probe = 1;
++ conn->ca_cert_verify = 0;
++ return 0;
++ }
++
++ if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
++#ifdef CONFIG_SHA256
++ const char *pos = ca_cert + 7;
++
++ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Unsupported ca_cert hash value '%s'",
++ ca_cert);
++ return -1;
++ }
++ pos += 14;
++ if (os_strlen(pos) != 32 * 2) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'",
++ ca_cert);
++ return -1;
++ }
++ if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'",
++ ca_cert);
++ return -1;
++ }
++ conn->server_cert_only = 1;
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Checking only server certificate match");
++ return 0;
++#else /* CONFIG_SHA256 */
++ wpa_printf(MSG_INFO,
++ "No SHA256 included in the build - cannot validate server certificate hash");
++ return -1;
++#endif /* CONFIG_SHA256 */
++ }
++
++ if (ca_cert_blob) {
++ if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
++ SSL_FILETYPE_ASN1) !=
++ SSL_SUCCESS) {
++ wpa_printf(MSG_INFO, "SSL: failed to load CA blob");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
++ return 0;
++ }
++
++ if (ca_cert || ca_path) {
++ WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
++
++ if (!cm) {
++ wpa_printf(MSG_INFO,
++ "SSL: failed to create certificate store");
++ return -1;
++ }
++ wolfSSL_CTX_set_cert_store(ctx, cm);
++
++ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
++ SSL_SUCCESS) {
++ wpa_printf(MSG_INFO,
++ "SSL: failed to load ca_cert as PEM");
++
++ if (!ca_cert)
++ return -1;
++
++ if (wolfSSL_CTX_der_load_verify_locations(
++ ctx, ca_cert, SSL_FILETYPE_ASN1) !=
++ SSL_SUCCESS) {
++ wpa_printf(MSG_INFO,
++ "SSL: failed to load ca_cert as DER");
++ return -1;
++ }
++ }
++ return 0;
++ }
++
++ conn->ca_cert_verify = 0;
++ return 0;
++}
++
++
++static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
++{
++#ifdef HAVE_SESSION_TICKET
++#if 0
++ if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
++ wolfSSL_UseSessionTicket(ssl);
++#endif
++#endif /* HAVE_SESSION_TICKET */
++
++ if (flags & TLS_CONN_DISABLE_TLSv1_0)
++ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
++ if (flags & TLS_CONN_DISABLE_TLSv1_1)
++ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
++ if (flags & TLS_CONN_DISABLE_TLSv1_2)
++ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
++}
++
++
++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
++ const struct tls_connection_params *params)
++{
++ wpa_printf(MSG_DEBUG, "SSL: set params");
++
++ if (tls_connection_set_subject_match(conn, params->subject_match,
++ params->altsubject_match,
++ params->suffix_match,
++ params->domain_match) < 0) {
++ wpa_printf(MSG_INFO, "Error setting subject match");
++ return -1;
++ }
++
++ if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
++ params->ca_cert_blob,
++ params->ca_cert_blob_len,
++ params->ca_path) < 0) {
++ wpa_printf(MSG_INFO, "Error setting CA cert");
++ return -1;
++ }
++
++ if (tls_connection_client_cert(conn, params->client_cert,
++ params->client_cert_blob,
++ params->client_cert_blob_len) < 0) {
++ wpa_printf(MSG_INFO, "Error setting client cert");
++ return -1;
++ }
++
++ if (tls_connection_private_key(tls_ctx, conn, params->private_key,
++ params->private_key_passwd,
++ params->private_key_blob,
++ params->private_key_blob_len) < 0) {
++ wpa_printf(MSG_INFO, "Error setting private key");
++ return -1;
++ }
++
++ if (tls_connection_dh(conn, params->dh_file, params->dh_blob,
++ params->dh_blob_len) < 0) {
++ wpa_printf(MSG_INFO, "Error setting DH");
++ return -1;
++ }
++
++ if (params->openssl_ciphers &&
++ wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "wolfSSL: Failed to set cipher string '%s'",
++ params->openssl_ciphers);
++ return -1;
++ }
++
++ tls_set_conn_flags(conn->ssl, params->flags);
++
++#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP,
++ WOLFSSL_CSR_OCSP_USE_NONCE) !=
++ SSL_SUCCESS)
++ return -1;
++ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
++ }
++#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
++#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ if (wolfSSL_UseOCSPStaplingV2(conn->ssl,
++ WOLFSSL_CSR2_OCSP_MULTI, 0) !=
++ SSL_SUCCESS)
++ return -1;
++ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
++ }
++#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
++#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
++ !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
++#ifdef HAVE_OCSP
++ if (params->flags & TLS_CONN_REQUEST_OCSP)
++ wolfSSL_CTX_EnableOCSP(ctx, 0);
++#else /* HAVE_OCSP */
++ if (params->flags & TLS_CONN_REQUIRE_OCSP) {
++ wpa_printf(MSG_INFO,
++ "wolfSSL: No OCSP support included - reject configuration");
++ return -1;
++ }
++ if (params->flags & TLS_CONN_REQUEST_OCSP) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: No OCSP support included - allow optional OCSP case to continue");
++ }
++#endif /* HAVE_OCSP */
++#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST &&
++ * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
++
++ conn->flags = params->flags;
++
++ return 0;
++}
++
++
++static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert)
++{
++ WOLFSSL_CTX *ctx = ssl_ctx;
++
++ if (ca_cert) {
++ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1)
++ {
++ wpa_printf(MSG_WARNING,
++ "Failed to load root certificates");
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG,
++ "TLS: Trusted root certificate(s) loaded");
++ }
++
++ return 0;
++}
++
++
++static int tls_global_client_cert(void *ssl_ctx, const char *client_cert)
++{
++ WOLFSSL_CTX *ctx = ssl_ctx;
++
++ if (!client_cert)
++ return 0;
++
++ if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert,
++ SSL_FILETYPE_ASN1) !=
++ SSL_SUCCESS &&
++ wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) !=
++ SSL_SUCCESS) {
++ wpa_printf(MSG_INFO, "Failed to load client certificate");
++ return -1;
++ }
++
++ wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s",
++ client_cert);
++
++ return 0;
++}
++
++
++static int tls_global_private_key(void *ssl_ctx, const char *private_key,
++ const char *private_key_passwd)
++{
++ WOLFSSL_CTX *ctx = ssl_ctx;
++ char *passwd = NULL;
++ int ret = 0;
++
++ if (!private_key)
++ return 0;
++
++ if (private_key_passwd) {
++ passwd = os_strdup(private_key_passwd);
++ if (!passwd)
++ return -1;
++ }
++
++ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
++ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
++
++ if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
++ SSL_FILETYPE_ASN1) != 1 &&
++ wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
++ SSL_FILETYPE_PEM) != 1) {
++ wpa_printf(MSG_INFO, "Failed to load private key");
++ ret = -1;
++ }
++
++ wpa_printf(MSG_DEBUG, "SSL: Loaded global private key");
++
++ os_free(passwd);
++ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
++
++ return ret;
++}
++
++
++static int tls_global_dh(void *ssl_ctx, const char *dh_file,
++ const u8 *dh_blob, size_t blob_len)
++{
++ WOLFSSL_CTX *ctx = ssl_ctx;
++
++ if (!dh_file && !dh_blob)
++ return 0;
++
++ if (dh_blob) {
++ if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: global use DH DER blob failed");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK");
++ return 0;
++ }
++
++ if (dh_file) {
++ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
++ 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: global use DH PEM file failed");
++ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file,
++ SSL_FILETYPE_ASN1) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: global use DH DER file failed");
++ return -1;
++ }
++ }
++ wpa_printf(MSG_DEBUG, "SSL: global use DH file OK");
++ return 0;
++ }
++
++ return 0;
++}
++
++
++#ifdef HAVE_OCSP
++
++int ocsp_status_cb(void *unused, const char *url, int url_sz,
++ unsigned char *request, int request_sz,
++ unsigned char **response)
++{
++ size_t len;
++
++ (void) unused;
++
++ if (!url) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: OCSP status callback - no response configured");
++ *response = NULL;
++ return 0;
++ }
++
++ *response = (unsigned char *) os_readfile(url, &len);
++ if (!*response) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: OCSP status callback - could not read response file");
++ return -1;
++ }
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: OCSP status callback - send cached response");
++ return len;
++}
++
++
++void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
++{
++ os_free(response);
++}
++
++#endif /* HAVE_OCSP */
++
++
++int tls_global_set_params(void *tls_ctx,
++ const struct tls_connection_params *params)
++{
++ wpa_printf(MSG_DEBUG, "SSL: global set params");
++
++ if (params->check_cert_subject)
++ return -1; /* not yet supported */
++
++ if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
++ wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
++ params->ca_cert);
++ return -1;
++ }
++
++ if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: Failed to load client cert file '%s'",
++ params->client_cert);
++ return -1;
++ }
++
++ if (tls_global_private_key(tls_ctx, params->private_key,
++ params->private_key_passwd) < 0) {
++ wpa_printf(MSG_INFO,
++ "SSL: Failed to load private key file '%s'",
++ params->private_key);
++ return -1;
++ }
++
++ if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob,
++ params->dh_blob_len) < 0) {
++ wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
++ params->dh_file);
++ return -1;
++ }
++
++ if (params->openssl_ciphers &&
++ wolfSSL_CTX_set_cipher_list(tls_ctx,
++ params->openssl_ciphers) != 1) {
++ wpa_printf(MSG_INFO,
++ "wolfSSL: Failed to set cipher string '%s'",
++ params->openssl_ciphers);
++ return -1;
++ }
++
++ if (params->openssl_ecdh_curves) {
++ wpa_printf(MSG_INFO,
++ "wolfSSL: openssl_ecdh_curves not supported");
++ return -1;
++ }
++
++#ifdef HAVE_SESSION_TICKET
++ /* Session ticket is off by default - can't disable once on. */
++ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
++ wolfSSL_CTX_UseSessionTicket(tls_ctx);
++#endif /* HAVE_SESSION_TICKET */
++
++#ifdef HAVE_OCSP
++ if (params->ocsp_stapling_response) {
++ wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
++ params->ocsp_stapling_response);
++ wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
++ ocsp_resp_free_cb, NULL);
++ }
++#endif /* HAVE_OCSP */
++
++ return 0;
++}
++
++
++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
++{
++ wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
++
++ if (check_crl) {
++ /* Hack to Enable CRLs. */
++ wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM);
++ }
++
++ return 0;
++}
++
++
++int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
++ int verify_peer, unsigned int flags,
++ const u8 *session_ctx, size_t session_ctx_len)
++{
++ if (!conn)
++ return -1;
++
++ wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
++
++ if (verify_peer) {
++ conn->ca_cert_verify = 1;
++ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
++ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
++ tls_verify_cb);
++ } else {
++ conn->ca_cert_verify = 0;
++ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
++ }
++
++ wolfSSL_set_accept_state(conn->ssl);
++
++ /* TODO: do we need to fake a session like OpenSSL does here? */
++
++ return 0;
++}
++
++
++static struct wpabuf * wolfssl_handshake(struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ int server)
++{
++ int res;
++
++ wolfssl_reset_out_data(&conn->output);
++
++ /* Initiate TLS handshake or continue the existing handshake */
++ if (server) {
++ wolfSSL_set_accept_state(conn->ssl);
++ res = wolfSSL_accept(conn->ssl);
++ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res);
++ } else {
++ wolfSSL_set_connect_state(conn->ssl);
++ res = wolfSSL_connect(conn->ssl);
++ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
++ }
++
++ if (res != 1) {
++ int err = wolfSSL_get_error(conn->ssl, res);
++
++ if (err == SSL_ERROR_WANT_READ) {
++ wpa_printf(MSG_DEBUG,
++ "SSL: wolfSSL_connect - want more data");
++ } else if (err == SSL_ERROR_WANT_WRITE) {
++ wpa_printf(MSG_DEBUG,
++ "SSL: wolfSSL_connect - want to write");
++ } else {
++ char msg[80];
++
++ wpa_printf(MSG_DEBUG,
++ "SSL: wolfSSL_connect - failed %s",
++ wolfSSL_ERR_error_string(err, msg));
++ conn->failed++;
++ }
++ }
++
++ return conn->output.out_data;
++}
++
++
++static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn,
++ size_t max_len)
++{
++ int res;
++ struct wpabuf *appl_data = wpabuf_alloc(max_len + 100);
++
++ if (!appl_data)
++ return NULL;
++
++ res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data),
++ wpabuf_size(appl_data));
++ if (res < 0) {
++ int err = wolfSSL_get_error(conn->ssl, res);
++
++ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
++ wpa_printf(MSG_DEBUG,
++ "SSL: No Application Data included");
++ } else {
++ char msg[80];
++
++ wpa_printf(MSG_DEBUG,
++ "Failed to read possible Application Data %s",
++ wolfSSL_ERR_error_string(err, msg));
++ }
++
++ wpabuf_free(appl_data);
++ return NULL;
++ }
++
++ wpabuf_put(appl_data, res);
++ wpa_hexdump_buf_key(MSG_MSGDUMP,
++ "SSL: Application Data in Finished message",
++ appl_data);
++ return appl_data;
++}
++
++
++static struct wpabuf *
++wolfssl_connection_handshake(struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data, int server)
++{
++ struct wpabuf *out_data;
++
++ wolfssl_reset_in_data(&conn->input, in_data);
++
++ if (appl_data)
++ *appl_data = NULL;
++
++ out_data = wolfssl_handshake(conn, in_data, server);
++ if (!out_data)
++ return NULL;
++
++ if (wolfSSL_is_init_finished(conn->ssl)) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Handshake finished - resumed=%d",
++ tls_connection_resumed(NULL, conn));
++ if (appl_data && in_data)
++ *appl_data = wolfssl_get_appl_data(conn,
++ wpabuf_len(in_data));
++ }
++
++ return out_data;
++}
++
++
++struct wpabuf * tls_connection_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ return wolfssl_connection_handshake(conn, in_data, appl_data, 0);
++}
++
++
++struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data,
++ struct wpabuf **appl_data)
++{
++ return wolfssl_connection_handshake(conn, in_data, appl_data, 1);
++}
++
++
++struct wpabuf * tls_connection_encrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res;
++
++ if (!conn)
++ return NULL;
++
++ wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
++
++ wolfssl_reset_out_data(&conn->output);
++
++ res = wolfSSL_write(conn->ssl, wpabuf_head(in_data),
++ wpabuf_len(in_data));
++ if (res < 0) {
++ int err = wolfSSL_get_error(conn->ssl, res);
++ char msg[80];
++
++ wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s",
++ wolfSSL_ERR_error_string(err, msg));
++ return NULL;
++ }
++
++ return conn->output.out_data;
++}
++
++
++struct wpabuf * tls_connection_decrypt(void *tls_ctx,
++ struct tls_connection *conn,
++ const struct wpabuf *in_data)
++{
++ int res;
++ struct wpabuf *buf;
++
++ if (!conn)
++ return NULL;
++
++ wpa_printf(MSG_DEBUG, "SSL: decrypt");
++
++ wolfssl_reset_in_data(&conn->input, in_data);
++
++ /* Read decrypted data for further processing */
++ /*
++ * Even though we try to disable TLS compression, it is possible that
++ * this cannot be done with all TLS libraries. Add extra buffer space
++ * to handle the possibility of the decrypted data being longer than
++ * input data.
++ */
++ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
++ if (!buf)
++ return NULL;
++ res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
++ if (res < 0) {
++ wpa_printf(MSG_INFO, "Decryption failed - SSL_read");
++ wpabuf_free(buf);
++ return NULL;
++ }
++ wpabuf_put(buf, res);
++
++ wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
++
++ return buf;
++}
++
++
++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
++{
++ return conn ? wolfSSL_session_reused(conn->ssl) : 0;
++}
++
++
++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
++ u8 *ciphers)
++{
++ char buf[128], *pos, *end;
++ u8 *c;
++ int ret;
++
++ if (!conn || !conn->ssl || !ciphers)
++ return -1;
++
++ buf[0] = '\0';
++ pos = buf;
++ end = pos + sizeof(buf);
++
++ c = ciphers;
++ while (*c != TLS_CIPHER_NONE) {
++ const char *suite;
++
++ switch (*c) {
++ case TLS_CIPHER_RC4_SHA:
++ suite = "RC4-SHA";
++ break;
++ case TLS_CIPHER_AES128_SHA:
++ suite = "AES128-SHA";
++ break;
++ case TLS_CIPHER_RSA_DHE_AES128_SHA:
++ suite = "DHE-RSA-AES128-SHA";
++ break;
++ case TLS_CIPHER_ANON_DH_AES128_SHA:
++ suite = "ADH-AES128-SHA";
++ break;
++ case TLS_CIPHER_RSA_DHE_AES256_SHA:
++ suite = "DHE-RSA-AES256-SHA";
++ break;
++ case TLS_CIPHER_AES256_SHA:
++ suite = "AES256-SHA";
++ break;
++ default:
++ wpa_printf(MSG_DEBUG,
++ "TLS: Unsupported cipher selection: %d", *c);
++ return -1;
++ }
++ ret = os_snprintf(pos, end - pos, ":%s", suite);
++ if (os_snprintf_error(end - pos, ret))
++ break;
++ pos += ret;
++
++ c++;
++ }
++
++ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
++
++ if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
++ wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
++ return -1;
++ }
++
++ return 0;
++}
++
++
++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ WOLFSSL_CIPHER *cipher;
++ const char *name;
++
++ if (!conn || !conn->ssl)
++ return -1;
++
++ cipher = wolfSSL_get_current_cipher(conn->ssl);
++ if (!cipher)
++ return -1;
++
++ name = wolfSSL_CIPHER_get_name(cipher);
++ if (!name)
++ return -1;
++
++ if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
++ os_strlcpy(buf, "RC4-SHA", buflen);
++ else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
++ os_strlcpy(buf, "AES128-SHA", buflen);
++ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
++ os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
++ else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
++ os_strlcpy(buf, "ADH-AES128-SHA", buflen);
++ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
++ os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
++ else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
++ os_strlcpy(buf, "AES256-SHA", buflen);
++ else
++ os_strlcpy(buf, name, buflen);
++
++ return 0;
++}
++
++
++int tls_connection_enable_workaround(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ /* no empty fragments in wolfSSL for now */
++ return 0;
++}
++
++
++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
++{
++ if (!conn)
++ return -1;
++
++ return conn->failed;
++}
++
++
++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
++{
++ if (!conn)
++ return -1;
++
++ /* TODO: this is not incremented anywhere */
++ return conn->read_alerts;
++}
++
++
++int tls_connection_get_write_alerts(void *tls_ctx,
++ struct tls_connection *conn)
++{
++ if (!conn)
++ return -1;
++
++ /* TODO: this is not incremented anywhere */
++ return conn->write_alerts;
++}
++
++
++
++int tls_get_library_version(char *buf, size_t buf_len)
++{
++ return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s",
++ WOLFSSL_VERSION, wolfSSL_lib_version());
++}
++
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++ char *buf, size_t buflen)
++{
++ const char *name;
++
++ if (!conn || !conn->ssl)
++ return -1;
++
++ name = wolfSSL_get_version(conn->ssl);
++ if (!name)
++ return -1;
++
++ os_strlcpy(buf, name, buflen);
++ return 0;
++}
++
++
++int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
++ struct tls_random *keys)
++{
++ WOLFSSL *ssl;
++
++ if (!conn || !keys)
++ return -1;
++ ssl = conn->ssl;
++ if (!ssl)
++ return -1;
++
++ os_memset(keys, 0, sizeof(*keys));
++ keys->client_random = conn->client_random;
++ keys->client_random_len = wolfSSL_get_client_random(
++ ssl, conn->client_random, sizeof(conn->client_random));
++ keys->server_random = conn->server_random;
++ keys->server_random_len = wolfSSL_get_server_random(
++ ssl, conn->server_random, sizeof(conn->server_random));
++
++ return 0;
++}
++
++
++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
++ const char *label, const u8 *context,
++ size_t context_len, u8 *out, size_t out_len)
++{
++ if (context)
++ return -1;
++ if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
++ return -1;
++ return 0;
++}
++
++
++#define SEED_LEN (RAN_LEN + RAN_LEN)
++
++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
++ u8 *out, size_t out_len)
++{
++ byte seed[SEED_LEN];
++ int ret = -1;
++ WOLFSSL *ssl;
++ byte *tmp_out;
++ byte *_out;
++ int skip = 0;
++ byte *master_key;
++ unsigned int master_key_len;
++ byte *server_random;
++ unsigned int server_len;
++ byte *client_random;
++ unsigned int client_len;
++
++ if (!conn || !conn->ssl)
++ return -1;
++ ssl = conn->ssl;
++
++ skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) +
++ wolfSSL_GetIVSize(ssl));
++
++ tmp_out = os_malloc(skip + out_len);
++ if (!tmp_out)
++ return -1;
++ _out = tmp_out;
++
++ wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random,
++ &server_len, &client_random, &client_len);
++ os_memcpy(seed, server_random, RAN_LEN);
++ os_memcpy(seed + RAN_LEN, client_random, RAN_LEN);
++
++ if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) {
++ tls_prf_sha256(master_key, master_key_len,
++ "key expansion", seed, sizeof(seed),
++ _out, skip + out_len);
++ ret = 0;
++ } else {
++ ret = tls_prf_sha1_md5(master_key, master_key_len,
++ "key expansion", seed, sizeof(seed),
++ _out, skip + out_len);
++ }
++
++ os_memset(master_key, 0, master_key_len);
++ if (ret == 0)
++ os_memcpy(out, _out + skip, out_len);
++ bin_clear_free(tmp_out, skip + out_len);
++
++ return ret;
++}
++
++
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++
++int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
++ int ext_type, const u8 *data,
++ size_t data_len)
++{
++ (void) ssl_ctx;
++
++ if (!conn || !conn->ssl || ext_type != 35)
++ return -1;
++
++ if (wolfSSL_set_SessionTicket(conn->ssl, data,
++ (unsigned int) data_len) != 1)
++ return -1;
++
++ return 0;
++}
++
++
++static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg)
++{
++ struct tls_connection *conn = arg;
++ int ret;
++ unsigned char client_random[RAN_LEN];
++ unsigned char server_random[RAN_LEN];
++ word32 ticket_len = sizeof(conn->session_ticket);
++
++ if (!conn || !conn->session_ticket_cb)
++ return 1;
++
++ if (wolfSSL_get_client_random(s, client_random,
++ sizeof(client_random)) == 0 ||
++ wolfSSL_get_server_random(s, server_random,
++ sizeof(server_random)) == 0 ||
++ wolfSSL_get_SessionTicket(s, conn->session_ticket,
++ &ticket_len) != 1)
++ return 1;
++
++ if (ticket_len == 0)
++ return 0;
++
++ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++ conn->session_ticket, ticket_len,
++ client_random, server_random, secret);
++ if (ret <= 0)
++ return 1;
++
++ *secret_len = SECRET_LEN;
++ return 0;
++}
++
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++
++
++int tls_connection_set_session_ticket_cb(void *tls_ctx,
++ struct tls_connection *conn,
++ tls_session_ticket_cb cb,
++ void *ctx)
++{
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++ conn->session_ticket_cb = cb;
++ conn->session_ticket_cb_ctx = ctx;
++
++ if (cb) {
++ if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
++ conn) != 1)
++ return -1;
++ } else {
++ if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
++ return -1;
++ }
++
++ return 0;
++#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++ return -1;
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Success data accepted for resumed session");
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++ WOLFSSL_SESSION *sess;
++
++ sess = wolfSSL_get_session(conn->ssl);
++ if (!sess)
++ return;
++
++ wolfSSL_SSL_SESSION_set_timeout(sess, 0);
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: Removed cached session to disable session resumption");
++}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++ struct wpabuf *data)
++{
++ WOLFSSL_SESSION *sess;
++ struct wpabuf *old;
++
++ wpa_printf(MSG_DEBUG, "wolfSSL: Set success data");
++
++ sess = wolfSSL_get_session(conn->ssl);
++ if (!sess) {
++ wpa_printf(MSG_DEBUG,
++ "wolfSSL: No session found for success data");
++ goto fail;
++ }
++
++ old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++ if (old) {
++ wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
++ old);
++ wpabuf_free(old);
++ }
++ if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
++ goto fail;
++
++ wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
++ conn->success_data = 1;
++ return;
++
++fail:
++ wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data");
++ wpabuf_free(data);
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++ WOLFSSL_SESSION *sess;
++
++ wpa_printf(MSG_DEBUG, "wolfSSL: Get success data");
++
++ sess = wolfSSL_get_session(conn->ssl);
++ if (!sess)
++ return NULL;
++ return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++}
+--- contrib/wpa/src/drivers/driver.h.orig
++++ contrib/wpa/src/drivers/driver.h
+@@ -1,6 +1,6 @@
+ /*
+ * Driver interface definition
+- * Copyright (c) 2003-2015, Jouni Malinen
++ * Copyright (c) 2003-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -21,6 +21,10 @@
+
+ #include "common/defs.h"
+ #include "common/ieee802_11_defs.h"
++#include "common/wpa_common.h"
++#ifdef CONFIG_MACSEC
++#include "pae/ieee802_1x_kay.h"
++#endif /* CONFIG_MACSEC */
+ #include "utils/list.h"
+
+ #define HOSTAPD_CHAN_DISABLED 0x00000001
+@@ -45,6 +49,36 @@
+ #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+ #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
++#define HOSTAPD_CHAN_VHT_10_150 0x00100000
++#define HOSTAPD_CHAN_VHT_30_130 0x00200000
++#define HOSTAPD_CHAN_VHT_50_110 0x00400000
++#define HOSTAPD_CHAN_VHT_70_90 0x00800000
++#define HOSTAPD_CHAN_VHT_90_70 0x01000000
++#define HOSTAPD_CHAN_VHT_110_50 0x02000000
++#define HOSTAPD_CHAN_VHT_130_30 0x04000000
++#define HOSTAPD_CHAN_VHT_150_10 0x08000000
++
++/* Allowed bandwidth mask */
++enum hostapd_chan_width_attr {
++ HOSTAPD_CHAN_WIDTH_10 = BIT(0),
++ HOSTAPD_CHAN_WIDTH_20 = BIT(1),
++ HOSTAPD_CHAN_WIDTH_40P = BIT(2),
++ HOSTAPD_CHAN_WIDTH_40M = BIT(3),
++ HOSTAPD_CHAN_WIDTH_80 = BIT(4),
++ HOSTAPD_CHAN_WIDTH_160 = BIT(5),
++};
++
++/* Filter gratuitous ARP */
++#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
++/* Filter unsolicited Neighbor Advertisement */
++#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1)
++/* Filter unicast IP packets encrypted using the GTK */
++#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
++
++#define HOSTAPD_DFS_REGION_FCC 1
++#define HOSTAPD_DFS_REGION_ETSI 2
++#define HOSTAPD_DFS_REGION_JP 3
++
+ /**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+@@ -87,6 +121,13 @@
+ int flag;
+
+ /**
++ * allowed_bw - Allowed channel width bitmask
++ *
++ * See enum hostapd_chan_width_attr.
++ */
++ u32 allowed_bw;
++
++ /**
+ * max_tx_power - Regulatory transmit power limit in dBm
+ */
+ u8 max_tx_power;
+@@ -117,6 +158,29 @@
+ unsigned int dfs_cac_ms;
+ };
+
++#define HE_MAX_NUM_SS 8
++#define HE_MAX_PHY_CAPAB_SIZE 3
++
++/**
++ * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold
++ */
++struct he_ppe_threshold {
++ u32 numss_m1;
++ u32 ru_count;
++ u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS];
++};
++
++/**
++ * struct he_capabilities - IEEE 802.11ax HE capabilities
++ */
++struct he_capabilities {
++ u8 he_supported;
++ u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
++ u32 mac_cap;
++ u32 mcs;
++ struct he_ppe_threshold ppet;
++};
++
+ #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+ #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+
+@@ -175,6 +239,11 @@
+ u8 vht_mcs_set[8];
+
+ unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
++
++ /**
++ * he_capab - HE (IEEE 802.11ax) capabilities
++ */
++ struct he_capabilities he_capab;
+ };
+
+
+@@ -217,6 +286,9 @@
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
++ * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
++ * of TSF of the BSS specified by %tsf_bssid.
++ * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+@@ -247,6 +319,8 @@
+ unsigned int age;
+ unsigned int est_throughput;
+ int snr;
++ u64 parent_tsf;
++ u8 tsf_bssid[ETH_ALEN];
+ size_t ie_len;
+ size_t beacon_ie_len;
+ /* Followed by ie_len + beacon_ie_len octets of IE data */
+@@ -284,6 +358,18 @@
+ #define WPAS_MAX_SCAN_SSIDS 16
+
+ /**
++ * struct wpa_driver_scan_ssid - SSIDs to scan for
++ * @ssid - specific SSID to scan for (ProbeReq)
++ * %NULL or zero-length SSID is used to indicate active scan
++ * with wildcard SSID.
++ * @ssid_len - Length of the SSID in octets
++ */
++struct wpa_driver_scan_ssid {
++ const u8 *ssid;
++ size_t ssid_len;
++};
++
++/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+@@ -291,18 +377,7 @@
+ /**
+ * ssids - SSIDs to scan for
+ */
+- struct wpa_driver_scan_ssid {
+- /**
+- * ssid - specific SSID to scan for (ProbeReq)
+- * %NULL or zero-length SSID is used to indicate active scan
+- * with wildcard SSID.
+- */
+- const u8 *ssid;
+- /**
+- * ssid_len: Length of the SSID in octets
+- */
+- size_t ssid_len;
+- } ssids[WPAS_MAX_SCAN_SSIDS];
++ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+
+ /**
+ * num_ssids - Number of entries in ssids array
+@@ -407,6 +482,120 @@
+ */
+ const u8 *mac_addr_mask;
+
++ /**
++ * sched_scan_plans - Scan plans for scheduled scan
++ *
++ * Each scan plan consists of the number of iterations to scan and the
++ * interval between scans. When a scan plan finishes (i.e., it was run
++ * for the specified number of iterations), the next scan plan is
++ * executed. The scan plans are executed in the order they appear in
++ * the array (lower index first). The last scan plan will run infinitely
++ * (until requested to stop), thus must not specify the number of
++ * iterations. All other scan plans must specify the number of
++ * iterations.
++ */
++ struct sched_scan_plan {
++ u32 interval; /* In seconds */
++ u32 iterations; /* Zero to run infinitely */
++ } *sched_scan_plans;
++
++ /**
++ * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
++ */
++ unsigned int sched_scan_plans_num;
++
++ /**
++ * sched_scan_start_delay - Delay to use before starting the first scan
++ *
++ * Delay (in seconds) before scheduling first scan plan cycle. The
++ * driver may ignore this parameter and start immediately (or at any
++ * other time), if this feature is not supported.
++ */
++ u32 sched_scan_start_delay;
++
++ /**
++ * bssid - Specific BSSID to scan for
++ *
++ * This optional parameter can be used to replace the default wildcard
++ * BSSID with a specific BSSID to scan for if results are needed from
++ * only a single BSS.
++ */
++ const u8 *bssid;
++
++ /**
++ * scan_cookie - Unique identification representing the scan request
++ *
++ * This scan_cookie carries a unique identification representing the
++ * scan request if the host driver/kernel supports concurrent scan
++ * requests. This cookie is returned from the corresponding driver
++ * interface.
++ *
++ * Note: Unlike other parameters in this structure, scan_cookie is used
++ * only to return information instead of setting parameters for the
++ * scan.
++ */
++ u64 scan_cookie;
++
++ /**
++ * duration - Dwell time on each channel
++ *
++ * This optional parameter can be used to set the dwell time on each
++ * channel. In TUs.
++ */
++ u16 duration;
++
++ /**
++ * duration_mandatory - Whether the specified duration is mandatory
++ *
++ * If this is set, the duration specified by the %duration field is
++ * mandatory (and the driver should reject the scan request if it is
++ * unable to comply with the specified duration), otherwise it is the
++ * maximum duration and the actual duration may be shorter.
++ */
++ unsigned int duration_mandatory:1;
++
++ /**
++ * relative_rssi_set - Whether relative RSSI parameters are set
++ */
++ unsigned int relative_rssi_set:1;
++
++ /**
++ * relative_rssi - Relative RSSI for reporting better BSSs
++ *
++ * Amount of RSSI by which a BSS should be better than the current
++ * connected BSS to report the new BSS to user space.
++ */
++ s8 relative_rssi;
++
++ /**
++ * relative_adjust_band - Band to which RSSI should be adjusted
++ *
++ * The relative_adjust_rssi should be added to the band specified
++ * by relative_adjust_band.
++ */
++ enum set_band relative_adjust_band;
++
++ /**
++ * relative_adjust_rssi - RSSI to be added to relative_adjust_band
++ *
++ * An amount of relative_band_rssi should be added to the BSSs that
++ * belong to the band specified by relative_adjust_band while comparing
++ * with other bands for BSS reporting.
++ */
++ s8 relative_adjust_rssi;
++
++ /**
++ * oce_scan
++ *
++ * Enable the following OCE scan features: (WFA OCE TechSpec v1.0)
++ * - Accept broadcast Probe Response frame.
++ * - Probe Request frame deferral and suppression.
++ * - Max Channel Time - driver fills FILS request params IE with
++ * Maximum Channel Time.
++ * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps.
++ */
++ unsigned int oce_scan:1;
++
+ /*
+ * NOTE: Whenever adding new parameters here, please make sure
+ * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+@@ -437,17 +626,18 @@
+ int p2p;
+
+ /**
+- * sae_data - SAE elements for Authentication frame
++ * auth_data - Additional elements for Authentication frame
+ *
+ * This buffer starts with the Authentication transaction sequence
+- * number field. If SAE is not used, this pointer is %NULL.
++ * number field. If no special handling of such elements is needed, this
++ * pointer is %NULL. This is used with SAE and FILS.
+ */
+- const u8 *sae_data;
++ const u8 *auth_data;
+
+ /**
+- * sae_data_len - Length of sae_data buffer in octets
++ * auth_data_len - Length of auth_data buffer in octets
+ */
+- size_t sae_data_len;
++ size_t auth_data_len;
+ };
+
+ /**
+@@ -529,6 +719,68 @@
+ };
+
+ /**
++ * struct wpa_driver_sta_auth_params - Authentication parameters
++ * Data for struct wpa_driver_ops::sta_auth().
++ */
++struct wpa_driver_sta_auth_params {
++
++ /**
++ * own_addr - Source address and BSSID for authentication frame
++ */
++ const u8 *own_addr;
++
++ /**
++ * addr - MAC address of the station to associate
++ */
++ const u8 *addr;
++
++ /**
++ * seq - authentication sequence number
++ */
++ u16 seq;
++
++ /**
++ * status - authentication response status code
++ */
++ u16 status;
++
++ /**
++ * ie - authentication frame ie buffer
++ */
++ const u8 *ie;
++
++ /**
++ * len - ie buffer length
++ */
++ size_t len;
++
++ /**
++ * fils_auth - Indicates whether FILS authentication is being performed
++ */
++ int fils_auth;
++
++ /**
++ * fils_anonce - ANonce (required for FILS)
++ */
++ u8 fils_anonce[WPA_NONCE_LEN];
++
++ /**
++ * fils_snonce - SNonce (required for FILS)
++ */
++ u8 fils_snonce[WPA_NONCE_LEN];
++
++ /**
++ * fils_kek - key for encryption (required for FILS)
++ */
++ u8 fils_kek[WPA_KEK_MAX_LEN];
++
++ /**
++ * fils_kek_len - Length of the fils_kek in octets (required for FILS)
++ */
++ size_t fils_kek_len;
++};
++
++/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+@@ -591,7 +843,7 @@
+ * WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use
+ * of this WPA IE is optional. If the driver generates the WPA
+- * IE, it can use pairwise_suite, group_suite, and
++ * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
+ * key_mgmt_suite to select proper algorithms. In this case,
+ * the driver has to notify wpa_supplicant about the used WPA
+ * IE by generating an event that the interface code will
+@@ -631,6 +883,13 @@
+ unsigned int group_suite;
+
+ /**
++ * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
++ *
++ * This is usually ignored if @wpa_ie is used.
++ */
++ unsigned int mgmt_group_suite;
++
++ /**
+ * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+@@ -669,50 +928,13 @@
+ enum mfp_options mgmt_frame_protection;
+
+ /**
+- * ft_ies - IEEE 802.11r / FT information elements
+- * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+- * for fast transition, this parameter is set to include the IEs that
+- * are to be sent in the next FT Authentication Request message.
+- * update_ft_ies() handler is called to update the IEs for further
+- * FT messages in the sequence.
+- *
+- * The driver should use these IEs only if the target AP is advertising
+- * the same mobility domain as the one included in the MDIE here.
+- *
+- * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+- * AP after the initial association. These IEs can only be used if the
+- * target AP is advertising support for FT and is using the same MDIE
+- * and SSID as the current AP.
+- *
+- * The driver is responsible for reporting the FT IEs received from the
+- * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+- * type. update_ft_ies() handler will then be called with the FT IEs to
+- * include in the next frame in the authentication sequence.
+- */
+- const u8 *ft_ies;
+-
+- /**
+- * ft_ies_len - Length of ft_ies in bytes
+- */
+- size_t ft_ies_len;
+-
+- /**
+- * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+- *
+- * This value is provided to allow the driver interface easier access
+- * to the current mobility domain. This value is set to %NULL if no
+- * mobility domain is currently active.
+- */
+- const u8 *ft_md;
+-
+- /**
+ * passphrase - RSN passphrase for PSK
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+- * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+- * the 8..63 character ASCII passphrase, if available. Please note that
+- * this can be %NULL if passphrase was not used to generate the PSK. In
+- * that case, the psk field must be used to fetch the PSK.
++ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
++ * is the 8..63 character ASCII passphrase, if available. Please note
++ * that this can be %NULL if passphrase was not used to generate the
++ * PSK. In that case, the psk field must be used to fetch the PSK.
+ */
+ const char *passphrase;
+
+@@ -720,9 +942,9 @@
+ * psk - RSN PSK (alternative for passphrase for PSK)
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+- * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+- * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+- * be prepared to handle %NULL value as an error.
++ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
++ * is the 32-octet (256-bit) PSK, if available. The driver wrapper
++ * should be prepared to handle %NULL value as an error.
+ */
+ const u8 *psk;
+
+@@ -828,6 +1050,70 @@
+ * RRM (Radio Resource Measurements)
+ */
+ int rrm_used;
++
++ /**
++ * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an
++ * AP as usual. Valid for DMG network only.
++ */
++ int pbss;
++
++ /**
++ * fils_kek - KEK for FILS association frame protection (AES-SIV)
++ */
++ const u8 *fils_kek;
++
++ /**
++ * fils_kek_len: Length of fils_kek in bytes
++ */
++ size_t fils_kek_len;
++
++ /**
++ * fils_nonces - Nonces for FILS association frame protection
++ * (AES-SIV AAD)
++ */
++ const u8 *fils_nonces;
++
++ /**
++ * fils_nonces_len: Length of fils_nonce in bytes
++ */
++ size_t fils_nonces_len;
++
++ /**
++ * fils_erp_username - Username part of keyName-NAI
++ */
++ const u8 *fils_erp_username;
++
++ /**
++ * fils_erp_username_len - Length of fils_erp_username in bytes
++ */
++ size_t fils_erp_username_len;
++
++ /**
++ * fils_erp_realm - Realm/domain name to use in FILS ERP
++ */
++ const u8 *fils_erp_realm;
++
++ /**
++ * fils_erp_realm_len - Length of fils_erp_realm in bytes
++ */
++ size_t fils_erp_realm_len;
++
++ /**
++ * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
++ * messages
++ */
++ u16 fils_erp_next_seq_num;
++
++ /**
++ * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
++ * specified by fils_erp_username@fils_erp_realm.
++ */
++ const u8 *fils_erp_rrk;
++
++ /**
++ * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
++ */
++ size_t fils_erp_rrk_len;
+ };
+
+ enum hide_ssid {
+@@ -886,6 +1172,22 @@
+ int *basic_rates;
+
+ /**
++ * beacon_rate: Beacon frame data rate
++ *
++ * This parameter can be used to set a specific Beacon frame data rate
++ * for the BSS. The interpretation of this value depends on the
++ * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If
++ * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default
++ * Beacon frame data rate is used.
++ */
++ unsigned int beacon_rate;
++
++ /**
++ * beacon_rate_type: Beacon data rate type (legacy/HT/VHT)
++ */
++ enum beacon_rate_type rate_type;
++
++ /**
+ * proberesp - Probe Response template
+ *
+ * This is used by drivers that reply to Probe Requests internally in
+@@ -1055,16 +1357,68 @@
+ * reenable - Whether this is to re-enable beaconing
+ */
+ int reenable;
++
++ /**
++ * pbss - Whether to start a PCP (in PBSS) instead of an AP in
++ * infrastructure BSS. Valid only for DMG network.
++ */
++ int pbss;
++
++ /**
++ * multicast_to_unicast - Whether to use multicast_to_unicast
++ *
++ * If this is non-zero, the AP is requested to perform multicast to
++ * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within
++ * 802.1Q). If enabled, such frames are to be sent to each station
++ * separately, with the DA replaced by their own MAC address rather
++ * than the group address.
++ *
++ * Note that this may break certain expectations of the receiver, such
++ * as the ability to drop unicast IP packets received within multicast
++ * L2 frames, or the ability to not send ICMP destination unreachable
++ * messages for packets received in L2 multicast (which is required,
++ * but the receiver can't tell the difference if this new option is
++ * enabled.)
++ *
++ * This also doesn't implement the 802.11 DMS (directed multicast
++ * service).
++ */
++ int multicast_to_unicast;
++
++ /**
++ * ftm_responder - Whether FTM responder is enabled
++ */
++ int ftm_responder;
++
++ /**
++ * lci - Binary data, the content of an LCI report IE with type 8 as
++ * defined in IEEE Std 802.11-2016, 9.4.2.22.10
++ */
++ const struct wpabuf *lci;
++
++ /**
++ * civic - Binary data, the content of a measurement report IE with
++ * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
++ */
++ const struct wpabuf *civic;
+ };
+
+ struct wpa_driver_mesh_bss_params {
+-#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001
++#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001
++#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002
++#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004
++#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008
++#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010
+ /*
+ * TODO: Other mesh configuration parameters would go here.
+ * See NL80211_MESHCONF_* for all the mesh config parameters.
+ */
+ unsigned int flags;
++ int auto_plinks;
+ int peer_link_timeout;
++ int max_peer_links;
++ int rssi_threshold;
++ u16 ht_opmode;
+ };
+
+ struct wpa_driver_mesh_join_params {
+@@ -1075,7 +1429,7 @@
+ int ie_len;
+ struct hostapd_freq_params freq;
+ int beacon_int;
+- int max_peer_links;
++ int dtim_period;
+ struct wpa_driver_mesh_bss_params conf;
+ #define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001
+ #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002
+@@ -1098,6 +1452,13 @@
+ #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
+ #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100
+ #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200
++#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400
++#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800
++#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000
++#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000
++#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
++#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
++#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
+ /** Bitfield of supported key management suites */
+ unsigned int key_mgmt;
+
+@@ -1131,7 +1492,7 @@
+ #define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004
+ /** Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
+-#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
++#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008
+ /** Driver is for a wired Ethernet interface */
+ #define WPA_DRIVER_FLAGS_WIRED 0x00000010
+ /** Driver provides separate commands for authentication and association (SME in
+@@ -1214,8 +1575,54 @@
+ #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
+ /** Driver supports automatic band selection */
+ #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
++/** Driver supports simultaneous off-channel operations */
++#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
++/** Driver supports full AP client state */
++#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
++/** Driver supports P2P Listen offload */
++#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
++/** Driver supports FILS */
++#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL
++/** Driver supports Beacon frame TX rate configuration (legacy rates) */
++#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL
++/** Driver supports Beacon frame TX rate configuration (HT rates) */
++#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL
++/** Driver supports Beacon frame TX rate configuration (VHT rates) */
++#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL
++/** Driver supports mgmt_tx with random TX address in non-connected state */
++#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL
++/** Driver supports mgmt_tx with random TX addr in connected state */
++#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL
++/** Driver supports better BSS reporting with sched_scan in connected mode */
++#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL
++/** Driver supports HE capabilities */
++#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL
++/** Driver supports FILS shared key offload */
++#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL
++/** Driver supports all OCE STA specific mandatory features */
++#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL
++/** Driver supports all OCE AP specific mandatory features */
++#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL
++/**
++ * Driver supports all OCE STA-CFON specific mandatory features only.
++ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
++ * userspace shall assume that this driver may not support all OCE AP
++ * functionality but can support only OCE STA-CFON functionality.
++ */
++#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
++/** Driver supports MFP-optional in the connect command */
++#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
++/** Driver is a self-managed regulatory device */
++#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
++/** Driver supports FTM responder functionality */
++#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL
++/** Driver support 4-way handshake offload for WPA-Personal */
++#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL
+ u64 flags;
+
++#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
++ (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
++
+ #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
+ #define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002
+ unsigned int smps_modes;
+@@ -1231,6 +1638,15 @@
+ /** Maximum number of supported active probe SSIDs for sched_scan */
+ int max_sched_scan_ssids;
+
++ /** Maximum number of supported scan plans for scheduled scan */
++ unsigned int max_sched_scan_plans;
++
++ /** Maximum interval in a scan plan. In seconds */
++ u32 max_sched_scan_plan_interval;
++
++ /** Maximum number of iterations in a single scan plan */
++ u32 max_sched_scan_plan_iterations;
++
+ /** Whether sched_scan (offloaded scanning) is supported */
+ int sched_scan_supported;
+
+@@ -1296,6 +1712,17 @@
+ * offset, namely the 6th byte in the Action frame body.
+ */
+ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
++/**
++ * Driver supports RRM. With this support, the driver will accept to use RRM in
++ * (Re)Association Request frames, without supporting quiet period.
++ */
++#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010
++
++/** Driver supports setting the scan dwell time */
++#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020
++/** Driver supports Beacon Report Measurement */
++#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040
++
+ u32 rrm_flags;
+
+ /* Driver concurrency capabilities */
+@@ -1304,21 +1731,43 @@
+ unsigned int max_conc_chan_2_4;
+ /* Maximum number of concurrent channels on 5 GHz */
+ unsigned int max_conc_chan_5_0;
++
++ /* Maximum number of supported CSA counters */
++ u16 max_csa_counters;
+ };
+
+
+ struct hostapd_data;
+
++#define STA_DRV_DATA_TX_MCS BIT(0)
++#define STA_DRV_DATA_RX_MCS BIT(1)
++#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
++#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
++#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
++#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
++#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
++#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
++#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
++
+ struct hostap_sta_driver_data {
+- unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
++ unsigned long rx_packets, tx_packets;
++ unsigned long long rx_bytes, tx_bytes;
++ int bytes_64bit; /* whether 64-bit byte counters are supported */
+ unsigned long current_tx_rate;
++ unsigned long current_rx_rate;
+ unsigned long inactive_msec;
+- unsigned long flags;
++ unsigned long flags; /* bitfield of STA_DRV_DATA_* */
+ unsigned long num_ps_buf_frames;
+ unsigned long tx_retry_failed;
+ unsigned long tx_retry_count;
+- int last_rssi;
+- int last_ack_rssi;
++ s8 last_ack_rssi;
++ s8 signal;
++ u8 rx_vhtmcs;
++ u8 tx_vhtmcs;
++ u8 rx_mcs;
++ u8 tx_mcs;
++ u8 rx_vht_nss;
++ u8 tx_vht_nss;
+ };
+
+ struct hostapd_sta_add_params {
+@@ -1336,6 +1785,7 @@
+ u32 flags_mask; /* unset bits in flags */
+ #ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
++ u16 peer_aid;
+ #endif /* CONFIG_MESH */
+ int set; /* Set STA parameters instead of add */
+ u8 qosinfo;
+@@ -1345,6 +1795,7 @@
+ size_t supp_channels_len;
+ const u8 *supp_oper_classes;
+ size_t supp_oper_classes_len;
++ int support_p2p_ps;
+ };
+
+ struct mac_address {
+@@ -1450,6 +1901,7 @@
+ #define WPA_STA_MFP BIT(3)
+ #define WPA_STA_TDLS_PEER BIT(4)
+ #define WPA_STA_AUTHENTICATED BIT(5)
++#define WPA_STA_ASSOCIATED BIT(6)
+
+ enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+@@ -1478,19 +1930,32 @@
+ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
+ };
+
+-/* enum chan_width - Channel width definitions */
+-enum chan_width {
+- CHAN_WIDTH_20_NOHT,
+- CHAN_WIDTH_20,
+- CHAN_WIDTH_40,
+- CHAN_WIDTH_80,
+- CHAN_WIDTH_80P80,
+- CHAN_WIDTH_160,
+- CHAN_WIDTH_UNKNOWN
++/* enum smps_mode - SMPS mode definitions */
++enum smps_mode {
++ SMPS_AUTOMATIC,
++ SMPS_OFF,
++ SMPS_DYNAMIC,
++ SMPS_STATIC,
++
++ /* Keep last */
++ SMPS_INVALID,
+ };
+
++#define WPA_INVALID_NOISE 9999
++
+ /**
+ * struct wpa_signal_info - Information about channel signal quality
++ * @frequency: control frequency
++ * @above_threshold: true if the above threshold was crossed
++ * (relevant for a CQM event)
++ * @current_signal: in dBm
++ * @avg_signal: in dBm
++ * @avg_beacon_signal: in dBm
++ * @current_noise: %WPA_INVALID_NOISE if not supported
++ * @current_txrate: current TX rate
++ * @chanwidth: channel width
++ * @center_frq1: center frequency for the first segment
++ * @center_frq2: center frequency for the second segment (if relevant)
+ */
+ struct wpa_signal_info {
+ u32 frequency;
+@@ -1506,6 +1971,26 @@
+ };
+
+ /**
++ * struct wpa_channel_info - Information about the current channel
++ * @frequency: Center frequency of the primary 20 MHz channel
++ * @chanwidth: Width of the current operating channel
++ * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1).
++ * This field is only filled in when using a 40 MHz channel.
++ * @center_frq1: Center frequency of frequency segment 0
++ * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels)
++ * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is
++ * derived from center_frq2 for convenience.
++ */
++struct wpa_channel_info {
++ u32 frequency;
++ enum chan_width chanwidth;
++ int sec_channel;
++ int center_frq1;
++ int center_frq2;
++ u8 seg1_idx;
++};
++
++/**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+@@ -1554,8 +2039,8 @@
+ struct beacon_data beacon_csa;
+ struct beacon_data beacon_after;
+
+- u16 counter_offset_beacon;
+- u16 counter_offset_presp;
++ u16 counter_offset_beacon[2];
++ u16 counter_offset_presp[2];
+ };
+
+ /* TDLS peer capabilities for send_tdls_mgmt() */
+@@ -1622,8 +2107,71 @@
+ const int *freq_list;
+ };
+
++struct wpa_bss_trans_info {
++ u8 mbo_transition_reason;
++ u8 n_candidates;
++ u8 *bssid;
++};
+
++struct wpa_bss_candidate_info {
++ u8 num;
++ struct candidate_list {
++ u8 bssid[ETH_ALEN];
++ u8 is_accept;
++ u32 reject_reason;
++ } *candidates;
++};
++
++struct wpa_pmkid_params {
++ const u8 *bssid;
++ const u8 *ssid;
++ size_t ssid_len;
++ const u8 *fils_cache_id;
++ const u8 *pmkid;
++ const u8 *pmk;
++ size_t pmk_len;
++};
++
++/* Mask used to specify which connection parameters have to be updated */
++enum wpa_drv_update_connect_params_mask {
++ WPA_DRV_UPDATE_ASSOC_IES = BIT(0),
++ WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1),
++ WPA_DRV_UPDATE_AUTH_TYPE = BIT(2),
++};
++
+ /**
++ * struct external_auth - External authentication trigger parameters
++ *
++ * These are used across the external authentication request and event
++ * interfaces.
++ * @action: Action type / trigger for external authentication. Only significant
++ * for the event interface.
++ * @bssid: BSSID of the peer with which the authentication has to happen. Used
++ * by both the request and event interface.
++ * @ssid: SSID of the AP. Used by both the request and event interface.
++ * @ssid_len: SSID length in octets.
++ * @key_mgmt_suite: AKM suite of the respective authentication. Optional for
++ * the request interface.
++ * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
++ * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
++ * the real status code for failures. Used only for the request interface
++ * from user space to the driver.
++ * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
++ */
++struct external_auth {
++ enum {
++ EXT_AUTH_START,
++ EXT_AUTH_ABORT,
++ } action;
++ const u8 *bssid;
++ const u8 *ssid;
++ size_t ssid_len;
++ unsigned int key_mgmt_suite;
++ u16 status;
++ const u8 *pmkid;
++};
++
++/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+@@ -1804,13 +2352,14 @@
+ /**
+ * add_pmkid - Add PMKSA cache entry to the driver
+ * @priv: private driver interface data
+- * @bssid: BSSID for the PMKSA cache entry
+- * @pmkid: PMKID for the PMKSA cache entry
++ * @params: PMKSA parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a new PMK is received, as a result of
+- * either normal authentication or RSN pre-authentication.
++ * either normal authentication or RSN pre-authentication. The PMKSA
++ * parameters are either a set of bssid, pmkid, and pmk; or a set of
++ * ssid, fils_cache_id, pmkid, and pmk.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), add_pmkid() can be used to add new PMKSA cache entries
+@@ -1818,18 +2367,18 @@
+ * driver_ops function does not need to be implemented. Likewise, if
+ * the driver does not support WPA, this function is not needed.
+ */
+- int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
++ int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
+
+ /**
+ * remove_pmkid - Remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+- * @bssid: BSSID for the PMKSA cache entry
+- * @pmkid: PMKID for the PMKSA cache entry
++ * @params: PMKSA parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+- * entry for any reason.
++ * entry for any reason. The PMKSA parameters are either a set of
++ * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+@@ -1838,7 +2387,7 @@
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+- int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
++ int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
+
+ /**
+ * flush_pmkid - Flush PMKSA cache
+@@ -1883,6 +2432,14 @@
+ void (*poll)(void *priv);
+
+ /**
++ * get_ifindex - Get interface index
++ * @priv: private driver interface data
++ *
++ * Returns: Interface index
++ */
++ unsigned int (*get_ifindex)(void *priv);
++
++ /**
+ * get_ifname - Get interface name
+ * @priv: private driver interface data
+ *
+@@ -1945,12 +2502,13 @@
+ * @priv: Private driver interface data
+ * @num_modes: Variable for returning the number of returned modes
+ * flags: Variable for returning hardware feature flags
++ * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
+ * Returns: Pointer to allocated hardware data on success or %NULL on
+ * failure. Caller is responsible for freeing this.
+ */
+ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+ u16 *num_modes,
+- u16 *flags);
++ u16 *flags, u8 *dfs);
+
+ /**
+ * send_mlme - Send management frame from MLME
+@@ -1960,10 +2518,13 @@
+ * @noack: Do not wait for this frame to be acked (disable retries)
+ * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+ * driver decide
++ * @csa_offs: Array of CSA offsets or %NULL
++ * @csa_offs_len: Number of elements in csa_offs
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+- int noack, unsigned int freq);
++ int noack, unsigned int freq, const u16 *csa_offs,
++ size_t csa_offs_len);
+
+ /**
+ * update_ft_ies - Update FT (IEEE 802.11r) IEs
+@@ -2013,6 +2574,7 @@
+
+ /**
+ * global_init - Global driver initialization
++ * @ctx: wpa_global pointer
+ * Returns: Pointer to private data (global), %NULL on failure
+ *
+ * This optional function is called to initialize the driver wrapper
+@@ -2022,7 +2584,7 @@
+ * use init2() function instead of init() to get the pointer to global
+ * data available to per-interface initializer.
+ */
+- void * (*global_init)(void);
++ void * (*global_init)(void *ctx);
+
+ /**
+ * global_deinit - Global driver deinitialization
+@@ -2308,12 +2870,17 @@
+ * @params: Station parameters
+ * Returns: 0 on success, -1 on failure
+ *
+- * This function is used to add a station entry to the driver once the
+- * station has completed association. This is only used if the driver
++ * This function is used to add or set (params->set 1) a station
++ * entry in the driver. Adding STA entries is used only if the driver
+ * does not take care of association processing.
+ *
+- * With TDLS, this function is also used to add or set (params->set 1)
+- * TDLS peer entries.
++ * With drivers that don't support full AP client state, this function
++ * is used to add a station entry to the driver once the station has
++ * completed association.
++ *
++ * With TDLS, this function is used to add or set (params->set 1)
++ * TDLS peer entries (even with drivers that do not support full AP
++ * client state).
+ */
+ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+@@ -2399,12 +2966,13 @@
+ * change interface address)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
+ * @use_existing: Whether to allow existing interface to be used
++ * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+- const char *bridge, int use_existing);
++ const char *bridge, int use_existing, int setup_ap);
+
+ /**
+ * if_remove - Remove a virtual interface
+@@ -2557,6 +3125,9 @@
+ * transmitted on that channel; alternatively the frame may be sent on
+ * the current operational channel (if in associated state in station
+ * mode or while operating as an AP.)
++ *
++ * If @src differs from the device MAC address, use of a random
++ * transmitter address is requested for this message exchange.
+ */
+ int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+@@ -2857,6 +3428,14 @@
+ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+
+ /**
++ * channel_info - Get parameters of the current operating channel
++ * @priv: Private driver interface data
++ * @channel_info: Channel info structure
++ * Returns: 0 on success, negative (<0) on failure
++ */
++ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
++
++ /**
+ * set_authmode - Set authentication algorithm(s) for static WEP
+ * @priv: Private driver interface data
+ * @authmode: 1=Open System, 2=Shared Key, 3=both
+@@ -2942,19 +3521,13 @@
+
+ /**
+ * sta_auth - Station authentication indication
+- * @priv: Private driver interface data
+- * @own_addr: Source address and BSSID for authentication frame
+- * @addr: MAC address of the station to associate
+- * @seq: authentication sequence number
+- * @status: authentication response status code
+- * @ie: authentication frame ie buffer
+- * @len: ie buffer length
++ * @priv: private driver interface data
++ * @params: Station authentication parameters
+ *
+- * This function indicates the driver to send Authentication frame
+- * to the station.
++ * Returns: 0 on success, -1 on failure
+ */
+- int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
+- u16 seq, u16 status, const u8 *ie, size_t len);
++ int (*sta_auth)(void *priv,
++ struct wpa_driver_sta_auth_params *params);
+
+ /**
+ * add_tspec - Add traffic stream
+@@ -2986,7 +3559,6 @@
+ * sched_scan - Request the driver to initiate scheduled scan
+ * @priv: Private driver interface data
+ * @params: Scan parameters
+- * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This operation should be used for scheduled scan offload to
+@@ -2997,8 +3569,7 @@
+ * and if not provided or if it returns -1, we fall back to
+ * normal host-scheduled scans.
+ */
+- int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
+- u32 interval);
++ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
+
+ /**
+ * stop_sched_scan - Request the driver to stop a scheduled scan
+@@ -3145,7 +3716,7 @@
+ /**
+ * status - Get driver interface status information
+ * @priv: Private driver interface data
+- * @buf: Buffer for printing tou the status information
++ * @buf: Buffer for printing the status information
+ * @buflen: Maximum length of the buffer
+ * Returns: Length of written status information or -1 on failure
+ */
+@@ -3168,6 +3739,17 @@
+ int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+ /**
++ * disable_fils - Enable/disable FILS feature
++ * @priv: Private driver interface data
++ * @disable: 0-enable and 1-disable FILS feature
++ * Returns: 0 on success, -1 on failure
++ *
++ * This callback can be used to configure driver and below layers to
++ * enable/disable all FILS features.
++ */
++ int (*disable_fils)(void *priv, int disable);
++
++ /**
+ * set_mac_addr - Set MAC address
+ * @priv: Private driver interface data
+ * @addr: MAC address to use or %NULL for setting back to permanent
+@@ -3181,6 +3763,14 @@
+ int (*macsec_deinit)(void *priv);
+
+ /**
++ * macsec_get_capability - Inform MKA of this driver's capability
++ * @priv: Private driver interface data
++ * @cap: Driver's capability
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
++
++ /**
+ * enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+@@ -3190,6 +3780,15 @@
+ int (*enable_protect_frames)(void *priv, Boolean enabled);
+
+ /**
++ * enable_encrypt - Set encryption status
++ * @priv: Private driver interface data
++ * @enabled: TRUE = encrypt outgoing traffic
++ * FALSE = integrity-only protection on outgoing traffic
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++ int (*enable_encrypt)(void *priv, Boolean enabled);
++
++ /**
+ * set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: TRUE = replay protect enabled
+@@ -3203,11 +3802,9 @@
+ * set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+- * @cs_len: Length of the cs buffer in octets
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*set_current_cipher_suite)(void *priv, const u8 *cs,
+- size_t cs_len);
++ int (*set_current_cipher_suite)(void *priv, u64 cs);
+
+ /**
+ * enable_controlled_port - Set controlled port status
+@@ -3221,155 +3818,137 @@
+ /**
+ * get_receive_lowest_pn - Get receive lowest pn
+ * @priv: Private driver interface data
+- * @channel: secure channel
+- * @an: association number
+- * @lowest_pn: lowest accept pn
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
+- u32 *lowest_pn);
++ int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+ /**
+ * get_transmit_next_pn - Get transmit next pn
+ * @priv: Private driver interface data
+- * @channel: secure channel
+- * @an: association number
+- * @next_pn: next pn
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
+- u32 *next_pn);
++ int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+- * @channel: secure channel
+- * @an: association number
+- * @next_pn: next pn
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
+- u32 next_pn);
++ int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+ /**
+- * get_available_receive_sc - get available receive channel
++ * set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+- * @channel: secure channel
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*get_available_receive_sc)(void *priv, u32 *channel);
++ int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+ /**
+ * create_receive_sc - create secure channel for receiving
+ * @priv: Private driver interface data
+- * @channel: secure channel
+- * @sci_addr: secure channel identifier - address
+- * @sci_port: secure channel identifier - port
++ * @sc: secure channel
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+- int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
+- u16 sci_port, unsigned int conf_offset,
++ int (*create_receive_sc)(void *priv, struct receive_sc *sc,
++ unsigned int conf_offset,
+ int validation);
+
+ /**
+ * delete_receive_sc - delete secure connection for receiving
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
++ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*delete_receive_sc)(void *priv, u32 channel);
++ int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
+
+ /**
+ * create_receive_sa - create secure association for receive
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
+- * @an: association number
+- * @lowest_pn: the lowest packet number can be received
+- * @sak: the secure association key
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*create_receive_sa)(void *priv, u32 channel, u8 an,
+- u32 lowest_pn, const u8 *sak);
++ int (*create_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
++ * delete_receive_sa - Delete secure association for receive
++ * @priv: Private driver interface data from init()
++ * @sa: Secure association
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
++
++ /**
+ * enable_receive_sa - enable the SA for receive
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
+- * @an: association number
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
++ int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+ * disable_receive_sa - disable SA for receive
+ * @priv: private driver interface data from init()
+- * @channel: secure channel index
+- * @an: association number
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
++ int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+- * get_available_transmit_sc - get available transmit channel
+- * @priv: Private driver interface data
+- * @channel: secure channel
+- * Returns: 0 on success, -1 on failure (or if not supported)
+- */
+- int (*get_available_transmit_sc)(void *priv, u32 *channel);
+-
+- /**
+ * create_transmit_sc - create secure connection for transmit
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
+- * @sci_addr: secure channel identifier - address
+- * @sci_port: secure channel identifier - port
++ * @sc: secure channel
++ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
+- u16 sci_port, unsigned int conf_offset);
++ int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
++ unsigned int conf_offset);
+
+ /**
+ * delete_transmit_sc - delete secure connection for transmit
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
++ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*delete_transmit_sc)(void *priv, u32 channel);
++ int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
+
+ /**
+ * create_transmit_sa - create secure association for transmit
+ * @priv: private driver interface data from init()
+- * @channel: secure channel index
+- * @an: association number
+- * @next_pn: the packet number used as next transmit packet
+- * @confidentiality: True if the SA is to provide confidentiality
+- * as well as integrity
+- * @sak: the secure association key
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
+- Boolean confidentiality, const u8 *sak);
++ int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
++ * delete_transmit_sa - Delete secure association for transmit
++ * @priv: Private driver interface data from init()
++ * @sa: Secure association
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
++
++ /**
+ * enable_transmit_sa - enable SA for transmit
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
+- * @an: association number
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
++ int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * disable_transmit_sa - disable SA for transmit
+ * @priv: private driver interface data from init()
+- * @channel: secure channel
+- * @an: association number
++ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+- int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
++ int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
+ #endif /* CONFIG_MACSEC */
+
+ /**
+@@ -3439,9 +4018,157 @@
+ * on. Local device is assuming P2P Client role.
+ */
+ int (*set_prob_oper_freq)(void *priv, unsigned int freq);
++
++ /**
++ * abort_scan - Request the driver to abort an ongoing scan
++ * @priv: Private driver interface data
++ * @scan_cookie: Cookie identifying the scan request. This is used only
++ * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
++ * was used to trigger scan. Otherwise, 0 is used.
++ * Returns 0 on success, -1 on failure
++ */
++ int (*abort_scan)(void *priv, u64 scan_cookie);
++
++ /**
++ * configure_data_frame_filters - Request to configure frame filters
++ * @priv: Private driver interface data
++ * @filter_flags: The type of frames to filter (bitfield of
++ * WPA_DATA_FRAME_FILTER_FLAG_*)
++ * Returns: 0 on success or -1 on failure
++ */
++ int (*configure_data_frame_filters)(void *priv, u32 filter_flags);
++
++ /**
++ * get_ext_capab - Get extended capabilities for the specified interface
++ * @priv: Private driver interface data
++ * @type: Interface type for which to get extended capabilities
++ * @ext_capab: Extended capabilities fetched
++ * @ext_capab_mask: Extended capabilities mask
++ * @ext_capab_len: Length of the extended capabilities
++ * Returns: 0 on success or -1 on failure
++ */
++ int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
++ const u8 **ext_capab, const u8 **ext_capab_mask,
++ unsigned int *ext_capab_len);
++
++ /**
++ * p2p_lo_start - Start offloading P2P listen to device
++ * @priv: Private driver interface data
++ * @freq: Listening frequency (MHz) for P2P listen
++ * @period: Length of the listen operation in milliseconds
++ * @interval: Interval for running the listen operation in milliseconds
++ * @count: Number of times to run the listen operation
++ * @device_types: Device primary and secondary types
++ * @dev_types_len: Number of bytes for device_types
++ * @ies: P2P IE and WSC IE for Probe Response frames
++ * @ies_len: Length of ies in bytes
++ * Returns: 0 on success or -1 on failure
++ */
++ int (*p2p_lo_start)(void *priv, unsigned int freq,
++ unsigned int period, unsigned int interval,
++ unsigned int count,
++ const u8 *device_types, size_t dev_types_len,
++ const u8 *ies, size_t ies_len);
++
++ /**
++ * p2p_lo_stop - Stop P2P listen offload
++ * @priv: Private driver interface data
++ * Returns: 0 on success or -1 on failure
++ */
++ int (*p2p_lo_stop)(void *priv);
++
++ /**
++ * set_default_scan_ies - Set default scan IEs
++ * @priv: Private driver interface data
++ * @ies: Scan default IEs buffer
++ * @ies_len: Length of IEs in bytes
++ * Returns: 0 on success or -1 on failure
++ *
++ * The driver can use these by default when there are no scan IEs coming
++ * in the subsequent scan requests. Also in case of one or more of IEs
++ * given in set_default_scan_ies() are missing in the subsequent scan
++ * request, the driver should merge the missing scan IEs in the scan
++ * request from the IEs set by set_default_scan_ies() in the Probe
++ * Request frames sent.
++ */
++ int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
++
++ /**
++ * set_tdls_mode - Set TDLS trigger mode to the host driver
++ * @priv: Private driver interface data
++ * @tdls_external_control: Represents if TDLS external trigger control
++ * mode is enabled/disabled.
++ *
++ * This optional callback can be used to configure the TDLS external
++ * trigger control mode to the host driver.
++ */
++ int (*set_tdls_mode)(void *priv, int tdls_external_control);
++
++ /**
++ * get_bss_transition_status - Get candidate BSS's transition status
++ * @priv: Private driver interface data
++ * @params: Candidate BSS list
++ *
++ * Get the accept or reject reason code for a list of BSS transition
++ * candidates.
++ */
++ struct wpa_bss_candidate_info *
++ (*get_bss_transition_status)(void *priv,
++ struct wpa_bss_trans_info *params);
++ /**
++ * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
++ * @priv: Private driver interface data
++ * @ignore_disallow: 0 to not ignore, 1 to ignore
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
++
++ /**
++ * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
++ * @priv: Private driver interface data
++ * @num_bssid: Number of blacklist BSSIDs
++ * @bssids: List of blacklisted BSSIDs
++ */
++ int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
++ const u8 *bssid);
++
++ /**
++ * update_connect_params - Update the connection parameters
++ * @priv: Private driver interface data
++ * @params: Association parameters
++ * @mask: Bit mask indicating which parameters in @params have to be
++ * updated
++ * Returns: 0 on success, -1 on failure
++ *
++ * Update the connection parameters when in connected state so that the
++ * driver uses the updated parameters for subsequent roaming. This is
++ * used only with drivers that implement internal BSS selection and
++ * roaming.
++ */
++ int (*update_connect_params)(
++ void *priv, struct wpa_driver_associate_params *params,
++ enum wpa_drv_update_connect_params_mask mask);
++
++ /**
++ * send_external_auth_status - Indicate the status of external
++ * authentication processing to the host driver.
++ * @priv: Private driver interface data
++ * @params: Status of authentication processing.
++ * Returns: 0 on success, -1 on failure
++ */
++ int (*send_external_auth_status)(void *priv,
++ struct external_auth *params);
++
++ /**
++ * set_4addr_mode - Set 4-address mode
++ * @priv: Private driver interface data
++ * @bridge_ifname: Bridge interface name
++ * @val: 0 - disable 4addr mode, 1 - enable 4addr mode
++ * Returns: 0 on success, < 0 on failure
++ */
++ int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
+ };
+
+-
+ /**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+@@ -3550,17 +4277,6 @@
+ EVENT_PMKID_CANDIDATE,
+
+ /**
+- * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
+- *
+- * This event can be used to inform wpa_supplicant about desire to set
+- * up secure direct link connection between two stations as defined in
+- * IEEE 802.11e with a new PeerKey mechanism that replaced the original
+- * STAKey negotiation. The caller will need to set peer address for the
+- * event.
+- */
+- EVENT_STKSTART,
+-
+- /**
+ * EVENT_TDLS - Request TDLS operation
+ *
+ * This event can be used to request a TDLS operation to be performed.
+@@ -3859,7 +4575,7 @@
+ * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
+ *
+ * The CAC was not successful, and the channel remains in the previous
+- * state. This may happen due to a radar beeing detected or other
++ * state. This may happen due to a radar being detected or other
+ * external influences.
+ */
+ EVENT_DFS_CAC_ABORTED,
+@@ -3923,6 +4639,70 @@
+ * on a DFS frequency by a driver that supports DFS Offload.
+ */
+ EVENT_DFS_CAC_STARTED,
++
++ /**
++ * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
++ */
++ EVENT_P2P_LO_STOP,
++
++ /**
++ * EVENT_BEACON_LOSS - Beacon loss detected
++ *
++ * This event indicates that no Beacon frames has been received from
++ * the current AP. This may indicate that the AP is not anymore in
++ * range.
++ */
++ EVENT_BEACON_LOSS,
++
++ /**
++ * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
++ * done previously (Pre-CAC) on the channel has expired. This would
++ * normally be on a non-ETSI DFS regulatory domain. DFS state of the
++ * channel will be moved from available to usable. A new CAC has to be
++ * performed before start operating on this channel.
++ */
++ EVENT_DFS_PRE_CAC_EXPIRED,
++
++ /**
++ * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
++ * that do not define separate commands for authentication and
++ * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
++ * authentication to wpa_supplicant. This event carries all the
++ * necessary information from the host driver for the authentication to
++ * happen.
++ */
++ EVENT_EXTERNAL_AUTH,
++
++ /**
++ * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
++ *
++ * This event should be indicated when the driver completes the 4-way
++ * handshake. This event should be preceded by an EVENT_ASSOC that
++ * indicates the completion of IEEE 802.11 association.
++ */
++ EVENT_PORT_AUTHORIZED,
++
++ /**
++ * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
++ * change event.
++ */
++ EVENT_STATION_OPMODE_CHANGED,
++
++ /**
++ * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
++ *
++ * This event is emitted when the MAC changes while the interface is
++ * enabled. When an interface was disabled and becomes enabled, it
++ * must be always assumed that the MAC possibly changed.
++ */
++ EVENT_INTERFACE_MAC_CHANGED,
++
++ /**
++ * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
++ *
++ * This event is emitted when an interface is added/removed for WDS STA.
++ */
++ EVENT_WDS_STA_INTERFACE_STATUS,
+ };
+
+
+@@ -4015,6 +4795,16 @@
+ size_t resp_ies_len;
+
+ /**
++ * resp_frame - (Re)Association Response frame
++ */
++ const u8 *resp_frame;
++
++ /**
++ * resp_frame_len - (Re)Association Response frame length
++ */
++ size_t resp_frame_len;
++
++ /**
+ * beacon_ies - Beacon or Probe Response IEs
+ *
+ * Optional Beacon/ProbeResp data: IEs included in Beacon or
+@@ -4091,6 +4881,8 @@
+
+ /**
+ * ptk_kek - The derived PTK KEK
++ * This is used in key management offload and also in FILS SK
++ * offload.
+ */
+ const u8 *ptk_kek;
+
+@@ -4098,6 +4890,42 @@
+ * ptk_kek_len - The length of ptk_kek
+ */
+ size_t ptk_kek_len;
++
++ /**
++ * subnet_status - The subnet status:
++ * 0 = unknown, 1 = unchanged, 2 = changed
++ */
++ u8 subnet_status;
++
++ /**
++ * The following information is used in FILS SK offload
++ * @fils_erp_next_seq_num
++ * @fils_pmk
++ * @fils_pmk_len
++ * @fils_pmkid
++ */
++
++ /**
++ * fils_erp_next_seq_num - The next sequence number to use in
++ * FILS ERP messages
++ */
++ u16 fils_erp_next_seq_num;
++
++ /**
++ * fils_pmk - A new PMK if generated in case of FILS
++ * authentication
++ */
++ const u8 *fils_pmk;
++
++ /**
++ * fils_pmk_len - Length of fils_pmk
++ */
++ size_t fils_pmk_len;
++
++ /**
++ * fils_pmkid - PMKID used or generated in FILS authentication
++ */
++ const u8 *fils_pmkid;
+ } assoc_info;
+
+ /**
+@@ -4174,6 +5002,7 @@
+ * struct interface_status - Data for EVENT_INTERFACE_STATUS
+ */
+ struct interface_status {
++ unsigned int ifindex;
+ char ifname[100];
+ enum {
+ EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+@@ -4193,13 +5022,6 @@
+ } pmkid_candidate;
+
+ /**
+- * struct stkstart - Data for EVENT_STKSTART
+- */
+- struct stkstart {
+- u8 peer[ETH_ALEN];
+- } stkstart;
+-
+- /**
+ * struct tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+@@ -4301,6 +5123,23 @@
+ * status_code - Status Code from (Re)association Response
+ */
+ u16 status_code;
++
++ /**
++ * timed_out - Whether failure is due to timeout (etc.) rather
++ * than explicit rejection response from the AP.
++ */
++ int timed_out;
++
++ /**
++ * timeout_reason - Reason for the timeout
++ */
++ const char *timeout_reason;
++
++ /**
++ * fils_erp_next_seq_num - The next sequence number to use in
++ * FILS ERP messages
++ */
++ u16 fils_erp_next_seq_num;
+ } assoc_reject;
+
+ struct timeout_event {
+@@ -4381,6 +5220,14 @@
+ * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+ * SSID)
+ * @num_ssids: Number of entries in ssids array
++ * @external_scan: Whether the scan info is for an external scan
++ * @nl_scan_event: 1 if the source of this scan event is a normal scan,
++ * 0 if the source of the scan event is a vendor scan
++ * @scan_start_tsf: Time when the scan started in terms of TSF of the
++ * BSS that the interface that requested the scan is connected to
++ * (if available).
++ * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
++ * is set.
+ */
+ struct scan_info {
+ int aborted;
+@@ -4388,6 +5235,10 @@
+ size_t num_freqs;
+ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+ size_t num_ssids;
++ int external_scan;
++ int nl_scan_event;
++ u64 scan_start_tsf;
++ u8 scan_start_tsf_bssid[ETH_ALEN];
+ } scan_info;
+
+ /**
+@@ -4477,9 +5328,12 @@
+ /**
+ * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+ * @addr: station address
++ * @num_packets: Number of packets lost (consecutive packets not
++ * acknowledged)
+ */
+ struct low_ack {
+ u8 addr[ETH_ALEN];
++ u32 num_packets;
+ } low_ack;
+
+ /**
+@@ -4630,6 +5484,58 @@
+ u16 ch_width;
+ enum hostapd_hw_mode hw_mode;
+ } acs_selected_channels;
++
++ /**
++ * struct p2p_lo_stop - Reason code for P2P Listen offload stop event
++ * @reason_code: Reason for stopping offload
++ * P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
++ * scheduled.
++ * P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
++ * be stopped.
++ * P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
++ * parameters.
++ * P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
++ * supported by device.
++ */
++ struct p2p_lo_stop {
++ enum {
++ P2P_LO_STOPPED_REASON_COMPLETE = 0,
++ P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
++ P2P_LO_STOPPED_REASON_INVALID_PARAM,
++ P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
++ } reason_code;
++ } p2p_lo_stop;
++
++ /* For EVENT_EXTERNAL_AUTH */
++ struct external_auth external_auth;
++
++ /**
++ * struct sta_opmode - Station's operation mode change event
++ * @addr: The station MAC address
++ * @smps_mode: SMPS mode of the station
++ * @chan_width: Channel width of the station
++ * @rx_nss: RX_NSS of the station
++ *
++ * This is used as data with EVENT_STATION_OPMODE_CHANGED.
++ */
++ struct sta_opmode {
++ const u8 *addr;
++ enum smps_mode smps_mode;
++ enum chan_width chan_width;
++ u8 rx_nss;
++ } sta_opmode;
++
++ /**
++ * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
++ */
++ struct wds_sta_interface {
++ const u8 *sta_addr;
++ const char *ifname;
++ enum {
++ INTERFACE_ADDED,
++ INTERFACE_REMOVED
++ } istatus;
++ } wds_sta_interface;
+ };
+
+ /**
+@@ -4645,6 +5551,18 @@
+ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
++/**
++ * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
++ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
++ * with struct wpa_driver_ops::init()
++ * @event: event type (defined above)
++ * @data: possible extra data for the event
++ *
++ * Same as wpa_supplicant_event(), but we search for the interface in
++ * wpa_global.
++ */
++void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
++ union wpa_event_data *data);
+
+ /*
+ * The following inline functions are provided for convenience to simplify
+@@ -4691,6 +5609,8 @@
+ /* Convert chan_width to a string for logging and control interfaces */
+ const char * channel_width_to_string(enum chan_width width);
+
++int channel_width_to_int(enum chan_width width);
++
+ int ht_supported(const struct hostapd_hw_modes *mode);
+ int vht_supported(const struct hostapd_hw_modes *mode);
+
+@@ -4697,8 +5617,56 @@
+ struct wowlan_triggers *
+ wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ const struct wpa_driver_capa *capa);
++/* Convert driver flag to string */
++const char * driver_flag_to_string(u64 flag);
+
+ /* NULL terminated array of linked in driver wrappers */
+ extern const struct wpa_driver_ops *const wpa_drivers[];
+
++
++/* Available drivers */
++
++#ifdef CONFIG_DRIVER_WEXT
++extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
++#endif /* CONFIG_DRIVER_WEXT */
++#ifdef CONFIG_DRIVER_NL80211
++/* driver_nl80211.c */
++extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
++#endif /* CONFIG_DRIVER_NL80211 */
++#ifdef CONFIG_DRIVER_HOSTAP
++extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
++#endif /* CONFIG_DRIVER_HOSTAP */
++#ifdef CONFIG_DRIVER_BSD
++extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
++#endif /* CONFIG_DRIVER_BSD */
++#ifdef CONFIG_DRIVER_OPENBSD
++/* driver_openbsd.c */
++extern const struct wpa_driver_ops wpa_driver_openbsd_ops;
++#endif /* CONFIG_DRIVER_OPENBSD */
++#ifdef CONFIG_DRIVER_NDIS
++extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
++#endif /* CONFIG_DRIVER_NDIS */
++#ifdef CONFIG_DRIVER_WIRED
++extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
++#endif /* CONFIG_DRIVER_WIRED */
++#ifdef CONFIG_DRIVER_MACSEC_QCA
++/* driver_macsec_qca.c */
++extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
++#endif /* CONFIG_DRIVER_MACSEC_QCA */
++#ifdef CONFIG_DRIVER_MACSEC_LINUX
++/* driver_macsec_linux.c */
++extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
++#endif /* CONFIG_DRIVER_MACSEC_LINUX */
++#ifdef CONFIG_DRIVER_ROBOSWITCH
++/* driver_roboswitch.c */
++extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
++#endif /* CONFIG_DRIVER_ROBOSWITCH */
++#ifdef CONFIG_DRIVER_ATHEROS
++/* driver_atheros.c */
++extern const struct wpa_driver_ops wpa_driver_atheros_ops;
++#endif /* CONFIG_DRIVER_ATHEROS */
++#ifdef CONFIG_DRIVER_NONE
++extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
++#endif /* CONFIG_DRIVER_NONE */
++
+ #endif /* DRIVER_H */
+--- contrib/wpa/src/drivers/driver_bsd.c.orig
++++ contrib/wpa/src/drivers/driver_bsd.c
+@@ -47,14 +47,25 @@
+
+ #include "l2_packet/l2_packet.h"
+
++struct bsd_driver_global {
++ void *ctx;
++ int sock; /* socket for 802.11 ioctls */
++ int route; /* routing socket for events */
++ char *event_buf;
++ size_t event_buf_len;
++ struct dl_list ifaces; /* list of interfaces */
++};
++
+ struct bsd_driver_data {
++ struct dl_list list;
++ struct bsd_driver_global *global;
+ struct hostapd_data *hapd; /* back pointer */
+
+- int sock; /* open socket for 802.11 ioctls */
+ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+- int route; /* routing socket for events */
+ char ifname[IFNAMSIZ+1]; /* interface name */
++ int flags;
+ unsigned int ifindex; /* interface index */
++ int if_removed; /* has the interface been removed? */
+ void *ctx;
+ struct wpa_driver_capa capa; /* driver capability */
+ int is_ap; /* Access point mode */
+@@ -62,11 +73,37 @@
+ int prev_privacy; /* privacy state to restore on deinit */
+ int prev_wpa; /* wpa state to restore on deinit */
+ enum ieee80211_opmode opmode; /* operation mode */
+- char *event_buf;
+- size_t event_buf_len;
+ };
+
+ /* Generic functions for hostapd and wpa_supplicant */
++static struct bsd_driver_data *
++bsd_get_drvindex(void *priv, unsigned int ifindex)
++{
++ struct bsd_driver_global *global = priv;
++ struct bsd_driver_data *drv;
++
++ dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
++ if (drv->ifindex == ifindex)
++ return drv;
++ }
++ return NULL;
++}
++
++#ifndef HOSTAPD
++static struct bsd_driver_data *
++bsd_get_drvname(void *priv, const char *ifname)
++{
++ struct bsd_driver_global *global = priv;
++ struct bsd_driver_data *drv;
++
++ dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
++ if (os_strcmp(drv->ifname, ifname) == 0)
++ return drv;
++ }
++ return NULL;
++}
++#endif /* HOSTAPD */
++
+ static int
+ bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+ {
+@@ -73,6 +110,9 @@
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req ireq;
+
++ if (drv->ifindex == 0 || drv->if_removed)
++ return -1;
++
+ os_memset(&ireq, 0, sizeof(ireq));
+ os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+ ireq.i_type = op;
+@@ -80,7 +120,7 @@
+ ireq.i_data = (void *) arg;
+ ireq.i_len = arg_len;
+
+- if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
++ if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+ "arg_len=%u]: %s", op, val, arg_len,
+ strerror(errno));
+@@ -101,8 +141,8 @@
+ ireq->i_len = arg_len;
+ ireq->i_data = arg;
+
+- if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+- wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
++ if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
++ wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, "
+ "arg_len=%u]: %s", op, arg_len, strerror(errno));
+ return -1;
+ }
+@@ -142,7 +182,7 @@
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+- if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
++ if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 ||
+ nwid.i_len > IEEE80211_NWID_LEN)
+ return -1;
+ os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+@@ -165,7 +205,7 @@
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+- return ioctl(drv->sock, SIOCS80211NWID, &ifr);
++ return ioctl(drv->global->sock, SIOCS80211NWID, &ifr);
+ #else
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+ #endif
+@@ -180,7 +220,7 @@
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+- if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
++ if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+@@ -199,7 +239,7 @@
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_media = media;
+
+- if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
++ if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+@@ -262,11 +302,12 @@
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+- if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
++ if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ return -1;
+ }
++ drv->flags = ifr.ifr_flags;
+
+ if (enable) {
+ if (ifr.ifr_flags & IFF_UP)
+@@ -278,12 +319,13 @@
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+- if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
++ if (ioctl(drv->global->sock, SIOCSIFFLAGS, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+ strerror(errno));
+ return -1;
+ }
+
++ drv->flags = ifr.ifr_flags;
+ return 0;
+ }
+
+@@ -575,7 +617,7 @@
+ os_memset(&creq, 0, sizeof(creq));
+ os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+ creq.i_channel = (u_int16_t)channel;
+- return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
++ return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq);
+ #else /* SIOCS80211CHANNEL */
+ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+ #endif /* SIOCS80211CHANNEL */
+@@ -729,7 +771,8 @@
+ static void
+ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+ {
+- struct bsd_driver_data *drv = ctx;
++ struct bsd_driver_global *global = sock_ctx;
++ struct bsd_driver_data *drv;
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+ struct ieee80211_michael_event *mic;
+@@ -738,7 +781,7 @@
+ int n;
+ union wpa_event_data data;
+
+- n = read(sock, drv->event_buf, drv->event_buf_len);
++ n = read(sock, global->event_buf, global->event_buf_len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s",
+@@ -746,15 +789,18 @@
+ return;
+ }
+
+- rtm = (struct rt_msghdr *) drv->event_buf;
++ rtm = (struct rt_msghdr *) global->event_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ return;
+ }
+- ifan = (struct if_announcemsghdr *) rtm;
+ switch (rtm->rtm_type) {
+ case RTM_IEEE80211:
++ ifan = (struct if_announcemsghdr *) rtm;
++ drv = bsd_get_drvindex(global, ifan->ifan_index);
++ if (drv == NULL)
++ return;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+@@ -810,21 +856,15 @@
+ return NULL;
+ }
+
+- drv->event_buf_len = rtbuf_len();
+-
+- drv->event_buf = os_malloc(drv->event_buf_len);
+- if (drv->event_buf == NULL) {
+- wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
++ drv->ifindex = if_nametoindex(params->ifname);
++ if (drv->ifindex == 0) {
++ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
++ __func__, params->ifname);
+ goto bad;
+ }
+
+ drv->hapd = hapd;
+- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+- if (drv->sock < 0) {
+- wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+- strerror(errno));
+- goto bad;
+- }
++ drv->global = params->global_priv;
+ os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+ drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+@@ -838,15 +878,6 @@
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto bad;
+
+- drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+- if (drv->route < 0) {
+- wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s",
+- strerror(errno));
+- goto bad;
+- }
+- eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
+- NULL);
+-
+ if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+@@ -853,13 +884,12 @@
+ goto bad;
+ }
+
++ dl_list_add(&drv->global->ifaces, &drv->list);
++
+ return drv;
+ bad:
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+- if (drv->sock >= 0)
+- close(drv->sock);
+- os_free(drv->event_buf);
+ os_free(drv);
+ return NULL;
+ }
+@@ -870,16 +900,10 @@
+ {
+ struct bsd_driver_data *drv = priv;
+
+- if (drv->route >= 0) {
+- eloop_unregister_read_sock(drv->route);
+- close(drv->route);
+- }
+- bsd_ctrl_iface(drv, 0);
+- if (drv->sock >= 0)
+- close(drv->sock);
++ if (drv->ifindex != 0)
++ bsd_ctrl_iface(drv, 0);
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+- os_free(drv->event_buf);
+ os_free(drv);
+ }
+
+@@ -931,7 +955,7 @@
+ struct ieee80211_bssid bs;
+
+ os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+- if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
++ if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0)
+ return -1;
+ os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+ return 0;
+@@ -965,7 +989,7 @@
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+- __FUNCTION__, wpa, privacy);
++ __func__, wpa, privacy);
+
+ if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+ ret = -1;
+@@ -980,7 +1004,7 @@
+ static int
+ wpa_driver_bsd_set_wpa(void *priv, int enabled)
+ {
+- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
++ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+ }
+@@ -1199,7 +1223,8 @@
+ static void
+ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+ {
+- struct bsd_driver_data *drv = sock_ctx;
++ struct bsd_driver_global *global = sock_ctx;
++ struct bsd_driver_data *drv;
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct rt_msghdr *rtm;
+@@ -1209,7 +1234,7 @@
+ struct ieee80211_join_event *join;
+ int n;
+
+- n = read(sock, drv->event_buf, drv->event_buf_len);
++ n = read(sock, global->event_buf, global->event_buf_len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s",
+@@ -1217,7 +1242,7 @@
+ return;
+ }
+
+- rtm = (struct rt_msghdr *) drv->event_buf;
++ rtm = (struct rt_msghdr *) global->event_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+@@ -1227,46 +1252,72 @@
+ switch (rtm->rtm_type) {
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+- if (ifan->ifan_index != drv->ifindex)
+- break;
+- os_strlcpy(event.interface_status.ifname, drv->ifname,
+- sizeof(event.interface_status.ifname));
+ switch (ifan->ifan_what) {
+ case IFAN_DEPARTURE:
++ drv = bsd_get_drvindex(global, ifan->ifan_index);
++ if (drv)
++ drv->if_removed = 1;
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
++ break;
++ case IFAN_ARRIVAL:
++ drv = bsd_get_drvname(global, ifan->ifan_name);
++ if (drv) {
++ drv->ifindex = ifan->ifan_index;
++ drv->if_removed = 0;
++ }
++ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
++ break;
+ default:
++ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+- event.interface_status.ifname,
++ ifan->ifan_name,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
++ os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
++ sizeof(event.interface_status.ifname));
++ if (drv) {
++ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
++ &event);
++ /*
++ * Set ifindex to zero after sending the event as the
++ * event might query the driver to ensure a match.
++ */
++ if (ifan->ifan_what == IFAN_DEPARTURE)
++ drv->ifindex = 0;
++ } else {
++ wpa_supplicant_event_global(global->ctx,
++ EVENT_INTERFACE_STATUS,
++ &event);
++ }
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+- if (ifan->ifan_index != drv->ifindex)
+- break;
++ drv = bsd_get_drvindex(global, ifan->ifan_index);
++ if (drv == NULL)
++ return;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ if (drv->is_ap)
+ break;
+- wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
++ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ if (drv->is_ap)
+ break;
+- wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
++ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ break;
+ case RTM_IEEE80211_SCAN:
+ if (drv->is_ap)
+ break;
+- wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
++ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
++ NULL);
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+- drv_event_disassoc(ctx, leave->iev_addr);
++ drv_event_disassoc(drv->ctx, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+ #ifdef RTM_IEEE80211_REJOIN
+@@ -1273,7 +1324,7 @@
+ case RTM_IEEE80211_REJOIN:
+ #endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+- bsd_new_sta(drv, ctx, join->iev_addr);
++ bsd_new_sta(drv, drv->ctx, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+@@ -1288,23 +1339,30 @@
+ os_memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast =
+ !IEEE80211_IS_MULTICAST(mic->iev_dst);
+- wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+- &event);
++ wpa_supplicant_event(drv->ctx,
++ EVENT_MICHAEL_MIC_FAILURE, &event);
+ break;
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+- if (ifm->ifm_index != drv->ifindex)
+- break;
+- if ((rtm->rtm_flags & RTF_UP) == 0) {
+- os_strlcpy(event.interface_status.ifname, drv->ifname,
+- sizeof(event.interface_status.ifname));
+- event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
++ drv = bsd_get_drvindex(global, ifm->ifm_index);
++ if (drv == NULL)
++ return;
++ if ((ifm->ifm_flags & IFF_UP) == 0 &&
++ (drv->flags & IFF_UP) != 0) {
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+- event.interface_status.ifname);
+- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
++ drv->ifname);
++ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
++ NULL);
++ } else if ((ifm->ifm_flags & IFF_UP) != 0 &&
++ (drv->flags & IFF_UP) == 0) {
++ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
++ drv->ifname);
++ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
++ NULL);
+ }
++ drv->flags = ifm->ifm_flags;
+ break;
+ }
+ }
+@@ -1331,11 +1389,16 @@
+ result->caps = sr->isr_capinfo;
+ result->qual = sr->isr_rssi;
+ result->noise = sr->isr_noise;
++
++#ifdef __FreeBSD__
+ /*
+ * the rssi value reported by the kernel is in 0.5dB steps relative to
+ * the reported noise floor. see ieee80211_node.h for details.
+ */
+ result->level = sr->isr_rssi / 2 + sr->isr_noise;
++#else
++ result->level = sr->isr_rssi;
++#endif
+
+ pos = (u8 *)(result + 1);
+
+@@ -1502,7 +1565,7 @@
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+- if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
++ if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+ if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+ if (ifmr.ifm_current & IFM_FLAG0)
+ return IEEE80211_M_AHDEMO;
+@@ -1524,7 +1587,7 @@
+ }
+
+ static void *
+-wpa_driver_bsd_init(void *ctx, const char *ifname)
++wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
+ {
+ #define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+@@ -1534,14 +1597,6 @@
+ if (drv == NULL)
+ return NULL;
+
+- drv->event_buf_len = rtbuf_len();
+-
+- drv->event_buf = os_malloc(drv->event_buf_len);
+- if (drv->event_buf == NULL) {
+- wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+- goto fail1;
+- }
+-
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+@@ -1552,25 +1607,13 @@
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+- goto fail1;
++ goto fail;
+ }
+- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+- if (drv->sock < 0)
+- goto fail1;
+
++ drv->ctx = ctx;
++ drv->global = priv;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+- /* Down interface during setup. */
+- if (bsd_ctrl_iface(drv, 0) < 0)
+- goto fail;
+
+- drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+- if (drv->route < 0)
+- goto fail;
+- eloop_register_read_sock(drv->route,
+- wpa_driver_bsd_event_receive, ctx, drv);
+-
+- drv->ctx = ctx;
+-
+ if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+ __func__, strerror(errno));
+@@ -1590,13 +1633,15 @@
+ if (wpa_driver_bsd_capa(drv))
+ goto fail;
+
++ /* Down interface during setup. */
++ if (bsd_ctrl_iface(drv, 0) < 0)
++ goto fail;
++
+ drv->opmode = get80211opmode(drv);
++ dl_list_add(&drv->global->ifaces, &drv->list);
+
+ return drv;
+ fail:
+- close(drv->sock);
+-fail1:
+- os_free(drv->event_buf);
+ os_free(drv);
+ return NULL;
+ #undef GETPARAM
+@@ -1607,22 +1652,25 @@
+ {
+ struct bsd_driver_data *drv = priv;
+
+- wpa_driver_bsd_set_wpa(drv, 0);
+- eloop_unregister_read_sock(drv->route);
++ if (drv->ifindex != 0 && !drv->if_removed) {
++ wpa_driver_bsd_set_wpa(drv, 0);
+
+- /* NB: mark interface down */
+- bsd_ctrl_iface(drv, 0);
++ /* NB: mark interface down */
++ bsd_ctrl_iface(drv, 0);
+
+- wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+- if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+- wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+- __func__);
++ wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa,
++ drv->prev_privacy);
+
++ if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)
++ < 0)
++ wpa_printf(MSG_DEBUG,
++ "%s: failed to restore roaming state",
++ __func__);
++ }
++
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+- (void) close(drv->route); /* ioctl socket */
+- (void) close(drv->sock); /* event socket */
+- os_free(drv->event_buf);
++ dl_list_del(&drv->list);
+ os_free(drv);
+ }
+
+@@ -1636,10 +1684,74 @@
+ }
+ #endif /* HOSTAPD */
+
++static void *
++bsd_global_init(void *ctx)
++{
++ struct bsd_driver_global *global;
+
++ global = os_zalloc(sizeof(*global));
++ if (global == NULL)
++ return NULL;
++
++ global->ctx = ctx;
++ dl_list_init(&global->ifaces);
++
++ global->sock = socket(PF_INET, SOCK_DGRAM, 0);
++ if (global->sock < 0) {
++ wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
++ strerror(errno));
++ goto fail1;
++ }
++
++ global->route = socket(PF_ROUTE, SOCK_RAW, 0);
++ if (global->route < 0) {
++ wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
++ strerror(errno));
++ goto fail;
++ }
++
++ global->event_buf_len = rtbuf_len();
++ global->event_buf = os_malloc(global->event_buf_len);
++ if (global->event_buf == NULL) {
++ wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
++ goto fail;
++ }
++
++#ifdef HOSTAPD
++ eloop_register_read_sock(global->route, bsd_wireless_event_receive,
++ NULL, global);
++
++#else /* HOSTAPD */
++ eloop_register_read_sock(global->route, wpa_driver_bsd_event_receive,
++ NULL, global);
++#endif /* HOSTAPD */
++
++ return global;
++
++fail:
++ close(global->sock);
++fail1:
++ os_free(global);
++ return NULL;
++}
++
++static void
++bsd_global_deinit(void *priv)
++{
++ struct bsd_driver_global *global = priv;
++
++ eloop_unregister_read_sock(global->route);
++ (void) close(global->route);
++ (void) close(global->sock);
++ os_free(global);
++}
++
++
+ const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support",
++ .global_init = bsd_global_init,
++ .global_deinit = bsd_global_deinit,
+ #ifdef HOSTAPD
+ .hapd_init = bsd_init,
+ .hapd_deinit = bsd_deinit,
+@@ -1652,7 +1764,7 @@
+ .sta_set_flags = bsd_set_sta_authorized,
+ .commit = bsd_commit,
+ #else /* HOSTAPD */
+- .init = wpa_driver_bsd_init,
++ .init2 = wpa_driver_bsd_init,
+ .deinit = wpa_driver_bsd_deinit,
+ .get_bssid = wpa_driver_bsd_get_bssid,
+ .get_ssid = wpa_driver_bsd_get_ssid,
+--- contrib/wpa/src/drivers/driver_common.c.orig
++++ contrib/wpa/src/drivers/driver_common.c
+@@ -1,6 +1,6 @@
+ /*
+ * Common driver-related functions
+- * Copyright (c) 2003-2011, Jouni Malinen
++ * Copyright (c) 2003-2017, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+@@ -35,7 +35,6 @@
+ E2S(ASSOCINFO);
+ E2S(INTERFACE_STATUS);
+ E2S(PMKID_CANDIDATE);
+- E2S(STKSTART);
+ E2S(TDLS);
+ E2S(FT_RESPONSE);
+ E2S(IBSS_RSN_START);
+@@ -80,6 +79,14 @@
+ E2S(NEW_PEER_CANDIDATE);
+ E2S(ACS_CHANNEL_SELECTED);
+ E2S(DFS_CAC_STARTED);
++ E2S(P2P_LO_STOP);
++ E2S(BEACON_LOSS);
++ E2S(DFS_PRE_CAC_EXPIRED);
++ E2S(EXTERNAL_AUTH);
++ E2S(PORT_AUTHORIZED);
++ E2S(STATION_OPMODE_CHANGED);
++ E2S(INTERFACE_MAC_CHANGED);
++ E2S(WDS_STA_INTERFACE_STATUS);
+ }
+
+ return "UNKNOWN";
+@@ -108,6 +115,25 @@
+ }
+
+
++int channel_width_to_int(enum chan_width width)
++{
++ switch (width) {
++ case CHAN_WIDTH_20_NOHT:
++ case CHAN_WIDTH_20:
++ return 20;
++ case CHAN_WIDTH_40:
++ return 40;
++ case CHAN_WIDTH_80:
++ return 80;
++ case CHAN_WIDTH_80P80:
++ case CHAN_WIDTH_160:
++ return 160;
++ default:
++ return 0;
++ }
++}
++
++
+ int ht_supported(const struct hostapd_hw_modes *mode)
+ {
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+@@ -183,12 +209,12 @@
+
+ start = buf;
+ while (*start != '\0') {
+- while (isblank(*start))
++ while (isblank((unsigned char) *start))
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+- while (!isblank(*end) && *end != '\0')
++ while (!isblank((unsigned char) *end) && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+@@ -218,3 +244,69 @@
+ os_free(buf);
+ return triggers;
+ }
++
++
++const char * driver_flag_to_string(u64 flag)
++{
++#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x
++ switch (flag) {
++ DF2S(DRIVER_IE);
++ DF2S(SET_KEYS_AFTER_ASSOC);
++ DF2S(DFS_OFFLOAD);
++ DF2S(4WAY_HANDSHAKE_PSK);
++ DF2S(4WAY_HANDSHAKE_8021X);
++ DF2S(WIRED);
++ DF2S(SME);
++ DF2S(AP);
++ DF2S(SET_KEYS_AFTER_ASSOC_DONE);
++ DF2S(HT_2040_COEX);
++ DF2S(P2P_CONCURRENT);
++ DF2S(P2P_DEDICATED_INTERFACE);
++ DF2S(P2P_CAPABLE);
++ DF2S(AP_TEARDOWN_SUPPORT);
++ DF2S(P2P_MGMT_AND_NON_P2P);
++ DF2S(SANE_ERROR_CODES);
++ DF2S(OFFCHANNEL_TX);
++ DF2S(EAPOL_TX_STATUS);
++ DF2S(DEAUTH_TX_STATUS);
++ DF2S(BSS_SELECTION);
++ DF2S(TDLS_SUPPORT);
++ DF2S(TDLS_EXTERNAL_SETUP);
++ DF2S(PROBE_RESP_OFFLOAD);
++ DF2S(AP_UAPSD);
++ DF2S(INACTIVITY_TIMER);
++ DF2S(AP_MLME);
++ DF2S(SAE);
++ DF2S(OBSS_SCAN);
++ DF2S(IBSS);
++ DF2S(RADAR);
++ DF2S(DEDICATED_P2P_DEVICE);
++ DF2S(QOS_MAPPING);
++ DF2S(AP_CSA);
++ DF2S(MESH);
++ DF2S(ACS_OFFLOAD);
++ DF2S(KEY_MGMT_OFFLOAD);
++ DF2S(TDLS_CHANNEL_SWITCH);
++ DF2S(HT_IBSS);
++ DF2S(VHT_IBSS);
++ DF2S(SUPPORT_HW_MODE_ANY);
++ DF2S(OFFCHANNEL_SIMULTANEOUS);
++ DF2S(FULL_AP_CLIENT_STATE);
++ DF2S(P2P_LISTEN_OFFLOAD);
++ DF2S(SUPPORT_FILS);
++ DF2S(BEACON_RATE_LEGACY);
++ DF2S(BEACON_RATE_HT);
++ DF2S(BEACON_RATE_VHT);
++ DF2S(MGMT_TX_RANDOM_TA);
++ DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
++ DF2S(SCHED_SCAN_RELATIVE_RSSI);
++ DF2S(HE_CAPABILITIES);
++ DF2S(FILS_SK_OFFLOAD);
++ DF2S(OCE_STA);
++ DF2S(OCE_AP);
++ DF2S(OCE_STA_CFON);
++ DF2S(MFP_OPTIONAL);
++ }
++ return "UNKNOWN";
++#undef DF2S
++}
+--- contrib/wpa/src/drivers/driver_macsec_linux.c.orig
++++ contrib/wpa/src/drivers/driver_macsec_linux.c
+@@ -0,0 +1,1437 @@
++/*
++ * Driver interaction with Linux MACsec kernel module
++ * Copyright (c) 2016, Sabrina Dubroca and Red Hat, Inc.
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ */
++
++#include "includes.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include "utils/common.h"
++#include "utils/eloop.h"
++#include "pae/ieee802_1x_kay.h"
++#include "driver.h"
++#include "driver_wired_common.h"
++
++#define DRV_PREFIX "macsec_linux: "
++
++#define UNUSED_SCI 0xffffffffffffffff
++
++struct cb_arg {
++ struct macsec_drv_data *drv;
++ u32 *pn;
++ int ifindex;
++ u8 txsa;
++ u8 rxsa;
++ u64 rxsci;
++};
++
++struct macsec_genl_ctx {
++ struct nl_sock *sk;
++ int macsec_genl_id;
++ struct cb_arg cb_arg;
++};
++
++struct macsec_drv_data {
++ struct driver_wired_common_data common;
++ struct rtnl_link *link;
++ struct nl_cache *link_cache;
++ struct nl_sock *sk;
++ struct macsec_genl_ctx ctx;
++
++ struct netlink_data *netlink;
++ struct nl_handle *nl;
++ char ifname[IFNAMSIZ + 1];
++ int ifi;
++ int parent_ifi;
++
++ Boolean created_link;
++
++ Boolean controlled_port_enabled;
++ Boolean controlled_port_enabled_set;
++
++ Boolean protect_frames;
++ Boolean protect_frames_set;
++
++ Boolean encrypt;
++ Boolean encrypt_set;
++
++ Boolean replay_protect;
++ Boolean replay_protect_set;
++
++ u32 replay_window;
++
++ u8 encoding_sa;
++ Boolean encoding_sa_set;
++};
++
++
++static int dump_callback(struct nl_msg *msg, void *argp);
++
++
++static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
++ const struct macsec_genl_ctx *ctx,
++ unsigned int ifindex)
++{
++ struct nl_msg *msg;
++
++ msg = nlmsg_alloc();
++ if (!msg) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
++ return NULL;
++ }
++
++ if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
++ goto nla_put_failure;
++ }
++
++ NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
++
++ return msg;
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return NULL;
++}
++
++
++static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
++{
++ struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
++
++ if (!nest)
++ return -1;
++
++ NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
++
++ nla_nest_end(msg, nest);
++
++ return 0;
++
++nla_put_failure:
++ return -1;
++}
++
++
++static int init_genl_ctx(struct macsec_drv_data *drv)
++{
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++
++ ctx->sk = nl_socket_alloc();
++ if (!ctx->sk) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
++ return -1;
++ }
++
++ if (genl_connect(ctx->sk) < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "connection to genl socket failed");
++ goto out_free;
++ }
++
++ ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
++ if (ctx->macsec_genl_id < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
++ goto out_free;
++ }
++
++ memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
++ ctx->cb_arg.drv = drv;
++
++ nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
++ &ctx->cb_arg);
++
++ return 0;
++
++out_free:
++ nl_socket_free(ctx->sk);
++ ctx->sk = NULL;
++ return -1;
++}
++
++
++static int try_commit(struct macsec_drv_data *drv)
++{
++ int err;
++
++ if (!drv->sk)
++ return 0;
++
++ if (!drv->link)
++ return 0;
++
++ if (drv->controlled_port_enabled_set) {
++ struct rtnl_link *change = rtnl_link_alloc();
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "%s: try_commit controlled_port_enabled=%d",
++ drv->ifname, drv->controlled_port_enabled);
++ if (!change)
++ return -1;
++
++ rtnl_link_set_name(change, drv->ifname);
++
++ if (drv->controlled_port_enabled)
++ rtnl_link_set_flags(change, IFF_UP);
++ else
++ rtnl_link_unset_flags(change, IFF_UP);
++
++ err = rtnl_link_change(drv->sk, change, change, 0);
++ if (err < 0)
++ return err;
++
++ rtnl_link_put(change);
++
++ drv->controlled_port_enabled_set = FALSE;
++ }
++
++ if (drv->protect_frames_set) {
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "%s: try_commit protect_frames=%d",
++ drv->ifname, drv->protect_frames);
++ rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
++ }
++
++ if (drv->encrypt_set) {
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
++ drv->ifname, drv->encrypt);
++ rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
++ }
++
++ if (drv->replay_protect_set) {
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "%s: try_commit replay_protect=%d replay_window=%d",
++ drv->ifname, drv->replay_protect,
++ drv->replay_window);
++ rtnl_link_macsec_set_replay_protect(drv->link,
++ drv->replay_protect);
++ if (drv->replay_protect)
++ rtnl_link_macsec_set_window(drv->link,
++ drv->replay_window);
++ }
++
++ if (drv->encoding_sa_set) {
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "%s: try_commit encoding_sa=%d",
++ drv->ifname, drv->encoding_sa);
++ rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
++ }
++
++ err = rtnl_link_add(drv->sk, drv->link, 0);
++ if (err < 0)
++ return err;
++
++ drv->protect_frames_set = FALSE;
++ drv->encrypt_set = FALSE;
++ drv->replay_protect_set = FALSE;
++
++ return 0;
++}
++
++
++static void macsec_drv_wpa_deinit(void *priv)
++{
++ struct macsec_drv_data *drv = priv;
++
++ driver_wired_deinit_common(&drv->common);
++ os_free(drv);
++}
++
++
++static int macsec_check_macsec(void)
++{
++ struct nl_sock *sk;
++ int err = -1;
++
++ sk = nl_socket_alloc();
++ if (!sk) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
++ return -1;
++ }
++
++ if (genl_connect(sk) < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "connection to genl socket failed");
++ goto out_free;
++ }
++
++ if (genl_ctrl_resolve(sk, "macsec") < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
++ goto out_free;
++ }
++
++ err = 0;
++
++out_free:
++ nl_socket_free(sk);
++ return err;
++}
++
++
++static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
++{
++ struct macsec_drv_data *drv;
++
++ if (macsec_check_macsec() < 0)
++ return NULL;
++
++ drv = os_zalloc(sizeof(*drv));
++ if (!drv)
++ return NULL;
++
++ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
++ os_free(drv);
++ return NULL;
++ }
++
++ return drv;
++}
++
++
++static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
++{
++ struct macsec_drv_data *drv = priv;
++ int err;
++
++ wpa_printf(MSG_DEBUG, "%s", __func__);
++
++ drv->sk = nl_socket_alloc();
++ if (!drv->sk)
++ return -1;
++
++ err = nl_connect(drv->sk, NETLINK_ROUTE);
++ if (err < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX
++ "Unable to connect NETLINK_ROUTE socket: %s",
++ strerror(errno));
++ goto sock;
++ }
++
++ err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
++ if (err < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
++ strerror(errno));
++ goto sock;
++ }
++
++ drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
++ if (drv->parent_ifi == 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX
++ "couldn't find ifindex for interface %s",
++ drv->common.ifname);
++ goto cache;
++ }
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
++ drv->common.ifname, drv->parent_ifi);
++
++ err = init_genl_ctx(drv);
++ if (err < 0)
++ goto cache;
++
++ return 0;
++
++cache:
++ nl_cache_free(drv->link_cache);
++ drv->link_cache = NULL;
++sock:
++ nl_socket_free(drv->sk);
++ drv->sk = NULL;
++ return -1;
++}
++
++
++static int macsec_drv_macsec_deinit(void *priv)
++{
++ struct macsec_drv_data *drv = priv;
++
++ wpa_printf(MSG_DEBUG, "%s", __func__);
++
++ if (drv->sk)
++ nl_socket_free(drv->sk);
++ drv->sk = NULL;
++
++ if (drv->link_cache)
++ nl_cache_free(drv->link_cache);
++ drv->link_cache = NULL;
++
++ if (drv->ctx.sk)
++ nl_socket_free(drv->ctx.sk);
++
++ return 0;
++}
++
++
++static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
++{
++ wpa_printf(MSG_DEBUG, "%s", __func__);
++
++ *cap = MACSEC_CAP_INTEG_AND_CONF;
++
++ return 0;
++}
++
++
++/**
++ * macsec_drv_enable_protect_frames - Set protect frames status
++ * @priv: Private driver interface data
++ * @enabled: TRUE = protect frames enabled
++ * FALSE = protect frames disabled
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled)
++{
++ struct macsec_drv_data *drv = priv;
++
++ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
++
++ drv->protect_frames_set = TRUE;
++ drv->protect_frames = enabled;
++
++ return try_commit(drv);
++}
++
++
++/**
++ * macsec_drv_enable_encrypt - Set protect frames status
++ * @priv: Private driver interface data
++ * @enabled: TRUE = protect frames enabled
++ * FALSE = protect frames disabled
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_enable_encrypt(void *priv, Boolean enabled)
++{
++ struct macsec_drv_data *drv = priv;
++
++ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
++
++ drv->encrypt_set = TRUE;
++ drv->encrypt = enabled;
++
++ return try_commit(drv);
++}
++
++
++/**
++ * macsec_drv_set_replay_protect - Set replay protect status and window size
++ * @priv: Private driver interface data
++ * @enabled: TRUE = replay protect enabled
++ * FALSE = replay protect disabled
++ * @window: replay window size, valid only when replay protect enabled
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_set_replay_protect(void *priv, Boolean enabled,
++ u32 window)
++{
++ struct macsec_drv_data *drv = priv;
++
++ wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
++ enabled ? "TRUE" : "FALSE", window);
++
++ drv->replay_protect_set = TRUE;
++ drv->replay_protect = enabled;
++ if (enabled)
++ drv->replay_window = window;
++
++ return try_commit(drv);
++}
++
++
++/**
++ * macsec_drv_set_current_cipher_suite - Set current cipher suite
++ * @priv: Private driver interface data
++ * @cs: EUI64 identifier
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
++{
++ wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
++ return 0;
++}
++
++
++/**
++ * macsec_drv_enable_controlled_port - Set controlled port status
++ * @priv: Private driver interface data
++ * @enabled: TRUE = controlled port enabled
++ * FALSE = controlled port disabled
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled)
++{
++ struct macsec_drv_data *drv = priv;
++
++ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
++
++ drv->controlled_port_enabled = enabled;
++ drv->controlled_port_enabled_set = TRUE;
++
++ return try_commit(drv);
++}
++
++
++static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
++ [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
++ [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
++ [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
++ [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
++};
++
++static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
++ [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
++ [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
++ [MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
++};
++
++static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
++ [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
++ [MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
++ [MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
++ [MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
++};
++
++static int dump_callback(struct nl_msg *msg, void *argp)
++{
++ struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
++ struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
++ struct cb_arg *arg = (struct cb_arg *) argp;
++ struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
++ int err;
++
++ if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
++ return 0;
++
++ err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), main_policy);
++ if (err < 0)
++ return 0;
++
++ if (!tb_msg[MACSEC_ATTR_IFINDEX])
++ return 0;
++
++ if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
++ return 0;
++
++ if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
++ return 0;
++ } else if (arg->txsa < 4) {
++ struct nlattr *nla;
++ int rem;
++
++ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
++ struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
++
++ err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
++ sa_policy);
++ if (err < 0)
++ continue;
++ if (!tb[MACSEC_SA_ATTR_AN])
++ continue;
++ if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
++ continue;
++ if (!tb[MACSEC_SA_ATTR_PN])
++ return 0;
++ *arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
++ return 0;
++ }
++
++ return 0;
++ }
++
++ if (arg->rxsci == UNUSED_SCI)
++ return 0;
++
++ if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
++ struct nlattr *nla;
++ int rem;
++
++ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
++ struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
++
++ err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
++ sc_policy);
++ if (err < 0)
++ return 0;
++ if (!tb[MACSEC_RXSC_ATTR_SCI])
++ continue;
++ if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
++ continue;
++ if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
++ return 0;
++
++ nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
++ rem) {
++ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
++
++ err = nla_parse_nested(tb_sa,
++ MACSEC_SA_ATTR_MAX, nla,
++ sa_policy);
++ if (err < 0)
++ continue;
++ if (!tb_sa[MACSEC_SA_ATTR_AN])
++ continue;
++ if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
++ arg->rxsa)
++ continue;
++ if (!tb_sa[MACSEC_SA_ATTR_PN])
++ return 0;
++ *arg->pn =
++ nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
++
++ return 0;
++ }
++
++ return 0;
++ }
++
++ return 0;
++ }
++
++ return 0;
++}
++
++
++static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
++{
++ int ret;
++
++ ret = nl_send_auto_complete(sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ return ret;
++ }
++
++ ret = nl_recvmsgs_default(sk);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++ return ret;
++}
++
++
++static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
++ u32 *pn)
++{
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ int ret = 1;
++
++ ctx->cb_arg.ifindex = drv->ifi;
++ ctx->cb_arg.rxsci = rxsci;
++ ctx->cb_arg.rxsa = rxsa;
++ ctx->cb_arg.txsa = txsa;
++ ctx->cb_arg.pn = pn;
++
++ msg = nlmsg_alloc();
++ if (!msg) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
++ __func__);
++ return 1;
++ }
++
++ if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
++ NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
++ __func__);
++ goto out_free_msg;
++ }
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0)
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "failed to communicate: %d (%s)",
++ ret, nl_geterror(-ret));
++
++ ctx->cb_arg.pn = NULL;
++
++out_free_msg:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
++ * @priv: Private driver interface data
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ int err;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
++
++ err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
++ &sa->lowest_pn);
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
++ sa->lowest_pn);
++
++ return err;
++}
++
++
++/**
++ * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
++ * @priv: Private driver interface data
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG,
++ DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
++ drv->ifname, sa->an, sa->next_pn);
++
++ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "failed to communicate: %d (%s)",
++ ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_get_transmit_next_pn - Get transmit next PN
++ * @priv: Private driver interface data
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ int err;
++
++ wpa_printf(MSG_DEBUG, "%s", __func__);
++
++ err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
++ sa->next_pn);
++ return err;
++}
++
++
++/**
++ * macsec_drv_set_transmit_next_pn - Set transmit next pn
++ * @priv: Private driver interface data
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
++
++ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "failed to communicate: %d (%s)",
++ ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++#define SCISTR MACSTR "::%hx"
++#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
++
++/**
++ * macsec_drv_create_receive_sc - Create secure channel for receiving
++ * @priv: Private driver interface data
++ * @sc: secure channel
++ * @sci_addr: secure channel identifier - address
++ * @sci_port: secure channel identifier - port
++ * @conf_offset: confidentiality offset (0, 30, or 50)
++ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
++ * 2 = Strict)
++ * Returns: 0 on success, -1 on failure (or if not supported)
++ */
++static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
++ unsigned int conf_offset,
++ int validation)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
++ " (conf_offset=%u validation=%d)",
++ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
++ conf_offset, validation);
++
++ msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
++ goto nla_put_failure;
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
++ * @priv: private driver interface data from init()
++ * @sc: secure channel
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
++ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
++
++ msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
++ goto nla_put_failure;
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_create_receive_sa - Create secure association for receive
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG,
++ DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
++ " (enable_receive=%d next_pn=%u)",
++ drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
++ sa->enable_receive, sa->next_pn);
++ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
++ &sa->pkey->key_identifier,
++ sizeof(sa->pkey->key_identifier));
++ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
++ sa->pkey->key, sa->pkey->key_len);
++
++ msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
++ goto nla_put_failure;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
++ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
++ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
++ &sa->pkey->key_identifier);
++ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_delete_receive_sa - Delete secure association for receive
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
++ goto nla_put_failure;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
++ u64 sci, unsigned char an, Boolean state)
++{
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
++ if (!msg)
++ return ret;
++
++ if (nla_put_rxsc_config(msg, sci))
++ goto nla_put_failure;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0)
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_enable_receive_sa - Enable the SA for receive
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
++ sa->an, TRUE);
++}
++
++
++/**
++ * macsec_drv_disable_receive_sa - Disable SA for receive
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
++ sa->an, FALSE);
++}
++
++
++static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
++{
++ struct rtnl_link *needle;
++ void *match;
++
++ needle = rtnl_link_macsec_alloc();
++ if (!needle)
++ return NULL;
++
++ rtnl_link_set_link(needle, parent);
++ rtnl_link_macsec_set_sci(needle, sci);
++
++ match = nl_cache_find(cache, (struct nl_object *) needle);
++ rtnl_link_put(needle);
++
++ return (struct rtnl_link *) match;
++}
++
++
++/**
++ * macsec_drv_create_transmit_sc - Create secure connection for transmit
++ * @priv: private driver interface data from init()
++ * @sc: secure channel
++ * @conf_offset: confidentiality offset
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_create_transmit_sc(
++ void *priv, struct transmit_sc *sc,
++ unsigned int conf_offset)
++{
++ struct macsec_drv_data *drv = priv;
++ struct rtnl_link *link;
++ char *ifname;
++ u64 sci;
++ int err;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
++ drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
++ conf_offset);
++
++ if (!drv->sk) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
++ return -1;
++ }
++
++ link = rtnl_link_macsec_alloc();
++ if (!link) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
++ return -1;
++ }
++
++ rtnl_link_set_link(link, drv->parent_ifi);
++
++ sci = mka_sci_u64(&sc->sci);
++ rtnl_link_macsec_set_sci(link, sci);
++
++ drv->created_link = TRUE;
++
++ err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
++ if (err == -NLE_BUSY) {
++ wpa_printf(MSG_INFO,
++ DRV_PREFIX "link already exists, using it");
++ drv->created_link = FALSE;
++ } else if (err < 0) {
++ rtnl_link_put(link);
++ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
++ err);
++ return err;
++ }
++
++ rtnl_link_put(link);
++
++ nl_cache_refill(drv->sk, drv->link_cache);
++ link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
++ if (!link) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
++ return -1;
++ }
++
++ drv->ifi = rtnl_link_get_ifindex(link);
++ ifname = rtnl_link_get_name(link);
++ wpa_printf(MSG_DEBUG,
++ DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
++ drv->common.ifname, drv->ifi, ifname);
++ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
++ rtnl_link_put(link);
++
++ drv->link = rtnl_link_macsec_alloc();
++ if (!drv->link) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
++ return -1;
++ }
++
++ rtnl_link_set_name(drv->link, drv->ifname);
++
++ /* In case some settings have already been done but we couldn't apply
++ * them. */
++ return try_commit(drv);
++}
++
++
++/**
++ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
++ * @priv: private driver interface data from init()
++ * @sc: secure channel
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
++{
++ struct macsec_drv_data *drv = priv;
++ int err;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
++ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
++
++ if (!drv->sk)
++ return 0;
++
++ if (!drv->created_link) {
++ rtnl_link_put(drv->link);
++ drv->link = NULL;
++ wpa_printf(MSG_DEBUG, DRV_PREFIX
++ "we didn't create the link, leave it alone");
++ return 0;
++ }
++
++ err = rtnl_link_delete(drv->sk, drv->link);
++ if (err < 0)
++ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
++ rtnl_link_put(drv->link);
++ drv->link = NULL;
++
++ return err;
++}
++
++
++/**
++ * macsec_drv_create_transmit_sa - Create secure association for transmit
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on "
++ SCISTR " (enable_transmit=%d next_pn=%u)",
++ drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
++ sa->enable_transmit, sa->next_pn);
++ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
++ &sa->pkey->key_identifier,
++ sizeof(sa->pkey->key_identifier));
++ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
++ sa->pkey->key, sa->pkey->key_len);
++
++ msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
++ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
++ &sa->pkey->key_identifier);
++ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
++ if (!msg)
++ return ret;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
++ unsigned char an, Boolean state)
++{
++ struct nl_msg *msg;
++ struct nlattr *nest;
++ int ret = -1;
++
++ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
++ if (!msg)
++ return ret;
++
++ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
++ if (!nest)
++ goto nla_put_failure;
++
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
++ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
++
++ nla_nest_end(msg, nest);
++
++ ret = nl_send_recv(ctx->sk, msg);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR,
++ DRV_PREFIX "%s: failed to communicate: %d (%s)",
++ __func__, ret, nl_geterror(-ret));
++ }
++
++nla_put_failure:
++ nlmsg_free(msg);
++ return ret;
++}
++
++
++/**
++ * macsec_drv_enable_transmit_sa - Enable SA for transmit
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++ int ret;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
++ if (ret < 0) {
++ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
++ return ret;
++ }
++
++ drv->encoding_sa_set = TRUE;
++ drv->encoding_sa = sa->an;
++
++ return try_commit(drv);
++}
++
++
++/**
++ * macsec_drv_disable_transmit_sa - Disable SA for transmit
++ * @priv: private driver interface data from init()
++ * @sa: secure association
++ * Returns: 0 on success, -1 on failure
++ */
++static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
++{
++ struct macsec_drv_data *drv = priv;
++ struct macsec_genl_ctx *ctx = &drv->ctx;
++
++ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
++ SCISTR, drv->ifname, sa->an,
++ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
++
++ return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
++}
++
++
++static int macsec_drv_status(void *priv, char *buf, size_t buflen)
++{
++ struct macsec_drv_data *drv = priv;
++ int res;
++ char *pos, *end;
++
++ pos = buf;
++ end = buf + buflen;
++
++ res = os_snprintf(pos, end - pos,
++ "ifname=%s\n"
++ "ifi=%d\n"
++ "parent_ifname=%s\n"
++ "parent_ifi=%d\n",
++ drv->common.ifname, drv->ifi,
++ drv->ifname, drv->parent_ifi);
++ if (os_snprintf_error(end - pos, res))
++ return pos - buf;
++ pos += res;
++
++ return pos - buf;
++}
++
++
++const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
++ .name = "macsec_linux",
++ .desc = "MACsec Ethernet driver for Linux",
++ .get_ssid = driver_wired_get_ssid,
++ .get_bssid = driver_wired_get_bssid,
++ .get_capa = driver_wired_get_capa,
++ .init = macsec_drv_wpa_init,
++ .deinit = macsec_drv_wpa_deinit,
++
++ .macsec_init = macsec_drv_macsec_init,
++ .macsec_deinit = macsec_drv_macsec_deinit,
++ .macsec_get_capability = macsec_drv_get_capability,
++ .enable_protect_frames = macsec_drv_enable_protect_frames,
++ .enable_encrypt = macsec_drv_enable_encrypt,
++ .set_replay_protect = macsec_drv_set_replay_protect,
++ .set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
++ .enable_controlled_port = macsec_drv_enable_controlled_port,
++ .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
++ .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
++ .get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
++ .set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
++ .create_receive_sc = macsec_drv_create_receive_sc,
++ .delete_receive_sc = macsec_drv_delete_receive_sc,
++ .create_receive_sa = macsec_drv_create_receive_sa,
++ .delete_receive_sa = macsec_drv_delete_receive_sa,
++ .enable_receive_sa = macsec_drv_enable_receive_sa,
++ .disable_receive_sa = macsec_drv_disable_receive_sa,
++ .create_transmit_sc = macsec_drv_create_transmit_sc,
++ .delete_transmit_sc = macsec_drv_delete_transmit_sc,
++ .create_transmit_sa = macsec_drv_create_transmit_sa,
++ .delete_transmit_sa = macsec_drv_delete_transmit_sa,
++ .enable_transmit_sa = macsec_drv_enable_transmit_sa,
++ .disable_transmit_sa = macsec_drv_disable_transmit_sa,
++
++ .status = macsec_drv_status,
++};
+--- contrib/wpa/src/drivers/driver_macsec_qca.c.orig
++++ contrib/wpa/src/drivers/driver_macsec_qca.c
+@@ -11,6 +11,7 @@
+ #include "includes.h"
+ #include
+ #include