This diff implements monitor mode for iwm(4). To use it, enable 11n mode, monitor mode, and configure a channel (e.g. 11):
ifconfig iwm0 mode 11n mediaopt monitor ifconfig iwm0 chan 11 ifconfig iwm0 up This command should now display wireless frames on the air: tcpdump -n -i iwm0 -y IEEE802_11_RADIO -v Record frames with tcpdump like this (pcap file can be viewed in wireshark): tcpdump -n -i iwm0 -s 4096 -y IEEE802_11_RADIO -w /tmp/iwm.pcap Note that 11n/11ac frames sent with >= 40 MHz channel width won't be captured. The hardware could do it but our stack is not passing the necessary configuration parameters to the driver, so the driver cannot configure the hardware appropriately (a similar problem as bwfm(4) has with our stack's incomplete 11n support). But the corresponding leading RTS and trailing BlockAck frames can be captured, which allows us to manually deduce the presence of such frames. Tested on a 7265 device. ok? Index: if_iwm.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v retrieving revision 1.229 diff -u -p -r1.229 if_iwm.c --- if_iwm.c 15 May 2018 19:48:23 -0000 1.229 +++ if_iwm.c 23 May 2018 10:10:45 -0000 @@ -426,7 +426,7 @@ uint8_t iwm_ridx2rate(struct ieee80211_r int iwm_rval2ridx(int); void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *); void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *, - struct iwm_mac_ctx_cmd *, uint32_t, int); + struct iwm_mac_ctx_cmd *, uint32_t); void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *, struct iwm_mac_data_sta *, int); int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int); @@ -4446,6 +4446,7 @@ void iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in, struct iwm_mac_power_cmd *cmd) { + struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; int dtim_period, dtim_msec, keep_alive; @@ -4467,7 +4468,8 @@ iwm_power_build_cmd(struct iwm_softc *sc keep_alive = roundup(keep_alive, 1000) / 1000; cmd->keep_alive_seconds = htole16(keep_alive); - cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK); + if (ic->ic_opmode != IEEE80211_M_MONITOR) + cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK); } int @@ -4494,13 +4496,15 @@ iwm_power_mac_update_mode(struct iwm_sof int iwm_power_update_device(struct iwm_softc *sc) { - struct iwm_device_power_cmd cmd = { - .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), - }; + struct iwm_device_power_cmd cmd = { }; + struct ieee80211com *ic = &sc->sc_ic; if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) return 0; + if (ic->ic_opmode != IEEE80211_M_MONITOR) + cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); + return iwm_send_cmd_pdu(sc, IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd); } @@ -4562,7 +4566,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, st add_sta_cmd.tfd_queue_msk |= htole32(1 << iwm_ac_to_tx_fifo[ac]); } - IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); + if (ic->ic_opmode == IEEE80211_M_MONITOR) + IEEE80211_ADDR_COPY(&add_sta_cmd.addr, + etherbroadcastaddr); + else + IEEE80211_ADDR_COPY(&add_sta_cmd.addr, + in->in_ni.ni_bssid); } add_sta_cmd.add_modify = update ? 1 : 0; add_sta_cmd.station_flags_msk @@ -5230,7 +5239,7 @@ iwm_ack_rates(struct iwm_softc *sc, stru void iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in, - struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc) + struct iwm_mac_ctx_cmd *cmd, uint32_t action) { #define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ struct ieee80211com *ic = &sc->sc_ic; @@ -5242,12 +5251,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc in->in_color)); cmd->action = htole32(action); - cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA); + if (ic->ic_opmode == IEEE80211_M_MONITOR) + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER); + else if (ic->ic_opmode == IEEE80211_M_STA) + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA); + else + panic("unsupported operating mode %d\n", ic->ic_opmode); cmd->tsf_id = htole32(IWM_TSF_ID_A); IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr); - IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr); + return; + } + IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates); cmd->cck_rates = htole32(cck_ack_rates); cmd->ofdm_rates = htole32(ofdm_ack_rates); @@ -5332,6 +5350,7 @@ int iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action, int assoc) { + struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; struct iwm_mac_ctx_cmd cmd; int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE); @@ -5343,11 +5362,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, s memset(&cmd, 0, sizeof(cmd)); - iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc); + iwm_mac_ctxt_cmd_common(sc, in, &cmd, action); - /* Allow beacons to pass through as long as we are not associated or we - * do not have dtim period information */ - if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod) + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC | + IWM_MAC_FILTER_IN_CONTROL_AND_MGMT | + IWM_MAC_FILTER_IN_BEACON | + IWM_MAC_FILTER_IN_PROBE_REQUEST | + IWM_MAC_FILTER_IN_CRC32); + } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod) + /* + * Allow beacons to pass through as long as we are not + * associated or we do not have dtim period information. + */ cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON); else iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc); @@ -5564,7 +5591,10 @@ iwm_auth(struct iwm_softc *sc) splassert(IPL_NET); - sc->sc_phyctxt[0].channel = in->in_ni.ni_chan; + if (ic->ic_opmode == IEEE80211_M_MONITOR) + sc->sc_phyctxt[0].channel = ic->ic_ibss_chan; + else + sc->sc_phyctxt[0].channel = in->in_ni.ni_chan; err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1, IWM_FW_CTXT_ACTION_MODIFY, 0); if (err) { @@ -5598,6 +5628,9 @@ iwm_auth(struct iwm_softc *sc) } sc->sc_flags |= IWM_FLAG_STA_ACTIVE; + if (ic->ic_opmode == IEEE80211_M_MONITOR) + return 0; + /* * Prevent the FW from wandering off channel during association * by "protecting" the session with a time event. @@ -5728,8 +5761,16 @@ iwm_run(struct iwm_softc *sc) splassert(IPL_NET); + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* Add a MAC context and a sniffing STA. */ + err = iwm_auth(sc); + if (err) + return err; + } + /* Configure Rx chains for MIMO. */ - if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) && + if ((ic->ic_opmode == IEEE80211_M_MONITOR || + (in->in_ni.ni_flags & IEEE80211_NODE_HT)) && !sc->sc_nvm.sku_cap_mimo_disable) { err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0); @@ -5797,6 +5838,11 @@ iwm_run(struct iwm_softc *sc) ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn); ieee80211_mira_node_init(&in->in_mn); + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + iwm_led_blink_start(sc); + return 0; + } + /* Start at lowest available bit-rate, AMRR will raise. */ in->in_ni.ni_txrate = 0; in->in_ni.ni_txmcs = 0; @@ -5817,6 +5863,9 @@ iwm_run_stop(struct iwm_softc *sc) splassert(IPL_NET); + if (ic->ic_opmode == IEEE80211_M_MONITOR) + iwm_led_blink_stop(sc); + err = iwm_sf_config(sc, IWM_SF_INIT_OFF); if (err) return err; @@ -6565,6 +6614,12 @@ iwm_init(struct ifnet *ifp) ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + return 0; + } + ieee80211_begin_scan(ifp); /* @@ -7894,6 +7949,7 @@ iwm_attach(struct device *parent, struct IEEE80211_C_RSN | /* WPA/RSN */ IEEE80211_C_SCANALL | /* device scans all channels at once */ IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ + IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_SHPREAMBLE; /* short preamble supported */