My athn RA patch resulted in mixed test reports. People running the device in client mode were happy all around, while others using the device as an access point reported mixed results. I have now figured out what goes wrong in hostap mode.
When auto-selecing a channel an athn(4) access point may end up running on a different channel than it should be. E.g. the athn device's radio might be tuned to channel 1 while net80211 has selected channel 2. Since neighbouring 802.11 channels overlap the AP might still appear to work somewhat, and clients will experience a lot of packet loss. Additionally, net80211 will run the hostap in MODE_AUTO if no mode was configured, which causes issues with selecting the set of 11a/b/g rates to use and also reslts in association problems (e.g. an iwn client will not manage to connect to the AP). As a workaround, both a channel and an operating mode can be set explicitly, like this: ifconfig athn0 nwid foo wpakey mysecretkey ifconfig athn0 mediaopt hostap mode 11n chan 1 ifconfig athn0 up This patch fixes the auto-selection case. 1. Make athn actually configure the channel channel selected by net80211. This is currently broken in hostap mode and monitor mode. 2. Fix the net80211 operating mode when the hostap comes up. With this, an athn hostap will work correctly after: ifconfig athn0 nwid foo wpakey mysecretkey ifconfig athn0 mediaopt hostap ifconfig athn0 up diff 85f491dae6d227f10e75412df0c572c8e5b04442 /usr/src blob - d20bb5615c20f30c2d7bc1bc4bfc66370dfeb232 file + sys/dev/ic/athn.c --- sys/dev/ic/athn.c +++ sys/dev/ic/athn.c @@ -2660,9 +2660,19 @@ athn_newstate(struct ieee80211com *ic, enum ieee80211_ break; case IEEE80211_S_RUN: athn_set_led(sc, 1); - - if (ic->ic_opmode == IEEE80211_M_MONITOR) +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + error = athn_switch_chan(sc, ic->ic_bss->ni_chan, NULL); + if (error != 0) + return (error); + } else +#endif + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + error = athn_switch_chan(sc, ic->ic_bss->ni_chan, NULL); + if (error != 0) + return (error); break; + } /* Fake a join to initialize the Tx rate. */ athn_newassoc(ic, ic->ic_bss, 1); blob - 610db9f671d581f847f227bbeeca30312d8d53aa file + sys/net80211/ieee80211_node.c --- sys/net80211/ieee80211_node.c +++ sys/net80211/ieee80211_node.c @@ -903,6 +903,7 @@ ieee80211_next_scan(struct ifnet *ifp) void ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) { + enum ieee80211_phymode mode; struct ieee80211_node *ni; struct ifnet *ifp = &ic->ic_if; @@ -911,7 +912,25 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct printf("%s: creating ibss\n", ifp->if_xname); ic->ic_flags |= IEEE80211_F_SIBSS; ni->ni_chan = chan; - ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + if ((ic->ic_flags & IEEE80211_F_VHTON) && IEEE80211_IS_CHAN_5GHZ(chan)) + mode = IEEE80211_MODE_11AC; + else if (ic->ic_flags & IEEE80211_F_HTON) + mode = IEEE80211_MODE_11N; + else + mode = ieee80211_chan2mode(ic, ni->ni_chan); + ieee80211_setmode(ic, mode); + /* Pick an appropriate mode for supported legacy rates. */ + if (ic->ic_curmode == IEEE80211_MODE_11AC) { + mode = IEEE80211_MODE_11A; + } else if (ic->ic_curmode == IEEE80211_MODE_11N) { + if (IEEE80211_IS_CHAN_5GHZ(chan)) + mode = IEEE80211_MODE_11A; + else + mode = IEEE80211_MODE_11G; + } else { + mode = ic->ic_curmode; + } + ni->ni_rates = ic->ic_sup_rates[mode]; ni->ni_txrate = 0; IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);