diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2009-01-26 19:09:42 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2009-01-26 19:09:42 +0000 |
commit | f5bde2a8b2c137a3c51797048f76f625179c249b (patch) | |
tree | e7ada894dc97c72e04d71f1fca20fee90e8dc80b /sys/net80211 | |
parent | ba892d095172c6f93edba4a0a5af1c2f8754d770 (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.h | 136 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.c | 14 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.h | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto_bip.c | 12 | ||||
-rw-r--r-- | sys/net80211/ieee80211_input.c | 1027 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 13 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 3 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 69 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 49 | ||||
-rw-r--r-- | sys/net80211/ieee80211_output.c | 382 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_input.c | 36 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_output.c | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_priv.h | 5 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 139 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.h | 25 | ||||
-rw-r--r-- | sys/net80211/ieee80211_var.h | 32 |
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 */ |