On 28/08/19(Wed) 12:54, Stefan Sperling wrote:
> I find myself regularly asking people to run 'ifconfig iwm0 debug' to
> figure out reasons behind association failures from dmesg output.
>
> I would like to make this information more accessible. Making it part
> of regular ifconfig output seems like a reasonable thing to do. This
> becomes possible now that node information is cached across scans.
>
> The current net80211 code is already computing failure reason codes which
> are visible in 'ifconfig debug' output. So far such codes were magic numbers.
> We can simply create macros for them and expose them in ieee80211_ioctl.h,
> and add another code to represent the "wrong-WPA-key" situation.
>
> To avoid changing the existing ifconfig output too much, the following
> association failure reasons are not displayed:
> [...]
> Is this going in the right direction?
I love it. Comments below
> diff refs/heads/master refs/heads/assocfail
> blob - 543e09f0d6e6b4e75456a21d4218468729b61a5e
> blob + 03e4818ef5fa11d7e5dbf8307affb9f0ea5522a0
> --- sbin/ifconfig/ifconfig.c
> +++ sbin/ifconfig/ifconfig.c
> @@ -2348,10 +2348,27 @@ print_cipherset(u_int32_t cipherset)
> }
>
> void
> +print_assoc_failures(uint32_t assoc_fail, const char *intro, const char
> *outro)
> +{
> + /* Filter out the most obvious failure cases. */
> + assoc_fail &= ~IEEE80211_NODEREQ_ASSOCFAIL_ESSID;
> + if (assoc_fail & IEEE80211_NODEREQ_ASSOCFAIL_PRIVACY)
> + assoc_fail &= ~IEEE80211_NODEREQ_ASSOCFAIL_WPA_PROTO;
> + assoc_fail &= ~IEEE80211_NODEREQ_ASSOCFAIL_PRIVACY;
It's not clear to me why you suggest we should filter out these failure
cases.
> +
> + if (assoc_fail == 0)
> + return;
> +
> + printf("%s", intro);
> + printb_status(assoc_fail, IEEE80211_NODEREQ_ASSOCFAIL_BITS);
> + printf("%s", outro);
> +}
> +
> +void
> ieee80211_status(void)
> {
> int len, inwid, ijoin, inwkey, ipsk, ichan, ipwr;
> - int ibssid, iwpa;
> + int ibssid, iwpa, assocfail = 0;
> struct ieee80211_nwid nwid;
> struct ieee80211_join join;
> struct ieee80211_nwkey nwkey;
> @@ -2431,11 +2448,15 @@ ieee80211_status(void)
> bzero(&nr, sizeof(nr));
> bcopy(bssid.i_bssid, &nr.nr_macaddr, sizeof(nr.nr_macaddr));
> strlcpy(nr.nr_ifname, name, sizeof(nr.nr_ifname));
> - if (ioctl(s, SIOCG80211NODE, &nr) == 0 && nr.nr_rssi) {
> - if (nr.nr_max_rssi)
> - printf(" %u%%", IEEE80211_NODEREQ_RSSI(&nr));
> - else
> - printf(" %ddBm", nr.nr_rssi);
> + if (ioctl(s, SIOCG80211NODE, &nr) == 0) {
> + if (nr.nr_rssi) {
> + if (nr.nr_max_rssi)
> + printf(" %u%%",
> + IEEE80211_NODEREQ_RSSI(&nr));
> + else
> + printf(" %ddBm", nr.nr_rssi);
> + }
> + assocfail = nr.nr_assoc_fail;
> }
> }
>
> @@ -2478,6 +2499,10 @@ ieee80211_status(void)
> putchar(' ');
> printb_status(ifr.ifr_flags, IEEE80211_F_USERBITS);
> }
> +
> + if (assocfail)
> + print_assoc_failures(assocfail, " !(", ")");
> +
> putchar('\n');
> if (show_join)
> join_status();
> @@ -2751,6 +2776,8 @@ ieee80211_printnode(struct ieee80211_nodereq *nr)
> if ((nr->nr_flags & IEEE80211_NODEREQ_AP) == 0)
> printb_status(IEEE80211_NODEREQ_STATE(nr->nr_state),
> IEEE80211_NODEREQ_STATE_BITS);
> + else if (nr->nr_assoc_fail)
> + print_assoc_failures(nr->nr_assoc_fail, "!(", ") ");
> }
>
> void
> blob - 425ee5f5a726e3fe0445c82debb0469d4878407f
> blob + 6b3b8a5834db39d5b2f2dc7693f42d0b5adbcfbb
> --- sys/net80211/ieee80211_ioctl.c
> +++ sys/net80211/ieee80211_ioctl.c
> @@ -104,6 +104,7 @@ ieee80211_node2req(struct ieee80211com *ic, const stru
> nr->nr_txseq = ni->ni_txseq;
> nr->nr_rxseq = ni->ni_rxseq;
> nr->nr_fails = ni->ni_fails;
> + nr->nr_assoc_fail = ni->ni_assoc_fail; /* flag values are the same */
> nr->nr_inact = ni->ni_inact;
> nr->nr_txrate = ni->ni_txrate;
> nr->nr_state = ni->ni_state;
> @@ -821,7 +822,11 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
> break;
> case SIOCG80211NODE:
> nr = (struct ieee80211_nodereq *)data;
> - ni = ieee80211_find_node(ic, nr->nr_macaddr);
> + if (ic->ic_bss &&
> + IEEE80211_ADDR_EQ(nr->nr_macaddr, ic->ic_bss->ni_macaddr))
> + ni = ic->ic_bss;
> + else
> + ni = ieee80211_find_node(ic, nr->nr_macaddr);
> if (ni == NULL) {
> error = ENOENT;
> break;
> blob - 99b17a709fec1326d108be8e39ae8596f5b7f0f6
> blob + ac031edda4662154bf384a3bd6d722c72547f136
> --- sys/net80211/ieee80211_node.c
> +++ sys/net80211/ieee80211_node.c
> @@ -780,7 +792,7 @@ ieee80211_reset_scan(struct ifnet *ifp)
> }
>
> /*
> - * Increase a node's inactitivy counter.
> + * Increase a node's inactivity counter.
> * This counter get reset to zero if a frame is received.
> * This function is intended for station mode only.
> * See ieee80211_node_cache_timeout() for hostap mode.
> @@ -1043,66 +1055,71 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ie
> * decline to associate with that AP.
> */
> if ((ni->ni_rsnprotos & ic->ic_rsnprotos) == 0)
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> if ((ni->ni_rsnakms & ic->ic_rsnakms) == 0)
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> if ((ni->ni_rsnakms & ic->ic_rsnakms &
> ~(IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK)) == 0) {
> /* AP only supports PSK AKMPs */
> if (!(ic->ic_flags & IEEE80211_F_PSK))
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> }
> if (ni->ni_rsngroupcipher != IEEE80211_CIPHER_WEP40 &&
> ni->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP &&
> ni->ni_rsngroupcipher != IEEE80211_CIPHER_CCMP &&
> ni->ni_rsngroupcipher != IEEE80211_CIPHER_WEP104)
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0)
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
>
> /* we only support BIP as the IGTK cipher */
> if ((ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC) &&
> ni->ni_rsngroupmgmtcipher != IEEE80211_CIPHER_BIP)
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
>
> /* we do not support MFP but AP requires it */
> if (!(ic->ic_caps & IEEE80211_C_MFP) &&
> (ni->ni_rsncaps & IEEE80211_RSNCAP_MFPR))
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
>
> /* we require MFP but AP does not support it */
> if ((ic->ic_caps & IEEE80211_C_MFP) &&
> (ic->ic_flags & IEEE80211_F_MFPR) &&
> !(ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC))
> - fail |= 0x40;
> + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
> }
>
> if (ic->ic_if.if_flags & IFF_DEBUG) {
> printf("%s: %c %s%c", ic->ic_if.if_xname, fail ? '-' : '+',
> ether_sprintf(ni->ni_bssid),
> - fail & 0x20 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_BSSID ? '!' : ' ');
> printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
> - fail & 0x01 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_CHAN ? '!' : ' ');
> printf(" %+4d", ni->ni_rssi);
> printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
> - fail & 0x08 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_BASIC_RATE ? '!' : ' ');
> printf(" %4s%c",
> (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
> (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
> "????",
> - fail & 0x02 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_IBSS ? '!' : ' ');
> printf(" %7s%c ",
> (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
> "privacy" : "no",
> - fail & 0x04 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_PRIVACY ? '!' : ' ');
> printf(" %3s%c ",
> (ic->ic_flags & IEEE80211_F_RSNON) ?
> "rsn" : "no",
> - fail & 0x40 ? '!' : ' ');
> + fail & IEEE80211_NODE_ASSOCFAIL_WPA_PROTO ? '!' : ' ');
> ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
> - printf("%s\n", fail & 0x10 ? "!" : "");
> + printf("%s\n",
> + fail & IEEE80211_NODE_ASSOCFAIL_ESSID ? "!" : "");
> }
Is there any value to keep the IFF_DEBUG output if ifconfig(8) already
report the failures?
> + ni->ni_assoc_fail |= fail;
> + if ((fail & IEEE80211_NODE_ASSOCFAIL_ESSID) == 0)
> + ic->ic_bss->ni_assoc_fail |= ni->ni_assoc_fail;
> +
> return fail;
> }
>
> @@ -1160,14 +1177,24 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struc
> {
> enum ieee80211_phymode mode;
> struct ieee80211_node *ni;
> + uint32_t assoc_fail = 0;
>
> /* Reinitialize media mode and channels if needed. */
> mode = ieee80211_chan2mode(ic, selbs->ni_chan);
> if (mode != ic->ic_curmode)
> ieee80211_setmode(ic, mode);
>
> + /* Keep recorded association failures for this BSS/ESS intact. */
> + if (IEEE80211_ADDR_EQ(ic->ic_bss->ni_macaddr, selbs->ni_macaddr) ||
> + (ic->ic_des_esslen > 0 && ic->ic_des_esslen == selbs->ni_esslen &&
> + memcmp(ic->ic_des_essid, selbs->ni_essid, selbs->ni_esslen) == 0))
> + assoc_fail = ic->ic_bss->ni_assoc_fail;
> +
> (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
> ni = ic->ic_bss;
> + ni->ni_assoc_fail |= assoc_fail;
I couldn't convince myself why two divers are overwriting `ic_node_copy',
but shouldn't this logic be part of ieee80211_node_copy() instead? It is
just to work around the copy, right?
> +
> + ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan);
>
> /* Make sure we send valid rates in an association request. */
> if (ic->ic_opmode == IEEE80211_M_STA)
> blob - b2b80656a4b4aa907b4d729ecaecb81827739c52
> blob + ddd746729a48b15fa3c21d85390682263d2934d9
> --- sys/net80211/ieee80211_node.h
> +++ sys/net80211/ieee80211_node.h
> @@ -353,6 +353,16 @@ struct ieee80211_node {
> u_int16_t ni_qos_txseqs[IEEE80211_NUM_TID];
> u_int16_t ni_qos_rxseqs[IEEE80211_NUM_TID];
> int ni_fails; /* failure count to associate */
> + uint32_t ni_assoc_fail; /* assoc failure reasons */
> +#define IEEE80211_NODE_ASSOCFAIL_CHAN 0x01
> +#define IEEE80211_NODE_ASSOCFAIL_IBSS 0x02
> +#define IEEE80211_NODE_ASSOCFAIL_PRIVACY 0x04
> +#define IEEE80211_NODE_ASSOCFAIL_BASIC_RATE 0x08
> +#define IEEE80211_NODE_ASSOCFAIL_ESSID 0x10
> +#define IEEE80211_NODE_ASSOCFAIL_BSSID 0x20
> +#define IEEE80211_NODE_ASSOCFAIL_WPA_PROTO 0x40
> +#define IEEE80211_NODE_ASSOCFAIL_WPA_KEY 0x80
> +
> int ni_inact; /* inactivity mark count */
> int ni_txrate; /* index to ni_rates[] */
> int ni_state;
> blob - ba8c1872a2df596a0667ecb7c306f7e7f9dd0ffc
> blob + daf43cd6457134728ce5dd485229036ea4822445
> --- sys/net80211/ieee80211_pae_input.c
> +++ sys/net80211/ieee80211_pae_input.c
> @@ -650,6 +650,7 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
> ether_sprintf(ni->ni_macaddr)));
> ni->ni_port_valid = 1;
> ieee80211_set_link_state(ic, LINK_STATE_UP);
> + ni->ni_assoc_fail = 0;
There are other places where `ni_port_valid' is set to 1 and another
one where the link is set to UP where the flags aren't cleared.
Another question, could these flags be used in AP mode? To report
client errors?