diff options
Diffstat (limited to 'sys/net80211/ieee80211.c')
-rw-r--r-- | sys/net80211/ieee80211.c | 254 |
1 files changed, 231 insertions, 23 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index db9b4d7d610..20af2f2d2ea 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211.c,v 1.3 2004/11/03 20:42:41 claudio Exp $ */ +/* $OpenBSD: ieee80211.c,v 1.4 2005/02/17 18:28:05 reyk Exp $ */ /* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */ /*- @@ -107,13 +107,29 @@ SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug, #endif #endif -int ieee80211_inact_max = IEEE80211_INACT_MAX; +int ieee80211_cache_size = IEEE80211_CACHE_SIZE; #ifdef __NetBSD__ -static int ieee80211_inact_max_nodenum; +static int ieee80211_cache_size_nodenum; #endif +struct ieee80211com_head ieee80211com_head = + LIST_HEAD_INITIALIZER(ieee80211com_head); + static void ieee80211_setbasicrates(struct ieee80211com *); +#ifdef __NetBSD__ +static void sysctl_ieee80211_fill_node(struct ieee80211_node *, + struct ieee80211_node_sysctl *, int, struct ieee80211_channel *, int); +static struct ieee80211_node *ieee80211_node_walknext( + struct ieee80211_node_walk *); +static struct ieee80211_node *ieee80211_node_walkfirst( + struct ieee80211_node_walk *, u_short); +static int sysctl_ieee80211_verify(SYSCTLFN_ARGS); +static int sysctl_ieee80211_node(SYSCTLFN_ARGS); +#endif /* __NetBSD__ */ + +#define LOGICALLY_EQUAL(x, y) (!(x) == !(y)) + #if 0 static const char *ieee80211_phymode_name[] = { "auto", /* IEEE80211_MODE_AUTO */ @@ -193,6 +209,7 @@ ieee80211_ifattach(struct ifnet *ifp) ic->ic_lintval = 100; /* default sleep */ ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ + LIST_INSERT_HEAD(&ieee80211com_head, ic, ic_list); ieee80211_node_attach(ifp); ieee80211_proto_attach(ifp); } @@ -205,6 +222,7 @@ ieee80211_ifdetach(struct ifnet *ifp) ieee80211_proto_detach(ifp); ieee80211_crypto_detach(ifp); ieee80211_node_detach(ifp); + LIST_REMOVE(ic, ic_list); #ifdef __FreeBSD__ ifmedia_removeall(&ic->ic_media); #else @@ -395,9 +413,6 @@ ieee80211_media_init(struct ifnet *ifp, if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); - if (ic->ic_max_aid == 0) - ic->ic_max_aid = IEEE80211_MAX_AID; - #undef ADD } @@ -631,10 +646,8 @@ ieee80211_watchdog(struct ifnet *ifp) if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) - ieee80211_timeout_nodes(ic); - if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0) + if (ic->ic_mgt_timer != 0) ifp->if_timer = 1; } @@ -765,9 +778,15 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) * * XXX what if we have stations already associated??? * XXX probably not right for autoselect? + * + * Short preamble is not interoperable with legacy .11b + * equipment, so it should not be the default for b or + * mixed b/g networks. -dcy */ +#if 0 if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) ic->ic_flags |= IEEE80211_F_SHPREAMBLE; +#endif if (mode == IEEE80211_MODE_11G) { if (ic->ic_caps & IEEE80211_C_SHSLOT) ic->ic_flags |= IEEE80211_F_SHSLOT; @@ -926,6 +945,16 @@ ieee80211_media2rate(int mword) } #ifdef __NetBSD__ +static void +ieee80211_clean_all_nodes(int cache_size) +{ + struct ieee80211com *ic; + LIST_FOREACH(ic, &ieee80211com_head, ic_list) { + ic->ic_max_nnodes = cache_size; + ieee80211_clean_nodes(ic); + } +} + /* TBD factor with sysctl_ath_verify. */ static int sysctl_ieee80211_verify(SYSCTLFN_ARGS) @@ -940,19 +969,14 @@ sysctl_ieee80211_verify(SYSCTLFN_ARGS) if (error || newp == NULL) return (error); - IEEE80211_DPRINTF(("%s: t = %d, nodenum = %d, rnodenum = %d\n", - __func__, t, node.sysctl_num, rnode->sysctl_num)); - - if (node.sysctl_num == ieee80211_inact_max_nodenum) { - if (t < 1) + if (node.sysctl_num == ieee80211_cache_size_nodenum) { + if (t < 0) return (EINVAL); - t = roundup(t, IEEE80211_INACT_WAIT) / IEEE80211_INACT_WAIT; #ifdef IEEE80211_DEBUG - } else if (node.sysctl_num == ieee80211_debug_nodenum) { - if (t < 0 || t > 2) - return (EINVAL); -#endif /* IEEE80211_DEBUG */ + } else if (node.sysctl_num != ieee80211_debug_nodenum) +#else /* IEEE80211_DEBUG */ } else +#endif /* IEEE80211_DEBUG */ return (EINVAL); *(int*)rnode->sysctl_data = t; @@ -961,6 +985,185 @@ sysctl_ieee80211_verify(SYSCTLFN_ARGS) } /* + * Pointers for testing: + * + * If there are no interfaces, or else no 802.11 interfaces, + * ieee80211_node_walkfirst must return NULL. + * + * If there is any single 802.11 interface, ieee80211_node_walkfirst + * must not return NULL. + */ +static struct ieee80211_node * +ieee80211_node_walkfirst(struct ieee80211_node_walk *nw, + u_short if_index) +{ + struct ieee80211com *ic; + (void)memset(nw, 0, sizeof(*nw)); + + nw->nw_ifindex = if_index; + + LIST_FOREACH(ic, &ieee80211com_head, ic_list) { + if (if_index != 0 && ic->ic_if.if_index != if_index) + continue; + nw->nw_ic = ic; + nw->nw_ni = ic->ic_bss; + break; + } + + KASSERT(LOGICALLY_EQUAL(nw->nw_ni == NULL, nw->nw_ic == NULL)); + + return nw->nw_ni; +} + +static struct ieee80211_node * +ieee80211_node_walknext(struct ieee80211_node_walk *nw) +{ + KASSERT(LOGICALLY_EQUAL(nw->nw_ni == NULL, nw->nw_ic == NULL)); + + if (nw->nw_ic == NULL && nw->nw_ni == NULL) + return NULL; + + if (nw->nw_ni == nw->nw_ic->ic_bss) + nw->nw_ni = TAILQ_FIRST(&nw->nw_ic->ic_node); + else + nw->nw_ni = TAILQ_NEXT(nw->nw_ni, ni_list); + + if (nw->nw_ni == NULL) { + if (nw->nw_ifindex != 0) + return NULL; + + nw->nw_ic = LIST_NEXT(nw->nw_ic, ic_list); + if (nw->nw_ic == NULL) + return NULL; + + nw->nw_ni = nw->nw_ic->ic_bss; + } + + KASSERT(LOGICALLY_EQUAL(nw->nw_ni == NULL, nw->nw_ic == NULL)); + + return nw->nw_ni; +} + +static void +sysctl_ieee80211_fill_node(struct ieee80211_node *ni, + struct ieee80211_node_sysctl *ns, int ifindex, + struct ieee80211_channel *chan0, int is_bss) +{ + ns->ns_ifindex = ifindex; + ns->ns_capinfo = ni->ni_capinfo; + ns->ns_flags = (is_bss) ? IEEE80211_NODE_SYSCTL_F_BSS : 0; + (void)memcpy(ns->ns_macaddr, ni->ni_macaddr, sizeof(ns->ns_macaddr)); + (void)memcpy(ns->ns_bssid, ni->ni_bssid, sizeof(ns->ns_bssid)); + if (ni->ni_chan != IEEE80211_CHAN_ANYC) { + ns->ns_freq = ni->ni_chan->ic_freq; + ns->ns_chanflags = ni->ni_chan->ic_flags; + ns->ns_chanidx = ni->ni_chan - chan0; + } else { + ns->ns_freq = ns->ns_chanflags = 0; + ns->ns_chanidx = 0; + } + ns->ns_rssi = ni->ni_rssi; + ns->ns_esslen = ni->ni_esslen; + (void)memcpy(ns->ns_essid, ni->ni_essid, sizeof(ns->ns_essid)); + ns->ns_pwrsave = ni->ni_pwrsave; + ns->ns_erp = ni->ni_erp; + ns->ns_associd = ni->ni_associd; + ns->ns_inact = ni->ni_inact * IEEE80211_INACT_WAIT; + ns->ns_rstamp = ni->ni_rstamp; + ns->ns_rates = ni->ni_rates; + ns->ns_txrate = ni->ni_txrate; + ns->ns_intval = ni->ni_intval; + (void)memcpy(ns->ns_tstamp, ni->ni_tstamp, sizeof(ns->ns_tstamp)); + ns->ns_txseq = ni->ni_txseq; + ns->ns_rxseq = ni->ni_rxseq; + ns->ns_fhdwell = ni->ni_fhdwell; + ns->ns_fhindex = ni->ni_fhindex; + ns->ns_fails = ni->ni_fails; +} + +/* Between two examinations of the sysctl tree, I expect each + * interface to add no more than 5 nodes. + */ +#define IEEE80211_SYSCTL_NODE_GROWTH 5 + +static int +sysctl_ieee80211_node(SYSCTLFN_ARGS) +{ + struct ieee80211_node_walk nw; + struct ieee80211_node *ni; + struct ieee80211_node_sysctl ns; + char *dp; + u_int cur_ifindex, ifcount, ifindex, last_ifindex, op, arg, hdr_type; + size_t len, needed, eltsize, out_size; + int error, s, nelt; + + if (namelen == 1 && name[0] == CTL_QUERY) + return (sysctl_query(SYSCTLFN_CALL(rnode))); + + if (namelen != IEEE80211_SYSCTL_NODENAMELEN) + return (EINVAL); + + /* ifindex.op.arg.header-type.eltsize.nelt */ + dp = oldp; + len = (oldp != NULL) ? *oldlenp : 0; + ifindex = name[IEEE80211_SYSCTL_NODENAME_IF]; + op = name[IEEE80211_SYSCTL_NODENAME_OP]; + arg = name[IEEE80211_SYSCTL_NODENAME_ARG]; + hdr_type = name[IEEE80211_SYSCTL_NODENAME_TYPE]; + eltsize = name[IEEE80211_SYSCTL_NODENAME_ELTSIZE]; + nelt = name[IEEE80211_SYSCTL_NODENAME_ELTCOUNT]; + out_size = MIN(sizeof(ns), eltsize); + + if (op != IEEE80211_SYSCTL_OP_ALL || arg != 0 || + hdr_type != IEEE80211_SYSCTL_T_NODE || eltsize < 1 || nelt < 0) + return (EINVAL); + + error = 0; + needed = 0; + ifcount = 0; + last_ifindex = 0; + + s = splnet(); + + for (ni = ieee80211_node_walkfirst(&nw, ifindex); ni != NULL; + ni = ieee80211_node_walknext(&nw)) { + struct ieee80211com *ic; + + ic = nw.nw_ic; + cur_ifindex = ic->ic_if.if_index; + + if (cur_ifindex != last_ifindex) { + ifcount++; + last_ifindex = cur_ifindex; + } + + if (nelt <= 0) + continue; + + if (len >= eltsize) { + sysctl_ieee80211_fill_node(ni, &ns, cur_ifindex, + &ic->ic_channels[0], ni == ic->ic_bss); + error = copyout(&ns, dp, out_size); + if (error) + goto cleanup; + dp += eltsize; + len -= eltsize; + } + needed += eltsize; + if (nelt != INT_MAX) + nelt--; + } +cleanup: + splx(s); + + *oldlenp = needed; + if (oldp == NULL) + *oldlenp += ifcount * IEEE80211_SYSCTL_NODE_GROWTH * eltsize; + + return (error); +} + +/* * Setup sysctl(3) MIB, net.ieee80211.* * * TBD condition CTLFLAG_PERMANENT on being an LKM or not @@ -987,6 +1190,11 @@ SYSCTL_SETUP(sysctl_ieee80211, "sysctl ieee80211 subtree setup") NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) goto err; + if ((rc = sysctl_createv(clog, 0, &rnode, NULL, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "nodes", "client/peer stations", + sysctl_ieee80211_node, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + #ifdef IEEE80211_DEBUG /* control debugging printfs */ @@ -1001,15 +1209,15 @@ SYSCTL_SETUP(sysctl_ieee80211, "sysctl ieee80211 subtree setup") #endif /* IEEE80211_DEBUG */ - /* control inactivity timer */ + /* control LRU cache size */ if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, - "maxinact", SYSCTL_DESCR("Station inactivity timeout"), - sysctl_ieee80211_verify, 0, &ieee80211_inact_max, + "maxnodecache", SYSCTL_DESCR("Maximum station cache size"), + sysctl_ieee80211_verify, 0, &ieee80211_cache_size, 0, CTL_CREATE, CTL_EOL)) != 0) goto err; - ieee80211_inact_max_nodenum = cnode->sysctl_num; + ieee80211_cache_size_nodenum = cnode->sysctl_num; return; err: |