From 020402a2d9a78338a2f43a558b13c1ceec7f9eae Mon Sep 17 00:00:00 2001 From: phessler Date: Wed, 11 Jul 2018 20:18:09 +0000 Subject: [PATCH] Introduce 'auto-join' to the wifi 802.11 stack. This allows a system to remember which ESSIDs it wants to connect to, any relevant security configuration, and switch to it when the network we are currently connected to is no longer available. Works when connecting and switching between WPA2/WPA1/WEP/clear encryptions. example hostname.if: join home wpakey password join work wpakey mekmitasdigoat join open-lounge join cafe wpakey cafe2018 join "wepnetwork" nwkey "12345" dhcp inet6 autoconf up OK stsp@ reyk@ and enthusiasm from every hackroom I've been in for the last 3 years --- sbin/ifconfig/ifconfig.8 | 23 ++- sbin/ifconfig/ifconfig.c | 104 +++++++++- sys/net80211/ieee80211_ioctl.c | 50 ++++- sys/net80211/ieee80211_ioctl.h | 21 +- sys/net80211/ieee80211_node.c | 346 ++++++++++++++++++++++++++++++++- sys/net80211/ieee80211_node.h | 16 +- sys/net80211/ieee80211_var.h | 30 ++- 7 files changed, 576 insertions(+), 14 deletions(-) diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 95622f0be9a..951b046716f 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ifconfig.8,v 1.308 2018/05/05 16:52:59 jmc Exp $ +.\" $OpenBSD: ifconfig.8,v 1.309 2018/07/11 20:18:09 phessler Exp $ .\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $ .\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $ .\" @@ -31,7 +31,7 @@ .\" .\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 .\" -.Dd $Mdocdate: May 5 2018 $ +.Dd $Mdocdate: July 11 2018 $ .Dt IFCONFIG 8 .Os .Sh NAME @@ -897,6 +897,7 @@ will begin advertising as master. .Op Oo Fl Oc Ns Cm chan Op Ar n .Op Oo Fl Oc Ns Cm nwflag Ar flag .Op Oo Fl Oc Ns Cm nwid Ar id +.Op Oo Fl Oc Ns Cm join Op Ar id .Op Oo Fl Oc Ns Cm nwkey Ar key .Op Oo Fl Oc Ns Cm powersave Op Ar duration .Op Cm scan @@ -969,6 +970,24 @@ Note that network ID is synonymous with Extended Service Set ID (ESSID). .It Cm -nwid Set the network ID to the empty string to allow the interface to connect to any available access point. +.It Cm join Op Ar id +Configure network ID. +The +.Ar id +will be used to auto-join wireless networks. +The +.Ar id +can either be any text string up to 32 characters in length, +or a series of hexadecimal digits up to 64 digits. +Any necessary +.Cm wpakey +or +.Cm nwkey +arguments should be specified on the same line. +.Pp +If no +.Ar id +is specified, show the list of currently configured auto-join networks. .It Cm nwkey Ar key Enable WEP encryption using the specified .Ar key . diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index b7413832a9a..af15dac4ae9 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ifconfig.c,v 1.367 2018/05/28 08:53:35 kn Exp $ */ +/* $OpenBSD: ifconfig.c,v 1.368 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */ /* @@ -185,6 +185,8 @@ void setifbroadaddr(const char *, int); void setifmtu(const char *, int); void setifllprio(const char *, int); void setifnwid(const char *, int); +void setifjoin(const char *, int); +void delifjoin(const char *, int); void setifbssid(const char *, int); void setifnwkey(const char *, int); void setifwpa(const char *, int); @@ -373,6 +375,8 @@ const struct cmd { { "mtu", NEXTARG, 0, setifmtu }, { "nwid", NEXTARG, 0, setifnwid }, { "-nwid", -1, 0, setifnwid }, + { "join", NEXTARG0, 0, setifjoin }, + { "-join", NEXTARG0, 0, delifjoin }, { "bssid", NEXTARG, 0, setifbssid }, { "-bssid", -1, 0, setifbssid }, { "nwkey", NEXTARG, 0, setifnwkey }, @@ -659,6 +663,9 @@ const struct afswtch { const struct afswtch *afp; /*the address family being set or asked about*/ +char joinname[IEEE80211_NWID_LEN]; +char nwidname[IEEE80211_NWID_LEN]; + int ifaliases = 0; int aflag = 0; @@ -1636,11 +1643,74 @@ setifnwid(const char *val, int d) } nwid.i_len = len; (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + (void)strlcpy(nwidname, nwid.i_nwid, sizeof(nwidname)); ifr.ifr_data = (caddr_t)&nwid; if (ioctl(s, SIOCS80211NWID, (caddr_t)&ifr) < 0) warn("SIOCS80211NWID"); } +void +setifjoin(const char *val, int d) +{ + struct ieee80211_join join; + int len; + + if (val == NULL) { + /* TODO: display the list of join'd networks */ + return; + } + + if (d != 0) { + /* no network id is especially desired */ + memset(&join, 0, sizeof(join)); + len = 0; + } else { + len = sizeof(join.i_nwid); + if (get_string(val, NULL, join.i_nwid, &len) == NULL) + return; + } + join.i_len = len; + (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + (void)strlcpy(joinname, join.i_nwid, sizeof(joinname)); + ifr.ifr_data = (caddr_t)&join; + if (ioctl(s, SIOCS80211JOIN, (caddr_t)&ifr) < 0) + warn("SIOCS80211JOIN"); +} + +void +delifjoin(const char *val, int d) +{ + struct ieee80211_join join; + int len; + + memset(&join, 0, sizeof(join)); + len = 0; + join.i_flags |= IEEE80211_JOIN_DEL; + + if (val == NULL) { + ifr.ifr_data = (caddr_t)&join; + if (ioctl(s, SIOCS80211JOIN, (caddr_t)&ifr) < 0) + warn("SIOCS80211JOIN"); + return; + } + + if (d != 0) { + /* no network id is especially desired */ + memset(&join, 0, sizeof(join)); + len = 0; + } else { + len = sizeof(join.i_nwid); + if (val != NULL && + get_string(val, NULL, join.i_nwid, &len) == NULL) + return; + } + join.i_len = len; + (void)strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (caddr_t)&join; + if (ioctl(s, SIOCS80211JOIN, (caddr_t)&ifr) < 0) + warn("SIOCS80211JOIN"); +} + void setifbssid(const char *val, int d) { @@ -1923,8 +1993,20 @@ setifwpakey(const char *val, int d) memset(&ifr, 0, sizeof(ifr)); ifr.ifr_data = (caddr_t)&nwid; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCG80211NWID, (caddr_t)&ifr)) - err(1, "SIOCG80211NWID"); + + /* Use the value specified in 'join' or 'nwid' */ + if (strlen(joinname) != 0) { + strlcpy(nwid.i_nwid, joinname, sizeof(nwid.i_nwid)); + nwid.i_len = strlen(joinname); + } else if (strlen(nwidname) != 0) { + strlcpy(nwid.i_nwid, nwidname, sizeof(nwid.i_nwid)); + nwid.i_len = strlen(nwidname); + } else { + warnx("no nwid or join command, guessing nwid to use"); + + if (ioctl(s, SIOCG80211NWID, (caddr_t)&ifr)) + err(1, "SIOCG80211NWID"); + } passlen = strlen(val); if (passlen == 2 + 2 * sizeof(psk.i_psk) && @@ -2082,9 +2164,10 @@ print_cipherset(u_int32_t cipherset) void ieee80211_status(void) { - int len, i, nwkey_verbose, inwid, inwkey, ipsk, ichan, ipwr; + int len, i, nwkey_verbose, inwid, ijoin, inwkey, ipsk, ichan, ipwr; int ibssid, iwpa; struct ieee80211_nwid nwid; + struct ieee80211_join join; struct ieee80211_nwkey nwkey; struct ieee80211_wpapsk psk; struct ieee80211_power power; @@ -2102,6 +2185,10 @@ ieee80211_status(void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); inwid = ioctl(s, SIOCG80211NWID, (caddr_t)&ifr); + ifr.ifr_data = (caddr_t)&join; + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ijoin = ioctl(s, SIOCG80211JOIN, (caddr_t)&ifr); + memset(&nwkey, 0, sizeof(nwkey)); strlcpy(nwkey.i_name, name, sizeof(nwkey.i_name)); inwkey = ioctl(s, SIOCG80211NWKEY, (caddr_t)&nwkey); @@ -2127,8 +2214,8 @@ ieee80211_status(void) iwpa = ioctl(s, SIOCG80211WPAPARMS, &wpa); /* check if any ieee80211 option is active */ - if (inwid == 0 || inwkey == 0 || ipsk == 0 || ipwr == 0 || - ichan == 0 || ibssid == 0 || iwpa == 0) + if (inwid == 0 || ijoin == 0 || inwkey == 0 || ipsk == 0 || + ipwr == 0 || ichan == 0 || ibssid == 0 || iwpa == 0) fputs("\tieee80211:", stdout); else return; @@ -2138,7 +2225,10 @@ ieee80211_status(void) len = nwid.i_len; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; - fputs(" nwid ", stdout); + if (ijoin == 0 && join.i_flags & IEEE80211_JOIN_FOUND) + fputs(" join ", stdout); + else + fputs(" nwid ", stdout); print_string(nwid.i_nwid, len); } diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index d934bb49a62..4d6b7eb1b71 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.c,v 1.60 2018/04/26 12:50:07 pirofti Exp $ */ +/* $OpenBSD: ieee80211_ioctl.c,v 1.61 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */ /*- @@ -392,6 +392,8 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ifreq *ifr = (struct ifreq *)data; int i, error = 0; struct ieee80211_nwid nwid; + struct ieee80211_join join; + struct ieee80211_ess *ess; struct ieee80211_wpapsk *psk; struct ieee80211_keyavail *ka; struct ieee80211_keyrun *kr; @@ -445,10 +447,53 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } error = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); break; + case SIOCS80211JOIN: + if ((error = suser(curproc)) != 0) + break; + if ((error = copyin(ifr->ifr_data, &join, sizeof(join))) != 0) + break; + if (join.i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + if (join.i_flags & IEEE80211_JOIN_DEL) + ieee80211_del_ess(ic, join.i_nwid, join.i_len ? 0 : 1); + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = join.i_len; + memcpy(ic->ic_des_essid, join.i_nwid, join.i_len); + /* disable WPA/WEP */ + ieee80211_disable_rsn(ic); + ieee80211_disable_wep(ic); + /* save nwid for auto-join */ + if (!(join.i_flags & IEEE80211_JOIN_DEL)) + ieee80211_add_ess(ic, ic->ic_des_essid, 0, 0); + ieee80211_set_ess(ic, ic->ic_des_essid); + error = ENETRESET; + break; + case SIOCG80211JOIN: + memset(&join, 0, sizeof(join)); + error = ENOENT; + if (ic->ic_bss == NULL) + break; + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + if (memcmp(ess->essid, ic->ic_bss->ni_essid, + IEEE80211_NWID_LEN) == 0) { + join.i_len = ic->ic_bss->ni_esslen; + memcpy(join.i_nwid, ic->ic_bss->ni_essid, + join.i_len); + join.i_flags = IEEE80211_JOIN_FOUND; + error = copyout(&join, ifr->ifr_data, + sizeof(join)); + break; + } + } + break; case SIOCS80211NWKEY: if ((error = suser(curproc)) != 0) break; error = ieee80211_ioctl_setnwkeys(ic, (void *)data); + if (error == ENETRESET) + ieee80211_add_ess(ic, ic->ic_des_essid, 0, 1); break; case SIOCG80211NWKEY: error = ieee80211_ioctl_getnwkeys(ic, (void *)data); @@ -457,6 +502,8 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if ((error = suser(curproc)) != 0) break; error = ieee80211_ioctl_setwpaparms(ic, (void *)data); + if (error == ENETRESET) + ieee80211_add_ess(ic, ic->ic_des_essid, 1, 0); break; case SIOCG80211WPAPARMS: error = ieee80211_ioctl_getwpaparms(ic, (void *)data); @@ -474,6 +521,7 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ic->ic_flags &= ~IEEE80211_F_PSK; memset(ic->ic_psk, 0, sizeof(ic->ic_psk)); } + ieee80211_add_ess(ic, ic->ic_des_essid, 1, 0); error = ENETRESET; break; case SIOCG80211WPAPSK: diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 3ed20bb0971..9ea74127b22 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.h,v 1.31 2017/10/27 12:22:40 jsg Exp $ */ +/* $OpenBSD: ieee80211_ioctl.h,v 1.32 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */ /*- @@ -275,6 +275,25 @@ struct ieee80211_keyrun { #define SIOCS80211SCAN _IOW('i', 210, struct ifreq) +#define SIOCS80211JOIN _IOWR('i', 255, struct ifreq) +#define SIOCG80211JOIN _IOWR('i', 256, struct ifreq) + +/* join is pointed at by ifr.ifr_data */ +struct ieee80211_join { + u_int8_t i_len; /* length of i_nwid */ + u_int8_t i_nwid[IEEE80211_NWID_LEN]; + u_int32_t i_flags; + + struct ieee80211_wpapsk i_wpapsk; + struct ieee80211_nwkey i_nwkey; +}; + +#define IEEE80211_JOIN_SHOW 0x01 +#define IEEE80211_JOIN_FOUND 0x02 +#define IEEE80211_JOIN_DEL 0x04 +#define IEEE80211_JOIN_NWKEY 0x08 +#define IEEE80211_JOIN_WPA 0x10 + /* node and requests */ struct ieee80211_nodereq { char nr_ifname[IFNAMSIZ]; /* e.g. "ath0" */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 3eba6e9c392..b5dbc7c083a 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.129 2018/04/28 14:49:07 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.130 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -67,6 +67,8 @@ u_int8_t ieee80211_node_getrssi(struct ieee80211com *, const struct ieee80211_node *); int ieee80211_node_checkrssi(struct ieee80211com *, const struct ieee80211_node *); +int ieee80211_ess_is_better(struct ieee80211com *ic, struct ieee80211_node *, + struct ieee80211_node *); void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *, const u_int8_t *); void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); @@ -119,6 +121,331 @@ ieee80211_node_cache_timeout(void *arg) } #endif +/* + * For debug purposes + */ +void +ieee80211_print_ess(struct ieee80211_ess *ess) +{ + ieee80211_print_essid(ess->essid, ess->esslen); + if (ess->flags & IEEE80211_F_RSNON) { + printf(" wpa"); + if (ess->rsnprotos & IEEE80211_PROTO_RSN) + printf(",wpa2"); + if (ess->rsnprotos & IEEE80211_PROTO_WPA) + printf(",wpa1"); + + if (ess->rsnakms & IEEE80211_AKM_8021X || + ess->rsnakms & IEEE80211_AKM_SHA256_8021X) + printf(",802.1x"); + printf(" "); + + if (ess->rsnciphers & IEEE80211_CIPHER_USEGROUP) + printf("usegroup"); + if (ess->rsnciphers & IEEE80211_CIPHER_WEP40) + printf("wep40"); + if (ess->rsnciphers & IEEE80211_CIPHER_WEP104) + printf("wep104"); + if (ess->rsnciphers & IEEE80211_CIPHER_TKIP) + printf("tkip"); + if (ess->rsnciphers & IEEE80211_CIPHER_CCMP) + printf("ccmp"); + } + if (ess->flags & IEEE80211_F_WEPON) { + int i = ess->def_txkey; + + printf(" wep,"); + if (ess->nw_keys[i].k_cipher & IEEE80211_CIPHER_WEP40) + printf("wep40"); + if (ess->nw_keys[i].k_cipher & IEEE80211_CIPHER_WEP104) + printf("wep104"); + } + if (ess->flags == 0) + printf(" clear"); + printf("\n"); +} + +void +ieee80211_print_ess_list(struct ieee80211com *ic) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_ess *ess; + + printf("%s: known networks\n", ifp->if_xname); + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + ieee80211_print_ess(ess); + } +} + +void +ieee80211_del_ess(struct ieee80211com *ic, char *nwid, int all) +{ + struct ieee80211_ess *ess, *next; + + TAILQ_FOREACH_SAFE(ess, &ic->ic_ess, ess_next, next) { + if (all == 1 || (memcmp(ess->essid, nwid, + IEEE80211_NWID_LEN) == 0)) { + TAILQ_REMOVE(&ic->ic_ess, ess, ess_next); + explicit_bzero(ess, sizeof(*ess)); + free(ess, M_DEVBUF, sizeof(*ess)); + if (all != 1) + return; + } + } + +} + +int +ieee80211_add_ess(struct ieee80211com *ic, char *nwid, int wpa, int wep) +{ + struct ieee80211_ess *ess; + int i = 0, new = 0, ness = 0; + + /* Don't save an empty nwid */ + if (strnlen(nwid, IEEE80211_NWID_LEN) == 0) + return (0); + + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + if (memcmp(ess->essid, nwid, IEEE80211_NWID_LEN) == 0) + break; + ness++; + } + + KASSERTMSG(wpa == 0 || wep == 0, + "%s: both wpa and wep are configured", __func__); + + if (ess == NULL) { + /* if not found, and wpa/wep are set, then return */ + if (wpa != 0 || wep != 0) { + return (ENOENT); + } + if (ness > IEEE80211_CACHE_SIZE) + return (ERANGE); + new = 1; + ess = malloc(sizeof(*ess), M_DEVBUF, M_NOWAIT|M_ZERO); + if (ess == NULL) + return (ENOMEM); + } + + memcpy(ess->essid, nwid, ic->ic_des_esslen); + ess->esslen = ic->ic_des_esslen; + + if (wpa) { + if (ic->ic_flags & (IEEE80211_F_RSNON|IEEE80211_F_PSK)) { + ess->flags = IEEE80211_F_RSNON; + if (ic->ic_flags & IEEE80211_F_PSK) + ess->flags |= IEEE80211_F_PSK; + explicit_bzero(ess->psk, sizeof(ess->psk)); + memcpy(ess->psk, ic->ic_psk, IEEE80211_PMK_LEN); + ess->rsnprotos = ic->ic_rsnprotos; + ess->rsnakms = ic->ic_rsnakms; + ess->rsngroupcipher = ic->ic_rsngroupcipher; + ess->rsnciphers = ic->ic_rsnciphers; + + /* Disable WEP */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + explicit_bzero(&ess->nw_keys[i], + sizeof(ess->nw_keys[0])); + } + ess->def_txkey = 0; + ess->flags &= ~IEEE80211_F_WEPON; + } else { + /* Disable WPA */ + ess->rsnprotos = ess->rsnakms = + ess->rsngroupcipher = ess->rsnciphers = 0; + explicit_bzero(ess->psk, sizeof(ess->psk)); + ess->flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON); + } + } else if (wep) { + if (ic->ic_flags & IEEE80211_F_WEPON) { + struct ieee80211_key *k; + int i; + + ess->flags = IEEE80211_F_WEPON; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + memcpy(&ess->nw_keys[i], &ic->ic_nw_keys[i], + sizeof(struct ieee80211_key)); + k = &ic->ic_nw_keys[i]; + k->k_priv = NULL; + } + ess->def_txkey = ic->ic_def_txkey; + + /* Disable WPA */ + ess->rsnprotos = ess->rsnakms = + ess->rsngroupcipher = ess->rsnciphers = 0; + explicit_bzero(ess->psk, sizeof(ess->psk)); + ess->flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON); + } else { + /* Disable WEP */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + explicit_bzero(&ess->nw_keys[i], + sizeof(ess->nw_keys[0])); + } + ess->def_txkey = 0; + ess->flags &= ~IEEE80211_F_WEPON; + } + } + + if (new) + TAILQ_INSERT_TAIL(&ic->ic_ess, ess, ess_next); + + return (0); +} + +int +ieee80211_ess_is_better(struct ieee80211com *ic, struct ieee80211_node *nicur, + struct ieee80211_node *selni) +{ + uint8_t min_5ghz_rssi; + + if (ic->ic_max_rssi) + min_5ghz_rssi = IEEE80211_RSSI_THRES_RATIO_5GHZ; + else + min_5ghz_rssi = (uint8_t)IEEE80211_RSSI_THRES_5GHZ; + + if (selni == NULL) + return 1; + + /* First 5GHz with acceptable signal */ + if ((IEEE80211_IS_CHAN_5GHZ(nicur->ni_chan) && + !IEEE80211_IS_CHAN_5GHZ(selni->ni_chan)) && + nicur->ni_rssi > min_5ghz_rssi) + return 1; + + /* Prefer 5GHz N over not-N */ + if ((IEEE80211_IS_CHAN_5GHZ(nicur->ni_chan) && + nicur->ni_rssi > min_5ghz_rssi) && + ieee80211_node_supports_ht(nicur)) + return 1; + + /* Settle for just N */ + if (ieee80211_node_supports_ht(nicur) && + !ieee80211_node_supports_ht(selni)) + return 1; + + /* Last resort of best rssi */ + if (nicur->ni_rssi > selni->ni_rssi) + return 1; + + return 0; +} + +int +ieee80211_match_ess(struct ieee80211com *ic) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_ess *ess, *seless = NULL; + struct ieee80211_node *ni, *selni = NULL; + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + return (0); + + /* + * Apple's iOS uses the algorithm described at + * https://support.apple.com/en-us/HT202831 + * + * basically: + * last network recently joined + * then strongest security + * then rssi level + */ + + /* skip if it's the same network */ + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + if (LINK_STATE_IS_UP(ifp->if_link_state) && + ess->esslen == ic->ic_des_esslen && + (memcmp(ic->ic_des_essid, ess->essid, + IEEE80211_NWID_LEN) == 0)) { + if (ifp->if_flags & IFF_DEBUG) { + printf(" %s: staying on ", + ifp->if_xname); + ieee80211_print_essid(ess->essid, ess->esslen); + printf("\n"); + } + return (0); + } + } + + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + RBT_FOREACH(ni, ieee80211_tree, &ic->ic_tree) { + if (memcmp(ess->essid, ni->ni_essid, + IEEE80211_NWID_LEN) != 0 || + ni->ni_fails != 0) + continue; + + if (selni == NULL || + ieee80211_ess_is_better(ic, ni, selni) > 1) { + seless = ess; + selni = ni; + } + } + } + + if (seless && !(seless->esslen == ic->ic_des_esslen && + (memcmp(ic->ic_des_essid, seless->essid, + IEEE80211_NWID_LEN) == 0))) { + ieee80211_set_ess(ic, seless->essid); + return (1); + } + + return (0); +} +void +ieee80211_set_ess(struct ieee80211com *ic, char *nwid) +{ + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_ess *ess; + + TAILQ_FOREACH(ess, &ic->ic_ess, ess_next) { + if (memcmp(ess->essid, nwid, IEEE80211_NWID_LEN) == 0) + break; + } + + if (ess == NULL) + return; + + memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); + ic->ic_des_esslen = ess->esslen; + memcpy(ic->ic_des_essid, ess->essid, ic->ic_des_esslen); + + ieee80211_disable_wep(ic); + ieee80211_disable_rsn(ic); + if (ess->flags & IEEE80211_F_RSNON) { + explicit_bzero(ic->ic_psk, sizeof(ic->ic_psk)); + memcpy(ic->ic_psk, ess->psk, sizeof(ic->ic_psk)); + + ic->ic_rsnprotos = ess->rsnprotos; + ic->ic_rsnakms = ess->rsnakms; + ic->ic_rsngroupcipher = ess->rsngroupcipher; + ic->ic_rsnciphers = ess->rsnciphers; + ic->ic_flags |= IEEE80211_F_RSNON; + if (ess->flags & IEEE80211_F_PSK) + ic->ic_flags |= IEEE80211_F_PSK; + } else if (ess->flags & IEEE80211_F_WEPON) { + struct ieee80211_key *k; + int i; + + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + k = &ic->ic_nw_keys[i]; + if (k->k_cipher != IEEE80211_CIPHER_NONE) + (*ic->ic_delete_key)(ic, NULL, k); + memcpy(&ic->ic_nw_keys[i], &ess->nw_keys[i], + sizeof(struct ieee80211_key)); + (*ic->ic_set_key)(ic, NULL, k); + } + ic->ic_def_txkey = ess->def_txkey; + ic->ic_flags |= IEEE80211_F_WEPON; + } + + /* join the (new) ess station */ + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + ieee80211_media_change(ifp); + } + + return; +} + void ieee80211_node_attach(struct ifnet *ifp) { @@ -165,6 +492,7 @@ ieee80211_node_attach(struct ifnet *ifp) ieee80211_node_cache_timeout, ic); } #endif + TAILQ_INIT(&ic->ic_ess); } struct ieee80211_node * @@ -205,6 +533,7 @@ ieee80211_node_detach(struct ifnet *ifp) (*ic->ic_node_free)(ic, ic->ic_bss); ic->ic_bss = NULL; } + ieee80211_del_ess(ic, NULL, 1); ieee80211_free_allnodes(ic, 1); #ifndef IEEE80211_STA_ONLY free(ic->ic_aid_bitmap, M_DEVBUF, @@ -740,6 +1069,10 @@ ieee80211_end_scan(struct ifnet *ifp) return; } + /* Possibly switch which ssid we are associated with */ + if (!bgscan) + ieee80211_match_ess(ic); + for (; ni != NULL; ni = nextbs) { nextbs = RBT_NEXT(ieee80211_tree, ni); if (ni->ni_fails) { @@ -2194,7 +2527,18 @@ ieee80211_node_cmp(const struct ieee80211_node *b1, return (memcmp(b1->ni_macaddr, b2->ni_macaddr, IEEE80211_ADDR_LEN)); } +/* + * Compare nodes in the tree by essid + */ +int +ieee80211_ess_cmp(const struct ieee80211_ess_rbt *b1, + const struct ieee80211_ess_rbt *b2) +{ + return (memcmp(b1->essid, b2->essid, IEEE80211_NWID_LEN)); +} + /* * Generate red-black tree function logic */ RBT_GENERATE(ieee80211_tree, ieee80211_node, ni_node, ieee80211_node_cmp); +RBT_GENERATE(ieee80211_ess_tree, ieee80211_ess_rbt, ess_rbt, ieee80211_ess_cmp); diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 5a414fef8b7..882df583190 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.74 2018/04/28 14:49:07 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.75 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -307,6 +307,17 @@ struct ieee80211_node { RBT_HEAD(ieee80211_tree, ieee80211_node); +struct ieee80211_ess_rbt { + RBT_ENTRY(ieee80211_ess_rbt) ess_rbt; + u_int8_t esslen; + u_int8_t essid[IEEE80211_NWID_LEN]; + struct ieee80211_node *ni2; + struct ieee80211_node *ni5; + struct ieee80211_node *ni; +}; + +RBT_HEAD(ieee80211_ess_tree, ieee80211_ess_rbt); + static inline void ieee80211_node_incref(struct ieee80211_node *ni) { @@ -412,6 +423,9 @@ void ieee80211_set_tim(struct ieee80211com *, int, int); int ieee80211_node_cmp(const struct ieee80211_node *, const struct ieee80211_node *); +int ieee80211_ess_cmp(const struct ieee80211_ess_rbt *, + const struct ieee80211_ess_rbt *); RBT_PROTOTYPE(ieee80211_tree, ieee80211_node, ni_node, ieee80211_node_cmp); +RBT_PROTOTYPE(ieee80211_ess_tree, ieee80211_ess_rbt, ess_rbt, ieee80211_ess_cmp); #endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 63bedcef508..49143878e69 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_var.h,v 1.85 2018/04/26 12:50:07 pirofti Exp $ */ +/* $OpenBSD: ieee80211_var.h,v 1.86 2018/07/11 20:18:09 phessler Exp $ */ /* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */ /*- @@ -334,6 +334,7 @@ struct ieee80211com { u_int8_t ic_aselcaps; u_int8_t ic_dialog_token; int ic_fixed_mcs; + TAILQ_HEAD(, ieee80211_ess) ic_ess; }; #define ic_if ic_ac.ac_if #define ic_softc ic_if.if_softc @@ -341,6 +342,30 @@ struct ieee80211com { LIST_HEAD(ieee80211com_head, ieee80211com); extern struct ieee80211com_head ieee80211com_head; +/* list of APs we want to automatically use */ +/* all data is copied from struct ieee80211com */ +struct ieee80211_ess { + /* nwid */ + int esslen; + u_int8_t essid[IEEE80211_NWID_LEN]; + + /* clear/wep/wpa */ + u_int32_t flags; + + /* nwkey */ + struct ieee80211_key nw_keys[IEEE80211_GROUP_NKID]; + int def_txkey; + + /* wpakey */ + u_int8_t psk[IEEE80211_PMK_LEN]; + u_int rsnprotos; + u_int rsnakms; + u_int rsnciphers; + enum ieee80211_cipher rsngroupcipher; + + TAILQ_ENTRY(ieee80211_ess) ess_next; +}; + #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) @@ -426,6 +451,9 @@ enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, const struct ieee80211_channel *); void ieee80211_disable_wep(struct ieee80211com *); void ieee80211_disable_rsn(struct ieee80211com *); +int ieee80211_add_ess(struct ieee80211com *, char *, int, int); +void ieee80211_del_ess(struct ieee80211com *, char *, int); +void ieee80211_set_ess(struct ieee80211com *, char *); extern int ieee80211_cache_size; -- 2.20.1