summaryrefslogtreecommitdiff
path: root/sys/net80211/ieee80211_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r--sys/net80211/ieee80211_input.c105
1 files changed, 104 insertions, 1 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 5ddfa6ed618..0d2ff372cdb 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,5 +1,5 @@
/* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */
-/* $OpenBSD: ieee80211_input.c,v 1.1 2004/06/22 22:53:52 millert Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.2 2004/11/02 02:15:49 reyk Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
@@ -60,6 +60,7 @@ __KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung
#endif
#include <sys/proc.h>
#include <sys/sysctl.h>
+#include <sys/endian.h>
#ifdef __FreeBSD__
#include <machine/atomic.h>
@@ -96,8 +97,14 @@ __KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung
#include <dev/rndvar.h>
#endif
+const struct timeval ieee80211_merge_print_intvl = {
+ .tv_sec = 1,
+ .tv_usec = 0
+};
+
static void ieee80211_recv_pspoll(struct ieee80211com *,
struct mbuf *, int, u_int32_t);
+static int ieee80211_do_slow_print(struct ieee80211com *, int *);
/*
* Process a received frame. The node associated with the sender
@@ -1603,5 +1610,101 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi,
IF_ENQUEUE(&ic->ic_pwrsaveq, m);
(*ifp->if_start)(ifp);
}
+
+static int
+ieee80211_do_slow_print(struct ieee80211com *ic, int *did_print)
+{
+ if ((ic->ic_if.if_flags & IFF_LINK0) == 0)
+ return 0;
+ if (!*did_print && (ic->ic_if.if_flags & IFF_DEBUG) == 0 &&
+ !ratecheck(&ic->ic_last_merge_print, &ieee80211_merge_print_intvl))
+ return 0;
+
+ *did_print = 1;
+ return 1;
+}
+
+/* ieee80211_ibss_merge helps merge 802.11 ad hoc networks. The
+ * convention, set by the Wireless Ethernet Compatibility Alliance
+ * (WECA), is that an 802.11 station will change its BSSID to match
+ * the "oldest" 802.11 ad hoc network, on the same channel, that
+ * has the station's desired SSID. The "oldest" 802.11 network
+ * sends beacons with the greatest TSF timestamp.
+ *
+ * Return ENETRESET if the BSSID changed, 0 otherwise.
+ *
+ * XXX Perhaps we should compensate for the time that elapses
+ * between the MAC receiving the beacon and the host processing it
+ * in ieee80211_ibss_merge.
+ */
+int
+ieee80211_ibss_merge(struct ieee80211com *ic, struct ieee80211_node *ni,
+ uint64_t local_tsft)
+{
+ uint64_t beacon_tsft;
+ int did_print = 0, sign;
+ union {
+ uint64_t word;
+ uint8_t tstamp[8];
+ } u;
+
+ /* ensure alignment */
+ (void)memcpy(&u, &ni->ni_tstamp[0], sizeof(u));
+ beacon_tsft = letoh64(u.word);
+
+ /* we are faster, let the other guy catch up */
+ if (beacon_tsft < local_tsft)
+ sign = -1;
+ else
+ sign = 1;
+
+ if (memcmp(ni->ni_bssid, ic->ic_bss->ni_bssid,
+ IEEE80211_ADDR_LEN) == 0) {
+ if (!ieee80211_do_slow_print(ic, &did_print))
+ return 0;
+ printf("%s: tsft offset %s%ull\n", ic->ic_if.if_xname,
+ (sign < 0) ? "-" : "",
+ (sign < 0)
+ ? (local_tsft - beacon_tsft)
+ : (beacon_tsft - local_tsft));
+ return 0;
+ }
+
+ if (sign < 0)
+ return 0;
+
+ if (ieee80211_match_bss(ic, ni) != 0)
+ return 0;
+
+ if (ieee80211_do_slow_print(ic, &did_print)) {
+ printf("%s: ieee80211_ibss_merge: bssid mismatch %s\n",
+ ic->ic_if.if_xname, ether_sprintf(ni->ni_bssid));
+ }
+
+ ic->ic_flags &= ~IEEE80211_F_SIBSS;
+
+ /* negotiate rates with new IBSS */
+ ieee80211_fix_rate(ic, ni, IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_rates.rs_nrates == 0) {
+ if (ieee80211_do_slow_print(ic, &did_print)) {
+ printf("%s: rates mismatch, BSSID %s\n",
+ ic->ic_if.if_xname, ether_sprintf(ni->ni_bssid));
+ }
+ return 0;
+ }
+
+ if (ieee80211_do_slow_print(ic, &did_print)) {
+ printf("%s: sync BSSID %s -> ",
+ ic->ic_if.if_xname, ether_sprintf(ic->ic_bss->ni_bssid));
+ printf("%s ", ether_sprintf(ni->ni_bssid));
+ printf("(from %s)\n", ether_sprintf(ni->ni_macaddr));
+ }
+
+ ieee80211_node_newstate(ni, IEEE80211_STA_BSS);
+ (*ic->ic_node_copy)(ic, ic->ic_bss, ni);
+
+ return ENETRESET;
+}
#undef IEEE80211_VERIFY_LENGTH
#undef IEEE80211_VERIFY_ELEMENT