From c338cf2c6d5eacdee813191d5976aa531d450ee7 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Sat, 6 Mar 2021 21:05:43 +0000 Subject: [PATCH] net80211: split up ieee80211_probereq() Factor out ieee80211_probereq_ie() and ieee80211_probereq_ie_len() and make the length dynamic rather than static max. The latter is needed as our current fixed length was longer than some "hw scan", e.g. that of ath10k, will take. This way we can pass what we have. Should this not be sufficient in the future we might have to deal with filtering and much more error handling. This also removes a duplicate calculation for ieee80211_ie_wpa [1]. Repoprted-by: Martin Husemann [1] Sponsored-by: Rubicon Communications, LLC ("Netgate") Sponsored-by: The FreeBSD Foundation (update for alloc) Reviewed-by: adrian, martin NetBSD.org (earlier version) Reviewed-by: philip MFC-after: 2 weeks Differential Revision: https://reviews.freebsd.org/D26545 --- sys/net80211/ieee80211_output.c | 171 +++++++++++++++++++++----------- sys/net80211/ieee80211_proto.h | 2 + 2 files changed, 117 insertions(+), 56 deletions(-) diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index b67302b8d66f..07f7349461ac 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -2414,80 +2414,76 @@ ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) } /* - * Send a probe request frame with the specified ssid - * and any optional information element data. + * ieee80211_send_probereq(): send a probe request frame with the specified ssid + * and any optional information element data; some helper functions as FW based + * HW scans need some of that information passed too. */ -int -ieee80211_send_probereq(struct ieee80211_node *ni, - const uint8_t sa[IEEE80211_ADDR_LEN], - const uint8_t da[IEEE80211_ADDR_LEN], - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t *ssid, size_t ssidlen) +static uint32_t +ieee80211_probereq_ie_len(struct ieee80211vap *vap, struct ieee80211com *ic) { - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct ieee80211_node *bss; - const struct ieee80211_txparam *tp; - struct ieee80211_bpf_params params; const struct ieee80211_rateset *rs; - struct mbuf *m; - uint8_t *frm; - int ret; - bss = ieee80211_ref_node(vap->iv_bss); - - if (vap->iv_state == IEEE80211_S_CAC) { - IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, - "block %s frame in CAC state", "probe request"); - vap->iv_stats.is_tx_badstate++; - ieee80211_free_node(bss); - return EIO; /* XXX */ - } - - /* - * Hold a reference on the node so it doesn't go away until after - * the xmit is complete all the way in the driver. On error we - * will remove our reference. - */ - IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, - "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", - __func__, __LINE__, - ni, ether_sprintf(ni->ni_macaddr), - ieee80211_node_refcnt(ni)+1); - ieee80211_ref_node(ni); + rs = ieee80211_get_suprates(ic, ic->ic_curchan); /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] RSN (optional) - * [tlv] extended supported rates + * [tlv] extended supported rates (if needed) * [tlv] HT cap (optional) * [tlv] VHT cap (optional) * [tlv] WPA (optional) * [tlv] user-specified ie's */ - m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), - 2 + IEEE80211_NWID_LEN + return ( 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE - + sizeof(struct ieee80211_ie_htcap) - + sizeof(struct ieee80211_ie_vhtcap) + + ((vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL) ? + vap->iv_rsn_ie[1] : 0) + + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? + 2 + (rs->rs_nrates - IEEE80211_RATE_SIZE) : 0) + + (((vap->iv_opmode == IEEE80211_M_IBSS) && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) ? + sizeof(struct ieee80211_ie_htcap) : 0) +#ifdef notyet + sizeof(struct ieee80211_ie_htinfo) /* XXX not needed? */ - + sizeof(struct ieee80211_ie_wpa) - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + sizeof(struct ieee80211_ie_wpa) + + sizeof(struct ieee80211_ie_vhtcap) +#endif + + ((vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL) ? + vap->iv_wpa_ie[1] : 0) + (vap->iv_appie_probereq != NULL ? vap->iv_appie_probereq->ie_len : 0) ); - if (m == NULL) { - vap->iv_stats.is_tx_nobuf++; - ieee80211_free_node(ni); - ieee80211_free_node(bss); - return ENOMEM; - } +} - frm = ieee80211_add_ssid(frm, ssid, ssidlen); +int +ieee80211_probereq_ie(struct ieee80211vap *vap, struct ieee80211com *ic, + uint8_t **frmp, uint32_t *frmlen, const uint8_t *ssid, size_t ssidlen, + bool alloc) +{ + const struct ieee80211_rateset *rs; + uint8_t *frm; + uint32_t len; + + if (!alloc && (frmp == NULL || frmlen == NULL)) + return (EINVAL); + + len = ieee80211_probereq_ie_len(vap, ic); + if (!alloc && len > *frmlen) + return (ENOBUFS); + + if (alloc) { + frm = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO); + *frmp = frm; + *frmlen = len; + } else + frm = *frmp; + + /* For HW scans we usually do not pass in the SSID as IE. */ + if (ssidlen == -1) + len -= (2 + IEEE80211_NWID_LEN); + else + frm = ieee80211_add_ssid(frm, ssid, ssidlen); rs = ieee80211_get_suprates(ic, ic->ic_curchan); frm = ieee80211_add_rates(frm, rs); frm = ieee80211_add_rsn(frm, vap); @@ -2517,8 +2513,8 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * XXX TODO: need to figure out what/how to update the * VHT channel. */ -#if 0 - (vap->iv_flags_vht & IEEE80211_FVHT_VHT) { +#ifdef notyet + if (vap->iv_flags_vht & IEEE80211_FVHT_VHT) { struct ieee80211_channel *c; c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, @@ -2531,8 +2527,71 @@ ieee80211_send_probereq(struct ieee80211_node *ni, frm = ieee80211_add_wpa(frm, vap); if (vap->iv_appie_probereq != NULL) frm = add_appie(frm, vap->iv_appie_probereq); - m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + if (!alloc) { + *frmp = frm; + *frmlen = len; + } + + return (0); +} + +int +ieee80211_send_probereq(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t *ssid, size_t ssidlen) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss; + const struct ieee80211_txparam *tp; + struct ieee80211_bpf_params params; + struct mbuf *m; + uint8_t *frm; + uint32_t frmlen; + int ret; + + bss = ieee80211_ref_node(vap->iv_bss); + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, + "block %s frame in CAC state", "probe request"); + vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(bss); + return EIO; /* XXX */ + } + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + /* See comments above for entire frame format. */ + frmlen = ieee80211_probereq_ie_len(vap, ic); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), frmlen); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + ieee80211_free_node(bss); + return ENOMEM; + } + + ret = ieee80211_probereq_ie(vap, ic, &frm, &frmlen, ssid, ssidlen, + false); + KASSERT(ret == 0, + ("%s: ieee80211_probereq_ie railed: %d\n", __func__, ret)); + + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), ("leading space %zd", M_LEADINGSPACE(m))); M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index fafedf46cab5..4b324caa694b 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -114,6 +114,8 @@ struct mbuf *ieee80211_encap(struct ieee80211vap *, struct ieee80211_node *, void ieee80211_free_mbuf(struct mbuf *); int ieee80211_send_mgmt(struct ieee80211_node *, int, int); struct ieee80211_appie; +int ieee80211_probereq_ie(struct ieee80211vap *, struct ieee80211com *, + uint8_t **, uint32_t *, const uint8_t *, size_t, bool); int ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN],