On Wed, Aug 28, 2019 at 10:39:24AM -0300, Martin Pieuchot wrote:
> On 28/08/19(Wed) 12:54, Stefan Sperling wrote:
> > +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.

Because these failures happen all the time. Annotating almost every
line with !essid and !privacy whenever you run 'scan' is not useful.
Try the diff without that chunk and you'll see what I mean.

> Is there any value to keep the IFF_DEBUG output if ifconfig(8) already
> report the failures?

I think so. The debug output provides a snapshot of what the stack
was seeing at that particular point in time, while ifconfig provides
an async view on what was cached when it ran an ioctl.

> > +   /* 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',

It looks like this override is required for ieee80211_ibss_merge().
The generic copy function does not copy driver-private state information,
such as the current Tx rate in case of ath(4) which is maintained while
the driver is in RUN state.
An IBSS merge may be necessary whenever a beacon from another device in the
same IBSS is received, which could happen in RUN state. See ath_recv_mgmt().

The non-IBSS modes don't call node_copy() while the driver is in RUN state
so it's a non-issue in those modes. Most drivers don't support IBSS in the
first place, and most people never use IBSS mode. But it's worth keeping
IBSS mode since makes it possible to use OpenBSD in wireless mesh networks,
e.g. in combination with the net/olsrd port.

> but shouldn't this logic be part of ieee80211_node_copy() instead?  It is
> just to work around the copy, right?

With the ic_node_copy() abstraction in place, it makes more sense to
keep this code where I have added it, I think.

> > +   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.

Thanks, there were two missing cases in ieee80211_pae_input.c which matter.
Updated diff below.

The others are only relevant for hostap mode, which I am deliberately
ignoring for the purposes of this diff.

> Another question, could these flags be used in AP mode?  To report
> client errors?

In theory yes, but AP mode uses entirely different code paths and
should really be handled as a separate diff.

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;
+
+       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 - 575a573d2e21e9863bdab5276471f32fd189cc57
blob + 4b5b8cb5d273de964469fcffb03532c349ecca84
--- sys/net80211/ieee80211_ioctl.h
+++ sys/net80211/ieee80211_ioctl.h
@@ -359,6 +359,8 @@ struct ieee80211_nodereq {
 
        /* VHT */
        uint8_t                 nr_vht_ss;
+
+       u_int32_t       nr_assoc_fail;  /* association failure reasons */
 };
 
 #define IEEE80211_NODEREQ_STATE(_s)    (1 << _s)
@@ -378,6 +380,17 @@ struct ieee80211_nodereq {
 #define SIOCG80211NODE         _IOWR('i', 211, struct ieee80211_nodereq)
 #define SIOCS80211NODE          _IOW('i', 212, struct ieee80211_nodereq)
 #define SIOCS80211DELNODE       _IOW('i', 213, struct ieee80211_nodereq)
+
+#define IEEE80211_NODEREQ_ASSOCFAIL_CHAN       0x01
+#define IEEE80211_NODEREQ_ASSOCFAIL_IBSS       0x02
+#define IEEE80211_NODEREQ_ASSOCFAIL_PRIVACY    0x04
+#define IEEE80211_NODEREQ_ASSOCFAIL_BASIC_RATE 0x08
+#define IEEE80211_NODEREQ_ASSOCFAIL_ESSID      0x10
+#define IEEE80211_NODEREQ_ASSOCFAIL_BSSID      0x20
+#define IEEE80211_NODEREQ_ASSOCFAIL_WPA_PROTO  0x40
+#define IEEE80211_NODEREQ_ASSOCFAIL_WPA_KEY    0x80
+#define IEEE80211_NODEREQ_ASSOCFAIL_BITS       \
+       "\20\1CHAN\2IBSS\3PRIVACY\4BASICRATE\5ESSID\6BSSID\7WPAPROTO\10WPAKEY"
 
 /* get the entire node cache */
 struct ieee80211_nodereq_all {
blob - 99b17a709fec1326d108be8e39ae8596f5b7f0f6
blob + ac031edda4662154bf384a3bd6d722c72547f136
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -523,28 +523,40 @@ ieee80211_match_ess(struct ieee80211_ess *ess, struct 
 {
        if (ess->esslen != 0 &&
            (ess->esslen != ni->ni_esslen ||
-           memcmp(ess->essid, ni->ni_essid, ess->esslen) != 0))
+           memcmp(ess->essid, ni->ni_essid, ess->esslen) != 0)) {
+               ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_ESSID;
                return 0;
+       }
 
        if (ess->flags & (IEEE80211_F_PSK | IEEE80211_F_RSNON)) {
                /* Ensure same WPA version. */
                if ((ni->ni_rsnprotos & IEEE80211_PROTO_RSN) &&
-                   (ess->rsnprotos & IEEE80211_PROTO_RSN) == 0)
+                   (ess->rsnprotos & IEEE80211_PROTO_RSN) == 0) {
+                       ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
                        return 0;
+               }
                if ((ni->ni_rsnprotos & IEEE80211_PROTO_WPA) &&
-                   (ess->rsnprotos & IEEE80211_PROTO_WPA) == 0)
+                   (ess->rsnprotos & IEEE80211_PROTO_WPA) == 0) {
+                       ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO;
                        return 0;
+               }
        } else if (ess->flags & IEEE80211_F_WEPON) {
-               if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+               if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) {
+                       ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY;
                        return 0;
+               }
        } else {
-               if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0)
+               if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) {
+                       ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY;
                        return 0;
+               }
        }
 
        if (ess->esslen == 0 &&
-           (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0)
+           (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) {
+               ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY;
                return 0;
+       }
 
        return 1;
 }
@@ -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.
@@ -1000,41 +1012,41 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ie
 
        fail = 0;
        if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
-               fail |= 0x01;
+               fail |= IEEE80211_NODE_ASSOCFAIL_CHAN;
        if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
            ni->ni_chan != ic->ic_des_chan)
-               fail |= 0x01;
+               fail |= IEEE80211_NODE_ASSOCFAIL_CHAN;
 #ifndef IEEE80211_STA_ONLY
        if (ic->ic_opmode == IEEE80211_M_IBSS) {
                if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
-                       fail |= 0x02;
+                       fail |= IEEE80211_NODE_ASSOCFAIL_IBSS;
        } else
 #endif
        {
                if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
-                       fail |= 0x02;
+                       fail |= IEEE80211_NODE_ASSOCFAIL_IBSS;
        }
        if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON)) {
                if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
-                       fail |= 0x04;
+                       fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY;
        } else {
                if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
-                       fail |= 0x04;
+                       fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY;
        }
 
        rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
        if (rate & IEEE80211_RATE_BASIC)
-               fail |= 0x08;
+               fail |= IEEE80211_NODE_ASSOCFAIL_BASIC_RATE;
        if (ISSET(ic->ic_flags, IEEE80211_F_AUTO_JOIN) &&
            ic->ic_des_esslen == 0)
-               fail |= 0x10;
+               fail |= IEEE80211_NODE_ASSOCFAIL_ESSID;
        if (ic->ic_des_esslen != 0 &&
            (ni->ni_esslen != ic->ic_des_esslen ||
             memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0))
-               fail |= 0x10;
+               fail |= IEEE80211_NODE_ASSOCFAIL_ESSID;
        if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
            !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
-               fail |= 0x20;
+               fail |= IEEE80211_NODE_ASSOCFAIL_BSSID;
 
        if (ic->ic_flags & IEEE80211_F_RSNON) {
                /*
@@ -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 ? "!" : "");
        }
 
+       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;
+
+       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 + 3ab5bacb42d9990898dd11db2e74a29ad3330b53
--- 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;
                }
        }
  deauth:
@@ -915,6 +916,7 @@ ieee80211_recv_rsn_group_msg1(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;
                }
        }
        /* update the last seen value of the key replay counter field */
@@ -1019,6 +1021,7 @@ ieee80211_recv_wpa_group_msg1(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;
                }
        }
        /* update the last seen value of the key replay counter field */
blob - 30c784c7b2f404dcdb35d37a8b1b29928f628184
blob + 5ab07bfe2215a99449a86e073bc474ed238ae6b3
--- sys/net80211/ieee80211_proto.c
+++ sys/net80211/ieee80211_proto.c
@@ -939,6 +939,30 @@ ieee80211_stop_ampdu_tx(struct ieee80211com *ic, struc
        }
 }
 
+void
+ieee80211_check_wpa_supplicant_failure(struct ieee80211com *ic,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211_node *ni2;
+
+       if (ic->ic_opmode != IEEE80211_M_STA &&
+           ic->ic_opmode != IEEE80211_M_IBSS)
+               return;
+
+       if (ni->ni_rsn_supp_state != RSNA_SUPP_PTKNEGOTIATING)
+               return;
+
+       ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_KEY;
+
+       if (ni != ic->ic_bss)
+               return;
+
+       /* Also update the copy of our AP's node in the node cache. */
+       ni2 = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+       if (ni2)
+               ni2->ni_assoc_fail |= ic->ic_bss->ni_assoc_fail;
+}
+
 int
 ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate,
     int mgt)
@@ -1037,6 +1061,7 @@ justcleanup:
                        break;
                }
                ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
+               ni->ni_assoc_fail = 0;
                if (ic->ic_flags & IEEE80211_F_RSNON)
                        ieee80211_crypto_clear_groupkeys(ic);
                break;
@@ -1097,6 +1122,8 @@ justcleanup:
                }
                break;
        case IEEE80211_S_AUTH:
+               if (ostate == IEEE80211_S_RUN)
+                       ieee80211_check_wpa_supplicant_failure(ic, ni);
                ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
                if (ic->ic_flags & IEEE80211_F_RSNON)
                        ieee80211_crypto_clear_groupkeys(ic);
@@ -1218,6 +1245,7 @@ justcleanup:
                                 * the link up until the port is valid.
                                 */
                                ieee80211_set_link_state(ic, LINK_STATE_UP);
+                               ni->ni_assoc_fail = 0;
                        }
                        ic->ic_mgt_timer = 0;
                        ieee80211_set_beacon_miss_threshold(ic);

Reply via email to