diff options
-rw-r--r-- | sbin/ifconfig/ifconfig.8 | 23 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.c | 104 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 50 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 21 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 346 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 16 | ||||
-rw-r--r-- | 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,12 +1643,75 @@ 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) { @@ -2195,6 +2528,17 @@ ieee80211_node_cmp(const struct ieee80211_node *b1, } /* + * 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; |