git: 93c6906e36a4 - main - wtap(4): implement monitor mode and handle radiotap

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Tue, 16 Jun 2026 14:58:22 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=93c6906e36a4e60e59279b6b6e18403ea23d56e1

commit 93c6906e36a4e60e59279b6b6e18403ea23d56e1
Author:     EN-WEU WU <enweiwu@FreeBSD.org>
AuthorDate: 2026-06-16 14:54:05 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-06-16 14:54:55 +0000

    wtap(4): implement monitor mode and handle radiotap
    
    Implement monitor mode by simply adding IEEE80211_C_MONITOR to ic->ic_cap.
    
    To get additional informations when capturing 802.11 frames, radiotap is
     inserted by wtap_tx_tap() when TX and wtap_rx_tap() when RX.
    
    There are some type faults in struct wtap_rx_radiotap_header which are
     mainly mistakenly store unsigned values into signed integers.
    I have fixed them (wtap(4)) by complying with the types defined
     in https://www.radiotap.org/fields/defined.
    
    Becuase the struct wtap_rx_radiotap_header comes from ath(4),
    there may be another patch to fix the type faults in ath(4).
    
    Differential Review: https://reviews.freebsd.org/D36469
---
 sys/dev/wtap/if_wtap.c      | 59 ++++++++++++++++++++++++++++++++++++++++++---
 sys/dev/wtap/if_wtapioctl.h | 31 ++++++++++++------------
 2 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/sys/dev/wtap/if_wtap.c b/sys/dev/wtap/if_wtap.c
index d22b79a20dc4..d23036575d7a 100644
--- a/sys/dev/wtap/if_wtap.c
+++ b/sys/dev/wtap/if_wtap.c
@@ -229,6 +229,43 @@ wtap_beacon_config(struct wtap_softc *sc, struct ieee80211vap *vap)
 	DWTAP_PRINTF("%s\n", __func__);
 }
 
+static void
+wtap_rx_tap(struct wtap_softc *sc, uint64_t tsf)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	const struct ieee80211_rate_table *rt = ic->ic_rt;
+	struct wtap_rx_radiotap_header *rh = &sc->sc_rx_th;
+
+	rh->wr_tsf = tsf;
+	rh->wr_flags = 0;
+	if (rt->rateCount) {
+		/* choose the fastest rate */
+		rh->wr_rate = IEEE80211_RV(rt->info[rt->rateCount - 1].dot11Rate);
+	}
+	rh->wr_chan_flags = IEEE80211_CHAN_2GHZ;
+	rh->wr_chan_freq = ic->ic_curchan->ic_freq;
+	rh->wr_chan_ieee = ic->ic_curchan->ic_ieee;
+	rh->wr_chan_maxpow = 0;
+}
+
+static void
+wtap_tx_tap(struct wtap_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	const struct ieee80211_rate_table *rt = ic->ic_rt;
+	struct wtap_tx_radiotap_header *th = &sc->sc_tx_th;
+
+	th->wt_flags = 0;
+	if (rt->rateCount) {
+		/* choose the fastest rate */
+		th->wt_rate = IEEE80211_RV(rt->info[rt->rateCount - 1].dot11Rate);
+	}
+	th->wt_chan_flags = IEEE80211_CHAN_2GHZ;
+	th->wt_chan_freq = ic->ic_curchan->ic_freq;
+	th->wt_chan_ieee = ic->ic_curchan->ic_ieee;
+	th->wt_chan_maxpow = 0;
+}
+
 static void
 wtap_beacon_intrp(void *arg)
 {
@@ -261,8 +298,10 @@ wtap_beacon_intrp(void *arg)
 	wh = mtod(m, struct ieee80211_frame *);
 	memcpy(&wh[1], &tsf, sizeof(tsf));
 
-	if (ieee80211_radiotap_active_vap(vap))
-	    ieee80211_radiotap_tx(vap, m);
+	if (ieee80211_radiotap_active_vap(vap)) {
+		wtap_tx_tap(sc);
+		ieee80211_radiotap_tx(vap, m);
+	}
 
 #if 0
 	medium_transmit(avp->av_md, avp->id, m);
@@ -459,6 +498,8 @@ static int
 wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
 	const struct ieee80211_bpf_params *params)
 {
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wtap_softc *sc = ic->ic_softc;
 #if 0
 	DWTAP_PRINTF("%s, %p\n", __func__, m);
 #endif
@@ -466,6 +507,7 @@ wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
 	struct wtap_vap 	*avp = WTAP_VAP(vap);
 
 	if (ieee80211_radiotap_active_vap(vap)) {
+		wtap_tx_tap(sc);
 		ieee80211_radiotap_tx(vap, m);
 	}
 	if (m->m_flags & M_TXCB)
@@ -529,6 +571,10 @@ wtap_rx_proc(void *arg, int npending)
 #if 0
 		ieee80211_dump_pkt(ic, mtod(m, caddr_t), 0,0,0);
 #endif
+		if (ieee80211_radiotap_active(ic)) {
+			uint64_t tsf = wtap_hal_get_tsf(sc->hal);
+			wtap_rx_tap(sc, tsf);
+		}
 
 		/*
 		 * Use arbitrary but sane values, and do the correct conversion
@@ -600,6 +646,7 @@ wtap_transmit(struct ieee80211com *ic, struct mbuf *m)
 	struct ieee80211_node *ni =
 	    (struct ieee80211_node *) m->m_pkthdr.rcvif;
 	struct ieee80211vap *vap = ni->ni_vap;
+	struct wtap_softc *sc = ic->ic_softc;
 	struct wtap_vap *avp = WTAP_VAP(vap);
 	struct ieee80211_key *k;
 	struct ieee80211_frame *wh;
@@ -612,8 +659,10 @@ wtap_transmit(struct ieee80211com *ic, struct mbuf *m)
 			return (ENOBUFS);
 	}
 
-	if (ieee80211_radiotap_active_vap(vap))
+	if (ieee80211_radiotap_active_vap(vap)) {
+		wtap_tx_tap(sc);
 		ieee80211_radiotap_tx(vap, m);
+	}
 	if (m->m_flags & M_TXCB)
 		ieee80211_process_callback(ni, m, 0);
 	ieee80211_free_node(ni);
@@ -668,7 +717,9 @@ wtap_attach(struct wtap_softc *sc, const uint8_t *macaddr)
 		| IEEE80211_C_IBSS		/* ibss, nee adhoc, mode */
 		| IEEE80211_C_STA		/* station mode */
 		| IEEE80211_C_HOSTAP		/* hostap mode */
-		| IEEE80211_C_WPA;		/* capable of WPA1+WPA2 */
+		| IEEE80211_C_WPA		/* capable of WPA1+WPA2 */
+		| IEEE80211_C_MONITOR		/* Enable monitor mode */
+		;
 
 	ic->ic_cryptocaps =
 		  IEEE80211_CRYPTO_WEP
diff --git a/sys/dev/wtap/if_wtapioctl.h b/sys/dev/wtap/if_wtapioctl.h
index 05f88a5dcb81..f9fe35813576 100644
--- a/sys/dev/wtap/if_wtapioctl.h
+++ b/sys/dev/wtap/if_wtapioctl.h
@@ -139,41 +139,42 @@ struct wtap_stats {
 /*
  * Radio capture format.
  */
-#define WTAP_RX_RADIOTAP_PRESENT (		\
+#define WTAP_RX_RADIOTAP_PRESENT (				\
+	(1 << IEEE80211_RADIOTAP_TSFT)		| 		\
+	(1 << IEEE80211_RADIOTAP_FLAGS)		| 		\
+	(1 << IEEE80211_RADIOTAP_RATE)		| 		\
+	(1 << IEEE80211_RADIOTAP_XCHANNEL)	| 		\
 	0)
 
 struct wtap_rx_radiotap_header {
 	struct ieee80211_radiotap_header wr_ihdr;
-#if 0
+
 	u_int64_t	wr_tsf;
 	u_int8_t	wr_flags;
 	u_int8_t	wr_rate;
-	int8_t		wr_antsignal;
-	int8_t		wr_antnoise;
-	u_int8_t	wr_antenna;
-	u_int8_t	wr_pad[3];
+	u_int8_t	wr_pad[2];
 	u_int32_t	wr_chan_flags;
 	u_int16_t	wr_chan_freq;
 	u_int8_t	wr_chan_ieee;
-	int8_t		wr_chan_maxpow;
-#endif
+	u_int8_t	wr_chan_maxpow;
 } __packed __aligned(8);
 
-#define WTAP_TX_RADIOTAP_PRESENT (		\
+#define WTAP_TX_RADIOTAP_PRESENT (				\
+	(1 << IEEE80211_RADIOTAP_FLAGS)		| 		\
+	(1 << IEEE80211_RADIOTAP_RATE)		| 		\
+	(1 << IEEE80211_RADIOTAP_XCHANNEL)	| 		\
 	0)
 
 struct wtap_tx_radiotap_header {
 	struct ieee80211_radiotap_header wt_ihdr;
-#if 0
+
 	u_int8_t	wt_flags;
 	u_int8_t	wt_rate;
-	u_int8_t	wt_txpower;
-	u_int8_t	wt_antenna;
+	u_int8_t	wr_pad[2];
 	u_int32_t	wt_chan_flags;
 	u_int16_t	wt_chan_freq;
 	u_int8_t	wt_chan_ieee;
-	int8_t		wt_chan_maxpow;
-#endif
-} __packed;
+	u_int8_t	wt_chan_maxpow;
+} __packed __aligned(8);
 
 #endif