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 */