Commit 9179dff8 authored by Mordechay Goodstein's avatar Mordechay Goodstein Committed by Johannes Berg

wifi: mac80211: add support for driver adding radiotap TLVs

The new TLV format enables adding TLVs after the fixed
fields in radiotap, as part of the radiotap header.
Support this and move vendor data to the TLV format,
allowing a reuse of the RX_FLAG_RADIOTAP_VENDOR_DATA as
the new RX_FLAG_RADIOTAP_TLV_AT_END flag.
Signed-off-by: default avatarMordechay Goodstein <mordechay.goodstein@intel.com>
Signed-off-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230301115906.b18fd5da8477.I576400ec40a7b35ef97a3b09a99b3a49e9174786@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 18cbf7c0
......@@ -209,33 +209,34 @@ static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm,
struct sk_buff *skb)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_vendor_radiotap *radiotap;
const int size = sizeof(*radiotap) + sizeof(__le16);
struct ieee80211_radiotap_vendor_tlv *radiotap;
const u16 vendor_data_len = sizeof(mvm->cur_aid);
const u16 padding = ALIGN(vendor_data_len, 4) - vendor_data_len;
if (!mvm->cur_aid)
return;
/* ensure alignment */
BUILD_BUG_ON((size + 2) % 4);
radiotap = skb_put(skb, sizeof(*radiotap) + vendor_data_len + padding);
radiotap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE);
radiotap->len = cpu_to_le16(sizeof(*radiotap) -
sizeof(struct ieee80211_radiotap_tlv) +
vendor_data_len);
radiotap = skb_put(skb, size + 2);
radiotap->align = 1;
/* Intel OUI */
radiotap->oui[0] = 0xf6;
radiotap->oui[1] = 0x54;
radiotap->oui[2] = 0x25;
/* radiotap sniffer config sub-namespace */
radiotap->subns = 1;
radiotap->present = 0x1;
radiotap->len = size - sizeof(*radiotap);
radiotap->pad = 2;
radiotap->oui_subtype = 1;
radiotap->vendor_type = 0;
/* clear reserved field */
radiotap->reserved = 0;
/* fill the data now */
memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid));
/* and clear the padding */
memset(radiotap->data + sizeof(__le16), 0, radiotap->pad);
memset(radiotap->data + vendor_data_len, 0, padding);
rx_status->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA;
rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */
......
......@@ -1534,37 +1534,38 @@ static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb)
* the values accordingly.
*/
#ifdef HWSIM_RADIOTAP_OUI
struct ieee80211_vendor_radiotap *rtap;
struct ieee80211_radiotap_vendor_tlv *rtap;
static const char vendor_data[8] = "ABCDEFGH";
// Make sure no padding is needed
BUILD_BUG_ON(sizeof(vendor_data) % 4);
/* this is last radiotap info before the mac header, so
* skb_reset_mac_header for mac8022 to know the end of
* the radiotap TLV/beginning of the 802.11 header
*/
skb_reset_mac_header(skb);
/*
* Note that this code requires the headroom in the SKB
* that was allocated earlier.
*/
rtap = skb_push(skb, sizeof(*rtap) + 8 + 4);
rtap = skb_push(skb, sizeof(*rtap) + sizeof(vendor_data));
rtap->len = cpu_to_le16(sizeof(*rtap) -
sizeof(struct ieee80211_radiotap_tlv) +
sizeof(vendor_data));
rtap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE);
rtap->oui[0] = HWSIM_RADIOTAP_OUI[0];
rtap->oui[1] = HWSIM_RADIOTAP_OUI[1];
rtap->oui[2] = HWSIM_RADIOTAP_OUI[2];
rtap->subns = 127;
rtap->oui_subtype = 127;
/* clear reserved field */
rtap->reserved = 0;
rtap->vendor_type = 0;
memcpy(rtap->data, vendor_data, sizeof(vendor_data));
/*
* Radiotap vendor namespaces can (and should) also be
* split into fields by using the standard radiotap
* presence bitmap mechanism. Use just BIT(0) here for
* the presence bitmap.
*/
rtap->present = BIT(0);
/* We have 8 bytes of (dummy) data */
rtap->len = 8;
/* For testing, also require it to be aligned */
rtap->align = 8;
/* And also test that padding works, 4 bytes */
rtap->pad = 4;
/* push the data */
memcpy(rtap->data, "ABCDEFGH", 8);
/* make sure to clear padding, mac80211 doesn't */
memset(rtap->data + 8, 0, 4);
IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA;
IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
#endif
}
......
......@@ -369,6 +369,26 @@ struct ieee80211_radiotap_tlv {
u8 data[];
} __packed;
/**
* struct ieee80211_radiotap_vendor_tlv - vendor radiotap data information
* @type: should always be set to IEEE80211_RADIOTAP_VENDOR_NAMESPACE
* @len: length of data
* @oui: radiotap vendor namespace OUI
* @oui_subtype: radiotap vendor sub namespace
* @vendor_type: radiotap vendor type
* @reserved: should always be set to zero (to avoid leaking memory)
* @data: the actual vendor namespace data
*/
struct ieee80211_radiotap_vendor_tlv {
__le16 type; /* IEEE80211_RADIOTAP_VENDOR_NAMESPACE */
__le16 len;
u8 oui[3];
u8 oui_subtype;
__le16 vendor_type;
__le16 reserved;
u8 data[];
} __packed;
/* ieee80211_radiotap_eht_usig - content of U-SIG tlv (type 33)
* see www.radiotap.org/fields/U-SIG.html for details
*/
......
......@@ -1375,9 +1375,12 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* subframes share the same sequence number. Reported subframes can be
* either regular MSDU or singly A-MSDUs. Subframes must not be
* interleaved with other frames.
* @RX_FLAG_RADIOTAP_VENDOR_DATA: This frame contains vendor-specific
* radiotap data in the skb->data (before the frame) as described by
* the &struct ieee80211_vendor_radiotap.
* @RX_FLAG_RADIOTAP_TLV_AT_END: This frame contains radiotap TLVs in the
* skb->data (before the 802.11 header).
* If used, the SKB's mac_header pointer must be set to point
* to the 802.11 header after the TLVs, and any padding added after TLV
* data to align to 4 must be cleared by the driver putting the TLVs
* in the skb.
* @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
* This is used for AMSDU subframes which can have the same PN as
* the first subframe.
......@@ -1429,7 +1432,7 @@ enum mac80211_rx_flags {
RX_FLAG_ONLY_MONITOR = BIT(17),
RX_FLAG_SKIP_MONITOR = BIT(18),
RX_FLAG_AMSDU_MORE = BIT(19),
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(20),
RX_FLAG_RADIOTAP_TLV_AT_END = BIT(20),
RX_FLAG_MIC_STRIPPED = BIT(21),
RX_FLAG_ALLOW_SAME_PN = BIT(22),
RX_FLAG_ICV_STRIPPED = BIT(23),
......@@ -1569,39 +1572,6 @@ ieee80211_rx_status_to_khz(struct ieee80211_rx_status *rx_status)
(rx_status->freq_offset ? 500 : 0);
}
/**
* struct ieee80211_vendor_radiotap - vendor radiotap data information
* @present: presence bitmap for this vendor namespace
* (this could be extended in the future if any vendor needs more
* bits, the radiotap spec does allow for that)
* @align: radiotap vendor namespace alignment. This defines the needed
* alignment for the @data field below, not for the vendor namespace
* description itself (which has a fixed 2-byte alignment)
* Must be a power of two, and be set to at least 1!
* @oui: radiotap vendor namespace OUI
* @subns: radiotap vendor sub namespace
* @len: radiotap vendor sub namespace skip length, if alignment is done
* then that's added to this, i.e. this is only the length of the
* @data field.
* @pad: number of bytes of padding after the @data, this exists so that
* the skb data alignment can be preserved even if the data has odd
* length
* @data: the actual vendor namespace data
*
* This struct, including the vendor data, goes into the skb->data before
* the 802.11 header. It's split up in mac80211 using the align/oui/subns
* data.
*/
struct ieee80211_vendor_radiotap {
u32 present;
u8 align;
u8 oui[3];
u8 subns;
u8 pad;
u16 len;
u8 data[];
} __packed;
/**
* enum ieee80211_conf_flags - configuration flags
*
......
......@@ -55,7 +55,7 @@ static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb,
/* After pulling radiotap header, clear all flags that indicate
* info in skb->data.
*/
status->flag &= ~(RX_FLAG_RADIOTAP_VENDOR_DATA |
status->flag &= ~(RX_FLAG_RADIOTAP_TLV_AT_END |
RX_FLAG_RADIOTAP_LSIG |
RX_FLAG_RADIOTAP_HE_MU |
RX_FLAG_RADIOTAP_HE);
......@@ -126,9 +126,6 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
/* allocate extra bitmaps */
if (status->chains)
len += 4 * hweight8(status->chains);
/* vendor presence bitmap */
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)
len += 4;
if (ieee80211_have_rx_timestamp(status)) {
len = ALIGN(len, 8);
......@@ -190,34 +187,28 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
len += 2 * hweight8(status->chains);
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
struct ieee80211_vendor_radiotap *rtap;
int vendor_data_offset = 0;
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) {
int tlv_offset = 0;
/*
* The position to look at depends on the existence (or non-
* existence) of other elements, so take that into account...
*/
if (status->flag & RX_FLAG_RADIOTAP_HE)
vendor_data_offset +=
tlv_offset +=
sizeof(struct ieee80211_radiotap_he);
if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
vendor_data_offset +=
tlv_offset +=
sizeof(struct ieee80211_radiotap_he_mu);
if (status->flag & RX_FLAG_RADIOTAP_LSIG)
vendor_data_offset +=
tlv_offset +=
sizeof(struct ieee80211_radiotap_lsig);
rtap = (void *)&skb->data[vendor_data_offset];
/* ensure 4 byte alignment for TLV */
len = ALIGN(len, 4);
/* alignment for fixed 6-byte vendor data header */
len = ALIGN(len, 2);
/* vendor data header */
len += 6;
if (WARN_ON(rtap->align == 0))
rtap->align = 1;
len = ALIGN(len, rtap->align);
len += rtap->len + rtap->pad;
/* TLVs until the mac header */
len += skb_mac_header(skb) - &skb->data[tlv_offset];
}
return len;
......@@ -313,9 +304,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
u32 it_present_val;
u16 rx_flags = 0;
u16 channel_flags = 0;
u32 tlvs_len = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
struct ieee80211_vendor_radiotap rtap = {};
struct ieee80211_radiotap_he he = {};
struct ieee80211_radiotap_he_mu he_mu = {};
struct ieee80211_radiotap_lsig lsig = {};
......@@ -336,18 +327,17 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
skb_pull(skb, sizeof(lsig));
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
/* rtap.len and rtap.pad are undone immediately */
skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad);
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) {
/* data is pointer at tlv all other info was pulled off */
tlvs_len = skb_mac_header(skb) - skb->data;
}
mpdulen = skb->len;
if (!(has_fcs && ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)))
mpdulen += FCS_LEN;
rthdr = skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
rthdr = skb_push(skb, rtap_len - tlvs_len);
memset(rthdr, 0, rtap_len - tlvs_len);
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
......@@ -369,13 +359,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
BIT(IEEE80211_RADIOTAP_EXT);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = rtap.present;
}
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END)
it_present_val |= BIT(IEEE80211_RADIOTAP_TLV);
put_unaligned_le32(it_present_val, it_present);
......@@ -706,22 +691,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
/* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1)
*pos++ = 0;
*pos++ = rtap.oui[0];
*pos++ = rtap.oui[1];
*pos++ = rtap.oui[2];
*pos++ = rtap.subns;
put_unaligned_le16(rtap.len, pos);
pos += 2;
/* align the actual payload as requested */
while ((pos - (u8 *)rthdr) & (rtap.align - 1))
*pos++ = 0;
/* data (and possible padding) already follows */
}
}
static struct sk_buff *
......@@ -797,6 +766,13 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
bool only_monitor = false;
unsigned int min_head_len;
if (WARN_ON_ONCE(status->flag & RX_FLAG_RADIOTAP_TLV_AT_END &&
!skb_mac_header_was_set(origskb))) {
/* with this skb no way to know where frame payload starts */
dev_kfree_skb(origskb);
return NULL;
}
if (status->flag & RX_FLAG_RADIOTAP_HE)
rtap_space += sizeof(struct ieee80211_radiotap_he);
......@@ -806,12 +782,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (status->flag & RX_FLAG_RADIOTAP_LSIG)
rtap_space += sizeof(struct ieee80211_radiotap_lsig);
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
struct ieee80211_vendor_radiotap *rtap =
(void *)(origskb->data + rtap_space);
rtap_space += sizeof(*rtap) + rtap->len + rtap->pad;
}
if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END)
rtap_space += skb_mac_header(origskb) - &origskb->data[rtap_space];
min_head_len = rtap_space;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment