summaryrefslogtreecommitdiff
path: root/sys/net80211
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2009-01-26 19:09:42 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2009-01-26 19:09:42 +0000
commitf5bde2a8b2c137a3c51797048f76f625179c249b (patch)
treee7ada894dc97c72e04d71f1fca20fee90e8dc80b /sys/net80211
parentba892d095172c6f93edba4a0a5af1c2f8754d770 (diff)
Add some initial HT bits (not enabled yet) based on 802.11n Draft 7.01:
- implement A-MPDU frames buffering and reordering - implement A-MSDU decapsulation - process/send ADDBA Request, ADDBA Response and DELBA action frames - process Block Ack Request control frames (including MTBAR) - implement PBAC support (Protected Block Ack) - add some incomplete HT Capabilities and HT Operation IEs parsing Add more Management Frame Protection bits based on 802.11w Draft 7.0: - implement SA Query procedure (both AP and STA) - cleanup BIP Fix some bugs: - fix check for WEP key length that otherwise caused a stack smash in ieee80211_wep_encrypt (pointed out by Xavier Santolaria on macppc) - properly stop EAPOL timeout: fixes a panic that occured in HostAP mode when turning the interface down while a 4-way handshake is in progress (pointed out by Doughertys) Did some code cleanup too. The HT bits are currently not compiled in (IEEE80211_NO_HT is defined) because they won't be ready until after the next release and I didn't want to grow the kernel or to inadvertently introduce new bugs. They are here such that other people can look at the code. Notice that I had to add an extra parameter to ic_send_mgmt() for action frames, that is why there are small changes in drivers defining their own ic_send_mgmt() handler. Sorry for the not very incremental diff but this has been sitting in my tree for too long now.
Diffstat (limited to 'sys/net80211')
-rw-r--r--sys/net80211/ieee80211.h136
-rw-r--r--sys/net80211/ieee80211_crypto.c14
-rw-r--r--sys/net80211/ieee80211_crypto.h4
-rw-r--r--sys/net80211/ieee80211_crypto_bip.c12
-rw-r--r--sys/net80211/ieee80211_input.c1027
-rw-r--r--sys/net80211/ieee80211_ioctl.c13
-rw-r--r--sys/net80211/ieee80211_ioctl.h3
-rw-r--r--sys/net80211/ieee80211_node.c69
-rw-r--r--sys/net80211/ieee80211_node.h49
-rw-r--r--sys/net80211/ieee80211_output.c382
-rw-r--r--sys/net80211/ieee80211_pae_input.c36
-rw-r--r--sys/net80211/ieee80211_pae_output.c4
-rw-r--r--sys/net80211/ieee80211_priv.h5
-rw-r--r--sys/net80211/ieee80211_proto.c139
-rw-r--r--sys/net80211/ieee80211_proto.h25
-rw-r--r--sys/net80211/ieee80211_var.h32
16 files changed, 1668 insertions, 282 deletions
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index 065cf011033..11d337f248d 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211.h,v 1.47 2008/09/27 15:00:08 damien Exp $ */
+/* $OpenBSD: ieee80211.h,v 1.48 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $ */
/*-
@@ -179,10 +179,10 @@ struct ieee80211_htframe_addr4 { /* 11n */
*/
#define IEEE80211_QOS_TXOP 0xff00
#define IEEE80211_QOS_AMSDU 0x0080 /* 11n */
-#define IEEE80211_QOS_ACK_POLICY_NORMAL 0
-#define IEEE80211_QOS_ACK_POLICY_NOACK 1
-#define IEEE80211_QOS_ACK_POLICY_NOEXPLACK 2
-#define IEEE80211_QOS_ACK_POLICY_BA 3
+#define IEEE80211_QOS_ACK_POLICY_NORMAL 0x0000
+#define IEEE80211_QOS_ACK_POLICY_NOACK 0x0020
+#define IEEE80211_QOS_ACK_POLICY_NOEXPLACK 0x0040
+#define IEEE80211_QOS_ACK_POLICY_BA 0x0060
#define IEEE80211_QOS_ACK_POLICY_MASK 0x0060
#define IEEE80211_QOS_ACK_POLICY_SHIFT 5
#define IEEE80211_QOS_EOSP 0x0010
@@ -325,11 +325,13 @@ enum {
IEEE80211_ELEMID_CHALLENGE = 16,
/* 17-31 reserved for challenge text extension */
IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_HTCAPS = 45, /* 11n */
IEEE80211_ELEMID_QOS_CAP = 46,
IEEE80211_ELEMID_RSN = 48,
IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_TIE = 56, /* 11r */
+ IEEE80211_ELEMID_HTOP = 61, /* 11n */
IEEE80211_ELEMID_MMIE = 76, /* 11w */
- IEEE80211_ELEMID_ASSOC_CBT = 77, /* 11w */
IEEE80211_ELEMID_TPC = 150,
IEEE80211_ELEMID_CCKM = 156,
IEEE80211_ELEMID_VENDOR = 221 /* vendor private */
@@ -344,7 +346,7 @@ enum {
IEEE80211_CATEG_DLS = 2,
IEEE80211_CATEG_BA = 3,
IEEE80211_CATEG_HT = 7, /* 11n */
- IEEE80211_CATEG_SALT = 8 /* 11w */
+ IEEE80211_CATEG_SA_QUERY = 8 /* 11w */
};
/*
@@ -355,10 +357,10 @@ enum {
#define IEEE80211_ACTION_DELBA 2
/*
- * SALT Action field values (see Table 7.57l).
+ * SA Query Action field values (see Table 7-57l).
*/
-#define IEEE80211_ACTION_SALT_REQ 0
-#define IEEE80211_ACTION_SALT_RESP 1
+#define IEEE80211_ACTION_SA_QUERY_REQ 0
+#define IEEE80211_ACTION_SA_QUERY_RESP 1
/*
* HT Action field values (see Table 7-57m).
@@ -371,6 +373,15 @@ enum {
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
/*
+ * BA/BAR Control field (see Figure 7-13).
+ */
+#define IEEE80211_BA_ACK_POLICY 0x0001
+#define IEEE80211_BA_MULTI_TID 0x0002
+#define IEEE80211_BA_COMPRESSED 0x0004
+#define IEEE80211_BA_TID_INFO_MASK 0xf000
+#define IEEE80211_BA_TID_INFO_SHIFT 12
+
+/*
* ERP information element (see 7.3.2.13).
*/
#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
@@ -395,6 +406,100 @@ enum {
#define IEEE80211_RSNCAP_PEERKEYENA 0x0200
#define IEEE80211_RSNCAP_SPPAMSDUC 0x0400 /* 11n */
#define IEEE80211_RSNCAP_SPPAMSDUR 0x0800 /* 11n */
+#define IEEE80211_RSNCAP_PBAC 0x1000 /* 11n */
+
+/*
+ * HT Capabilities Info (see 7.3.2.57.2).
+ */
+#define IEEE80211_HTCAP_LDPC 0x00000001
+#define IEEE80211_HTCAP_CBW20_40 0x00000002
+#define IEEE80211_HTCAP_SMPS_MASK 0x0000000c
+#define IEEE80211_HTCAP_SMPS_SHIFT 2
+#define IEEE80211_HTCAP_SMPS_STA 0
+#define IEEE80211_HTCAP_SMPS_DYN 1
+#define IEEE80211_HTCAP_SMPS_DIS 3
+#define IEEE80211_HTCAP_GF 0x00000010
+#define IEEE80211_HTCAP_SGI20 0x00000020
+#define IEEE80211_HTCAP_SGI40 0x00000040
+#define IEEE80211_HTCAP_TXSTBC 0x00000080
+#define IEEE80211_HTCAP_RXSTBC_MASK 0x00000300
+#define IEEE80211_HTCAP_RXSTBC_SHIFT 8
+#define IEEE80211_HTCAP_DELAYEDBA 0x00000400
+#define IEEE80211_HTCAP_AMSDU7935 0x00000800
+#define IEEE80211_HTCAP_DSSSCCK40 0x00001000
+#define IEEE80211_HTCAP_PSMP 0x00002000
+#define IEEE80211_HTCAP_40INTOLERANT 0x00004000
+#define IEEE80211_HTCAP_LSIGTXOPPROT 0x00008000
+
+/*
+ * HT Extended Capabilities (see 7.3.2.57.5).
+ */
+#define IEEE80211_HTXCAP_PCO 0x0001
+#define IEEE80211_HTXCAP_PCOTT_MASK 0x0006
+#define IEEE80211_HTXCAP_PCOTT_SHIFT 1
+#define IEEE80211_HTXCAP_PCOTT_400 1
+#define IEEE80211_HTXCAP_PCOTT_1500 2
+#define IEEE80211_HTXCAP_PCOTT_5000 3
+/* Bits 3-7 are reserved. */
+#define IEEE80211_HTXCAP_MFB_MASK 0x0300
+#define IEEE80211_HTXCAP_MFB_SHIFT 8
+#define IEEE80211_HTXCAP_MFB_NONE 0
+#define IEEE80211_HTXCAP_MFB_UNSOL 2
+#define IEEE80211_HTXCAP_MFB_BOTH 3
+#define IEEE80211_HTXCAP_HTC 0x0400
+#define IEEE80211_HTXCAP_RDRESP 0x0800
+/* Bits 12-15 are reserved. */
+
+/*
+ * Transmit Beamforming (TxBF) Capabilities (see 7.3.2.57.6).
+ */
+#define IEEE80211_TXBFCAP_IMPLICIT_RX 0x00000001
+#define IEEE80211_TXBFCAP_RSSC 0x00000002
+#define IEEE80211_TXBFCAP_TSSC 0x00000004
+#define IEEE80211_TXBFCAP_RNDP 0x00000008
+#define IEEE80211_TXBFCAP_TNDP 0x00000010
+#define IEEE80211_TXBFCAP_IMPLICIT_TX 0x00000020
+#define IEEE80211_TXBFCAP_CALIB_MASK 0x000000c0
+#define IEEE80211_TXBFCAP_CALIB_SHIFT 6
+#define IEEE80211_TXBFCAP_TX_CSI 0x00000100
+
+/*
+ * Antenna Selection (ASEL) Capability (see 7.3.2.57.7).
+ */
+#define IEEE80211_ASELCAP_ASEL 0x01
+#define IEEE80211_ASELCAP_CSIFB 0x02
+/* Bit 7 is reserved. */
+
+/*
+ * HT Operation element (see 7.3.2.58).
+ */
+/* Byte 1. */
+#define IEEE80211_HTOP0_SCO_MASK 0x03
+#define IEEE80211_HTOP0_SCO_SHIFT 0
+#define IEEE80211_HTOP0_SCO_SCN 0
+#define IEEE80211_HTOP0_SCO_SCA 1
+#define IEEE80211_HTOP0_SCO_SCB 3
+#define IEEE80211_HTOP0_CHW 0x04
+#define IEEE80211_HTOP0_RIFS 0x08
+#define IEEE80211_HTOP0_SPSMP 0x10
+#define IEEE80211_HTOP0_SIG_MASK 0xe0
+#define IEEE80211_HTOP0_SIG_SHIFT 5
+/* Bytes 2-3. */
+#define IEEE80211_HTOP1_PROT_MASK 0x0003
+#define IEEE80211_HTOP1_PROT_SHIFT 0
+#define IEEE80211_HTOP1_NONGTSTA 0x0004
+/* Bit 3 is reserved. */
+#define IEEE80211_HTOP1_OBSS_NONHTSTA 0x0010
+/* Bits 5-15 are reserved. */
+/* Bytes 4-5. */
+/* Bits 0-5 are reserved. */
+#define IEEE80211_HTOP2_DUALBEACON 0x0040
+#define IEEE80211_HTOP2_DUALCTSPROT 0x0080
+#define IEEE80211_HTOP2_STBCBEACON 0x0100
+#define IEEE80211_HTOP2_LSIGTXOP 0x0200
+#define IEEE80211_HTOP2_PCOACTIVE 0x0400
+#define IEEE80211_HTOP2_PCOPHASE40 0x0800
+/* Bits 12-15 are reserved. */
/*
* EDCA Access Categories.
@@ -484,8 +589,9 @@ enum {
IEEE80211_REASON_RSN_IE_BAD_CAP = 22,
IEEE80211_REASON_CIPHER_REJ_POLICY = 24,
- IEEE80211_REASON_BAD_GROUP_MGMT_CIPHER = 25, /* 11w */
- IEEE80211_REASON_MFP_POLICY = 26 /* 11w */
+
+ IEEE80211_REASON_SETUP_REQUIRED = 38,
+ IEEE80211_REASON_TIMEOUT = 39
};
/*
@@ -511,6 +617,12 @@ enum {
IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25,
IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26,
+ IEEE80211_STATUS_TRY_AGAIN_LATER = 30,
+ IEEE80211_STATUS_MFP_POLICY = 31,
+
+ IEEE80211_STATUS_REFUSED = 37,
+ IEEE80211_STATUS_INVALID_PARAM = 38,
+
IEEE80211_STATUS_IE_INVALID = 40,
IEEE80211_STATUS_BAD_GROUP_CIPHER = 41,
IEEE80211_STATUS_BAD_PAIRWISE_CIPHER = 42,
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 109b1519e3f..c6a6a165cbf 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_crypto.c,v 1.56 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_crypto.c,v 1.57 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -72,7 +72,7 @@ ieee80211_crypto_attach(struct ifnet *ifp)
ic->ic_rsnciphers = IEEE80211_CIPHER_TKIP |
IEEE80211_CIPHER_CCMP;
ic->ic_rsngroupcipher = IEEE80211_CIPHER_TKIP;
- ic->ic_rsngroupmgmtcipher = IEEE80211_CIPHER_AES128_CMAC;
+ ic->ic_rsngroupmgmtcipher = IEEE80211_CIPHER_BIP;
}
ic->ic_set_key = ieee80211_set_key;
ic->ic_delete_key = ieee80211_delete_key;
@@ -119,7 +119,7 @@ ieee80211_cipher_keylen(enum ieee80211_cipher cipher)
return 16;
case IEEE80211_CIPHER_WEP104:
return 13;
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
return 16;
default: /* unknown cipher */
return 0;
@@ -143,7 +143,7 @@ ieee80211_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
case IEEE80211_CIPHER_CCMP:
error = ieee80211_ccmp_set_key(ic, k);
break;
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
error = ieee80211_bip_set_key(ic, k);
break;
default:
@@ -168,7 +168,7 @@ ieee80211_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
case IEEE80211_CIPHER_CCMP:
ieee80211_ccmp_delete_key(ic, k);
break;
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
ieee80211_bip_delete_key(ic, k);
break;
default:
@@ -213,7 +213,7 @@ ieee80211_encrypt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_CIPHER_CCMP:
m0 = ieee80211_ccmp_encrypt(ic, m0, k);
break;
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
m0 = ieee80211_bip_encap(ic, m0, k);
break;
default:
@@ -285,7 +285,7 @@ ieee80211_decrypt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_CIPHER_CCMP:
m0 = ieee80211_ccmp_decrypt(ic, m0, k);
break;
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
m0 = ieee80211_bip_decap(ic, m0, k);
break;
default:
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index 29c88d6b076..9e7cdc072fc 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_crypto.h,v 1.21 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_crypto.h,v 1.22 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -33,7 +33,7 @@ enum ieee80211_cipher {
IEEE80211_CIPHER_TKIP = 0x00000004,
IEEE80211_CIPHER_CCMP = 0x00000008,
IEEE80211_CIPHER_WEP104 = 0x00000010,
- IEEE80211_CIPHER_AES128_CMAC = 0x00000020 /* 11w */
+ IEEE80211_CIPHER_BIP = 0x00000020 /* 11w */
};
/*
diff --git a/sys/net80211/ieee80211_crypto_bip.c b/sys/net80211/ieee80211_crypto_bip.c
index f40e71ce9bb..efa96835521 100644
--- a/sys/net80211/ieee80211_crypto_bip.c
+++ b/sys/net80211/ieee80211_crypto_bip.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_crypto_bip.c,v 1.1 2008/08/12 16:51:39 damien Exp $ */
+/* $OpenBSD: ieee80211_crypto_bip.c,v 1.2 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -18,7 +18,7 @@
/*
* This code implements the Broadcast/Multicast Integrity Protocol (BIP)
- * defined in IEEE P802.11w/D6.0 section 8.3.4.
+ * defined in IEEE P802.11w/D7.0 section 8.3.4.
*/
#include <sys/param.h>
@@ -199,11 +199,9 @@ ieee80211_bip_decap(struct ieee80211com *ic, struct mbuf *m0,
AES_CMAC_Update(&ctx->cmac, (u_int8_t *)&wh[1],
m0->m_len - sizeof(*wh));
AES_CMAC_Final(mic, &ctx->cmac);
- /* truncate AES-128-CMAC to 64-bit */
- memcpy(&mmie[10], mic, 8);
/* check that MIC matches the one in MMIE */
- if (memcmp(&mmie[10], mic0, 8) != 0) {
+ if (memcmp(mic, mic0, 8) != 0) {
ic->ic_stats.is_cmac_icv_errs++;
m_freem(m0);
return NULL;
@@ -211,8 +209,8 @@ ieee80211_bip_decap(struct ieee80211com *ic, struct mbuf *m0,
/*
* There is no need to trim the MMIE from the mbuf since it is
* an information element and will be ignored by upper layers.
- * We do it anyway as it is cheap to do it here and because we
- * did not check for the presence of fixed fields yet.
+ * We do it anyway as it is cheap to do it here and because it
+ * may be confused with fixed fields by upper layers.
*/
m_adj(m0, -IEEE80211_MMIE_LEN);
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 24ef3ed4b6a..13afce16102 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,9 +1,9 @@
-/* $OpenBSD: ieee80211_input.c,v 1.106 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.107 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
- * Copyright (c) 2007, 2008 Damien Bergamini
+ * Copyright (c) 2007-2009 Damien Bergamini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -62,6 +62,19 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_priv.h>
+#ifndef IEEE80211_NO_HT
+void ieee80211_input_ba(struct ifnet *, struct mbuf *,
+ struct ieee80211_node *, int, struct ieee80211_rxinfo *);
+void ieee80211_ba_move_window(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t, u_int16_t);
+#endif
+struct mbuf *ieee80211_align_mbuf(struct mbuf *);
+void ieee80211_decap(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *, int);
+#ifndef IEEE80211_NO_HT
+void ieee80211_amsdu_decap(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *, int);
+#endif
void ieee80211_deliver_data(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
int ieee80211_parse_edca_params_body(struct ieee80211com *,
@@ -73,10 +86,6 @@ enum ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t[]);
int ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *,
u_int, struct ieee80211_rsnparams *);
int ieee80211_save_ie(const u_int8_t *, u_int8_t **);
-#ifndef IEEE80211_STA_ONLY
-void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
-#endif
void ieee80211_recv_probe_resp(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, struct ieee80211_rxinfo *, int);
#ifndef IEEE80211_STA_ONLY
@@ -95,11 +104,35 @@ void ieee80211_recv_deauth(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
+#ifndef IEEE80211_NO_HT
+void ieee80211_recv_addba_req(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+void ieee80211_recv_addba_resp(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+void ieee80211_recv_delba(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+#endif
+void ieee80211_recv_sa_query_req(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+#ifndef IEEE80211_STA_ONLY
+void ieee80211_recv_sa_query_resp(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+#endif
void ieee80211_recv_action(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
+#ifndef IEEE80211_STA_ONLY
+void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+#endif
+#ifndef IEEE80211_NO_HT
+void ieee80211_recv_bar(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+void ieee80211_bar_tid(struct ieee80211com *, struct ieee80211_node *,
+ u_int8_t, u_int16_t);
+#endif
/*
- * Retrieve the length in bytes of a 802.11 header.
+ * Retrieve the length in bytes of an 802.11 header.
*/
u_int
ieee80211_get_hdrlen(const struct ieee80211_frame *wh)
@@ -134,14 +167,12 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
{
struct ieee80211com *ic = (void *)ifp;
struct ieee80211_frame *wh;
- u_int16_t *orxseq, nrxseq;
+ u_int16_t *orxseq, nrxseq, qos;
u_int8_t dir, type, subtype, tid;
- int hdrlen;
+ int hdrlen, hasqos;
+
+ KASSERT(ni != NULL);
-#ifdef DIAGNOSTIC
- if (ni == NULL)
- panic("null node");
-#endif
/* in monitor mode, send everything directly to bpf */
if (ic->ic_opmode == IEEE80211_M_MONITOR)
goto out;
@@ -175,15 +206,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
goto err;
}
}
- /* check and save sequence control field, if present */
+ if ((hasqos = ieee80211_has_qos(wh))) {
+ qos = ieee80211_get_qos(wh);
+ tid = qos & IEEE80211_QOS_TID;
+ }
+
+ /* duplicate detection (see 9.2.9) */
if (ieee80211_has_seq(wh) &&
ic->ic_state != IEEE80211_S_SCAN) {
nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >>
IEEE80211_SEQ_SEQ_SHIFT;
- if (ieee80211_has_qos(wh)) {
- tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID;
+ if (hasqos)
orxseq = &ni->ni_qos_rxseqs[tid];
- } else
+ else
orxseq = &ni->ni_rxseq;
if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
nrxseq == *orxseq) {
@@ -324,6 +359,26 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
goto out;
}
+#ifndef IEEE80211_NO_HT
+ if (!(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE) &&
+ hasqos && (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
+ IEEE80211_QOS_ACK_POLICY_BA) {
+ /* check if we have a BA agreement for this RA/TID */
+ if (ni->ni_ba[tid].ba_state != IEEE80211_BA_AGREED) {
+ DPRINTF(("no BA agreement for %s, TID %d\n",
+ ether_sprintf(ni->ni_macaddr), tid));
+ /* send a DELBA with reason code UNKNOWN-BA */
+ IEEE80211_SEND_ACTION(ic, ni,
+ IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA,
+ IEEE80211_REASON_SETUP_REQUIRED << 16 |
+ tid);
+ goto err;
+ }
+ /* go through A-MPDU reordering */
+ ieee80211_input_ba(ifp, m, ni, tid, rxi);
+ return; /* don't free m! */
+ }
+#endif
if ((ic->ic_flags & IEEE80211_F_WEPON) ||
((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_flags & IEEE80211_NODE_RXPROT))) {
@@ -354,14 +409,14 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN);
#endif
- m = ieee80211_decap(ifp, m, hdrlen);
- if (m == NULL) {
- DPRINTF(("decapsulation error for src %s\n",
- ether_sprintf(wh->i_addr2)));
- ic->ic_stats.is_rx_decap++;
- goto err;
- }
- ieee80211_deliver_data(ic, m, ni);
+
+#ifndef IEEE80211_NO_HT
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ hasqos && (qos & IEEE80211_QOS_AMSDU))
+ ieee80211_amsdu_decap(ic, m, ni, hdrlen);
+ else
+#endif
+ ieee80211_decap(ic, m, ni, hdrlen);
return;
case IEEE80211_FC0_TYPE_MGT:
@@ -467,11 +522,12 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
ieee80211_recv_pspoll(ic, m, ni);
break;
#endif
+#ifndef IEEE80211_NO_HT
case IEEE80211_FC0_SUBTYPE_BAR:
- /* NYI */
+ ieee80211_recv_bar(ic, m, ni);
break;
- case IEEE80211_FC0_SUBTYPE_BA:
- /* NYI */
+#endif
+ default:
break;
}
goto out;
@@ -493,6 +549,120 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
}
}
+#ifndef IEEE80211_NO_HT
+/*
+ * Process a received data MPDU related to a specific HT-immediate Block Ack
+ * agreement (see 9.10.7.6).
+ */
+void
+ieee80211_input_ba(struct ifnet *ifp, struct mbuf *m,
+ struct ieee80211_node *ni, int tid, struct ieee80211_rxinfo *rxi)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+ struct ieee80211_frame *wh;
+ int idx, count;
+ u_int16_t sn;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ sn = letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+
+ /* reset Block Ack inactivity timer */
+ timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
+
+ if (SEQ_LT(sn, ba->ba_winstart)) { /* SN < WinStartB */
+ ifp->if_ierrors++;
+ m_freem(m); /* discard the MPDU */
+ return;
+ }
+ if (SEQ_LT(ba->ba_winend, sn)) { /* WinEndB < SN */
+ count = (sn - ba->ba_winend) & 0xfff;
+ if (count > ba->ba_winsize) /* no overlap */
+ count = ba->ba_winsize;
+ while (count-- > 0) {
+ /* gaps may exist */
+ if (ba->ba_buf[ba->ba_head].m != NULL) {
+ ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m,
+ ni, &ba->ba_buf[ba->ba_head].rxi);
+ ba->ba_buf[ba->ba_head].m = NULL;
+ }
+ ba->ba_head = (ba->ba_head + 1) %
+ IEEE80211_BA_MAX_WINSZ;
+ }
+ /* move window forward */
+ ba->ba_winend = sn;
+ ba->ba_winstart = (sn - ba->ba_winsize + 1) & 0xfff;
+ }
+ /* WinStartB <= SN <= WinEndB */
+
+ idx = (sn - ba->ba_winstart) & 0xfff;
+ idx = (ba->ba_head + idx) % IEEE80211_BA_MAX_WINSZ;
+ /* store the received MPDU in the buffer */
+ if (ba->ba_buf[idx].m != NULL) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ return;
+ }
+ ba->ba_buf[idx].m = m;
+ /* store Rx meta-data too */
+ rxi->rxi_flags |= IEEE80211_RXI_AMPDU_DONE;
+ ba->ba_buf[idx].rxi = *rxi;
+
+ /* pass reordered MPDUs up to the next MAC process */
+ while (ba->ba_buf[ba->ba_head].m != NULL) {
+ ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni,
+ &ba->ba_buf[ba->ba_head].rxi);
+ ba->ba_buf[ba->ba_head].m = NULL;
+
+ ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
+ /* move window forward */
+ ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff;
+ }
+ ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
+}
+
+/*
+ * Change the value of WinStartB (move window forward) upon reception of a
+ * Block Ack Request frame or an ADDBA Request (PBAC).
+ */
+void
+ieee80211_ba_move_window(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t tid, u_int16_t ssn)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+ int count;
+
+ /* assert(WinStartB <= SSN) */
+
+ count = (ssn - ba->ba_winstart) & 0xfff;
+ if (count > ba->ba_winsize) /* no overlap */
+ count = ba->ba_winsize;
+ while (count-- > 0) {
+ /* gaps may exist */
+ if (ba->ba_buf[ba->ba_head].m != NULL) {
+ ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni,
+ &ba->ba_buf[ba->ba_head].rxi);
+ ba->ba_buf[ba->ba_head].m = NULL;
+ }
+ ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
+ }
+ /* move window forward */
+ ba->ba_winstart = ssn;
+
+ /* pass reordered MPDUs up to the next MAC process */
+ while (ba->ba_buf[ba->ba_head].m != NULL) {
+ ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni,
+ &ba->ba_buf[ba->ba_head].rxi);
+ ba->ba_buf[ba->ba_head].m = NULL;
+
+ ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
+ /* move window forward */
+ ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff;
+ }
+ ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
+}
+#endif /* !IEEE80211_NO_HT */
+
void
ieee80211_deliver_data(struct ieee80211com *ic, struct mbuf *m,
struct ieee80211_node *ni)
@@ -570,17 +740,78 @@ ieee80211_deliver_data(struct ieee80211com *ic, struct mbuf *m,
}
}
+/*
+ * Make sure protocol header (e.g. IP) is aligned on a 32-bit boundary.
+ * This is achieved by copying mbufs so drivers should try to map their
+ * buffers such that this copying is not necessary. It is however not
+ * always possible because 802.11 header length may vary (non-QoS+LLC
+ * is 32 bytes while QoS+LLC is 34 bytes). Some devices are smart and
+ * add 2 padding bytes after the 802.11 header in the QoS case so this
+ * function is there for brain-dead devices only.
+ */
struct mbuf *
-ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen)
+ieee80211_align_mbuf(struct mbuf *m)
+{
+ struct mbuf *n, *n0, **np;
+ caddr_t newdata;
+ int off, pktlen;
+
+ n0 = NULL;
+ np = &n0;
+ off = 0;
+ pktlen = m->m_pkthdr.len;
+ while (pktlen > off) {
+ if (n0 == NULL) {
+ MGETHDR(n, M_DONTWAIT, MT_DATA);
+ if (n == NULL) {
+ m_freem(m);
+ return NULL;
+ }
+ M_DUP_PKTHDR(n, m);
+ n->m_len = MHLEN;
+ } else {
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n == NULL) {
+ m_freem(m);
+ m_freem(n0);
+ return NULL;
+ }
+ n->m_len = MLEN;
+ }
+ if (pktlen - off >= MINCLSIZE) {
+ MCLGET(n, M_DONTWAIT);
+ if (n->m_flags & M_EXT)
+ n->m_len = n->m_ext.ext_size;
+ }
+ if (n0 == NULL) {
+ newdata = (caddr_t)ALIGN(n->m_data + ETHER_HDR_LEN) -
+ ETHER_HDR_LEN;
+ n->m_len -= newdata - n->m_data;
+ n->m_data = newdata;
+ }
+ if (n->m_len > pktlen - off)
+ n->m_len = pktlen - off;
+ m_copydata(m, off, n->m_len, mtod(n, caddr_t));
+ off += n->m_len;
+ *np = n;
+ np = &n->m_next;
+ }
+ m_freem(m);
+ return n0;
+}
+
+void
+ieee80211_decap(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni, int hdrlen)
{
struct ieee80211_qosframe_addr4 wh; /* largest 802.11 header */
struct ether_header *eh;
struct llc *llc;
- if (m->m_len < hdrlen + LLC_SNAPFRAMELEN) {
- m = m_pullup(m, hdrlen + LLC_SNAPFRAMELEN);
- if (m == NULL)
- return NULL;
+ if (m->m_len < hdrlen + LLC_SNAPFRAMELEN &&
+ (m = m_pullup(m, hdrlen + LLC_SNAPFRAMELEN)) == NULL) {
+ ic->ic_stats.is_rx_decap++;
+ return;
}
memcpy(&wh, mtod(m, caddr_t), hdrlen);
llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
@@ -590,10 +821,10 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen)
llc->llc_snap.org_code[0] == 0 &&
llc->llc_snap.org_code[1] == 0 &&
llc->llc_snap.org_code[2] == 0) {
- m_adj(m, hdrlen + LLC_SNAPFRAMELEN - sizeof(*eh));
+ m_adj(m, hdrlen + LLC_SNAPFRAMELEN - ETHER_HDR_LEN);
llc = NULL;
} else {
- m_adj(m, hdrlen - sizeof(*eh));
+ m_adj(m, hdrlen - ETHER_HDR_LEN);
}
eh = mtod(m, struct ether_header *);
switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
@@ -614,62 +845,90 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m, int hdrlen)
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
break;
}
- if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
- struct mbuf *n, *n0, **np;
- caddr_t newdata;
- int off, pktlen;
-
- n0 = NULL;
- np = &n0;
- off = 0;
- pktlen = m->m_pkthdr.len;
- while (pktlen > off) {
- if (n0 == NULL) {
- MGETHDR(n, M_DONTWAIT, MT_DATA);
- if (n == NULL) {
- m_freem(m);
- return NULL;
- }
- M_DUP_PKTHDR(n, m);
- n->m_len = MHLEN;
- } else {
- MGET(n, M_DONTWAIT, MT_DATA);
- if (n == NULL) {
- m_freem(m);
- m_freem(n0);
- return NULL;
- }
- n->m_len = MLEN;
- }
- if (pktlen - off >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
- }
- if (n0 == NULL) {
- newdata =
- (caddr_t)ALIGN(n->m_data + sizeof(*eh)) -
- sizeof(*eh);
- n->m_len -= newdata - n->m_data;
- n->m_data = newdata;
- }
- if (n->m_len > pktlen - off)
- n->m_len = pktlen - off;
- m_copydata(m, off, n->m_len, mtod(n, caddr_t));
- off += n->m_len;
- *np = n;
- np = &n->m_next;
+ if (!ALIGNED_POINTER(mtod(m, caddr_t) + ETHER_HDR_LEN, u_int32_t)) {
+ if ((m = ieee80211_align_mbuf(m)) == NULL) {
+ ic->ic_stats.is_rx_decap++;
+ return;
}
- m_freem(m);
- m = n0;
}
if (llc != NULL) {
eh = mtod(m, struct ether_header *);
- eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
+ eh->ether_type = htons(m->m_pkthdr.len - ETHER_HDR_LEN);
}
- return m;
+ ieee80211_deliver_data(ic, m, ni);
}
+#ifndef IEEE80211_NO_HT
+/*
+ * Decapsulate an Aggregate MSDU (see 7.2.2.2).
+ */
+void
+ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni, int hdrlen)
+{
+ struct mbuf *n;
+ struct ether_header *eh;
+ struct llc *llc;
+ int len, pad;
+
+ /* strip 802.11 header */
+ m_adj(m, hdrlen);
+
+ for (;;) {
+ /* process an A-MSDU subframe */
+ if (m->m_len < ETHER_HDR_LEN + LLC_SNAPFRAMELEN) {
+ m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN);
+ if (m == NULL) {
+ ic->ic_stats.is_rx_decap++;
+ break;
+ }
+ }
+ eh = mtod(m, struct ether_header *);
+ /* examine 802.3 header */
+ len = ntohs(eh->ether_type);
+ if (len < LLC_SNAPFRAMELEN) {
+ DPRINTF(("A-MSDU subframe too short (%d)\n", len));
+ /* stop processing A-MSDU subframes */
+ ic->ic_stats.is_rx_decap++;
+ m_freem(m);
+ break;
+ }
+ llc = (struct llc *)&eh[1];
+ /* examine 802.2 LLC header */
+ if (llc->llc_dsap == LLC_SNAP_LSAP &&
+ llc->llc_ssap == LLC_SNAP_LSAP &&
+ llc->llc_control == LLC_UI &&
+ llc->llc_snap.org_code[0] == 0 &&
+ llc->llc_snap.org_code[1] == 0 &&
+ llc->llc_snap.org_code[2] == 0) {
+ /* convert to Ethernet II header */
+ eh->ether_type = llc->llc_snap.ether_type;
+ /* strip LLC+SNAP headers */
+ ovbcopy(eh, (u_int8_t *)eh + LLC_SNAPFRAMELEN,
+ ETHER_HDR_LEN);
+ m_adj(m, LLC_SNAPFRAMELEN);
+ len -= LLC_SNAPFRAMELEN;
+ }
+ len += ETHER_HDR_LEN;
+
+ /* "detach" our A-MSDU subframe from the others */
+ n = m_split(m, len, M_NOWAIT);
+ if (n == NULL) {
+ /* stop processing A-MSDU subframes */
+ ic->ic_stats.is_rx_decap++;
+ m_freem(m);
+ break;
+ }
+ ieee80211_deliver_data(ic, m, ni);
+
+ m = n;
+ /* remove padding */
+ pad = ((len + 3) & ~3) - len;
+ m_adj(m, pad);
+ }
+}
+#endif /* !IEEE80211_NO_HT */
+
/*
* Parse an EDCA Parameter Set element (see 7.3.2.27).
*/
@@ -758,8 +1017,8 @@ ieee80211_parse_rsn_cipher(const u_int8_t selector[4])
return IEEE80211_CIPHER_CCMP;
case 5: /* WEP-104 */
return IEEE80211_CIPHER_WEP104;
- case 6: /* AES-128-CMAC */
- return IEEE80211_CIPHER_AES128_CMAC;
+ case 6: /* BIP */
+ return IEEE80211_CIPHER_BIP;
}
}
return IEEE80211_CIPHER_NONE; /* ignore unknown ciphers */
@@ -814,8 +1073,8 @@ ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm,
rsn->rsn_groupcipher = IEEE80211_CIPHER_CCMP;
rsn->rsn_nciphers = 1;
rsn->rsn_ciphers = IEEE80211_CIPHER_CCMP;
- /* if Group Management Cipher Suite missing, defaut to AES-128-CMAC */
- rsn->rsn_groupmgmtcipher = IEEE80211_CIPHER_AES128_CMAC;
+ /* if Group Management Cipher Suite missing, defaut to BIP */
+ rsn->rsn_groupmgmtcipher = IEEE80211_CIPHER_BIP;
/* if AKM Suite missing, default to 802.1X */
rsn->rsn_nakms = 1;
rsn->rsn_akms = IEEE80211_AKM_8021X;
@@ -936,17 +1195,19 @@ ieee80211_save_ie(const u_int8_t *frm, u_int8_t **ie)
/*-
* Beacon/Probe response frame format:
- * [8] Timestamp
- * [2] Beacon interval
- * [2] Capability
- * [tlv] Service Set Identifier (SSID)
- * [tlv] Supported rates
- * [tlv*] DS Parameter Set (802.11g)
- * [tlv] ERP Information (802.11g)
- * [tlv] Extended Supported Rates (802.11g)
- * [tlv] RSN (802.11i)
- * [tlv] EDCA Parameter Set (802.11e)
- * [tlv] QoS Capability (Beacon only, 802.11e)
+ * [8] Timestamp
+ * [2] Beacon interval
+ * [2] Capability
+ * [tlv] Service Set Identifier (SSID)
+ * [tlv] Supported rates
+ * [tlv] DS Parameter Set (802.11g)
+ * [tlv] ERP Information (802.11g)
+ * [tlv] Extended Supported Rates (802.11g)
+ * [tlv] RSN (802.11i)
+ * [tlv] EDCA Parameter Set (802.11e)
+ * [tlv] QoS Capability (Beacon only, 802.11e)
+ * [tlv] HT Capabilities (802.11n)
+ * [tlv] HT Operation (802.11n)
*/
void
ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
@@ -955,7 +1216,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie;
- const u_int8_t *rsnie, *wpaie;
+ const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
u_int16_t capinfo, bintval;
u_int8_t chan, bchan, erp;
int is_new;
@@ -996,6 +1257,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
capinfo = LE_READ_2(frm); frm += 2;
ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = NULL;
+ htcaps = htop = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
erp = 0;
@@ -1040,6 +1302,14 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_ELEMID_QOS_CAP:
break;
+#ifndef IEEE80211_NO_HT
+ case IEEE80211_ELEMID_HTCAPS:
+ htcaps = frm;
+ break;
+ case IEEE80211_ELEMID_HTOP:
+ htop = frm;
+ break;
+#endif
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1260,6 +1530,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] SSID
* [tlv] Supported rates
* [tlv] Extended Supported Rates (802.11g)
+ * [tlv] HT Capabilities (802.11n)
*/
void
ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0,
@@ -1267,7 +1538,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0,
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates;
+ const u_int8_t *ssid, *rates, *xrates, *htcaps;
u_int8_t rate;
if (ic->ic_opmode == IEEE80211_M_STA ||
@@ -1278,7 +1549,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0,
frm = (const u_int8_t *)&wh[1];
efrm = mtod(m0, u_int8_t *) + m0->m_len;
- ssid = rates = xrates = NULL;
+ ssid = rates = xrates = htcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1294,6 +1565,11 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+#ifndef IEEE80211_NO_HT
+ case IEEE80211_ELEMID_HTCAPS:
+ htcaps = frm;
+ break;
+#endif
}
frm += 2 + frm[1];
}
@@ -1379,8 +1655,8 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0,
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
/* XXX hack to workaround calling convention */
IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_ALG<<16));
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_STATUS_ALG << 16 | ((seq + 1) & 0xffff));
}
#endif
return;
@@ -1399,6 +1675,7 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] Extended Supported Rates (802.11g)
* [tlv] RSN (802.11i)
* [tlv] QoS Capability (802.11e)
+ * [tlv] HT Capabilities (802.11n)
*/
void
ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
@@ -1406,7 +1683,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie;
+ const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *htcaps;
u_int16_t capinfo, bintval;
int resp, status = 0;
struct ieee80211_rsnparams rsn;
@@ -1433,10 +1710,13 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
}
capinfo = LE_READ_2(frm); frm += 2;
bintval = LE_READ_2(frm); frm += 2;
- if (reassoc)
+ if (reassoc) {
frm += IEEE80211_ADDR_LEN; /* skip current AP address */
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- ssid = rates = xrates = rsnie = wpaie = NULL;
+ ssid = rates = xrates = rsnie = wpaie = htcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1457,6 +1737,11 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_ELEMID_QOS_CAP:
break;
+#ifndef IEEE80211_NO_HT
+ case IEEE80211_ELEMID_HTCAPS:
+ htcaps = frm;
+ break;
+#endif
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1503,6 +1788,27 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
return;
}
+ if (ni->ni_state == IEEE80211_STA_ASSOC &&
+ (ni->ni_flags & IEEE80211_NODE_MFP)) {
+ if (ni->ni_flags & IEEE80211_NODE_SA_QUERY_FAILED) {
+ /* send a protected Disassociate frame */
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ /* terminate the old SA */
+ ieee80211_node_leave(ic, ni);
+ } else {
+ /* reject the (Re)Association Request temporarily */
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_STATUS_TRY_AGAIN_LATER);
+ /* start SA Query procedure if not already engaged */
+ if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY))
+ ieee80211_sa_query_request(ic, ni);
+ /* do not modify association state */
+ }
+ return;
+ }
+
if (!(capinfo & IEEE80211_CAPINFO_ESS)) {
ic->ic_stats.is_rx_assoc_capmismatch++;
status = IEEE80211_STATUS_CAPINFO;
@@ -1569,23 +1875,28 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
goto end;
}
- if (!(rsn.rsn_caps & IEEE80211_RSNCAP_MFPC) &&
- (ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPR)) {
- status = IEEE80211_REASON_MFP_POLICY; /* XXX */
+ if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPR) &&
+ !(rsn.rsn_caps & IEEE80211_RSNCAP_MFPC)) {
+ status = IEEE80211_STATUS_MFP_POLICY;
goto end;
}
if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPC) &&
(rsn.rsn_caps & (IEEE80211_RSNCAP_MFPC |
IEEE80211_RSNCAP_MFPR)) == IEEE80211_RSNCAP_MFPR) {
/* STA advertises an invalid setting */
- status = IEEE80211_REASON_MFP_POLICY; /* XXX */
+ status = IEEE80211_STATUS_MFP_POLICY;
goto end;
}
+ /*
+ * A STA that has associated with Management Frame Protection
+ * enabled shall not use cipher suite pairwise selector WEP40,
+ * WEP104, TKIP, or "Use Group cipher suite".
+ */
if ((rsn.rsn_caps & IEEE80211_RSNCAP_MFPC) &&
- rsn.rsn_groupmgmtcipher !=
- ic->ic_bss->ni_rsngroupmgmtcipher) {
- /* XXX satus not reason?! */
- status = IEEE80211_REASON_BAD_GROUP_MGMT_CIPHER;
+ (rsn.rsn_ciphers != IEEE80211_CIPHER_CCMP ||
+ rsn.rsn_groupmgmtcipher !=
+ ic->ic_bss->ni_rsngroupmgmtcipher)) {
+ status = IEEE80211_STATUS_MFP_POLICY;
goto end;
}
@@ -1641,8 +1952,6 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
ni->ni_capinfo = capinfo;
ni->ni_chan = ic->ic_bss->ni_chan;
end:
- resp = reassoc ? IEEE80211_FC0_SUBTYPE_REASSOC_RESP :
- IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
if (status != 0) {
IEEE80211_SEND_MGMT(ic, ni, resp, status);
ieee80211_node_leave(ic, ni);
@@ -1659,6 +1968,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] Supported rates
* [tlv] Extended Supported Rates (802.11g)
* [tlv] EDCA Parameter Set (802.11e)
+ * [tlv] HT Capabilities (802.11n)
+ * [tlv] HT Operation (802.11n)
*/
void
ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
@@ -1667,7 +1978,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
struct ifnet *ifp = &ic->ic_if;
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *rates, *xrates, *edcaie, *wmmie;
+ const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
u_int16_t capinfo, status, associd;
u_int8_t rate;
@@ -1701,7 +2012,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
}
associd = LE_READ_2(frm); frm += 2;
- rates = xrates = edcaie = wmmie = NULL;
+ rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1717,6 +2028,14 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_ELEMID_EDCAPARMS:
edcaie = frm;
break;
+#ifndef IEEE80211_NO_HT
+ case IEEE80211_ELEMID_HTCAPS:
+ htcaps = frm;
+ break;
+ case IEEE80211_ELEMID_HTOP:
+ htop = frm;
+ break;
+#endif
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1883,54 +2202,374 @@ ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0,
}
}
+#ifndef IEEE80211_NO_HT
+/*-
+ * ADDBA Request frame format:
+ * [1] Category
+ * [1] Action
+ * [1] Dialog Token
+ * [2] Block Ack Parameter Set
+ * [2] Block Ack Timeout Value
+ * [2] Block Ack Starting Sequence Control
+ */
+void
+ieee80211_recv_addba_req(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame *wh;
+ const u_int8_t *frm;
+ struct ieee80211_ba *ba;
+ u_int16_t params, ssn, bufsz, timeout, status;
+ u_int8_t token, tid;
+
+ if (!(ni->ni_flags & IEEE80211_NODE_HT)) {
+ DPRINTF(("received ADDBA req from non-HT STA %s\n",
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ if (m->m_len < sizeof(*wh) + 9) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ /* MLME-ADDBA.indication */
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (const u_int8_t *)&wh[1];
+
+ token = frm[2];
+ params = LE_READ_2(&frm[3]);
+ tid = (params >> 2) & 0xf;
+ bufsz = (params >> 6) & 0x3ff;
+ timeout = LE_READ_2(&frm[5]);
+ ssn = LE_READ_2(&frm[7]) >> 4;
+
+ ba = &ni->ni_ba[tid];
+ /* check if we already have a BA agreement for this RA/TID */
+ if (ba->ba_state == IEEE80211_BA_AGREED) {
+ /* XXX should we update the timeout value? */
+ /* reset BA inactivity timer */
+ timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
+
+ /* check if it's a Protected Block Ack agreement */
+ if (!(ni->ni_flags & IEEE80211_NODE_MFP) ||
+ !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))
+ return; /* not a PBAC, ignore */
+
+ /* PBAC: treat the ADDBA Request like a BAR */
+ if (SEQ_LT(ba->ba_winstart, ssn))
+ ieee80211_ba_move_window(ic, ni, tid, ssn);
+ return;
+ }
+ /* if PBAC required but RA does not support it, refuse request */
+ if ((ic->ic_flags & IEEE80211_F_PBAR) &&
+ (!(ni->ni_flags & IEEE80211_NODE_MFP) ||
+ !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))) {
+ status = IEEE80211_STATUS_REFUSED;
+ goto resp;
+ }
+ /*
+ * If the TID for which the Block Ack agreement is requested is
+ * configured with a no-ACK policy, refuse the agreement.
+ */
+ if (ic->ic_tid_noack & (1 << tid)) {
+ status = IEEE80211_STATUS_REFUSED;
+ goto resp;
+ }
+ /* check that we support the requested Block Ack Policy */
+ if (!(ic->ic_htcaps & IEEE80211_HTCAP_DELAYEDBA) &&
+ !(params & IEEE80211_BA_ACK_POLICY)) {
+ status = IEEE80211_STATUS_INVALID_PARAM;
+ goto resp;
+ }
+
+ /* setup Block Ack agreement */
+ ba->ba_state = IEEE80211_BA_INIT;
+ ba->ba_token = token; /* save Dialog Token for ADDBA Resp */
+ ba->ba_timeout_val = timeout * IEEE80211_DUR_TU;
+ if (ba->ba_timeout_val < IEEE80211_BA_MIN_TIMEOUT)
+ ba->ba_timeout_val = IEEE80211_BA_MIN_TIMEOUT;
+ else if (ba->ba_timeout_val > IEEE80211_BA_MAX_TIMEOUT)
+ ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT;
+ timeout_set(&ba->ba_to, ieee80211_ba_timeout, ba);
+ ba->ba_winsize = bufsz;
+ if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ)
+ ba->ba_winsize = IEEE80211_BA_MAX_WINSZ;
+ ba->ba_winstart = ssn;
+ ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
+ /* allocate and setup our reordering buffer */
+ ba->ba_buf = malloc(IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ba->ba_buf == NULL) {
+ status = IEEE80211_STATUS_REFUSED;
+ goto resp;
+ }
+ ba->ba_head = 0;
+
+ /* notify drivers of this new Block Ack agreement */
+ if (ic->ic_htimmba_start != NULL &&
+ ic->ic_htimmba_start(ic, ni, tid) != 0) {
+ /* driver failed to setup, rollback */
+ free(ba->ba_buf, M_DEVBUF);
+ ba->ba_buf = NULL;
+ status = IEEE80211_STATUS_REFUSED;
+ goto resp;
+ }
+ ba->ba_state = IEEE80211_BA_AGREED;
+ /* start Block Ack inactivity timer */
+ timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
+ status = IEEE80211_STATUS_SUCCESS;
+ resp:
+ /* MLME-ADDBA.response */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
+ IEEE80211_ACTION_ADDBA_RESP, status << 16 | tid);
+}
+
+/*-
+ * ADDBA Response frame format:
+ * [1] Category
+ * [1] Action
+ * [1] Dialog Token
+ * [2] Status Code
+ * [2] Block Ack Parameter Set
+ * [2] Block Ack Timeout Value
+ */
+void
+ieee80211_recv_addba_resp(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame *wh;
+ const u_int8_t *frm;
+ struct ieee80211_ba *ba;
+ u_int16_t status, params, bufsz, timeout;
+ u_int8_t token, tid;
+
+ if (m->m_len < sizeof(*wh) + 9) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (const u_int8_t *)&wh[1];
+
+ token = frm[2];
+ status = LE_READ_2(&frm[3]);
+ params = LE_READ_2(&frm[5]);
+ tid = (params >> 2) & 0xf;
+ bufsz = (params >> 6) & 0x3ff;
+ timeout = LE_READ_2(&frm[7]);
+
+ DPRINTF(("received ADDBA resp from %s, TID %d, status %d\n",
+ ether_sprintf(ni->ni_macaddr), tid, status));
+
+ /*
+ * Ignore if no ADDBA request has been sent for this RA/TID or
+ * if we already have a Block Ack agreement.
+ */
+ ba = &ni->ni_ba[tid];
+ if (ba->ba_state != IEEE80211_BA_REQUESTED) {
+ DPRINTF(("no matching ADDBA req found\n"));
+ return;
+ }
+ if (token != ba->ba_token) {
+ DPRINTF(("ignoring ADDBA resp from %s: token %x!=%x\n",
+ ether_sprintf(ni->ni_macaddr), token, ba->ba_token));
+ return;
+ }
+ /* we got an ADDBA Response matching our request, stop timeout */
+ timeout_del(&ba->ba_to);
+
+ if (status != IEEE80211_STATUS_SUCCESS) {
+ /* MLME-ADDBA.confirm(Failure) */
+ ba->ba_state = IEEE80211_BA_INIT;
+ return;
+ }
+ /* MLME-ADDBA.confirm(Success) */
+ ba->ba_state = IEEE80211_BA_AGREED;
+
+ /* notify drivers of this new Block Ack agreement */
+ if (ic->ic_htimmba_start != NULL)
+ (void)ic->ic_htimmba_start(ic, ni, tid);
+
+ /* start Block Ack inactivity timeout */
+ timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
+}
+
+/*-
+ * DELBA frame format:
+ * [1] Category
+ * [1] Action
+ * [2] DELBA Parameter Set
+ * [2] Reason Code
+ */
+void
+ieee80211_recv_delba(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame *wh;
+ const u_int8_t *frm;
+ struct ieee80211_ba *ba;
+ u_int16_t params, reason;
+ u_int8_t tid;
+ int i;
+
+ if (m->m_len < sizeof(*wh) + 6) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (const u_int8_t *)&wh[1];
+
+ params = LE_READ_2(&frm[2]);
+ reason = LE_READ_2(&frm[4]);
+ tid = params >> 12;
+
+ DPRINTF(("received DELBA from %s, TID %d, reason %d\n",
+ ether_sprintf(ni->ni_macaddr), tid, reason));
+
+ ba = &ni->ni_ba[tid];
+ if (ba->ba_state != IEEE80211_BA_AGREED &&
+ ba->ba_state != IEEE80211_BA_REQUESTED) {
+ DPRINTF(("no matching BA agreement found\n"));
+ return;
+ }
+ /* MLME-DELBA.indication */
+
+ /* notify drivers of the end of the Block Ack agreement */
+ if (ic->ic_htimmba_stop != NULL)
+ ic->ic_htimmba_stop(ic, ni, tid);
+
+ ba->ba_state = IEEE80211_BA_INIT;
+ /* stop Block Ack inactivity timer */
+ timeout_del(&ba->ba_to);
+ if (ba->ba_buf != NULL) {
+ /* free all MSDUs stored in reordering buffer */
+ for (i = 0; i < IEEE80211_BA_MAX_WINSZ; i++)
+ if (ba->ba_buf[i].m != NULL)
+ m_freem(ba->ba_buf[i].m);
+ /* free reordering buffer */
+ free(ba->ba_buf, M_DEVBUF);
+ ba->ba_buf = NULL;
+ }
+}
+#endif /* !IEEE80211_NO_HT */
+
+/*-
+ * SA Query Request frame format:
+ * [1] Category
+ * [1] Action
+ * [16] Transaction Identifier
+ */
+void
+ieee80211_recv_sa_query_req(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame *wh;
+ const u_int8_t *frm;
+
+ if (ic->ic_opmode != IEEE80211_M_STA ||
+ !(ni->ni_flags & IEEE80211_NODE_MFP)) {
+ DPRINTF(("unexpected SA Query req from %s\n",
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ if (m->m_len < sizeof(*wh) + 18) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (const u_int8_t *)&wh[1];
+
+ /* MLME-SAQuery.indication */
+
+ /* save Transaction Identifier for SA Query Response */
+ memcpy(ni->ni_sa_query_trid, &frm[2], 16);
+
+ /* MLME-SAQuery.response */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_SA_QUERY,
+ IEEE80211_ACTION_SA_QUERY_RESP, 0);
+}
+
+#ifndef IEEE80211_STA_ONLY
+/*-
+ * SA Query Response frame format:
+ * [1] Category
+ * [1] Action
+ * [16] Transaction Identifier
+ */
+void
+ieee80211_recv_sa_query_resp(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame *wh;
+ const u_int8_t *frm;
+
+ /* ignore if we're not engaged in an SA Query with that STA */
+ if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY)) {
+ DPRINTF(("unexpected SA Query resp from %s\n",
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ if (m->m_len < sizeof(*wh) + 18) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (const u_int8_t *)&wh[1];
+
+ /* check that Transaction Identifier matches */
+ if (memcmp(&frm[2], ni->ni_sa_query_trid, 16) != 0) {
+ DPRINTF(("transaction identifier does not match\n"));
+ return;
+ }
+ /* MLME-SAQuery.confirm */
+ timeout_del(&ni->ni_sa_query_to);
+ ni->ni_flags &= ~IEEE80211_NODE_SA_QUERY;
+}
+#endif
+
/*-
* Action frame format:
* [1] Category
* [1] Action
*/
void
-ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m,
struct ieee80211_node *ni)
{
const struct ieee80211_frame *wh;
const u_int8_t *frm;
- if (m0->m_len < sizeof(*wh) + 2) {
+ if (m->m_len < sizeof(*wh) + 2) {
DPRINTF(("frame too short\n"));
return;
}
- wh = mtod(m0, struct ieee80211_frame *);
+ wh = mtod(m, struct ieee80211_frame *);
frm = (const u_int8_t *)&wh[1];
switch (frm[0]) {
+#ifndef IEEE80211_NO_HT
case IEEE80211_CATEG_BA:
switch (frm[1]) {
case IEEE80211_ACTION_ADDBA_REQ:
- /* NYI */
+ ieee80211_recv_addba_req(ic, m, ni);
break;
case IEEE80211_ACTION_ADDBA_RESP:
- /* NYI */
+ ieee80211_recv_addba_resp(ic, m, ni);
break;
case IEEE80211_ACTION_DELBA:
- /* NYI */
+ ieee80211_recv_delba(ic, m, ni);
break;
}
break;
- case IEEE80211_CATEG_HT:
- switch (frm[1]) {
- case IEEE80211_ACTION_NOTIFYCW:
- /* NYI */
- break;
- }
- break;
- case IEEE80211_CATEG_SALT:
+#endif
+ case IEEE80211_CATEG_SA_QUERY:
switch (frm[1]) {
- case IEEE80211_ACTION_SALT_REQ:
- /* NYI */
+ case IEEE80211_ACTION_SA_QUERY_REQ:
+ ieee80211_recv_sa_query_req(ic, m, ni);
break;
- case IEEE80211_ACTION_SALT_RESP:
- /* NYI */
+#ifndef IEEE80211_STA_ONLY
+ case IEEE80211_ACTION_SA_QUERY_RESP:
+ ieee80211_recv_sa_query_resp(ic, m, ni);
break;
+#endif
}
break;
default:
@@ -1940,46 +2579,46 @@ ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m0,
}
void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, int subtype)
{
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
- ieee80211_recv_probe_resp(ic, m0, ni, rxi, 0);
+ ieee80211_recv_probe_resp(ic, m, ni, rxi, 0);
break;
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- ieee80211_recv_probe_resp(ic, m0, ni, rxi, 1);
+ ieee80211_recv_probe_resp(ic, m, ni, rxi, 1);
break;
#ifndef IEEE80211_STA_ONLY
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- ieee80211_recv_probe_req(ic, m0, ni, rxi);
+ ieee80211_recv_probe_req(ic, m, ni, rxi);
break;
#endif
case IEEE80211_FC0_SUBTYPE_AUTH:
- ieee80211_recv_auth(ic, m0, ni, rxi);
+ ieee80211_recv_auth(ic, m, ni, rxi);
break;
#ifndef IEEE80211_STA_ONLY
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
- ieee80211_recv_assoc_req(ic, m0, ni, rxi, 0);
+ ieee80211_recv_assoc_req(ic, m, ni, rxi, 0);
break;
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
- ieee80211_recv_assoc_req(ic, m0, ni, rxi, 1);
+ ieee80211_recv_assoc_req(ic, m, ni, rxi, 1);
break;
#endif
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
- ieee80211_recv_assoc_resp(ic, m0, ni, 0);
+ ieee80211_recv_assoc_resp(ic, m, ni, 0);
break;
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
- ieee80211_recv_assoc_resp(ic, m0, ni, 1);
+ ieee80211_recv_assoc_resp(ic, m, ni, 1);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- ieee80211_recv_deauth(ic, m0, ni);
+ ieee80211_recv_deauth(ic, m, ni);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- ieee80211_recv_disassoc(ic, m0, ni);
+ ieee80211_recv_disassoc(ic, m, ni);
break;
case IEEE80211_FC0_SUBTYPE_ACTION:
- ieee80211_recv_action(ic, m0, ni);
+ ieee80211_recv_action(ic, m, ni);
break;
default:
DPRINTF(("mgmt frame with subtype 0x%x not handled\n",
@@ -2042,3 +2681,93 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m,
(*ifp->if_start)(ifp);
}
#endif /* IEEE80211_STA_ONLY */
+
+#ifndef IEEE80211_NO_HT
+/*
+ * Process an incoming Block Ack Request control frame (see 7.2.1.7).
+ */
+void
+ieee80211_recv_bar(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ const struct ieee80211_frame_min *wh;
+ const u_int8_t *frm;
+ u_int16_t ctl, ssn;
+ u_int8_t tid, ntids;
+
+ if (!(ni->ni_flags & IEEE80211_NODE_HT)) {
+ DPRINTF(("received BAR from non-HT STA %s\n",
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ if (m->m_len < sizeof(*wh) + 4) {
+ DPRINTF(("frame too short\n"));
+ return;
+ }
+ wh = mtod(m, struct ieee80211_frame_min *);
+ frm = (const u_int8_t *)&wh[1];
+
+ /* read BAR Control field */
+ ctl = LE_READ_2(&frm[0]);
+ tid = ctl >> 12;
+
+ /* determine BlockAckReq frame variant */
+ if (ctl & IEEE80211_BA_MULTI_TID) {
+ /* Multi-TID BlockAckReq variant (PSMP only) */
+ ntids = tid + 1;
+
+ if (m->m_len < sizeof(*wh) + 2 + 4 * ntids) {
+ DPRINTF(("MTBAR frame too short\n"));
+ return;
+ }
+ frm += 2; /* skip BAR Control field */
+ while (ntids-- > 0) {
+ /* read MTBAR Information field */
+ tid = LE_READ_2(&frm[0]) >> 12;
+ ssn = LE_READ_2(&frm[2]) >> 4;
+ ieee80211_bar_tid(ic, ni, tid, ssn);
+ frm += 4;
+ }
+ } else {
+ /* Basic or Compressed BlockAckReq variants */
+ ssn = LE_READ_2(&frm[2]) >> 4;
+ ieee80211_bar_tid(ic, ni, tid, ssn);
+ }
+}
+
+/*
+ * Process a Block Ack Request for a specific TID (see 9.10.7.6.3).
+ * This is the common back-end for all BlockAckReq frame variants.
+ */
+void
+ieee80211_bar_tid(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t tid, u_int16_t ssn)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+
+ /* check if we have a Block Ack agreement for RA/TID */
+ if (ba->ba_state != IEEE80211_BA_AGREED) {
+ /* XXX not sure in PBAC case */
+ /* send a DELBA with reason code UNKNOWN-BA */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
+ IEEE80211_ACTION_DELBA,
+ IEEE80211_REASON_SETUP_REQUIRED << 16 | tid);
+ return;
+ }
+ /* check if it is a Protected Block Ack agreement */
+ if ((ni->ni_flags & IEEE80211_NODE_MFP) &&
+ (ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC)) {
+ /* ADDBA Requests must be used in PBAC case */
+ if (SEQ_LT(ssn, ba->ba_winstart) ||
+ SEQ_LT(ba->ba_winend, ssn))
+ ic->ic_stats.is_pbac_errs++;
+ return; /* PBAC, do not move window */
+ }
+ /* reset Block Ack inactivity timer */
+ timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
+
+ if (SEQ_LT(ba->ba_winstart, ssn))
+ ieee80211_ba_move_window(ic, ni, tid, ssn);
+}
+#endif /* !IEEE80211_NO_HT */
+
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 7477bebd4a1..44903e5091f 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_ioctl.c,v 1.28 2008/12/14 11:59:41 jsg Exp $ */
+/* $OpenBSD: ieee80211_ioctl.c,v 1.29 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */
/*-
@@ -164,10 +164,9 @@ ieee80211_ioctl_setnwkeys(struct ieee80211com *ic,
k->k_cipher = IEEE80211_CIPHER_WEP40;
else
k->k_cipher = IEEE80211_CIPHER_WEP104;
- k->k_len = nwkey->i_key[i].i_keylen;
+ k->k_len = ieee80211_cipher_keylen(k->k_cipher);
k->k_flags = IEEE80211_KEY_GROUP | IEEE80211_KEY_TX;
- error = copyin(nwkey->i_key[i].i_keydat, k->k_key,
- nwkey->i_key[i].i_keylen);
+ error = copyin(nwkey->i_key[i].i_keydat, k->k_key, k->k_len);
if (error != 0)
return error;
if ((error = (*ic->ic_set_key)(ic, NULL, k)) != 0)
@@ -245,8 +244,8 @@ ieee80211_ioctl_setwpaparms(struct ieee80211com *ic,
IEEE80211_AKM_8021X | IEEE80211_AKM_SHA256_8021X;
if (ic->ic_rsnakms == 0) /* set to default (PSK+802.1X) */
ic->ic_rsnakms =
- IEEE80211_AKM_PSK | IEEE80211_AKM_8021X |
- IEEE80211_AKM_SHA256_PSK | IEEE80211_AKM_SHA256_8021X;
+ IEEE80211_AKM_PSK | IEEE80211_AKM_8021X /*|
+ IEEE80211_AKM_SHA256_PSK | IEEE80211_AKM_SHA256_8021X*/;
if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP40)
ic->ic_rsngroupcipher = IEEE80211_CIPHER_WEP40;
@@ -446,8 +445,6 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
ka = (struct ieee80211_keyavail *)data;
(void)ieee80211_pmksa_add(ic, IEEE80211_AKM_8021X,
ka->i_macaddr, ka->i_key, ka->i_lifetime);
- (void)ieee80211_pmksa_add(ic, IEEE80211_AKM_SHA256_8021X,
- ka->i_macaddr, ka->i_key, ka->i_lifetime);
break;
case SIOCS80211KEYRUN:
if ((error = suser(curproc, 0)) != 0)
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index bee876d2436..9d8db88b3d5 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_ioctl.h,v 1.14 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_ioctl.h,v 1.15 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */
/*-
@@ -95,6 +95,7 @@ struct ieee80211_stats {
u_int32_t is_ccmp_dec_errs;
u_int32_t is_cmac_replays;
u_int32_t is_cmac_icv_errs;
+ u_int32_t is_pbac_errs;
};
#define SIOCG80211STATS _IOWR('i', 242, struct ifreq)
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 5d10129c9b4..7cc051cb2d9 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.c,v 1.49 2008/12/14 10:17:24 damien Exp $ */
+/* $OpenBSD: ieee80211_node.c,v 1.50 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */
/*-
@@ -84,8 +84,14 @@ struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *);
#ifndef IEEE80211_STA_ONLY
+#ifndef IEEE80211_NO_HT
+void ieee80211_node_join_ht(struct ieee80211com *, struct ieee80211_node *);
+#endif
void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
+#ifndef IEEE80211_NO_HT
+void ieee80211_node_leave_ht(struct ieee80211com *, struct ieee80211_node *);
+#endif
void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_set_tim(struct ieee80211com *, int, int);
@@ -435,9 +441,9 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0)
fail |= 0x40;
- /* we only support AES-128-CMAC as the IGTK cipher */
+ /* we only support BIP as the IGTK cipher */
if ((ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC) &&
- ni->ni_rsngroupmgmtcipher != IEEE80211_CIPHER_AES128_CMAC)
+ ni->ni_rsngroupmgmtcipher != IEEE80211_CIPHER_BIP)
fail |= 0x40;
/* we do not support MFP but AP requires it */
@@ -753,8 +759,11 @@ ieee80211_setup_node(struct ieee80211com *ic,
ieee80211_node_newstate(ni, IEEE80211_STA_CACHE);
ni->ni_ic = ic; /* back-pointer */
+ IFQ_SET_MAXLEN(&ni->ni_savedq, IEEE80211_PS_MAX_QUEUE);
+ timeout_set(&ni->ni_eapol_to, ieee80211_eapol_timeout, ni);
+ timeout_set(&ni->ni_sa_query_to, ieee80211_sa_query_timeout, ni);
- /*
+ /*
* Note we don't enable the inactive timer when acting
* as a station. Nodes created in this mode represent
* AP's identified while scanning. If we time them out
@@ -1025,6 +1034,8 @@ ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
panic("freeing bss node");
DPRINTF(("%s\n", ether_sprintf(ni->ni_macaddr)));
+ timeout_del(&ni->ni_eapol_to);
+ timeout_del(&ni->ni_sa_query_to);
IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
RB_REMOVE(ieee80211_tree, &ic->ic_tree, ni);
ic->ic_nnodes--;
@@ -1200,6 +1211,17 @@ ieee80211_needs_auth(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
}
+#ifndef IEEE80211_NO_HT
+/*
+ * Handle an HT STA joining an HT network.
+ */
+void
+ieee80211_node_join_ht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ /* TBD */
+}
+#endif /* !IEEE80211_NO_HT */
+
/*
* Handle a station joining an RSN network.
*/
@@ -1220,7 +1242,6 @@ ieee80211_node_join_rsn(struct ieee80211com *ic, struct ieee80211_node *ni)
ni->ni_replaycnt = -1; /* XXX */
ni->ni_rsn_retries = 0;
ni->ni_rsncipher = ni->ni_rsnciphers;
- timeout_set(&ni->ni_rsn_timeout, ieee80211_eapol_timeout, ni);
ni->ni_rsn_state = RSNA_AUTHENTICATION_2;
@@ -1331,6 +1352,11 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni,
} else
ieee80211_node_join_rsn(ic, ni);
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_node_join_ht(ic, ni);
+#endif
+
#if NBRIDGE > 0
/*
* If the parent interface belongs to a bridge, learn
@@ -1342,6 +1368,31 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni,
#endif
}
+#ifndef IEEE80211_NO_HT
+/*
+ * Handle an HT STA leaving an HT network.
+ */
+void
+ieee80211_node_leave_ht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_ba *ba;
+ u_int8_t tid;
+ int i;
+
+ /* free all Block Ack records */
+ for (tid = 0; tid < IEEE80211_NUM_TID; tid++) {
+ ba = &ni->ni_ba[tid];
+ if (ba->ba_buf != NULL) {
+ for (i = 0; i < IEEE80211_BA_MAX_WINSZ; i++)
+ if (ba->ba_buf[i].m != NULL)
+ m_freem(ba->ba_buf[i].m);
+ free(ba->ba_buf, M_DEVBUF);
+ ba->ba_buf = NULL;
+ }
+ }
+}
+#endif /* !IEEE80211_NO_HT */
+
/*
* Handle a station leaving an RSN network.
*/
@@ -1360,7 +1411,8 @@ ieee80211_node_leave_rsn(struct ieee80211com *ic, struct ieee80211_node *ni)
ni->ni_flags &= ~IEEE80211_NODE_PMK;
ni->ni_rsn_gstate = RSNA_IDLE;
- timeout_del(&ni->ni_rsn_timeout);
+ timeout_del(&ni->ni_sa_query_to);
+ timeout_del(&ni->ni_eapol_to);
ni->ni_rsn_retries = 0;
ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
ni->ni_port_valid = 0;
@@ -1445,6 +1497,11 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
if (ic->ic_curmode == IEEE80211_MODE_11G)
ieee80211_node_leave_11g(ic, ni);
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_node_leave_ht(ic, ni);
+#endif
+
ieee80211_node_newstate(ni, IEEE80211_STA_COLLECT);
#if NBRIDGE > 0
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index be2081e59c3..cfd101fc248 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.h,v 1.34 2008/12/14 10:17:24 damien Exp $ */
+/* $OpenBSD: ieee80211_node.h,v 1.35 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
/*-
@@ -96,6 +96,40 @@ enum {
RSNA_KEYERROR
};
+struct ieee80211_rxinfo {
+ u_int32_t rxi_flags;
+ u_int32_t rxi_tstamp;
+ int rxi_rssi;
+};
+#define IEEE80211_RXI_HWDEC 0x00000001
+#define IEEE80211_RXI_AMPDU_DONE 0x00000002
+
+/* Block Acknowledgement Record */
+struct ieee80211_ba {
+ struct ieee80211_node *ba_ni; /* backpointer for callbacks */
+ struct {
+ struct mbuf *m;
+ struct ieee80211_rxinfo rxi;
+ } *ba_buf;
+ struct timeout ba_to;
+ int ba_timeout_val;
+#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000) /* 10msec */
+#define IEEE80211_BA_MAX_TIMEOUT (10 * 1000 * 1000) /* 10sec */
+
+ int ba_state;
+#define IEEE80211_BA_INIT 0
+#define IEEE80211_BA_REQUESTED 1
+#define IEEE80211_BA_AGREED 2
+
+ u_int16_t ba_winstart;
+ u_int16_t ba_winend;
+ u_int16_t ba_winsize;
+#define IEEE80211_BA_MAX_WINSZ 128 /* maximum we will accept */
+
+ u_int16_t ba_head;
+ u_int8_t ba_token;
+};
+
/*
* Node specific information. Note that drivers are expected
* to derive from this structure to add device-specific per-node
@@ -144,10 +178,10 @@ struct ieee80211_node {
struct ifqueue ni_savedq; /* packets queued for pspoll */
/* RSN */
+ struct timeout ni_eapol_to;
u_int ni_rsn_state;
u_int ni_rsn_gstate;
u_int ni_rsn_retries;
- struct timeout ni_rsn_timeout;
u_int ni_rsnprotos;
u_int ni_rsnakms;
u_int ni_rsnciphers;
@@ -168,6 +202,14 @@ struct ieee80211_node {
u_int8_t ni_key_count;
int ni_port_valid;
+ /* SA Query */
+ u_int8_t ni_sa_query_trid[16];
+ struct timeout ni_sa_query_to;
+ int ni_sa_query_count;
+
+ /* HT-immediate Block Ack */
+ struct ieee80211_ba ni_ba[IEEE80211_NUM_TID];
+
/* others */
u_int16_t ni_associd; /* assoc response */
u_int16_t ni_txseq; /* seq to be transmitted */
@@ -192,6 +234,9 @@ struct ieee80211_node {
#define IEEE80211_NODE_MFP 0x0080 /* MFP negotiated */
#define IEEE80211_NODE_PMK 0x0100 /* ni_pmk set */
#define IEEE80211_NODE_PMKID 0x0200 /* ni_pmkid set */
+#define IEEE80211_NODE_HT 0x0400 /* HT negotiated */
+#define IEEE80211_NODE_SA_QUERY 0x0800 /* SA Query in progress */
+#define IEEE80211_NODE_SA_QUERY_FAILED 0x1000 /* last SA Query failed */
};
RB_HEAD(ieee80211_tree, ieee80211_node);
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index bd2e55a90bf..74642f11169 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,10 +1,10 @@
-/* $OpenBSD: ieee80211_output.c,v 1.80 2008/12/02 17:37:11 damien Exp $ */
+/* $OpenBSD: ieee80211_output.c,v 1.81 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
- * Copyright (c) 2007, 2008 Damien Bergamini
+ * Copyright (c) 2007-2009 Damien Bergamini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -93,6 +93,18 @@ struct mbuf *ieee80211_get_assoc_resp(struct ieee80211com *,
#endif
struct mbuf *ieee80211_get_disassoc(struct ieee80211com *,
struct ieee80211_node *, u_int16_t);
+#ifndef IEEE80211_NO_HT
+struct mbuf *ieee80211_get_addba_req(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t);
+struct mbuf *ieee80211_get_addba_resp(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t, u_int16_t);
+struct mbuf *ieee80211_get_delba(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t, u_int16_t);
+#endif
+struct mbuf *ieee80211_get_sa_query(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t);
+struct mbuf *ieee80211_get_action(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t, u_int8_t, int);
/*
* IEEE 802.11 output routine. Normally this will directly call the
@@ -583,14 +595,16 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
if (addqos) {
struct ieee80211_qosframe *qwh =
(struct ieee80211_qosframe *)wh;
+ u_int16_t qos = tid;
+
+ if (ic->ic_tid_noack & (1 << tid))
+ qos |= IEEE80211_QOS_ACK_POLICY_NOACK;
+ else if (ni->ni_ba[tid].ba_state == IEEE80211_BA_AGREED)
+ qos |= IEEE80211_QOS_ACK_POLICY_BA;
+
qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
- qwh->i_qos[0] = tid;
- if (ic->ic_tid_noack & (1 << tid)) {
- qwh->i_qos[0] |= IEEE80211_QOS_ACK_POLICY_NOACK <<
- IEEE80211_QOS_ACK_POLICY_SHIFT;
- }
- qwh->i_qos[1] = 0; /* unused/set by hardware */
- *(u_int16_t *)&qwh->i_seq[0] =
+ *(u_int16_t *)qwh->i_qos = htole16(qos);
+ *(u_int16_t *)qwh->i_seq =
htole16(ni->ni_qos_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
ni->ni_qos_txseqs[tid]++;
} else {
@@ -950,7 +964,7 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic,
/* write Group Integrity Cipher Suite field */
memcpy(frm, oui, 3); frm += 3;
switch (ic->ic_rsngroupmgmtcipher) {
- case IEEE80211_CIPHER_AES128_CMAC:
+ case IEEE80211_CIPHER_BIP:
*frm++ = 6;
break;
default:
@@ -1013,6 +1027,55 @@ ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
return frm + nrates;
}
+#ifndef IEEE80211_NO_HT
+/*
+ * Add an HT Capabilities element to a frame (see 7.3.2.57).
+ */
+u_int8_t *
+ieee80211_add_htcaps(u_int8_t *frm, struct ieee80211com *ic)
+{
+ *frm++ = IEEE80211_ELEMID_HTCAPS;
+ *frm++ = 26;
+ LE_WRITE_2(frm, ic->ic_htcaps); frm += 2;
+ *frm++ = 0;
+ memcpy(frm, ic->ic_sup_mcs, 16); frm += 16;
+ LE_WRITE_2(frm, ic->ic_htxcaps); frm += 2;
+ LE_WRITE_4(frm, ic->ic_txbfcaps); frm += 4;
+ *frm++ = ic->ic_aselcaps;
+ return frm;
+}
+
+/*
+ * Add an HT Operation element to a frame (see 7.3.2.58).
+ */
+u_int8_t *
+ieee80211_add_htop(u_int8_t *frm, struct ieee80211com *ic)
+{
+ *frm++ = IEEE80211_ELEMID_HTOP;
+ *frm++ = 22;
+ *frm++ = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ LE_WRITE_2(frm, 0); frm += 2;
+ LE_WRITE_2(frm, 0); frm += 2;
+ memset(frm, 0, 16); frm += 16;
+ return frm;
+}
+#endif /* !IEEE80211_NO_HT */
+
+#ifndef IEEE80211_STA_ONLY
+/*
+ * Add a Timeout Interval element to a frame (see 7.3.2.49).
+ */
+u_int8_t *
+ieee80211_add_tie(u_int8_t *frm, u_int8_t type, u_int32_t value)
+{
+ *frm++ = IEEE80211_ELEMID_TIE;
+ *frm++ = 5; /* length */
+ *frm++ = type; /* Timeout Interval type */
+ LE_WRITE_4(frm, value);
+ return frm + 4;
+}
+#endif
+
struct mbuf *
ieee80211_getmgmt(int flags, int type, u_int pktlen)
{
@@ -1040,6 +1103,7 @@ ieee80211_getmgmt(int flags, int type, u_int pktlen)
* [tlv] SSID
* [tlv] Supported rates
* [tlv] Extended Supported Rates (802.11g)
+ * [tlv] HT Capabilities (802.11n)
*/
struct mbuf *
ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
@@ -1053,7 +1117,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
2 + ic->ic_des_esslen +
2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
- 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0));
+ 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
+ ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
if (m == NULL)
return NULL;
@@ -1062,6 +1127,10 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
frm = ieee80211_add_rates(frm, rs);
if (rs->rs_nrates > IEEE80211_RATE_SIZE)
frm = ieee80211_add_xrates(frm, rs);
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ frm = ieee80211_add_htcaps(frm, ic);
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1071,16 +1140,18 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
#ifndef IEEE80211_STA_ONLY
/*-
* Probe response frame format:
- * [8] Timestamp
- * [2] Beacon interval
- * [2] Capability
- * [tlv] Service Set Identifier (SSID)
- * [tlv] Supported rates
- * [tlv*] DS Parameter Set (802.11g)
- * [tlv] ERP Information (802.11g)
- * [tlv] Extended Supported Rates (802.11g)
- * [tlv] RSN (802.11i)
- * [tlv] EDCA Parameter Set (802.11e)
+ * [8] Timestamp
+ * [2] Beacon interval
+ * [2] Capability
+ * [tlv] Service Set Identifier (SSID)
+ * [tlv] Supported rates
+ * [tlv] DS Parameter Set (802.11g)
+ * [tlv] ERP Information (802.11g)
+ * [tlv] Extended Supported Rates (802.11g)
+ * [tlv] RSN (802.11i)
+ * [tlv] EDCA Parameter Set (802.11e)
+ * [tlv] HT Capabilities (802.11n)
+ * [tlv] HT Operation (802.11n)
*/
struct mbuf *
ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni)
@@ -1104,7 +1175,8 @@ ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni)
((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) +
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
- 2 + IEEE80211_WPAIE_MAXLEN : 0));
+ 2 + IEEE80211_WPAIE_MAXLEN : 0) +
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 24 : 0));
if (m == NULL)
return NULL;
@@ -1130,6 +1202,12 @@ ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni)
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA))
frm = ieee80211_add_wpa(frm, ic, ic->ic_bss);
+#ifndef IEEE80211_NO_HT
+ if (ic->ic_flags & IEEE80211_F_HTON) {
+ frm = ieee80211_add_htcaps(frm, ic);
+ frm = ieee80211_add_htop(frm, ic);
+ }
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1195,6 +1273,7 @@ ieee80211_get_deauth(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] Extended Supported Rates (802.11g)
* [tlv] RSN (802.11i)
* [tlv] QoS Capability (802.11e)
+ * [tlv] HT Capabilities (802.11n)
*/
struct mbuf *
ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
@@ -1216,10 +1295,11 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ?
2 + IEEE80211_RSNIE_MAXLEN : 0) +
- ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 1 : 0) +
+ ((ni->ni_flags & IEEE80211_NODE_QOS) ? 2 + 1 : 0) +
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
- 2 + IEEE80211_WPAIE_MAXLEN : 0));
+ 2 + IEEE80211_WPAIE_MAXLEN : 0) +
+ ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
if (m == NULL)
return NULL;
@@ -1246,12 +1326,15 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_RSN))
frm = ieee80211_add_rsn(frm, ic, ni);
- if ((ic->ic_flags & IEEE80211_F_QOS) &&
- (ni->ni_flags & IEEE80211_NODE_QOS))
+ if (ni->ni_flags & IEEE80211_NODE_QOS)
frm = ieee80211_add_qos_capability(frm, ic);
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
frm = ieee80211_add_wpa(frm, ic, ni);
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ frm = ieee80211_add_htcaps(frm, ic);
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1267,6 +1350,9 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] Supported rates
* [tlv] Extended Supported Rates (802.11g)
* [tlv] EDCA Parameter Set (802.11e)
+ * [tlv] Timeout Interval (802.11w)
+ * [tlv] HT Capabilities (802.11n)
+ * [tlv] HT Operation (802.11n)
*/
struct mbuf *
ieee80211_get_assoc_resp(struct ieee80211com *ic, struct ieee80211_node *ni,
@@ -1281,7 +1367,9 @@ ieee80211_get_assoc_resp(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_QOS) ? 2 + 18 : 0));
+ ((ni->ni_flags & IEEE80211_NODE_QOS) ? 2 + 18 : 0) +
+ ((status == IEEE80211_STATUS_TRY_AGAIN_LATER) ? 2 + 7 : 0) +
+ ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 24 : 0));
if (m == NULL)
return NULL;
@@ -1296,9 +1384,19 @@ ieee80211_get_assoc_resp(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_rates(frm, rs);
if (rs->rs_nrates > IEEE80211_RATE_SIZE)
frm = ieee80211_add_xrates(frm, rs);
- if ((ic->ic_flags & IEEE80211_F_QOS) &&
- (ni->ni_flags & IEEE80211_NODE_QOS))
+ if (ni->ni_flags & IEEE80211_NODE_QOS)
frm = ieee80211_add_edca_params(frm, ic);
+ if ((ni->ni_flags & IEEE80211_NODE_MFP) &&
+ status == IEEE80211_STATUS_TRY_AGAIN_LATER) {
+ /* Association Comeback Time */
+ frm = ieee80211_add_tie(frm, 3, 1000 /* XXX */);
+ }
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ frm = ieee80211_add_htcaps(frm, ic);
+ frm = ieee80211_add_htop(frm, ic);
+ }
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1327,6 +1425,174 @@ ieee80211_get_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
return m;
}
+#ifndef IEEE80211_NO_HT
+/*-
+ * ADDBA Request frame format:
+ * [1] Category
+ * [1] Action
+ * [1] Dialog Token
+ * [2] Block Ack Parameter Set
+ * [2] Block Ack Timeout Value
+ * [2] Block Ack Starting Sequence Control
+ */
+struct mbuf *
+ieee80211_get_addba_req(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t tid)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+ struct mbuf *m;
+ u_int8_t *frm;
+ u_int16_t params;
+
+ m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 9);
+ if (m == NULL)
+ return m;
+
+ frm = mtod(m, u_int8_t *);
+ *frm++ = IEEE80211_CATEG_BA;
+ *frm++ = IEEE80211_ACTION_ADDBA_REQ;
+ *frm++ = ba->ba_token;
+ params = ba->ba_winsize << 6 | tid << 2 | IEEE80211_BA_ACK_POLICY;
+ LE_WRITE_2(frm, params); frm += 2;
+ LE_WRITE_2(frm, ba->ba_timeout_val); frm += 2;
+ LE_WRITE_2(frm, ba->ba_winstart); frm += 2;
+
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ return m;
+}
+
+/*-
+ * ADDBA Response frame format:
+ * [1] Category
+ * [1] Action
+ * [1] Dialog Token
+ * [2] Status Code
+ * [2] Block Ack Parameter Set
+ * [2] Block Ack Timeout Value
+ */
+struct mbuf *
+ieee80211_get_addba_resp(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t tid, u_int16_t status)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+ struct mbuf *m;
+ u_int8_t *frm;
+ u_int16_t params;
+
+ m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 9);
+ if (m == NULL)
+ return m;
+
+ frm = mtod(m, u_int8_t *);
+ *frm++ = IEEE80211_CATEG_BA;
+ *frm++ = IEEE80211_ACTION_ADDBA_RESP;
+ *frm++ = ba->ba_token;
+ LE_WRITE_2(frm, status); frm += 2;
+ params = ba->ba_winsize << 6 | tid << 2 | IEEE80211_BA_ACK_POLICY;
+ LE_WRITE_2(frm, params); frm += 2;
+ LE_WRITE_2(frm, ba->ba_timeout_val); frm += 2;
+
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ return m;
+}
+
+/*-
+ * DELBA frame format:
+ * [1] Category
+ * [1] Action
+ * [2] DELBA Parameter Set
+ * [2] Reason Code
+ */
+struct mbuf *
+ieee80211_get_delba(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t tid, u_int16_t reason)
+{
+ struct mbuf *m;
+ u_int8_t *frm;
+ u_int16_t params;
+
+ m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 6);
+ if (m == NULL)
+ return m;
+
+ frm = mtod(m, u_int8_t *);
+ *frm++ = IEEE80211_CATEG_BA;
+ *frm++ = IEEE80211_ACTION_DELBA;
+ params = tid << 12; /* XXX initiator */
+ LE_WRITE_2(frm, params); frm += 2;
+ LE_WRITE_2(frm, reason); frm += 2;
+
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+ return m;
+}
+#endif /* !IEEE80211_NO_HT */
+
+/*-
+ * SA Query Request/Reponse frame format:
+ * [1] Category
+ * [1] Action
+ * [16] Transaction Identifier
+ */
+struct mbuf *
+ieee80211_get_sa_query(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t action)
+{
+ struct mbuf *m;
+ u_int8_t *frm;
+
+ m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 18);
+ if (m == NULL)
+ return NULL;
+
+ frm = mtod(m, u_int8_t *);
+ *frm++ = IEEE80211_CATEG_SA_QUERY;
+ *frm++ = action; /* ACTION_SA_QUERY_REQ/RESP */
+ memcpy(frm, ni->ni_sa_query_trid, 16);
+
+ return m;
+}
+
+struct mbuf *
+ieee80211_get_action(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int8_t categ, u_int8_t action, int arg)
+{
+ struct mbuf *m = NULL;
+
+ switch (categ) {
+#ifndef IEEE80211_NO_HT
+ case IEEE80211_CATEG_BA:
+ switch (action) {
+ case IEEE80211_ACTION_ADDBA_REQ:
+ m = ieee80211_get_addba_req(ic, ni, arg & 0xffff);
+ break;
+ case IEEE80211_ACTION_ADDBA_RESP:
+ m = ieee80211_get_addba_resp(ic, ni, arg & 0xffff,
+ arg >> 16);
+ break;
+ case IEEE80211_ACTION_DELBA:
+ m = ieee80211_get_delba(ic, ni, arg & 0xffff,
+ arg >> 16);
+ break;
+ }
+ break;
+#endif
+ case IEEE80211_CATEG_SA_QUERY:
+ switch (action) {
+#ifndef IEEE80211_STA_ONLY
+ case IEEE80211_ACTION_SA_QUERY_REQ:
+#endif
+ case IEEE80211_ACTION_SA_QUERY_RESP:
+ m = ieee80211_get_sa_query(ic, ni, action);
+ break;
+ }
+ break;
+ }
+ return m;
+}
+
/*
* Send a management frame. The node is for the destination (or ic_bss
* when in station mode). Nodes other than ic_bss have their reference
@@ -1334,7 +1600,7 @@ ieee80211_get_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni,
*/
int
ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int arg)
+ int type, int arg1, int arg2)
{
#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
struct ifnet *ifp = &ic->ic_if;
@@ -1365,7 +1631,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
break;
#endif
case IEEE80211_FC0_SUBTYPE_AUTH:
- m = ieee80211_get_auth(ic, ni, arg >> 16, arg & 0xffff);
+ m = ieee80211_get_auth(ic, ni, arg1 >> 16, arg1 & 0xffff);
if (m == NULL)
senderr(ENOMEM, is_tx_nombuf);
@@ -1374,12 +1640,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- if ((m = ieee80211_get_deauth(ic, ni, arg)) == NULL)
+ if ((m = ieee80211_get_deauth(ic, ni, arg1)) == NULL)
senderr(ENOMEM, is_tx_nombuf);
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: station %s deauthenticate (reason %d)\n",
- ifp->if_xname, ether_sprintf(ni->ni_macaddr), arg);
+ ifp->if_xname, ether_sprintf(ni->ni_macaddr),
+ arg1);
}
break;
@@ -1393,20 +1660,28 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
#ifndef IEEE80211_STA_ONLY
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
- if ((m = ieee80211_get_assoc_resp(ic, ni, arg)) == NULL)
+ if ((m = ieee80211_get_assoc_resp(ic, ni, arg1)) == NULL)
senderr(ENOMEM, is_tx_nombuf);
break;
#endif
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- if ((m = ieee80211_get_disassoc(ic, ni, arg)) == NULL)
+ if ((m = ieee80211_get_disassoc(ic, ni, arg1)) == NULL)
senderr(ENOMEM, is_tx_nombuf);
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: station %s disassociate (reason %d)\n",
- ifp->if_xname, ether_sprintf(ni->ni_macaddr), arg);
+ ifp->if_xname, ether_sprintf(ni->ni_macaddr),
+ arg1);
}
break;
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ m = ieee80211_get_action(ic, ni, arg1 >> 16, arg1 & 0xffff,
+ arg2);
+ if (m == NULL)
+ senderr(ENOMEM, is_tx_nombuf);
+ break;
+
default:
DPRINTF(("invalid mgmt frame type %u\n", type));
senderr(EINVAL, is_tx_unknownmgt);
@@ -1480,18 +1755,20 @@ ieee80211_get_cts_to_self(struct ieee80211com *ic, u_int16_t dur)
#ifndef IEEE80211_STA_ONLY
/*-
* Beacon frame format:
- * [8] Timestamp
- * [2] Beacon interval
- * [2] Capability
- * [tlv] Service Set Identifier (SSID)
- * [tlv] Supported rates
- * [tlv*] DS Parameter Set (802.11g)
- * [tlv*] IBSS Parameter Set
- * [tlv] Traffic Indication Map (TIM)
- * [tlv] ERP Information (802.11g)
- * [tlv] Extended Supported Rates (802.11g)
- * [tlv] RSN (802.11i)
- * [tlv] EDCA Parameter Set (802.11e)
+ * [8] Timestamp
+ * [2] Beacon interval
+ * [2] Capability
+ * [tlv] Service Set Identifier (SSID)
+ * [tlv] Supported rates
+ * [tlv] DS Parameter Set (802.11g)
+ * [tlv] IBSS Parameter Set
+ * [tlv] Traffic Indication Map (TIM)
+ * [tlv] ERP Information (802.11g)
+ * [tlv] Extended Supported Rates (802.11g)
+ * [tlv] RSN (802.11i)
+ * [tlv] EDCA Parameter Set (802.11e)
+ * [tlv] HT Capabilities (802.11n)
+ * [tlv] HT Operation (802.11n)
*/
struct mbuf *
ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni)
@@ -1516,7 +1793,8 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni)
((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) +
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
- 2 + IEEE80211_WPAIE_MAXLEN : 0));
+ 2 + IEEE80211_WPAIE_MAXLEN : 0) +
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 24 : 0));
if (m == NULL)
return NULL;
@@ -1559,6 +1837,12 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni)
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
frm = ieee80211_add_wpa(frm, ic, ni);
+#ifndef IEEE80211_NO_HT
+ if (ic->ic_flags & IEEE80211_F_HTON) {
+ frm = ieee80211_add_htcaps(frm, ic);
+ frm = ieee80211_add_htop(frm, ic);
+ }
+#endif
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
m->m_pkthdr.rcvif = (void *)ni;
diff --git a/sys/net80211/ieee80211_pae_input.c b/sys/net80211/ieee80211_pae_input.c
index ababaf5853b..0f92b742c98 100644
--- a/sys/net80211/ieee80211_pae_input.c
+++ b/sys/net80211/ieee80211_pae_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_pae_input.c,v 1.14 2008/12/06 09:02:47 damien Exp $ */
+/* $OpenBSD: ieee80211_pae_input.c,v 1.15 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -78,7 +78,7 @@ void ieee80211_recv_eapol_key_req(struct ieee80211com *,
* EAPOL-Key frames with an IEEE 802.11 or WPA descriptor type.
*/
void
-ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m,
struct ieee80211_node *ni)
{
struct ifnet *ifp = &ic->ic_if;
@@ -87,23 +87,23 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
u_int16_t info, desc;
int totlen;
- ifp->if_ibytes += m0->m_pkthdr.len;
+ ifp->if_ibytes += m->m_pkthdr.len;
- eh = mtod(m0, struct ether_header *);
+ eh = mtod(m, struct ether_header *);
if (IEEE80211_IS_MULTICAST(eh->ether_dhost)) {
ifp->if_imcasts++;
goto done;
}
- m_adj(m0, sizeof(*eh));
+ m_adj(m, sizeof(*eh));
- if (m0->m_pkthdr.len < sizeof(*key))
+ if (m->m_pkthdr.len < sizeof(*key))
goto done;
- if (m0->m_len < sizeof(*key) &&
- (m0 = m_pullup(m0, sizeof(*key))) == NULL) {
+ if (m->m_len < sizeof(*key) &&
+ (m = m_pullup(m, sizeof(*key))) == NULL) {
ic->ic_stats.is_rx_nombuf++;
goto done;
}
- key = mtod(m0, struct ieee80211_eapol_key *);
+ key = mtod(m, struct ieee80211_eapol_key *);
if (key->type != EAPOL_KEY)
goto done;
@@ -116,12 +116,12 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
goto done;
/* check packet body length */
- if (m0->m_pkthdr.len < 4 + BE_READ_2(key->len))
+ if (m->m_pkthdr.len < 4 + BE_READ_2(key->len))
goto done;
/* check key data length */
totlen = sizeof(*key) + BE_READ_2(key->paylen);
- if (m0->m_pkthdr.len < totlen || totlen > MCLBYTES)
+ if (m->m_pkthdr.len < totlen || totlen > MCLBYTES)
goto done;
info = BE_READ_2(key->info);
@@ -141,11 +141,11 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
}
/* make sure the key data field is contiguous */
- if (m0->m_len < totlen && (m0 = m_pullup2(m0, totlen)) == NULL) {
+ if (m->m_len < totlen && (m = m_pullup2(m, totlen)) == NULL) {
ic->ic_stats.is_rx_nombuf++;
goto done;
}
- key = mtod(m0, struct ieee80211_eapol_key *);
+ key = mtod(m, struct ieee80211_eapol_key *);
/* determine message type (see 8.5.3.7) */
if (info & EAPOL_KEY_REQUEST) {
@@ -180,8 +180,8 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
#endif
}
done:
- if (m0 != NULL)
- m_freem(m0);
+ if (m != NULL)
+ m_freem(m);
}
/*
@@ -303,7 +303,7 @@ ieee80211_recv_4way_msg2(struct ieee80211com *ic,
return; /* will timeout.. */
}
- timeout_del(&ni->ni_rsn_timeout);
+ timeout_del(&ni->ni_eapol_to);
ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING_2;
ni->ni_rsn_retries = 0;
@@ -637,7 +637,7 @@ ieee80211_recv_4way_msg4(struct ieee80211com *ic,
return; /* will timeout.. */
}
- timeout_del(&ni->ni_rsn_timeout);
+ timeout_del(&ni->ni_eapol_to);
ni->ni_rsn_state = RSNA_PTKINITDONE;
ni->ni_rsn_retries = 0;
@@ -989,7 +989,7 @@ ieee80211_recv_group_msg2(struct ieee80211com *ic,
return;
}
- timeout_del(&ni->ni_rsn_timeout);
+ timeout_del(&ni->ni_eapol_to);
ni->ni_rsn_gstate = RSNA_REKEYESTABLISHED;
if ((ni->ni_flags & IEEE80211_NODE_REKEY) &&
diff --git a/sys/net80211/ieee80211_pae_output.c b/sys/net80211/ieee80211_pae_output.c
index b6f27393d5b..21b09cf8fd6 100644
--- a/sys/net80211/ieee80211_pae_output.c
+++ b/sys/net80211/ieee80211_pae_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_pae_output.c,v 1.14 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_pae_output.c,v 1.15 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -129,7 +129,7 @@ ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m,
#ifndef IEEE80211_STA_ONLY
/* start a 100ms timeout if an answer is expected from supplicant */
if (info & EAPOL_KEY_KEYACK)
- timeout_add(&ni->ni_rsn_timeout, hz / 10);
+ timeout_add_msec(&ni->ni_eapol_to, 100);
#endif
IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
if (error == 0) {
diff --git a/sys/net80211/ieee80211_priv.h b/sys/net80211/ieee80211_priv.h
index f1dab4ec26c..d1775b11a08 100644
--- a/sys/net80211/ieee80211_priv.h
+++ b/sys/net80211/ieee80211_priv.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_priv.h,v 1.4 2008/09/27 15:16:09 damien Exp $ */
+/* $OpenBSD: ieee80211_priv.h,v 1.5 2009/01/26 19:09:41 damien Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -31,6 +31,9 @@ extern int ieee80211_debug;
#define DPRINTF(X)
#endif
+#define SEQ_LT(a,b) \
+ ((((u_int16_t)(a) - (u_int16_t)(b)) & 0xfff) > 2048)
+
#define IEEE80211_AID_SET(b, w) \
((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
#define IEEE80211_AID_CLR(b, w) \
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index c79455e450c..b1eb2f5dcbe 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,10 +1,10 @@
-/* $OpenBSD: ieee80211_proto.c,v 1.37 2008/10/15 19:12:18 blambert Exp $ */
+/* $OpenBSD: ieee80211_proto.c,v 1.38 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
- * Copyright (c) 2008 Damien Bergamini
+ * Copyright (c) 2008, 2009 Damien Bergamini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -511,8 +511,143 @@ ieee80211_gtk_rekey_timeout(void *arg)
/* re-schedule a GTK rekeying after 3600s */
timeout_add_sec(&ic->ic_rsn_timeout, 3600);
}
+
+void
+ieee80211_sa_query_timeout(void *arg)
+{
+ struct ieee80211_node *ni = arg;
+ struct ieee80211com *ic = ni->ni_ic;
+ int s;
+
+ s = splnet();
+ if (++ni->ni_sa_query_count >= 3) {
+ ni->ni_flags &= ~IEEE80211_NODE_SA_QUERY;
+ ni->ni_flags |= IEEE80211_NODE_SA_QUERY_FAILED;
+ } else /* retry SA Query Request */
+ ieee80211_sa_query_request(ic, ni);
+ splx(s);
+}
+
+/*
+ * Request that a SA Query Request frame be sent to a specified peer STA
+ * to which the STA is associated.
+ */
+void
+ieee80211_sa_query_request(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ /* MLME-SAQuery.request */
+
+ if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY)) {
+ ni->ni_flags |= IEEE80211_NODE_SA_QUERY;
+ ni->ni_flags &= ~IEEE80211_NODE_SA_QUERY_FAILED;
+ ni->ni_sa_query_count = 0;
+ }
+ /* generate random Transaction Identifier */
+ arc4random_buf(ni->ni_sa_query_trid, 16);
+
+ /* send SA Query Request */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_SA_QUERY,
+ IEEE80211_ACTION_SA_QUERY_REQ, 0);
+ timeout_add_msec(&ni->ni_sa_query_to, 10);
+}
#endif /* IEEE80211_STA_ONLY */
+#ifndef IEEE80211_NO_HT
+void
+ieee80211_ba_timeout(void *arg)
+{
+ struct ieee80211_ba *ba = arg;
+ struct ieee80211_node *ni = ba->ba_ni;
+ struct ieee80211com *ic = ni->ni_ic;
+ u_int8_t tid;
+ int s;
+
+ s = splnet();
+ if (ba->ba_state == IEEE80211_BA_REQUESTED) {
+ /* MLME-ADDBA.confirm(TIMEOUT) */
+ ba->ba_state = IEEE80211_BA_INIT;
+ free(ba->ba_buf, M_DEVBUF);
+ ba->ba_buf = NULL;
+
+ } else if (ba->ba_state == IEEE80211_BA_AGREED) {
+ /* Block Ack inactivity timeout */
+ tid = ((caddr_t)ba - (caddr_t)ni->ni_ba) / sizeof(*ba);
+ ieee80211_delba_request(ic, ni, IEEE80211_REASON_TIMEOUT, tid);
+ }
+ splx(s);
+}
+
+/*
+ * Request initiation of Block Ack with the specified peer.
+ */
+int
+ieee80211_addba_request(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int16_t ssn, u_int8_t tid)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+
+ /* MLME-ADDBA.request */
+
+ /* setup Block Ack */
+ ba->ba_state = IEEE80211_BA_REQUESTED;
+ ba->ba_token = ic->ic_dialog_token++;
+ ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT;
+ timeout_set(&ba->ba_to, ieee80211_ba_timeout, ba);
+ ba->ba_winsize = IEEE80211_BA_MAX_WINSZ;
+ ba->ba_winstart = ssn;
+ ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
+ /* allocate and setup our reordering buffer */
+ ba->ba_buf = malloc(IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ba->ba_buf == NULL)
+ return ENOMEM;
+ ba->ba_head = 0;
+
+ timeout_add_sec(&ba->ba_to, 1); /* dot11ADDBAResponseTimeout */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
+ IEEE80211_ACTION_ADDBA_REQ, tid);
+ return 0;
+}
+
+/*
+ * Request the deletion of Block Ack with a peer.
+ */
+void
+ieee80211_delba_request(struct ieee80211com *ic, struct ieee80211_node *ni,
+ u_int16_t reason, u_int8_t tid)
+{
+ struct ieee80211_ba *ba = &ni->ni_ba[tid];
+ int i;
+
+ /* MLME-DELBA.request */
+
+ /* transmit a DELBA frame */
+ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
+ IEEE80211_ACTION_DELBA, reason << 16 | tid);
+ /*
+ * XXX We should wait for an acknowledgment of the DELBA frame but
+ * drivers are not necessarily notified when a frame is ACK'ed by
+ * hardware.
+ */
+ /* MLME-DELBA.confirm */
+ if (ic->ic_htimmba_stop != NULL)
+ ic->ic_htimmba_stop(ic, ni, tid);
+
+ ba->ba_state = IEEE80211_BA_INIT;
+ /* stop Block Ack inactivity timer */
+ timeout_del(&ba->ba_to);
+ if (ba->ba_buf != NULL) {
+ /* free all MSDUs stored in reordering buffer */
+ for (i = 0; i < IEEE80211_BA_MAX_WINSZ; i++)
+ if (ba->ba_buf[i].m != NULL)
+ m_freem(ba->ba_buf[i].m);
+ /* free reordering buffer */
+ free(ba->ba_buf, M_DEVBUF);
+ ba->ba_buf = NULL;
+ }
+}
+#endif /* !IEEE80211_NO_HT */
+
void
ieee80211_auth_open(struct ieee80211com *ic, const struct ieee80211_frame *wh,
struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, u_int16_t seq,
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 7a337e5c01b..f5c57028ddc 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_proto.h,v 1.36 2008/09/28 06:43:07 damien Exp $ */
+/* $OpenBSD: ieee80211_proto.h,v 1.37 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */
/*-
@@ -47,7 +47,11 @@ enum ieee80211_state {
#define IEEE80211_S_MAX (IEEE80211_S_RUN+1)
#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
- ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
+ ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg, 0))
+/* shortcut */
+#define IEEE80211_SEND_ACTION(_ic,_ni,_categ,_action,_arg) \
+ ((*(_ic)->ic_send_mgmt)(_ic, _ni, IEEE80211_FC0_SUBTYPE_ACTION, \
+ (_categ) << 16 | (_action), _arg))
extern const char * const ieee80211_mgt_subtype_name[];
extern const char * const ieee80211_state_name[IEEE80211_S_MAX];
@@ -67,7 +71,7 @@ extern int ieee80211_output(struct ifnet *, struct mbuf *, struct sockaddr *,
extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, struct ieee80211_rxinfo *, int);
extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
- int, int);
+ int, int, int);
extern void ieee80211_eapol_key_input(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
@@ -97,7 +101,6 @@ extern int ieee80211_send_eapol_key_req(struct ieee80211com *,
struct ieee80211_node *, u_int16_t, u_int64_t);
extern int ieee80211_pwrsave(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *);
-extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *, int);
#define ieee80211_new_state(_ic, _nstate, _arg) \
(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
extern enum ieee80211_edca_ac ieee80211_up_to_ac(struct ieee80211com *, int);
@@ -123,6 +126,10 @@ extern u_int8_t *ieee80211_add_wpa(u_int8_t *, struct ieee80211com *,
const struct ieee80211_node *);
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_tie(u_int8_t *, u_int8_t, u_int32_t);
+
extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
struct ieee80211_rsnparams *);
extern int ieee80211_parse_wpa(struct ieee80211com *, const u_int8_t *,
@@ -142,5 +149,15 @@ extern void ieee80211_gtk_rekey_timeout(void *);
extern int ieee80211_keyrun(struct ieee80211com *, u_int8_t *);
extern void ieee80211_setkeys(struct ieee80211com *);
extern void ieee80211_setkeysdone(struct ieee80211com *);
+extern void ieee80211_sa_query_timeout(void *);
+extern void ieee80211_sa_query_request(struct ieee80211com *,
+ struct ieee80211_node *);
+#ifndef IEEE80211_NO_HT
+extern void ieee80211_ba_timeout(void *);
+extern int ieee80211_addba_request(struct ieee80211com *,
+ struct ieee80211_node *, u_int16_t, u_int8_t);
+extern void ieee80211_delba_request(struct ieee80211com *,
+ struct ieee80211_node *, u_int16_t, u_int8_t);
+#endif
#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 5ca29ad46d5..13e6ff1bc8a 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_var.h,v 1.55 2008/11/13 13:42:35 djm Exp $ */
+/* $OpenBSD: ieee80211_var.h,v 1.56 2009/01/26 19:09:41 damien Exp $ */
/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
/*-
@@ -38,8 +38,9 @@
*/
#ifdef SMALL_KERNEL
-#define IEEE80211_STA_ONLY 1
+#define IEEE80211_STA_ONLY 1
#endif
+#define IEEE80211_NO_HT 1 /* no HT yet */
#include <sys/timeout.h>
@@ -171,13 +172,6 @@ struct ieee80211_edca_ac_params {
#define IEEE80211_PROTO_RSN (1 << 0)
#define IEEE80211_PROTO_WPA (1 << 1)
-struct ieee80211_rxinfo {
- u_int32_t rxi_flags;
- u_int32_t rxi_tstamp;
- int rxi_rssi;
-};
-#define IEEE80211_RXI_HWDEC 0x00000001
-
#define IEEE80211_SCAN_UNLOCKED 0x0
#define IEEE80211_SCAN_LOCKED 0x1
#define IEEE80211_SCAN_REQUEST 0x2
@@ -192,7 +186,7 @@ struct ieee80211com {
struct mbuf *, struct ieee80211_node *,
struct ieee80211_rxinfo *, int);
int (*ic_send_mgmt)(struct ieee80211com *,
- struct ieee80211_node *, int, int);
+ struct ieee80211_node *, int, int, int);
int (*ic_newstate)(struct ieee80211com *,
enum ieee80211_state, int);
void (*ic_newassoc)(struct ieee80211com *,
@@ -206,6 +200,10 @@ struct ieee80211com {
void (*ic_delete_key)(struct ieee80211com *,
struct ieee80211_node *,
struct ieee80211_key *);
+ int (*ic_htimmba_start)(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t);
+ void (*ic_htimmba_stop)(struct ieee80211com *,
+ struct ieee80211_node *, u_int8_t);
u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
@@ -291,6 +289,15 @@ struct ieee80211com {
u_int ic_tim_mcast_pending;
u_int ic_dtim_period;
u_int ic_dtim_count;
+
+ u_int32_t ic_txbfcaps;
+ u_int16_t ic_htcaps;
+ u_int16_t ic_htxcaps;
+ u_int8_t ic_aselcaps;
+ u_int8_t ic_sup_mcs[16];
+ u_int8_t ic_dialog_token;
+
+ LIST_HEAD(, ieee80211_vap) ic_vaps;
};
#define ic_if ic_ac.ac_if
#define ic_softc ic_if.if_softc
@@ -320,7 +327,9 @@ extern struct ieee80211com_head ieee80211com_head;
#define IEEE80211_F_RSNON 0x00200000 /* CONF: RSN enabled */
#define IEEE80211_F_PSK 0x00400000 /* CONF: pre-shared key set */
#define IEEE80211_F_COUNTERM 0x00800000 /* STATUS: countermeasures */
-#define IEEE80211_F_MFPR 0x01000000 /* CONF: MFP requested */
+#define IEEE80211_F_MFPR 0x01000000 /* CONF: MFP required */
+#define IEEE80211_F_HTON 0x02000000 /* CONF: HT enabled */
+#define IEEE80211_F_PBAR 0x04000000 /* CONF: PBAC required */
#define IEEE80211_F_USERMASK 0xf0000000 /* CONF: ioctl flag mask */
/* ic_caps */
@@ -338,7 +347,6 @@ extern struct ieee80211com_head ieee80211com_head;
#define IEEE80211_C_QOS 0x00000800 /* CAPABILITY: QoS avail */
#define IEEE80211_C_RSN 0x00001000 /* CAPABILITY: RSN avail */
#define IEEE80211_C_MFP 0x00002000 /* CAPABILITY: MFP avail */
-#define IEEE80211_C_HT 0x00004000 /* CAPABILITY: HT avail */
/* flags for ieee80211_fix_rate() */
#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */