diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 9 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 16 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 105 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 15 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_input.c | 5 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 33 |
6 files changed, 139 insertions, 44 deletions
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 425ee5f5a72..b7b2ea6d631 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.c,v 1.74 2019/05/12 18:12:38 stsp Exp $ */ +/* $OpenBSD: ieee80211_ioctl.c,v 1.75 2019/09/02 12:54:21 stsp Exp $ */ /* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */ /*- @@ -104,6 +104,7 @@ ieee80211_node2req(struct ieee80211com *ic, const struct ieee80211_node *ni, nr->nr_txseq = ni->ni_txseq; nr->nr_rxseq = ni->ni_rxseq; nr->nr_fails = ni->ni_fails; + nr->nr_assoc_fail = ni->ni_assoc_fail; /* flag values are the same */ nr->nr_inact = ni->ni_inact; nr->nr_txrate = ni->ni_txrate; nr->nr_state = ni->ni_state; @@ -821,7 +822,11 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCG80211NODE: nr = (struct ieee80211_nodereq *)data; - ni = ieee80211_find_node(ic, nr->nr_macaddr); + if (ic->ic_bss && + IEEE80211_ADDR_EQ(nr->nr_macaddr, ic->ic_bss->ni_macaddr)) + ni = ic->ic_bss; + else + ni = ieee80211_find_node(ic, nr->nr_macaddr); if (ni == NULL) { error = ENOENT; break; diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 575a573d2e2..94931282fe5 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.h,v 1.38 2019/05/12 18:12:38 stsp Exp $ */ +/* $OpenBSD: ieee80211_ioctl.h,v 1.39 2019/09/02 12:54:21 stsp Exp $ */ /* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */ /*- @@ -359,6 +359,8 @@ struct ieee80211_nodereq { /* VHT */ uint8_t nr_vht_ss; + + u_int32_t nr_assoc_fail; /* association failure reasons */ }; #define IEEE80211_NODEREQ_STATE(_s) (1 << _s) @@ -379,6 +381,18 @@ struct ieee80211_nodereq { #define SIOCS80211NODE _IOW('i', 212, struct ieee80211_nodereq) #define SIOCS80211DELNODE _IOW('i', 213, struct ieee80211_nodereq) +#define IEEE80211_NODEREQ_ASSOCFAIL_CHAN 0x01 +#define IEEE80211_NODEREQ_ASSOCFAIL_IBSS 0x02 +#define IEEE80211_NODEREQ_ASSOCFAIL_PRIVACY 0x04 +#define IEEE80211_NODEREQ_ASSOCFAIL_BASIC_RATE 0x08 +#define IEEE80211_NODEREQ_ASSOCFAIL_ESSID 0x10 +#define IEEE80211_NODEREQ_ASSOCFAIL_BSSID 0x20 +#define IEEE80211_NODEREQ_ASSOCFAIL_WPA_PROTO 0x40 +#define IEEE80211_NODEREQ_ASSOCFAIL_WPA_KEY 0x80 +#define IEEE80211_NODEREQ_ASSOCFAIL_BITS \ + "\20\1!CHAN\2!IBSS\3!PRIVACY\4!BASICRATE\5!ESSID\6!BSSID\7!WPAPROTO" \ + "\10!WPAKEY" + /* get the entire node cache */ struct ieee80211_nodereq_all { char na_ifname[IFNAMSIZ]; /* e.g. "ath0" */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 99b17a709fe..313d7249f09 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.172 2019/08/27 14:57:48 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.173 2019/09/02 12:54:21 stsp Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -523,28 +523,40 @@ ieee80211_match_ess(struct ieee80211_ess *ess, struct ieee80211_node *ni) { if (ess->esslen != 0 && (ess->esslen != ni->ni_esslen || - memcmp(ess->essid, ni->ni_essid, ess->esslen) != 0)) + memcmp(ess->essid, ni->ni_essid, ess->esslen) != 0)) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_ESSID; return 0; + } if (ess->flags & (IEEE80211_F_PSK | IEEE80211_F_RSNON)) { /* Ensure same WPA version. */ if ((ni->ni_rsnprotos & IEEE80211_PROTO_RSN) && - (ess->rsnprotos & IEEE80211_PROTO_RSN) == 0) + (ess->rsnprotos & IEEE80211_PROTO_RSN) == 0) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; return 0; + } if ((ni->ni_rsnprotos & IEEE80211_PROTO_WPA) && - (ess->rsnprotos & IEEE80211_PROTO_WPA) == 0) + (ess->rsnprotos & IEEE80211_PROTO_WPA) == 0) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; return 0; + } } else if (ess->flags & IEEE80211_F_WEPON) { - if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY; return 0; + } } else { - if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY; return 0; + } } if (ess->esslen == 0 && - (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) + (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0) { + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY; return 0; + } return 1; } @@ -780,7 +792,7 @@ ieee80211_reset_scan(struct ifnet *ifp) } /* - * Increase a node's inactitivy counter. + * Increase a node's inactivity counter. * This counter get reset to zero if a frame is received. * This function is intended for station mode only. * See ieee80211_node_cache_timeout() for hostap mode. @@ -993,48 +1005,49 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) #endif /* IEEE80211_STA_ONLY */ int -ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni, + int bgscan) { u_int8_t rate; int fail; fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) - fail |= 0x01; + fail |= IEEE80211_NODE_ASSOCFAIL_CHAN; if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && ni->ni_chan != ic->ic_des_chan) - fail |= 0x01; + fail |= IEEE80211_NODE_ASSOCFAIL_CHAN; #ifndef IEEE80211_STA_ONLY if (ic->ic_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) - fail |= 0x02; + fail |= IEEE80211_NODE_ASSOCFAIL_IBSS; } else #endif { if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) - fail |= 0x02; + fail |= IEEE80211_NODE_ASSOCFAIL_IBSS; } if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON)) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) - fail |= 0x04; + fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY; } else { if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) - fail |= 0x04; + fail |= IEEE80211_NODE_ASSOCFAIL_PRIVACY; } rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); if (rate & IEEE80211_RATE_BASIC) - fail |= 0x08; + fail |= IEEE80211_NODE_ASSOCFAIL_BASIC_RATE; if (ISSET(ic->ic_flags, IEEE80211_F_AUTO_JOIN) && ic->ic_des_esslen == 0) - fail |= 0x10; + fail |= IEEE80211_NODE_ASSOCFAIL_ESSID; if (ic->ic_des_esslen != 0 && (ni->ni_esslen != ic->ic_des_esslen || memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) - fail |= 0x10; + fail |= IEEE80211_NODE_ASSOCFAIL_ESSID; if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) - fail |= 0x20; + fail |= IEEE80211_NODE_ASSOCFAIL_BSSID; if (ic->ic_flags & IEEE80211_F_RSNON) { /* @@ -1043,66 +1056,76 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) * decline to associate with that AP. */ if ((ni->ni_rsnprotos & ic->ic_rsnprotos) == 0) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; if ((ni->ni_rsnakms & ic->ic_rsnakms) == 0) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; if ((ni->ni_rsnakms & ic->ic_rsnakms & ~(IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK)) == 0) { /* AP only supports PSK AKMPs */ if (!(ic->ic_flags & IEEE80211_F_PSK)) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; } if (ni->ni_rsngroupcipher != IEEE80211_CIPHER_WEP40 && ni->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP && ni->ni_rsngroupcipher != IEEE80211_CIPHER_CCMP && ni->ni_rsngroupcipher != IEEE80211_CIPHER_WEP104) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; /* we only support BIP as the IGTK cipher */ if ((ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC) && ni->ni_rsngroupmgmtcipher != IEEE80211_CIPHER_BIP) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; /* we do not support MFP but AP requires it */ if (!(ic->ic_caps & IEEE80211_C_MFP) && (ni->ni_rsncaps & IEEE80211_RSNCAP_MFPR)) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; /* we require MFP but AP does not support it */ if ((ic->ic_caps & IEEE80211_C_MFP) && (ic->ic_flags & IEEE80211_F_MFPR) && !(ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC)) - fail |= 0x40; + fail |= IEEE80211_NODE_ASSOCFAIL_WPA_PROTO; } if (ic->ic_if.if_flags & IFF_DEBUG) { printf("%s: %c %s%c", ic->ic_if.if_xname, fail ? '-' : '+', ether_sprintf(ni->ni_bssid), - fail & 0x20 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_BSSID ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), - fail & 0x01 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_CHAN ? '!' : ' '); printf(" %+4d", ni->ni_rssi); printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, - fail & 0x08 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_BASIC_RATE ? '!' : ' '); printf(" %4s%c", (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", - fail & 0x02 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_IBSS ? '!' : ' '); printf(" %7s%c ", (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "privacy" : "no", - fail & 0x04 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_PRIVACY ? '!' : ' '); printf(" %3s%c ", (ic->ic_flags & IEEE80211_F_RSNON) ? "rsn" : "no", - fail & 0x40 ? '!' : ' '); + fail & IEEE80211_NODE_ASSOCFAIL_WPA_PROTO ? '!' : ' '); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); - printf("%s\n", fail & 0x10 ? "!" : ""); + printf("%s\n", + fail & IEEE80211_NODE_ASSOCFAIL_ESSID ? "!" : ""); } + /* We don't care about unrelated networks during background scans. */ + if (bgscan) { + if ((fail & IEEE80211_NODE_ASSOCFAIL_ESSID) == 0) + ni->ni_assoc_fail = fail; + } else + ni->ni_assoc_fail = fail; + if ((fail & IEEE80211_NODE_ASSOCFAIL_ESSID) == 0) + ic->ic_bss->ni_assoc_fail = ni->ni_assoc_fail; + return fail; } @@ -1160,14 +1183,24 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struct ieee80211_node *selbs) { enum ieee80211_phymode mode; struct ieee80211_node *ni; + uint32_t assoc_fail = 0; /* Reinitialize media mode and channels if needed. */ mode = ieee80211_chan2mode(ic, selbs->ni_chan); if (mode != ic->ic_curmode) ieee80211_setmode(ic, mode); + /* Keep recorded association failures for this BSS/ESS intact. */ + if (IEEE80211_ADDR_EQ(ic->ic_bss->ni_macaddr, selbs->ni_macaddr) || + (ic->ic_des_esslen > 0 && ic->ic_des_esslen == selbs->ni_esslen && + memcmp(ic->ic_des_essid, selbs->ni_essid, selbs->ni_esslen) == 0)) + assoc_fail = ic->ic_bss->ni_assoc_fail; + (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); ni = ic->ic_bss; + ni->ni_assoc_fail |= assoc_fail; + + ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan); /* Make sure we send valid rates in an association request. */ if (ic->ic_opmode == IEEE80211_M_STA) @@ -1249,7 +1282,7 @@ ieee80211_node_choose_bss(struct ieee80211com *ic, int bgscan, if (curbs && ieee80211_node_cmp(ic->ic_bss, ni) == 0) *curbs = ni; - if (ieee80211_match_bss(ic, ni) != 0) + if (ieee80211_match_bss(ic, ni, bgscan) != 0) continue; if (ic->ic_caps & IEEE80211_C_SCANALLBAND) { @@ -2822,7 +2855,7 @@ ieee80211_ibss_merge(struct ieee80211com *ic, struct ieee80211_node *ni, if (sign < 0) return 0; - if (ieee80211_match_bss(ic, ni) != 0) + if (ieee80211_match_bss(ic, ni, 0) != 0) return 0; if (ieee80211_do_slow_print(ic, &did_print)) { diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index b2b80656a4b..7fada592641 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.82 2019/08/27 14:57:48 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.83 2019/09/02 12:54:21 stsp Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -353,6 +353,16 @@ struct ieee80211_node { u_int16_t ni_qos_txseqs[IEEE80211_NUM_TID]; u_int16_t ni_qos_rxseqs[IEEE80211_NUM_TID]; int ni_fails; /* failure count to associate */ + uint32_t ni_assoc_fail; /* assoc failure reasons */ +#define IEEE80211_NODE_ASSOCFAIL_CHAN 0x01 +#define IEEE80211_NODE_ASSOCFAIL_IBSS 0x02 +#define IEEE80211_NODE_ASSOCFAIL_PRIVACY 0x04 +#define IEEE80211_NODE_ASSOCFAIL_BASIC_RATE 0x08 +#define IEEE80211_NODE_ASSOCFAIL_ESSID 0x10 +#define IEEE80211_NODE_ASSOCFAIL_BSSID 0x20 +#define IEEE80211_NODE_ASSOCFAIL_WPA_PROTO 0x40 +#define IEEE80211_NODE_ASSOCFAIL_WPA_KEY 0x80 + int ni_inact; /* inactivity mark count */ int ni_txrate; /* index to ni_rates[] */ int ni_state; @@ -515,8 +525,7 @@ void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *, int); void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); -int ieee80211_match_bss(struct ieee80211com *, - struct ieee80211_node *); +int ieee80211_match_bss(struct ieee80211com *, struct ieee80211_node *, int); struct ieee80211_node *ieee80211_node_choose_bss(struct ieee80211com *, int, struct ieee80211_node **); void ieee80211_node_join_bss(struct ieee80211com *, struct ieee80211_node *); diff --git a/sys/net80211/ieee80211_pae_input.c b/sys/net80211/ieee80211_pae_input.c index ba8c1872a2d..73d028e9995 100644 --- a/sys/net80211/ieee80211_pae_input.c +++ b/sys/net80211/ieee80211_pae_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_pae_input.c,v 1.32 2018/11/02 14:40:24 stsp Exp $ */ +/* $OpenBSD: ieee80211_pae_input.c,v 1.33 2019/09/02 12:54:21 stsp Exp $ */ /*- * Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr> @@ -650,6 +650,7 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic, ether_sprintf(ni->ni_macaddr))); ni->ni_port_valid = 1; ieee80211_set_link_state(ic, LINK_STATE_UP); + ni->ni_assoc_fail = 0; } } deauth: @@ -915,6 +916,7 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic, ether_sprintf(ni->ni_macaddr))); ni->ni_port_valid = 1; ieee80211_set_link_state(ic, LINK_STATE_UP); + ni->ni_assoc_fail = 0; } } /* update the last seen value of the key replay counter field */ @@ -1019,6 +1021,7 @@ ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic, ether_sprintf(ni->ni_macaddr))); ni->ni_port_valid = 1; ieee80211_set_link_state(ic, LINK_STATE_UP); + ni->ni_assoc_fail = 0; } } /* update the last seen value of the key replay counter field */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 30c784c7b2f..d00d1b631f2 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.c,v 1.94 2019/07/29 10:50:09 stsp Exp $ */ +/* $OpenBSD: ieee80211_proto.c,v 1.95 2019/09/02 12:54:21 stsp Exp $ */ /* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ /*- @@ -939,6 +939,33 @@ ieee80211_stop_ampdu_tx(struct ieee80211com *ic, struct ieee80211_node *ni, } } +void +ieee80211_check_wpa_supplicant_failure(struct ieee80211com *ic, + struct ieee80211_node *ni) +{ + struct ieee80211_node *ni2; + + if (ic->ic_opmode != IEEE80211_M_STA +#ifndef IEEE80211_STA_ONLY + && ic->ic_opmode != IEEE80211_M_IBSS +#endif + ) + return; + + if (ni->ni_rsn_supp_state != RSNA_SUPP_PTKNEGOTIATING) + return; + + ni->ni_assoc_fail |= IEEE80211_NODE_ASSOCFAIL_WPA_KEY; + + if (ni != ic->ic_bss) + return; + + /* Also update the copy of our AP's node in the node cache. */ + ni2 = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + if (ni2) + ni2->ni_assoc_fail |= ic->ic_bss->ni_assoc_fail; +} + int ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) @@ -1037,6 +1064,7 @@ justcleanup: break; } ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; + ni->ni_assoc_fail = 0; if (ic->ic_flags & IEEE80211_F_RSNON) ieee80211_crypto_clear_groupkeys(ic); break; @@ -1097,6 +1125,8 @@ justcleanup: } break; case IEEE80211_S_AUTH: + if (ostate == IEEE80211_S_RUN) + ieee80211_check_wpa_supplicant_failure(ic, ni); ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; if (ic->ic_flags & IEEE80211_F_RSNON) ieee80211_crypto_clear_groupkeys(ic); @@ -1218,6 +1248,7 @@ justcleanup: * the link up until the port is valid. */ ieee80211_set_link_state(ic, LINK_STATE_UP); + ni->ni_assoc_fail = 0; } ic->ic_mgt_timer = 0; ieee80211_set_beacon_miss_threshold(ic); |