Module Name: src Committed By: maxv Date: Thu Jan 18 13:24:01 UTC 2018
Modified Files: src/sys/net80211: ieee80211_output.c Log Message: Several changes: * Make the code more readable. In particular, declare variables as const along the way. * Explain what we're doing in ieee80211_send_mgmt(). The IEEE80211_FC0_SUBTYPE_PROBE_RESP case has some inconsistencies, but they are not inherently wrong so I'm not changing that. * When sending IEEE80211_FC0_SUBTYPE_REASSOC_RESP frames, make sure to zero out the 'association ID', otherwise two bytes are leaked. * Fix a possible memory leak in ieee80211_send_probereq(). To generate a diff of this commit: cvs rdiff -u -r1.59 -r1.60 src/sys/net80211/ieee80211_output.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net80211/ieee80211_output.c diff -u src/sys/net80211/ieee80211_output.c:1.59 src/sys/net80211/ieee80211_output.c:1.60 --- src/sys/net80211/ieee80211_output.c:1.59 Tue Sep 26 07:42:06 2017 +++ src/sys/net80211/ieee80211_output.c Thu Jan 18 13:24:01 2018 @@ -1,5 +1,6 @@ -/* $NetBSD: ieee80211_output.c,v 1.59 2017/09/26 07:42:06 knakahara Exp $ */ -/*- +/* $NetBSD: ieee80211_output.c,v 1.60 2018/01/18 13:24:01 maxv Exp $ */ + +/* * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. @@ -36,7 +37,7 @@ __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.34 2005/08/10 16:22:29 sam Exp $"); #endif #ifdef __NetBSD__ -__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.59 2017/09/26 07:42:06 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.60 2018/01/18 13:24:01 maxv Exp $"); #endif #ifdef _KERNEL_OPT @@ -115,6 +116,7 @@ ieee80211_send_setup(struct ieee80211com #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { switch (ic->ic_opmode) { case IEEE80211_M_STA: @@ -123,6 +125,7 @@ ieee80211_send_setup(struct ieee80211com IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, da); break; + case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; @@ -130,12 +133,14 @@ ieee80211_send_setup(struct ieee80211com IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); break; + case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, bssid); IEEE80211_ADDR_COPY(wh->i_addr3, sa); break; + case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ break; } @@ -145,6 +150,7 @@ ieee80211_send_setup(struct ieee80211com IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } + *(u_int16_t *)&wh->i_dur[0] = 0; /* NB: use non-QoS tid */ *(u_int16_t *)&wh->i_seq[0] = @@ -187,9 +193,9 @@ ieee80211_mgmt_output(struct ieee80211co M_SETCTX(m, ni); wh = mtod(m, struct ieee80211_frame *); - ieee80211_send_setup(ic, ni, wh, - IEEE80211_FC0_TYPE_MGT | type, - ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + ieee80211_send_setup(ic, ni, wh, IEEE80211_FC0_TYPE_MGT | type, + ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { m->m_flags &= ~M_LINK0; IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, @@ -197,6 +203,7 @@ ieee80211_mgmt_output(struct ieee80211co ether_sprintf(wh->i_addr1), __func__); wh->i_fc[1] |= IEEE80211_FC1_WEP; } + #ifdef IEEE80211_DEBUG /* avoid printing too many frames */ if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || @@ -209,6 +216,7 @@ ieee80211_mgmt_output(struct ieee80211co ieee80211_chan2ieee(ic, ic->ic_curchan)); } #endif + IEEE80211_NODE_STAT(ni, tx_mgmt); IF_ENQUEUE(&ic->ic_mgtq, m); if (timer) { @@ -247,13 +255,17 @@ ieee80211_send_nulldata(struct ieee80211 M_SETCTX(m, ni); wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(ic, ni, wh, - IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, - ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + /* NB: power management bit is never sent by an AP */ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - ic->ic_opmode != IEEE80211_M_HOSTAP) + ic->ic_opmode != IEEE80211_M_HOSTAP) { wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + } + m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); IEEE80211_NODE_STAT(ni, tx_data); @@ -277,7 +289,8 @@ ieee80211_send_nulldata(struct ieee80211 * applied. */ int -ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) +ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) { int v_wme_ac, d_wme_ac, ac; #ifdef INET @@ -405,6 +418,7 @@ ieee80211_mbuf_adjust(struct ieee80211co needed_space += key->wk_cipher->ic_header; /* XXX frags */ } + /* * We know we are called just before stripping an Ethernet * header and prepending an LLC header. This means we know @@ -423,8 +437,10 @@ ieee80211_mbuf_adjust(struct ieee80211co m_freem(m); return NULL; } + IASSERT(needed_space <= MHLEN, ("not enough room, need %u got %zu\n", needed_space, MHLEN)); + /* * Setup new mbuf to have leading space to prepend the * 802.11 header and any crypto header bits that are @@ -451,7 +467,8 @@ ieee80211_mbuf_adjust(struct ieee80211co n->m_next = m; m = n; } else { - /* We will overwrite the ethernet header in the + /* + * We will overwrite the ethernet header in the * 802.11 encapsulation stage. Make sure that it * is writable. */ @@ -539,10 +556,11 @@ ieee80211_encap(struct ieee80211com *ic, */ if (ic->ic_flags & IEEE80211_F_PRIVACY) { if (ic->ic_opmode == IEEE80211_M_STA || - !IEEE80211_IS_MULTICAST(eh.ether_dhost)) + !IEEE80211_IS_MULTICAST(eh.ether_dhost)) { key = ieee80211_crypto_getucastkey(ic, ni); - else + } else { key = ieee80211_crypto_getmcastkey(ic, ni); + } if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, "[%s] no default transmit key (%s) deftxkey %u\n", @@ -550,10 +568,13 @@ ieee80211_encap(struct ieee80211com *ic, ic->ic_def_txkey); ic->ic_stats.is_tx_nodefkey++; } - } else + } else { key = NULL; - /* XXX 4-address format */ + } + /* + * XXX 4-address format. + * * XXX Some ap's don't handle QoS-encapsulated EAPOL * frames so suppress use. This may be an issue if other * ap's require all data frames to be QoS-encapsulated @@ -561,13 +582,14 @@ ieee80211_encap(struct ieee80211com *ic, * configurable. */ addqos = (ni->ni_flags & IEEE80211_NODE_QOS) && - eh.ether_type != htons(ETHERTYPE_PAE); + eh.ether_type != htons(ETHERTYPE_PAE); if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else hdrsize = sizeof(struct ieee80211_frame); if (ic->ic_flags & IEEE80211_F_DATAPAD) hdrsize = roundup(hdrsize, sizeof(u_int32_t)); + m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); if (m == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ @@ -590,9 +612,11 @@ ieee80211_encap(struct ieee80211com *ic, ic->ic_stats.is_tx_nobuf++; goto bad; } + wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; *(u_int16_t *)wh->i_dur = 0; + switch (ic->ic_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; @@ -600,6 +624,7 @@ ieee80211_encap(struct ieee80211com *ic, IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); break; + case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; @@ -611,22 +636,26 @@ ieee80211_encap(struct ieee80211com *ic, */ IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); break; + case IEEE80211_M_HOSTAP: #ifndef IEEE80211_NO_HOSTAP wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); -#endif /* !IEEE80211_NO_HOSTAP */ +#endif break; + case IEEE80211_M_MONITOR: goto bad; } + if (m->m_flags & M_MORE_DATA) wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + if (addqos) { struct ieee80211_qosframe *qwh = - (struct ieee80211_qosframe *) wh; + (struct ieee80211_qosframe *)wh; int ac, tid; ac = M_WME_GETAC(m); @@ -646,10 +675,12 @@ ieee80211_encap(struct ieee80211com *ic, htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[0]++; } + /* check if xmit fragmentation is required */ txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold && !IEEE80211_IS_MULTICAST(wh->i_addr1) && (m->m_flags & M_FF) == 0); /* NB: don't fragment ff's */ + if (key != NULL) { /* * IEEE 802.1X: send EAPOL frames always in the clear. @@ -670,6 +701,7 @@ ieee80211_encap(struct ieee80211com *ic, } } } + if (txfrag && !ieee80211_fragment(ic, m, hdrsize, key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold)) goto bad; @@ -678,6 +710,7 @@ ieee80211_encap(struct ieee80211com *ic, IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); return m; + bad: if (m != NULL) m_freem(m); @@ -1338,14 +1371,17 @@ ieee80211_send_probereq(struct ieee80211 m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); - if (m == NULL) + if (m == NULL) { + ic->ic_stats.is_tx_nobuf++; + ieee80211_free_node(ni); return ENOMEM; + } M_SETCTX(m, ni); wh = mtod(m, struct ieee80211_frame *); ieee80211_send_setup(ic, ni, wh, - IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, - sa, da, bssid); + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + sa, da, bssid); /* XXX power management? */ IEEE80211_NODE_STAT(ni, tx_probereq); @@ -1374,7 +1410,7 @@ ieee80211_send_mgmt(struct ieee80211com struct mbuf *m; u_int8_t *frm; u_int16_t capinfo; - int has_challenge, is_shared_key, ret, timer, status; + int ret, timer, status; IASSERT(ni != NULL, ("null node")); @@ -1392,7 +1428,9 @@ ieee80211_send_mgmt(struct ieee80211com timer = 0; switch (type) { - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: { + const bool has_wpa = (ic->ic_flags & IEEE80211_F_WPA) != 0; + /* * probe response frame format * [8] time stamp @@ -1408,27 +1446,31 @@ ieee80211_send_mgmt(struct ieee80211com * [tlv] WME (optional) */ m = ieee80211_getmgtframe(&frm, - 8 - + sizeof(u_int16_t) - + sizeof(u_int16_t) - + 2 + IEEE80211_NWID_LEN - + 2 + IEEE80211_RATE_SIZE - + 7 /* max(7,3) */ - + 6 - + 3 + 8 /* timestamp */ + + sizeof(u_int16_t) /* interval */ + + sizeof(u_int16_t) /* capinfo */ + + 2 + IEEE80211_NWID_LEN /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* rates */ + + 7 /* max(7,3) */ + + 6 /* ibss (XXX could be 4?) */ + + 3 /* erp */ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) /* XXX !WPA1+WPA2 fits w/o a cluster */ - + (ic->ic_flags & IEEE80211_F_WPA ? - 2*sizeof(struct ieee80211_ie_wpa) : 0) + + (has_wpa ? (2 * sizeof(struct ieee80211_ie_wpa)) : 0) + sizeof(struct ieee80211_wme_param) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - memset(frm, 0, 8); /* timestamp should be filled later */ + /* timestamp (should be filled later) */ + memset(frm, 0, 8); frm += 8; + + /* interval */ *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); frm += 2; + + /* capinfo */ if (ic->ic_opmode == IEEE80211_M_IBSS) capinfo = IEEE80211_CAPINFO_IBSS; else @@ -1443,10 +1485,14 @@ ieee80211_send_mgmt(struct ieee80211com *(u_int16_t *)frm = htole16(capinfo); frm += 2; + /* ssid */ frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, - ic->ic_bss->ni_esslen); + ic->ic_bss->ni_esslen); + + /* rates */ frm = ieee80211_add_rates(frm, &ni->ni_rates); + /* variable */ if (ic->ic_phytype == IEEE80211_T_FH) { *frm++ = IEEE80211_ELEMID_FHPARMS; *frm++ = 5; @@ -1463,27 +1509,39 @@ ieee80211_send_mgmt(struct ieee80211com *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); } + /* ibss */ if (ic->ic_opmode == IEEE80211_M_IBSS) { *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; - *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ } - if (ic->ic_flags & IEEE80211_F_WPA) + + /* wpa */ + if (has_wpa) frm = ieee80211_add_wpa(frm, ic); + + /* erp */ if (ic->ic_curmode == IEEE80211_MODE_11G) frm = ieee80211_add_erp(frm, ic); + + /* xrates */ frm = ieee80211_add_xrates(frm, &ni->ni_rates); + + /* wme */ if (ic->ic_flags & IEEE80211_F_WME) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); break; + } - case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_AUTH: { status = arg >> 16; arg &= 0xffff; - has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || - arg == IEEE80211_AUTH_SHARED_RESPONSE) && - ni->ni_challenge != NULL); + const bool has_challenge = + (arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL; /* * Deduce whether we're doing open authentication or @@ -1492,41 +1550,44 @@ ieee80211_send_mgmt(struct ieee80211com * handshake or if we're initiating an authentication * request and configured to use shared key. */ - is_shared_key = has_challenge || - arg >= IEEE80211_AUTH_SHARED_RESPONSE || - (arg == IEEE80211_AUTH_SHARED_REQUEST && - ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + const bool is_shared_key = has_challenge || + (arg >= IEEE80211_AUTH_SHARED_RESPONSE) || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + + const bool need_challenge = + has_challenge && (status == IEEE80211_STATUS_SUCCESS); + + const int frm_size = 3 * sizeof(u_int16_t) + + (need_challenge ? + sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0); - m = ieee80211_getmgtframe(&frm, - 3 * sizeof(u_int16_t) - + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? - sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0) - ); + m = ieee80211_getmgtframe(&frm, frm_size); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); ((u_int16_t *)frm)[0] = - (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + is_shared_key ? htole16(IEEE80211_AUTH_ALG_SHARED) : htole16(IEEE80211_AUTH_ALG_OPEN); ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ ((u_int16_t *)frm)[2] = htole16(status);/* status */ - if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + if (need_challenge) { ((u_int16_t *)frm)[3] = htole16((IEEE80211_CHALLENGE_LEN << 8) | IEEE80211_ELEMID_CHALLENGE); memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, IEEE80211_CHALLENGE_LEN); - m->m_pkthdr.len = m->m_len = - 4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN; + if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, "[%s] request encrypt frame (%s)\n", ether_sprintf(ni->ni_macaddr), __func__); m->m_flags |= M_LINK0; /* WEP-encrypt, please */ } - } else - m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t); + } + + m->m_pkthdr.len = m->m_len = frm_size; /* XXX not right for shared key */ if (status == IEEE80211_STATUS_SUCCESS) @@ -1537,6 +1598,7 @@ ieee80211_send_mgmt(struct ieee80211com if (ic->ic_opmode == IEEE80211_M_STA) timer = IEEE80211_TRANS_WAIT; break; + } case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, @@ -1583,7 +1645,7 @@ ieee80211_send_mgmt(struct ieee80211com capinfo = 0; if (ic->ic_opmode == IEEE80211_M_IBSS) capinfo |= IEEE80211_CAPINFO_IBSS; - else /* IEEE80211_M_STA */ + else /* IEEE80211_M_STA */ capinfo |= IEEE80211_CAPINFO_ESS; if (ic->ic_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; @@ -1661,8 +1723,10 @@ ieee80211_send_mgmt(struct ieee80211com if (arg == IEEE80211_STATUS_SUCCESS) { *(u_int16_t *)frm = htole16(ni->ni_associd); IEEE80211_NODE_STAT(ni, tx_assoc); - } else + } else { + *(u_int16_t *)frm = 0; IEEE80211_NODE_STAT(ni, tx_assoc_fail); + } frm += 2; frm = ieee80211_add_rates(frm, &ni->ni_rates);