On Fri, Jan 20, 2012 at 06:57:54PM +0000, Steven Chamberlain wrote:
> On 10:23, Edd Barrett wrote:
> > I will be willing to test any diffs relating to this. I have some soekris
> > with Ral cards which i intended to use in hostap mode.
> 
> Hi,
> 
> I think finally we have a solution for this :)
> 
> First you'll need to apply the two recent CVS commits by Stefan Sperling, 
> ieee80211_node.c,v 1.64 and ieee80211_proto.c,v 1.46.
> 
> You may then try his 'inactivity' patch included below, otherwise active 
> stations could be deauthed when the node cache becomes full.  There used to 
> be an LRU list that would caused inactive entries to be removed first, but 
> unfortunately that is gone so this seems to be necessary instead.
> 
> And I suggest applying my small diff which follows it, to fix the behaviour 
> of the clean_nodes function, otherwise it would have tried to clear more 
> nodes out of the cache than was intended.
> 
> I'm currently trialling these diffs in production, so far with success.  
> Further testing would be very helpful right now.  Thanks!
> 

Please try this diff instead with whatever hostap-capable card you've got.
It includes Steven's change (which is very good) and some more of my own.

Index: ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.64
diff -u -p -r1.64 ieee80211_node.c
--- ieee80211_node.c    18 Jan 2012 14:35:34 -0000      1.64
+++ ieee80211_node.c    20 Jan 2012 17:44:02 -0000
@@ -95,10 +95,46 @@ void ieee80211_node_leave_ht(struct ieee
 void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_set_tim(struct ieee80211com *, int, int);
+void ieee80211_inact_timeout(void *);
+void ieee80211_node_cache_timeout(void *);
 #endif
 
 #define M_80211_NODE   M_DEVBUF
 
+#ifndef IEEE80211_STA_ONLY
+void
+ieee80211_inact_timeout(void *arg)
+{
+       struct ieee80211com *ic = arg;
+       struct ieee80211_node *ni, *next_ni;
+       int s;
+
+       s = splnet();
+       for (ni = RB_MIN(ieee80211_tree, &ic->ic_tree);
+           ni != NULL; ni = next_ni) {
+               next_ni = RB_NEXT(ieee80211_tree, &ic->ic_tree, ni);
+               if (ni == ic->ic_bss)
+                       continue;
+               if (ni->ni_refcnt > 0)
+                       continue;
+               if (ni->ni_inact < IEEE80211_INACT_MAX)
+                       ni->ni_inact++;
+       }
+       splx(s);
+
+       timeout_add_sec(&ic->ic_inact_timeout, IEEE80211_INACT_WAIT);
+}
+
+void
+ieee80211_node_cache_timeout(void *arg)
+{
+       struct ieee80211com *ic = arg;
+
+       ieee80211_clean_nodes(ic, 1);
+       timeout_add_sec(&ic->ic_node_cache_timeout, IEEE80211_CACHE_WAIT);
+}
+#endif
+
 void
 ieee80211_node_attach(struct ifnet *ifp)
 {
@@ -138,6 +174,10 @@ ieee80211_node_attach(struct ifnet *ifp)
                        ic->ic_set_tim = ieee80211_set_tim;
                timeout_set(&ic->ic_rsn_timeout,
                    ieee80211_gtk_rekey_timeout, ic);
+               timeout_set(&ic->ic_inact_timeout,
+                   ieee80211_inact_timeout, ic);
+               timeout_set(&ic->ic_node_cache_timeout,
+                   ieee80211_node_cache_timeout, ic);
        }
 #endif
 }
@@ -147,7 +187,7 @@ ieee80211_alloc_node_helper(struct ieee8
 {
        struct ieee80211_node *ni;
        if (ic->ic_nnodes >= ic->ic_max_nnodes)
-               ieee80211_clean_nodes(ic);
+               ieee80211_clean_nodes(ic, 0);
        if (ic->ic_nnodes >= ic->ic_max_nnodes)
                return NULL;
        ni = (*ic->ic_node_alloc)(ic);
@@ -185,6 +225,8 @@ ieee80211_node_detach(struct ifnet *ifp)
                free(ic->ic_aid_bitmap, M_DEVBUF);
        if (ic->ic_tim_bitmap != NULL)
                free(ic->ic_tim_bitmap, M_DEVBUF);
+       timeout_del(&ic->ic_inact_timeout);
+       timeout_del(&ic->ic_node_cache_timeout);
 #endif
        timeout_del(&ic->ic_rsn_timeout);
 }
@@ -378,6 +420,8 @@ ieee80211_create_ibss(struct ieee80211co
                /* schedule a GTK/IGTK rekeying after 3600s */
                timeout_add_sec(&ic->ic_rsn_timeout, 3600);
        }
+       timeout_add_sec(&ic->ic_inact_timeout, IEEE80211_INACT_WAIT);
+       timeout_add_sec(&ic->ic_node_cache_timeout, IEEE80211_CACHE_WAIT);
        ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
 }
 #endif /* IEEE80211_STA_ONLY */
@@ -771,20 +815,7 @@ ieee80211_setup_node(struct ieee80211com
        timeout_set(&ni->ni_eapol_to, ieee80211_eapol_timeout, ni);
        timeout_set(&ni->ni_sa_query_to, ieee80211_sa_query_timeout, ni);
 #endif
-
-       /*
-        * Note we don't enable the inactive timer when acting
-        * as a station.  Nodes created in this mode represent
-        * AP's identified while scanning.  If we time them out
-        * then several things happen: we can't return the data
-        * to users to show the list of AP's we encountered, and
-        * more importantly, we'll incorrectly deauthenticate
-        * ourself because the inactivity timer will kick us off.
-        */
        s = splnet();
-       if (ic->ic_opmode != IEEE80211_M_STA &&
-           RB_EMPTY(&ic->ic_tree))
-               ic->ic_inact_timer = IEEE80211_INACT_WAIT;
        RB_INSERT(ieee80211_tree, &ic->ic_tree, ni);
        splx(s);
 }
@@ -1067,8 +1098,6 @@ ieee80211_free_node(struct ieee80211com 
                        (*ic->ic_set_tim)(ic, ni->ni_associd, 0);
        }
 #endif
-       if (RB_EMPTY(&ic->ic_tree))
-               ic->ic_inact_timer = 0;
        (*ic->ic_node_free)(ic, ni);
        /* TBD indicate to drivers that a new node can be allocated */
 }
@@ -1080,12 +1109,12 @@ ieee80211_release_node(struct ieee80211c
 
        DPRINTF(("%s refcnt %d\n", ether_sprintf(ni->ni_macaddr),
            ni->ni_refcnt));
+       s = splnet();
        if (ieee80211_node_decref(ni) == 0 &&
            ni->ni_state == IEEE80211_STA_COLLECT) {
-               s = splnet();
                ieee80211_free_node(ic, ni);
-               splx(s);
        }
+       splx(s);
 }
 
 void
@@ -1106,9 +1135,18 @@ ieee80211_free_allnodes(struct ieee80211
 
 /*
  * Timeout inactive nodes.
+ *
+ * If called because of a cache timeout, which happens only in hostap and ibss
+ * modes, clean all inactive cached nodes but don't de-auth any authenticated
+ * or associated nodes.
+ *
+ * Else, this function is called because a new node must be allocated but the
+ * node cache is full. In this case, return as soon as a free slot was made
+ * available, and if acting as hostap also allow de-authing inactive
+ * authenticated or associated stations.
  */
 void
-ieee80211_clean_nodes(struct ieee80211com *ic)
+ieee80211_clean_nodes(struct ieee80211com *ic, int cache_timeout)
 {
        struct ieee80211_node *ni, *next_ni;
        u_int gen = ic->ic_scangen++;           /* NB: ok 'cuz single-threaded*/
@@ -1118,20 +1156,34 @@ ieee80211_clean_nodes(struct ieee80211co
        for (ni = RB_MIN(ieee80211_tree, &ic->ic_tree);
            ni != NULL; ni = next_ni) {
                next_ni = RB_NEXT(ieee80211_tree, &ic->ic_tree, ni);
-               if (ic->ic_nnodes < ic->ic_max_nnodes)
+               if (!cache_timeout && ic->ic_nnodes < ic->ic_max_nnodes)
                        break;
                if (ni->ni_scangen == gen)      /* previously handled */
                        continue;
                ni->ni_scangen = gen;
                if (ni->ni_refcnt > 0)
                        continue;
+#ifndef IEEE80211_STA_ONLY
+               if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+                   ic->ic_opmode == IEEE80211_M_IBSS) &&
+                   ic->ic_state == IEEE80211_S_RUN &&
+                   ni->ni_inact < IEEE80211_INACT_MAX)
+                       continue;
+               if (cache_timeout && ni->ni_state >= IEEE80211_STA_AUTH &&
+                   ni->ni_state != IEEE80211_STA_COLLECT)
+                       continue;
+#endif
                DPRINTF(("station %s purged from LRU cache\n",
                    ether_sprintf(ni->ni_macaddr)));
                /*
-                * Send a deauthenticate frame.
+                * If we're hostap and the node is authenticated, send
+                * a deauthentication frame. The node will be freed when
+                * the driver calls ieee80211_release_node().
                 */
 #ifndef IEEE80211_STA_ONLY
-               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+                   ni->ni_state >= IEEE80211_STA_AUTH &&
+                   ni->ni_state != IEEE80211_STA_COLLECT) {
                        splx(s);
                        IEEE80211_SEND_MGMT(ic, ni,
                            IEEE80211_FC0_SUBTYPE_DEAUTH,
Index: ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.41
diff -u -p -r1.41 ieee80211_node.h
--- ieee80211_node.h    26 Mar 2009 20:38:29 -0000      1.41
+++ ieee80211_node.h    20 Jan 2012 13:03:57 -0000
@@ -38,6 +38,7 @@
 #define        IEEE80211_INACT_WAIT    5               /* inactivity timer 
interval */
 #define        IEEE80211_INACT_MAX     (300/IEEE80211_INACT_WAIT)
 #define        IEEE80211_CACHE_SIZE    100
+#define        IEEE80211_CACHE_WAIT    3600
 
 struct ieee80211_rateset {
        u_int8_t                rs_nrates;
@@ -317,7 +318,7 @@ extern      void ieee80211_free_allnodes(stru
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
 extern void ieee80211_iterate_nodes(struct ieee80211com *ic,
                ieee80211_iter_func *, void *);
-extern void ieee80211_clean_nodes(struct ieee80211com *);
+extern void ieee80211_clean_nodes(struct ieee80211com *, int);
 extern int ieee80211_setup_rates(struct ieee80211com *,
            struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
 extern  int ieee80211_iserp_sta(const struct ieee80211_node *);
Index: ieee80211_var.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.61
diff -u -p -r1.61 ieee80211_var.h
--- ieee80211_var.h     19 Jul 2010 18:53:52 -0000      1.61
+++ ieee80211_var.h     20 Jan 2012 12:58:41 -0000
@@ -269,7 +269,10 @@ struct ieee80211com {
        u_int16_t               ic_rsnsta;      /* # RSN stations */
        u_int16_t               ic_pssta;       /* # ps mode stations */
        int                     ic_mgt_timer;   /* mgmt timeout */
-       int                     ic_inact_timer; /* inactivity timer wait */
+#ifndef IEEE80211_STA_ONLY
+       struct timeout          ic_inact_timeout; /* node inactivity timeout */
+       struct timeout          ic_node_cache_timeout;
+#endif
        int                     ic_des_esslen;
        u_int8_t                ic_des_essid[IEEE80211_NWID_LEN];
        struct ieee80211_channel *ic_des_chan;  /* desired channel */

Reply via email to