Add initial 802.11ac (VHT) support to net80211.
authorstsp <stsp@openbsd.org>
Mon, 14 Mar 2022 15:07:24 +0000 (15:07 +0000)
committerstsp <stsp@openbsd.org>
Mon, 14 Mar 2022 15:07:24 +0000 (15:07 +0000)
Add VHT capability and operation IE definitions to ieee80211.h.
Introduce channel flags to identify 80MHz and 160MHz capable channels.

Parse VHT IEs in beacons, announce the driver's VHT capabilities in
probe requests and assoc requests, and hop into 11ac mode after
association to the AP if possible.

Enable VHT by default if the driver announces support for it.

ok claudio@

sys/net80211/ieee80211.c
sys/net80211/ieee80211.h
sys/net80211/ieee80211_input.c
sys/net80211/ieee80211_ioctl.h
sys/net80211/ieee80211_node.c
sys/net80211/ieee80211_node.h
sys/net80211/ieee80211_output.c
sys/net80211/ieee80211_proto.c
sys/net80211/ieee80211_proto.h
sys/net80211/ieee80211_var.h

index 83d4eac..fcd7637 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211.c,v 1.86 2021/12/05 11:33:45 stsp Exp $     */
+/*     $OpenBSD: ieee80211.c,v 1.87 2022/03/14 15:07:24 stsp Exp $     */
 /*     $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $    */
 
 /*-
@@ -472,11 +472,10 @@ ieee80211_media_init(struct ifnet *ifp,
                                ADD(ic, IFM_IEEE80211_VHT_MCS0 + i,
                                    mopt | IFM_IEEE80211_MONITOR);
                }
-#if 0
                ic->ic_flags |= IEEE80211_F_VHTON; /* enable 11ac by default */
+               ic->ic_flags |= IEEE80211_F_HTON; /* 11ac implies 11n */
                if (ic->ic_caps & IEEE80211_C_QOS)
                        ic->ic_flags |= IEEE80211_F_QOS;
-#endif
        }
 
        ieee80211_media_status(ifp, &imr);
@@ -663,6 +662,7 @@ ieee80211_media_change(struct ifnet *ifp)
            (newphymode == IEEE80211_MODE_AUTO ||
            newphymode == IEEE80211_MODE_11AC)) {
                ic->ic_flags |= IEEE80211_F_VHTON;
+               ic->ic_flags |= IEEE80211_F_HTON;
                ieee80211_configure_ampdu_tx(ic, 1);
        } else if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) &&
            (newphymode == IEEE80211_MODE_AUTO ||
index 2d31a17..74fb5bd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211.h,v 1.62 2019/02/19 08:12:30 stsp Exp $     */
+/*     $OpenBSD: ieee80211.h,v 1.63 2022/03/14 15:07:24 stsp Exp $     */
 /*     $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $     */
 
 /*-
@@ -441,7 +441,17 @@ enum {
        IEEE80211_ELEMID_U_APSD_COEX            = 142,
        /* 143-174 reserved */
        IEEE80211_ELEMID_MCCAOP_ADVERT_OVIEW    = 174,
-       /* 175-220 reserved */
+       /* 175-190 reserved */
+       IEEE80211_ELEMID_VHTCAPS                = 191,  /* 11ac */
+       IEEE80211_ELEMID_VHTOP                  = 192,  /* 11ac */
+       IEEE80211_ELEMID_EXT_BSS_LOAD           = 193,  /* 11ac */
+       IEEE80211_ELEMID_WIDEBAND_CHNL_SWITCH   = 194,  /* 11ac */
+       IEEE80211_ELEMID_VHT_TXPOWER            = 195,  /* 11ac */
+       IEEE80211_ELEMID_CHNL_SWITCH_WRAPPER    = 196,  /* 11ac */
+       IEEE80211_ELEMID_AID                    = 197,  /* 11ac */
+       IEEE80211_ELEMID_QUIET_CHNL             = 198,  /* 11ac */
+       IEEE80211_ELEMID_OPMODE_NOTIF           = 199,  /* 11ac */
+       /* 200-220 reserved */
        IEEE80211_ELEMID_VENDOR                 = 221   /* vendor private */
        /* 222-255 reserved */
 };
@@ -711,6 +721,84 @@ enum {
 #define IEEE80211_HTOP2_PCOPHASE40     0x0800
 /* Bits 12-15 are reserved. */
 
+/*
+ * VHT Capabilities Info (see 802.11ac-2013 8.4.2.160.2).
+ */
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_MASK  0x00000003
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_SHIFT 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895  0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991  1
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_MASK       0x0c
+#define IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT      2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_80         0
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160                1
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160_8080   2
+#define IEEE80211_VHTCAP_RX_LDPC               0x00000010
+#define IEEE80211_VHTCAP_SGI80                 0x00000020
+#define IEEE80211_VHTCAP_SGI160                        0x00000040
+#define IEEE80211_VHTCAP_TX_STBC               0x00000080
+#define IEEE80211_VHTCAP_RX_STBC_SS_MASK       0x00000700
+#define IEEE80211_VHTCAP_RX_STBC_SS_SHIFT      8
+#define IEEE80211_VHTCAP_SU_BEAMFORMER         0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE         0x00001000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK   0x0000e000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT  13
+#define IEEE80211_VHTCAP_NUM_STS_MASK          0x00070000
+#define IEEE80211_VHTCAP_NUM_STS_SHIFT         16
+#define IEEE80211_VHTCAP_MU_BEAMFORMER         0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE         0x00100000
+#define IEEE80211_VHTCAP_TXOP_PS               0x00200000
+#define IEEE80211_VHTCAP_HTC_VHT               0x00400000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK    0x03800000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT   23
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_8K      0
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_16K     1
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_32K     2
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K     3
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_128K    4
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_256K    5
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_512K    6
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_1024K   7
+#define IEEE80211_VHTCAP_LINK_ADAPT_MASK       0x0c000000
+#define IEEE80211_VHTCAP_LINK_ADAPT_SHIFT      26
+#define IEEE80211_VHTCAP_LINK_ADAPT_UNSOL_MFB  2
+#define IEEE80211_VHTCAP_LINK_ADAPT_MRQ_MFB    3
+#define IEEE80211_VHTCAP_RX_ANT_PATTERN                0x10000000
+#define IEEE80211_VHTCAP_TX_ANT_PATTERN                0x20000000
+
+/*
+ * VHT-MCS and NSS map (see 802.11ac-2013 8.4.2.160.3, Figure 8-401bs).
+ * Set of VHT MCS supported for a given number of spatial streams, `n'.
+ * Used by the VHT capabilities IE and by the basic VHT MSC set in
+ * the VHT operation IE.
+ */
+#define IEEE80211_VHT_MCS_FOR_SS_MASK(n)       (0x3 << (2*((n)-1)))
+#define IEEE80211_VHT_MCS_FOR_SS_SHIFT(n)      (2*((n)-1))
+#define IEEE80211_VHT_MCS_0_7          0
+#define IEEE80211_VHT_MCS_0_8          1
+#define IEEE80211_VHT_MCS_0_9          2
+#define IEEE80211_VHT_MCS_SS_NOT_SUPP  3
+
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_MASK      0x1fff
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_SHIFT     0
+
+/* The highest number of spatial streams supported by VHT. */
+#define IEEE80211_VHT_NUM_SS   8
+
+/*
+ * VHT Operation element (see 802.11ac-2013 8.4.2.161).
+ */
+/* Byte 0. */
+#define IEEE80211_VHTOP0_CHAN_WIDTH_MASK       0x03
+#define IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT      0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_HT         0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_80         1
+#define IEEE80211_VHTOP0_CHAN_WIDTH_160                2
+#define IEEE80211_VHTOP0_CHAN_WIDTH_8080       3
+/* Byte 1 contains channel center frequency index 0 for 80, 80+80, 160 MHz. */
+/* Byte 2 contains channel center frequency index 1 for 80+80 MHz only. */
+
 /*
  * EDCA Access Categories.
  */
index 1483da3..6ce36c2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_input.c,v 1.244 2022/01/28 07:11:15 guenther Exp $  */
+/*     $OpenBSD: ieee80211_input.c,v 1.245 2022/03/14 15:07:24 stsp Exp $      */
 
 /*-
  * Copyright (c) 2001 Atsushi Onoe
@@ -1606,7 +1606,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
        const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
-       const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
+       const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop;
        u_int16_t capinfo, bintval;
        u_int8_t chan, bchan, erp;
        int is_new;
@@ -1647,7 +1647,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
        capinfo = LE_READ_2(frm); frm += 2;
 
        ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
-       htcaps = htop = NULL;
+       htcaps = htop = vhtcaps = vhtop = NULL;
        bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
        chan = bchan;
        erp = 0;
@@ -1692,6 +1692,12 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
                case IEEE80211_ELEMID_HTOP:
                        htop = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
+               case IEEE80211_ELEMID_VHTOP:
+                       vhtop = frm;
+                       break;
                case IEEE80211_ELEMID_TIM:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -1779,6 +1785,11 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
                htop = NULL; /* invalid HTOP */
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+               if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+                       vhtop = NULL; /* invalid VHTOP */
+       }
 
        if (tim) {
                ni->ni_dtimcount = tim[2];
@@ -2021,7 +2032,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m,
 {
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
-       const u_int8_t *ssid, *rates, *xrates, *htcaps;
+       const u_int8_t *ssid, *rates, *xrates, *htcaps, *vhtcaps;
        u_int8_t rate;
 
        if (ic->ic_opmode == IEEE80211_M_STA ||
@@ -2032,7 +2043,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m,
        frm = (const u_int8_t *)&wh[1];
        efrm = mtod(m, u_int8_t *) + m->m_len;
 
-       ssid = rates = xrates = htcaps = NULL;
+       ssid = rates = xrates = htcaps = vhtcaps = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2051,6 +2062,9 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m,
                case IEEE80211_ELEMID_HTCAPS:
                        htcaps = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
                }
                frm += 2 + frm[1];
        }
@@ -2101,6 +2115,10 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m,
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        else
                ieee80211_clear_htcaps(ni);
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+       else
+               ieee80211_clear_vhtcaps(ni);
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
 }
 #endif /* IEEE80211_STA_ONLY */
@@ -2170,7 +2188,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m,
 {
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
-       const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie, *htcaps;
+       const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie;
+       const u_int8_t *htcaps, *vhtcaps;
        u_int16_t capinfo, bintval;
        int resp, status = 0;
        struct ieee80211_rsnparams rsn;
@@ -2204,7 +2223,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m,
        } else
                resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
 
-       ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = NULL;
+       ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = vhtcaps = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2228,6 +2247,9 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m,
                case IEEE80211_ELEMID_HTCAPS:
                        htcaps = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
                case IEEE80211_ELEMID_VENDOR:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -2479,6 +2501,10 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m,
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        else
                ieee80211_clear_htcaps(ni);
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+       else
+               ieee80211_clear_vhtcaps(ni);
  end:
        if (status != 0) {
                IEEE80211_SEND_MGMT(ic, ni, resp, status);
@@ -2507,6 +2533,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m,
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
        const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
+       const u_int8_t *vhtcaps, *vhtop;
        u_int16_t capinfo, status, associd;
        u_int8_t rate;
 
@@ -2541,6 +2568,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m,
        associd = LE_READ_2(frm); frm += 2;
 
        rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
+       vhtcaps = vhtop = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2562,6 +2590,12 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m,
                case IEEE80211_ELEMID_HTOP:
                        htop = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
+               case IEEE80211_ELEMID_VHTOP:
+                       vhtop = frm;
+                       break;
                case IEEE80211_ELEMID_VENDOR:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -2609,8 +2643,17 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m,
                ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
        ieee80211_ht_negotiate(ic, ni);
 
-       /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
-       if (ni->ni_flags & IEEE80211_NODE_HT)
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+               if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+                       vhtop = NULL; /* invalid VHTOP */
+       }
+       ieee80211_vht_negotiate(ic, ni);
+
+       /* Hop into 11n/11ac modes after associating to a HT/VHT AP. */
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               ieee80211_setmode(ic, IEEE80211_MODE_11AC);
+       else if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_setmode(ic, IEEE80211_MODE_11N);
        else
                ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
index 65e93c2..a2c69e0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_ioctl.h,v 1.42 2022/03/07 08:13:13 stsp Exp $       */
+/*     $OpenBSD: ieee80211_ioctl.h,v 1.43 2022/03/14 15:07:24 stsp Exp $       */
 /*     $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $       */
 
 /*-
@@ -110,6 +110,8 @@ struct ieee80211_stats {
        u_int32_t       is_ht_rx_ba_window_gap_timeout;
        u_int32_t       is_ht_rx_ba_timeout;
        u_int32_t       is_ht_tx_ba_timeout;
+       u_int32_t       is_vht_nego_no_mandatory_mcs;
+       u_int32_t       is_vht_nego_no_basic_mcs;
 };
 
 #define        SIOCG80211STATS         _IOWR('i', 242, struct ifreq)
index 02c5a00..2296365 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_node.c,v 1.191 2022/01/12 08:29:27 stsp Exp $       */
+/*     $OpenBSD: ieee80211_node.c,v 1.192 2022/03/14 15:07:24 stsp Exp $       */
 /*     $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $       */
 
 /*-
@@ -87,6 +87,7 @@ void ieee80211_node_join_ht(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_ht(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_leave_vht(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_pwrsave(struct ieee80211com *,
@@ -2410,6 +2411,71 @@ ieee80211_setup_htop(struct ieee80211_node *ni, const uint8_t *data,
        return 1;
 }
 
+/*
+ * Install received VHT caps information in the node's state block.
+ */
+void
+ieee80211_setup_vhtcaps(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len)
+{
+       if (len != 12)
+               return;
+
+       ni->ni_vhtcaps = (data[0] | (data[1] << 8) | data[2] << 16 |
+           data[3] << 24);
+       ni->ni_vht_rxmcs = (data[4] | (data[5] << 8));
+       ni->ni_vht_rx_max_lgi_mbit_s = ((data[6] | (data[7] << 8)) &
+           IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+       ni->ni_vht_txmcs = (data[8] | (data[9] << 8));
+       ni->ni_vht_tx_max_lgi_mbit_s = ((data[10] | (data[11] << 8)) &
+           IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+
+       ni->ni_flags |= IEEE80211_NODE_VHTCAP;
+}
+
+/*
+ * Install received VHT op information in the node's state block.
+ */
+int
+ieee80211_setup_vhtop(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len, int isprobe)
+{
+
+       if (len != 5)
+               return 0;
+
+       if (data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_HT &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_8080)
+               return 0;
+
+       ni->ni_vht_chan_width = data[0];
+       ni->ni_vht_chan_center_freq_idx0 = data[1];
+       ni->ni_vht_chan_center_freq_idx1 = data[2];
+       ni->ni_vht_basic_mcs = (data[3] | data[4] << 8);
+       return 1;
+}
+
+#ifndef IEEE80211_STA_ONLY
+/* 
+ * Handle nodes switching from 11ac into legacy modes.
+ */
+void
+ieee80211_clear_vhtcaps(struct ieee80211_node *ni)
+{
+       ni->ni_vhtcaps = 0;
+       ni->ni_vht_rxmcs = 0;
+       ni->ni_vht_rx_max_lgi_mbit_s = 0;
+       ni->ni_vht_txmcs = 0;
+       ni->ni_vht_tx_max_lgi_mbit_s = 0;
+
+       ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+           IEEE80211_NODE_VHT_SGI160 | IEEE80211_NODE_VHTCAP);
+
+}
+#endif
+
 /*
  * Install received rate set information in the node's state block.
  */
@@ -2771,6 +2837,15 @@ ieee80211_node_leave_ht(struct ieee80211com *ic, struct ieee80211_node *ni)
        ieee80211_clear_htcaps(ni);
 }
 
+/*
+ * Handle a VHT STA leaving a VHT network.
+ */
+void
+ieee80211_node_leave_vht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       ieee80211_clear_vhtcaps(ni);
+}
+
 /*
  * Handle a station leaving an RSN network.
  */
@@ -2910,6 +2985,8 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
 
        if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_node_leave_ht(ic, ni);
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               ieee80211_node_leave_vht(ic, ni);
 
        if (ic->ic_node_leave != NULL)
                (*ic->ic_node_leave)(ic, ni);
index 82cddbf..d1535a3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_node.h,v 1.91 2022/01/12 08:29:27 stsp Exp $        */
+/*     $OpenBSD: ieee80211_node.h,v 1.92 2022/03/14 15:07:24 stsp Exp $        */
 /*     $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $        */
 
 /*-
@@ -352,6 +352,19 @@ struct ieee80211_node {
        uint16_t                ni_htop2;
        uint8_t                 ni_basic_mcs[howmany(128,NBBY)];
 
+       /* VHT capabilities */
+       uint32_t                ni_vhtcaps;
+       uint16_t                ni_vht_rxmcs;
+       uint16_t                ni_vht_rx_max_lgi_mbit_s;
+       uint16_t                ni_vht_txmcs;
+       uint16_t                ni_vht_tx_max_lgi_mbit_s;
+
+       /* VHT operation */
+       uint8_t                 ni_vht_chan_width;
+       uint8_t                 ni_vht_chan_center_freq_idx0;
+       uint8_t                 ni_vht_chan_center_freq_idx1;
+       uint16_t                ni_vht_basic_mcs;
+
        /* Timeout handlers which trigger Tx Block Ack negotiation. */
        struct timeout          ni_addba_req_to[IEEE80211_NUM_TID];
        int                     ni_addba_req_intval[IEEE80211_NUM_TID];
@@ -406,6 +419,9 @@ struct ieee80211_node {
 #define IEEE80211_NODE_HT_SGI40                0x8000  /* SGI on 40 MHz negotiated */ 
 #define IEEE80211_NODE_VHT             0x10000 /* VHT negotiated */
 #define IEEE80211_NODE_HTCAP           0x20000 /* claims to support HT */
+#define IEEE80211_NODE_VHTCAP          0x40000 /* claims to support VHT */
+#define IEEE80211_NODE_VHT_SGI80       0x80000 /* SGI on 80 MHz negotiated */ 
+#define IEEE80211_NODE_VHT_SGI160      0x100000 /* SGI on 160 MHz negotiated */ 
 
        /* If not NULL, this function gets called when ni_refcnt hits zero. */
        void                    (*ni_unref_cb)(struct ieee80211com *,
@@ -500,6 +516,64 @@ ieee80211_node_supports_ht_chan40(struct ieee80211_node *ni)
            (ni->ni_htop0 & IEEE80211_HTOP0_CHW));
 }
 
+/* 
+ * Check if the peer supports VHT.
+ * Require a VHT capabilities IE and support for VHT MCS with a single
+ * spatial stream.
+ */
+static inline int
+ieee80211_node_supports_vht(struct ieee80211_node *ni)
+{
+       uint16_t rx_mcs;
+
+       rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(1)) >>
+           IEEE80211_VHT_MCS_FOR_SS_SHIFT(1);
+
+       return ((ni->ni_flags & IEEE80211_NODE_VHTCAP) &&
+           rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 80 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi80(struct ieee80211_node *ni)
+{
+       return ieee80211_node_supports_vht(ni) &&
+           (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 160 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi160(struct ieee80211_node *ni)
+{
+       return ieee80211_node_supports_vht(ni) &&
+           (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160);
+}
+
+/* Check if the peer can receive frames sent on an 80 MHz channel. */
+static inline int
+ieee80211_node_supports_vht_chan80(struct ieee80211_node *ni)
+{
+       uint8_t cap_chan_width, op_chan_width;
+
+       if (!ieee80211_node_supports_vht(ni))
+               return 0;
+
+       cap_chan_width = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
+           IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
+       if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_80 &&  
+           cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160 &&         
+           cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160_8080)
+               return 0;
+
+       op_chan_width = (ni->ni_vht_chan_width &
+           IEEE80211_VHTOP0_CHAN_WIDTH_MASK) >>
+           IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT;
+
+       return (op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 ||
+           op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 ||
+           op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_8080);
+}
+
 struct ieee80211com;
 
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
@@ -537,6 +611,11 @@ void ieee80211_setup_htcaps(struct ieee80211_node *, const uint8_t *,
 void ieee80211_clear_htcaps(struct ieee80211_node *);
 int ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *,
     uint8_t, int);
+void ieee80211_setup_vhtcaps(struct ieee80211_node *, const uint8_t *,
+    uint8_t);
+void ieee80211_clear_vhtcaps(struct ieee80211_node *);
+int ieee80211_setup_vhtop(struct ieee80211_node *, const uint8_t *,
+    uint8_t, int);
 int ieee80211_setup_rates(struct ieee80211com *,
            struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
 void ieee80211_node_trigger_addba_req(struct ieee80211_node *, int);
index 16373cf..a89ac8c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_output.c,v 1.136 2022/01/05 05:18:25 dlg Exp $      */
+/*     $OpenBSD: ieee80211_output.c,v 1.137 2022/03/14 15:07:24 stsp Exp $     */
 /*     $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $     */
 
 /*-
@@ -319,6 +319,12 @@ const struct ieee80211_edca_ac_params
                [EDCA_AC_VI] = { 3,  4, 2,  94 },
                [EDCA_AC_VO] = { 2,  3, 2,  47 }
        },
+       [IEEE80211_MODE_11AC] = {
+               [EDCA_AC_BK] = { 4, 10, 7,   0 },
+               [EDCA_AC_BE] = { 4, 10, 3,   0 },
+               [EDCA_AC_VI] = { 3,  4, 2,  94 },
+               [EDCA_AC_VO] = { 2,  3, 2,  47 }
+       },
 };
 
 #ifndef IEEE80211_STA_ONLY
@@ -348,6 +354,12 @@ const struct ieee80211_edca_ac_params
                [EDCA_AC_VI] = { 3,  4, 1,  94 },
                [EDCA_AC_VO] = { 2,  3, 1,  47 }
        },
+       [IEEE80211_MODE_11AC] = {
+               [EDCA_AC_BK] = { 4, 10, 7,   0 },
+               [EDCA_AC_BE] = { 4,  6, 3,   0 },
+               [EDCA_AC_VI] = { 3,  4, 1,  94 },
+               [EDCA_AC_VO] = { 2,  3, 1,  47 }
+       },
 };
 #endif /* IEEE80211_STA_ONLY */
 
@@ -1177,6 +1189,22 @@ ieee80211_add_htop(u_int8_t *frm, struct ieee80211com *ic)
 }
 #endif /* !IEEE80211_STA_ONLY */
 
+/*
+ * Add a VHT Capabilities element to a frame (see 802.11ac-2013 8.4.2.160.2).
+ */
+u_int8_t *
+ieee80211_add_vhtcaps(u_int8_t *frm, struct ieee80211com *ic)
+{
+       *frm++ = IEEE80211_ELEMID_VHTCAPS;
+       *frm++ = 12;
+       LE_WRITE_4(frm, ic->ic_vhtcaps); frm += 4;
+       LE_WRITE_2(frm, ic->ic_vht_rxmcs); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_rx_max_lgi_mbit_s); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_txmcs); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_tx_max_lgi_mbit_s); frm += 2;
+       return frm;
+}
+
 #ifndef IEEE80211_STA_ONLY
 /*
  * Add a Timeout Interval element to a frame (see 7.3.2.49).
@@ -1234,7 +1262,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
            2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
            ((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
                2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
-           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+           ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1247,6 +1276,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
                frm = ieee80211_add_htcaps(frm, ic);
                frm = ieee80211_add_wme_info(frm, ic);
        }
+       if (ic->ic_flags & IEEE80211_F_VHTON)
+               frm = ieee80211_add_htcaps(frm, ic);
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
@@ -1414,7 +1445,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
            (((ic->ic_flags & IEEE80211_F_RSNON) &&
              (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
                2 + IEEE80211_WPAIE_MAXLEN : 0) +
-           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+           ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1449,6 +1481,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
                frm = ieee80211_add_htcaps(frm, ic);
                frm = ieee80211_add_wme_info(frm, ic);
        }
+       if (ic->ic_flags & IEEE80211_F_VHTON)
+               frm = ieee80211_add_vhtcaps(frm, ic);
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
index afffb2b..7f8175d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_proto.c,v 1.107 2021/12/05 11:33:45 stsp Exp $      */
+/*     $OpenBSD: ieee80211_proto.c,v 1.108 2022/03/14 15:07:24 stsp Exp $      */
 /*     $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $       */
 
 /*-
@@ -75,6 +75,7 @@ const char * const ieee80211_phymode_name[] = {
        "11b",          /* IEEE80211_MODE_11B */
        "11g",          /* IEEE80211_MODE_11G */
        "11n",          /* IEEE80211_MODE_11N */
+       "11ac",         /* IEEE80211_MODE_11AC */
 };
 
 void ieee80211_set_beacon_miss_threshold(struct ieee80211com *);
@@ -615,6 +616,58 @@ ieee80211_ht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
                ni->ni_flags |= IEEE80211_NODE_HT_SGI40;
 }
 
+void
+ieee80211_vht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       int n;
+
+       ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+           IEEE80211_NODE_VHT_SGI160);
+
+       /* Check if we support VHT. */
+       if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11AC)) == 0)
+               return;
+
+       /* Check if VHT support has been explicitly disabled. */
+       if ((ic->ic_flags & IEEE80211_F_VHTON) == 0)
+               return;
+
+       /*
+        * Check if the peer supports VHT.
+        * MCS 0-7 for a single spatial stream are mandatory.
+        */
+       if (!ieee80211_node_supports_vht(ni)) {
+               ic->ic_stats.is_vht_nego_no_mandatory_mcs++;
+               return;
+       }
+
+       if (ic->ic_opmode == IEEE80211_M_STA) {
+               /* We must support the AP's basic MCS set. */
+               for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+                       uint16_t basic_mcs = (ni->ni_vht_basic_mcs &
+                           IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+                           IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+                       uint16_t rx_mcs = (ic->ic_vht_rxmcs &
+                           IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+                           IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+                       if (basic_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP &&
+                           basic_mcs > rx_mcs) {
+                               ic->ic_stats.is_vht_nego_no_basic_mcs++;
+                               return;
+                       }
+               }
+       }
+
+       ni->ni_flags |= IEEE80211_NODE_VHT;
+
+       if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80) &&
+           (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI80))
+               ni->ni_flags |= IEEE80211_NODE_VHT_SGI80;
+       if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160) &&
+           (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI160))
+               ni->ni_flags |= IEEE80211_NODE_VHT_SGI160;
+}
+
 void
 ieee80211_tx_ba_timeout(void *arg)
 {
@@ -1259,7 +1312,7 @@ justcleanup:
                                else
                                        printf(" start %u%sMb",
                                            rate / 2, (rate & 1) ? ".5" : "");
-                               printf(" %s preamble %s slot time%s%s\n",
+                               printf(" %s preamble %s slot time%s%s%s\n",
                                    (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
                                        "short" : "long",
                                    (ic->ic_flags & IEEE80211_F_SHSLOT) ?
@@ -1267,7 +1320,9 @@ justcleanup:
                                    (ic->ic_flags & IEEE80211_F_USEPROT) ?
                                        " protection enabled" : "",
                                    (ni->ni_flags & IEEE80211_NODE_HT) ?
-                                       " HT enabled" : "");
+                                       " HT enabled" : "",
+                                   (ni->ni_flags & IEEE80211_NODE_VHT) ?
+                                       " VHT enabled" : "");
                        }
                        if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
                                /*
index da0ab91..83fa8a3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_proto.h,v 1.47 2021/12/05 11:33:45 stsp Exp $       */
+/*     $OpenBSD: ieee80211_proto.h,v 1.48 2022/03/14 15:07:24 stsp Exp $       */
 /*     $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $       */
 
 /*-
@@ -138,6 +138,7 @@ extern      u_int8_t *ieee80211_add_xrates(u_int8_t *,
                const struct ieee80211_rateset *);
 extern u_int8_t *ieee80211_add_htcaps(u_int8_t *, struct ieee80211com *);
 extern u_int8_t *ieee80211_add_htop(u_int8_t *, struct ieee80211com *);
+extern u_int8_t *ieee80211_add_vhtcaps(u_int8_t *, struct ieee80211com *);
 extern u_int8_t *ieee80211_add_tie(u_int8_t *, u_int8_t, u_int32_t);
 
 extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
@@ -168,6 +169,8 @@ extern      void ieee80211_sa_query_request(struct ieee80211com *,
            struct ieee80211_node *);
 extern void ieee80211_ht_negotiate(struct ieee80211com *,
     struct ieee80211_node *);
+extern void ieee80211_vht_negotiate(struct ieee80211com *,
+    struct ieee80211_node *);
 extern void ieee80211_tx_ba_timeout(void *);
 extern void ieee80211_rx_ba_timeout(void *);
 extern int ieee80211_addba_request(struct ieee80211com *,
index 161853a..2a427b5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ieee80211_var.h,v 1.110 2022/01/21 15:51:03 stsp Exp $        */
+/*     $OpenBSD: ieee80211_var.h,v 1.111 2022/03/14 15:07:24 stsp Exp $        */
 /*     $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
 
 /*-
@@ -120,6 +120,7 @@ enum ieee80211_protmode {
 struct ieee80211_channel {
        u_int16_t       ic_freq;        /* setting in MHz */
        u_int16_t       ic_flags;       /* see below */
+       u_int32_t       ic_xflags;      /* extra flags; see below */
 };
 
 /*
@@ -136,6 +137,12 @@ struct ieee80211_channel {
 #define IEEE80211_CHAN_VHT     0x4000  /* 11ac/VHT channel */
 #define IEEE80211_CHAN_40MHZ   0x8000  /* use of 40 MHz is allowed */
 
+/*
+ * Extra channel flags.
+ */
+#define IEEE80211_CHANX_80MHZ  0x00000001 /* use of 80 MHz is allowed */
+#define IEEE80211_CHANX_160MHZ 0x00000002 /* use of 160 MHz is allowed */
+
 /*
  * Useful combinations of channel characteristics.
  */
@@ -172,6 +179,13 @@ struct ieee80211_channel {
 #define        IEEE80211_IS_CHAN_XR(_c) \
        (((_c)->ic_flags & IEEE80211_CHAN_XR) != 0)
 
+#define        IEEE80211_CHAN_40MHZ_ALLOWED(_c) \
+       (((_c)->ic_flags & IEEE80211_CHAN_40MHZ) != 0)
+#define        IEEE80211_CHAN_80MHZ_ALLOWED(_c) \
+       (((_c)->ic_xflags & IEEE80211_CHANX_80MHZ) != 0)
+#define        IEEE80211_CHAN_160MHZ_ALLOWED(_c) \
+       (((_c)->ic_xflags & IEEE80211_CHANX_160MHZ) != 0)
+
 /*
  * EDCA AC parameters.
  */
@@ -362,6 +376,13 @@ struct ieee80211com {
        u_int8_t                ic_aselcaps;
        u_int8_t                ic_dialog_token;
        int                     ic_fixed_mcs;
+
+       uint32_t                ic_vhtcaps;
+       uint16_t                ic_vht_rxmcs;
+       uint16_t                ic_vht_rx_max_lgi_mbit_s;
+       uint16_t                ic_vht_txmcs;
+       uint16_t                ic_vht_tx_max_lgi_mbit_s;
+
        TAILQ_HEAD(, ieee80211_ess)      ic_ess;
 };
 #define        ic_if           ic_ac.ac_if