├── README.md ├── README ├── patches └── 610-drop_low_rssi.patch └── files └── hostapd.sh /README.md: -------------------------------------------------------------------------------- 1 | # lede-modhostapd 2 | 3 | Modification to hostapd / wpad that supports restricting AP clients to 4 | only those which have a dBm signal above a settable threshold. 5 | 6 | Patches apply to LEDE 17.01.4; obtain it from git.lede-project.org 7 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | lede-modhostapd 2 | 3 | Modified version of hostapd / wpad that supports restricting AP clients to 4 | only those which have a dBm signal above a settable threshold. 5 | 6 | Use with LEDE 17.01.4. (This version of LEDE includes KRACK attack 7 | countermeasures.) 8 | 9 | At a public wifi hotspot, you will often find users (STAs) trying to use 10 | the AP from the fringe area of coverage. 11 | This is not desirable for several reasons: 12 | 13 | 1 They must be served at a low data rate and excessive retries, reducing 14 | the usable bandwidth of the AP. 15 | 2 They create a "hidden node" problem since STA's far on one side of the AP 16 | cannot detect when those far on the other side are transmitting. This 17 | results in packet collisions at the AP, requiring both STAs to retry. 18 | 3 Users may be outside the intended geographic coverage, such as a cafe. This 19 | is a disadvantage to the owner who established the hotspot to encourage 20 | customers to stay inside the business. 21 | 22 | Drawbacks 2 and 3 can be addressed by reducing the transmitter power of 23 | the AP. But that also reduces the signal delivered to all desired users, 24 | reducing system performance. 25 | 26 | This modification causes hostapd to monitor the signal strength from the 27 | STA's. If they are above a (settable) threshold, operation of the AP 28 | proceeds normally. If a STA's signal is below the threshold, that STA is 29 | not allowed to connect, or dropped if it is already connected. Probe requests 30 | from weak STAs are also dropped. This may have some usefulness by limiting 31 | the AP's transmission of Probe Responses in a crowded area. 32 | 33 | This was tested with LEDE 17.01.4 on ar71xx hardware, specifically a Netgear 34 | R6100. The MediaTek driver is known not to work (no signal report on 35 | management packets). Other drivers may or may not work. 36 | 37 | BUILD: 38 | Install the LEDE buildroot. (git.lede-project.org/source.git) 39 | Check out stable version 17.01.4 (git checkout v17.01.4) 40 | Follow LEDE instructions to build a default config for your router. Be 41 | sure one of the variants of wpad is selected (usually wpad-mini or wpad, 42 | the full version supports 802.1X) 43 | Copy the files and patches from this git to the buildroot at 44 | packages/network/services/hostapd 45 | Rebuild the hostapd package: 46 | make package/hostapd/{clean,compile} 47 | Find the wpad-mini .ipk file in bin/packages//base 48 | 49 | INSTALL: 50 | At this point it is ready to install on your router. Use either your built 51 | binary or the 17.01.4 release binary from LEDE server. The official binary 52 | is preferable because it is compatible with online installation of pre-built 53 | packages from the official server. 54 | 55 | scp the just built wpad .ipk file your router's /tmp 56 | also send the hostapd-common ipk (the configuration script) 57 | ssh to the router 58 | root@LEDE:~# opkg --force-reinstall install /tmp/hostapd-common.ipk 59 | root@LEDE:~# opkg --force-reinstall install /tmp/wpad-mini.ipk 60 | root@LEDE:~# reboot 61 | A reboot seems to be necessary to get the UCI configuration system working 62 | properly. 63 | hostapd should now have restarted with the new version. Confirm you are 64 | running the mod version by checking if the set_required_signal ubus call 65 | exists: 66 | root@LEDE:~# ubus -v list | grep set_required_signal 67 | "set_required_signal":{"connect":"Integer","stay":"Integer","strikes": 68 | "Integer","poll_time":"Integer","reason":"Integer"} 69 | 70 | Important: For routers with only 4 MB flash, it will not be possible to 71 | do as shown above and remove the original wpad-mini to install a new one. 72 | A complete image must be built and flashed with the new wpad-mini in the squashfs. 73 | 74 | USAGE: 75 | The threshold signal levels and other parameters can be set through UCI 76 | in /etc/config/wireless, or through ubus calls. ubus calls do not require 77 | restarting the wifi system. 78 | 79 | Configuration is on a per AP basis. This means that one of your APs can 80 | have signal limits, while another one on the same radio can have different 81 | or no limits. 82 | 83 | The following 5 keys are defined: 84 | option signal_connect "-60" # dBm requred to connect 85 | option signal_stay "-75" # dBm required to stay connected 86 | option signal_strikes "3" # Number of polls below stay threshold before disconnection 87 | option signal_poll_time "10" # Seconds between polls (min. 3, default 10) 88 | option signal_drop_reason "3" # IEEE802.11 reason sent to the STA when it is dropped. 89 | 90 | The settings above of -60 and -75 are reasonable starting points. A dBm 91 | value that is more negative means a weaker signal. Observing the log with 92 | typical users will show if the levels should be changed for your application. 93 | Setting log_level '0' in the per-radio configuration will show more details, 94 | including the signal measurements of each connected STA at each poll. 95 | 96 | When running with a "signal_stay" above -128 dBm you should see signal reports: 97 | daemon.debug hostapd: wlan0: STA f8:e0:79:XX:XX:XX IAPP: -35 -38 (0) 98 | daemon.info hostapd: wlan0: IAPP signal poll: 1 STAs, 0 dropped 99 | The report is instantaneous signal, average signal and (number of strikes) 100 | 101 | Settable parameters are: 102 | "connect": signal (dBm) required for a received Probe, Auth Request, or 103 | Assoc Request to be acted upon. 104 | "stay" : signal (dBm) required to stay connected. 105 | "strikes" : Number of consecutive bad signal polls allowed before disconnection. 106 | Default 3. 107 | "poll_time" : Time in seconds between signal polls. Default 10. 108 | "reason" : IEEE 802.11 reason that will be transmitted to the STA upon 109 | disconnection. Default 3 (Local Choice). 5 (AP too busy) is a 110 | reasonable alternative. This does not affect the operation of 111 | hostapd at all. It may (or may not) affect what the STA does 112 | after it has been disconnected. 113 | 114 | By default, signal_connect and signal_stay are -128 dBm. This means that 115 | all connections will be accepted and no polling will be done, the same as 116 | with an unmodified hostapd. 117 | 118 | The options may be set with a ubus call. This will not disconnect all the clients 119 | like re-reading the config file does. 120 | 121 | root@LEDE:~# ubus call hostapd.wlan0 set_required_signal '{"connect":-50,"stay":-50}' 122 | 123 | Code details: 124 | This project is based mostly on extending the ubus feature which allows dynamically 125 | "banning" a STA based on MAC address. A hook from the main part of hostapd 126 | calls into the OpenWrt/LEDE-specific ubus.c module whenever a STA sends a 127 | Probe, Authentication Request, or Association Request. If the STA is not 128 | on the "banned" list, hostapd_ubus_handle_event will return 0 and hostapd 129 | will respond to the request normally. If the station is banned, the module 130 | returns -2, which causes hostapd to drop (ignore) the request. 131 | 132 | So it is straightforward to extend this logic by examining the signal level 133 | metadata accompaining the packet and either accept or deny based on that. 134 | 135 | Once allowed to connect, it is desirable to monitor the signal level of the 136 | STA and drop them if they move away from the AP. This is done by the 137 | hostapd_bss_signal_check() routine. Once a station is fully connected, 138 | hostapd offloads the handling of data packets to the kernel driver. Thus 139 | hostapd is not aware of all packets received from the STA. It is necessary 140 | to call the driver to take a snapshot of the signal level. These are the 141 | same level numbers reported by for example iw station dump. 142 | 143 | Since snapshot signal checks can be momentarilly low, signal_check has several 144 | provisions to discourage the unwarranted dropping of a STA. First if the 145 | instantaneous signal is more than 5 dB below the "average", the signal report 146 | is ignored completely. The higher of the instantaneous and average is 147 | compared to the "stay" threshold. If it is below threshold, a "strike" is 148 | recorded for that STA. When the strikeout count is reached, the STA is de- 149 | authenticated (dropped). If the signal is above threshold, the strike count 150 | is reset to 0. 151 | 152 | -------------------------------------------------------------------------------- /patches/610-drop_low_rssi.patch: -------------------------------------------------------------------------------- 1 | --- a/hostapd/config_file.c 2 | +++ b/hostapd/config_file.c 3 | @@ -2809,6 +2809,26 @@ static int hostapd_config_fill(struct ho 4 | return 1; 5 | } 6 | conf->send_probe_response = val; 7 | +#ifdef UBUS_SUPPORT 8 | + } else if (os_strcmp(buf, "signal_connect") == 0) { 9 | + bss->signal_auth_min = atoi(pos); 10 | + } else if (os_strcmp(buf, "signal_stay") == 0) { 11 | + bss->signal_stay_min = atoi(pos); 12 | + } else if (os_strcmp(buf, "signal_poll_time") == 0) { 13 | + bss->signal_poll_time = atoi(pos); 14 | + if (bss->signal_poll_time < 2) { 15 | + wpa_printf(MSG_ERROR, "Line %d: invalid signal poll time", line); 16 | + return 1; 17 | + } 18 | + } else if (os_strcmp(buf, "signal_strikes") == 0) { 19 | + bss->signal_strikes = atoi(pos); 20 | + } else if (os_strcmp(buf, "signal_drop_reason") == 0) { 21 | + bss->signal_drop_reason = atoi(pos); 22 | + if (bss->signal_drop_reason < 1 || bss->signal_drop_reason > 54) { 23 | + wpa_printf(MSG_ERROR, "Line %d: invalid signal drop reason", line); 24 | + return 1; 25 | + } 26 | +#endif /* UBUS_SUPPORT */ 27 | } else if (os_strcmp(buf, "supported_rates") == 0) { 28 | if (hostapd_parse_intlist(&conf->supported_rates, pos)) { 29 | wpa_printf(MSG_ERROR, "Line %d: invalid rate list", 30 | --- a/src/drivers/driver_nl80211.c 31 | +++ b/src/drivers/driver_nl80211.c 32 | @@ -5875,6 +5875,8 @@ static int get_sta_handler(struct nl_msg 33 | [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, 34 | [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, 35 | [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, 36 | + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, 37 | + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, 38 | }; 39 | 40 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 41 | @@ -5926,6 +5928,12 @@ static int get_sta_handler(struct nl_msg 42 | if (stats[NL80211_STA_INFO_TX_FAILED]) 43 | data->tx_retry_failed = 44 | nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); 45 | + if (stats[NL80211_STA_INFO_SIGNAL]) 46 | + data->last_rssi = 47 | + (int) (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]); 48 | + if (stats[NL80211_STA_INFO_SIGNAL_AVG]) 49 | + data->last_ack_rssi = 50 | + (int) (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]); 51 | 52 | return NL_SKIP; 53 | } 54 | --- a/src/ap/ap_config.c 55 | +++ b/src/ap/ap_config.c 56 | @@ -78,6 +78,13 @@ void hostapd_config_defaults_bss(struct 57 | bss->eapol_version = EAPOL_VERSION; 58 | 59 | bss->max_listen_interval = 65535; 60 | +#ifdef UBUS_SUPPORT 61 | + bss->signal_auth_min = -128; /* this is lower than any real signal, so all stations will be accepted */ 62 | + bss->signal_stay_min = -128; 63 | + bss->signal_strikes = 3; 64 | + bss->signal_poll_time = 10; 65 | + bss->signal_drop_reason = 3; /* "Local choice" */ 66 | +#endif /* UBUS_SUPPORT */ 67 | 68 | bss->pwd_group = 19; /* ECC: GF(p=256) */ 69 | 70 | --- a/src/ap/ap_config.h 71 | +++ b/src/ap/ap_config.h 72 | @@ -305,6 +305,12 @@ struct hostapd_bss_config { 73 | int wds_sta; 74 | int isolate; 75 | int start_disabled; 76 | + int signal_auth_min; /* Minimum signal a STA needs to authenticate */ 77 | + int signal_stay_min; /* Minimum signal needed to stay connected. */ 78 | + int signal_poll_time; /* Time in seconds between checks of connected STAs */ 79 | + int signal_strikes; /* Number of consecutive times signal can be low 80 | + before dropping the STA. */ 81 | + int signal_drop_reason; /* IEEE802.11 reason code transmitted when dropping a STA. */ 82 | 83 | int auth_algs; /* bitfield of allowed IEEE 802.11 authentication 84 | * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ 85 | --- a/src/ap/sta_info.c 86 | +++ b/src/ap/sta_info.c 87 | @@ -673,7 +673,9 @@ struct sta_info * ap_sta_add(struct host 88 | sta_track_claim_taxonomy_info(hapd->iface, addr, 89 | &sta->probe_ie_taxonomy); 90 | #endif /* CONFIG_TAXONOMY */ 91 | - 92 | +#ifdef UBUS_SUPPORT 93 | + sta->sig_drop_strikes = 0; 94 | +#endif /* UBUS_SUPPORT */ 95 | return sta; 96 | } 97 | 98 | --- a/src/ap/sta_info.h 99 | +++ b/src/ap/sta_info.h 100 | @@ -226,6 +226,9 @@ struct sta_info { 101 | u8 fils_snonce[FILS_NONCE_LEN]; 102 | u8 fils_session[FILS_SESSION_LEN]; 103 | #endif /* CONFIG_FILS */ 104 | +#ifdef UBUS_SUPPORT 105 | + int sig_drop_strikes; /* Number of times signal was below threshold. */ 106 | +#endif /* UBUS_SUPPORT */ 107 | }; 108 | 109 | 110 | --- a/src/ap/ubus.c 111 | +++ b/src/ap/ubus.c 112 | @@ -137,6 +137,58 @@ hostapd_bss_ban_client(struct hostapd_da 113 | eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd); 114 | } 115 | 116 | +static void 117 | +hostapd_bss_signal_check(void *eloop_data, void *user_ctx) 118 | +/* This is called by an eloop timeout. All stations in the list are checked 119 | + * for signal level. This requires calling the driver, since hostapd doesn't 120 | + * see packets from a station once it is fully authorized. 121 | + * Stations with signal level below the threshold will be dropped. 122 | + * Cases where the last RSSI is significantly less than the average are usually 123 | + * a bad reading and should not lead to a drop. 124 | + */ 125 | + { struct hostapd_data *hapd = user_ctx; 126 | + struct hostap_sta_driver_data data; 127 | + struct sta_info *sta, *sta_next; 128 | + u8 addr[ETH_ALEN]; // Buffer the address for logging purposes, in case it is destroyed while dropping 129 | + int strikes; // same with strike count on this station. 130 | + int num_sta = 0; 131 | + int num_drop = 0; 132 | + int signal_inst; 133 | + int signal_avg; 134 | + 135 | + 136 | + for (sta = hapd->sta_list; sta; sta = sta_next) { 137 | + sta_next = sta->next; 138 | + memcpy(addr, sta->addr, ETH_ALEN); 139 | + if (!hostapd_drv_read_sta_data(hapd, &data, addr)) { 140 | + signal_inst = data.last_rssi; 141 | + signal_avg = data.last_ack_rssi; 142 | + num_sta++; 143 | + strikes = sta->sig_drop_strikes; 144 | + if (signal_inst > signal_avg) 145 | + signal_avg = signal_inst; 146 | + if (signal_inst > (signal_avg - 5)) { // ignore unusually low instantaneous signal. 147 | + if (signal_avg < hapd->conf->signal_stay_min) { // signal bad. 148 | + strikes = ++sta->sig_drop_strikes; 149 | + if (strikes >= hapd->conf->signal_strikes) { // Struck out--, drop. 150 | + ap_sta_deauthenticate(hapd, sta, hapd->conf->signal_drop_reason); 151 | + num_drop++; 152 | + } 153 | + } 154 | + else { 155 | + sta->sig_drop_strikes = 0; // signal OK, reset the strike counter. 156 | + strikes = 0; 157 | + } 158 | + } 159 | + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "%i %i (%i)", 160 | + data.last_rssi, data.last_ack_rssi, strikes); 161 | + } 162 | + } 163 | + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_INFO, "signal poll: %i STAs, %i dropped", num_sta, num_drop); 164 | + 165 | + eloop_register_timeout(hapd->conf->signal_poll_time, 0, hostapd_bss_signal_check, eloop_data, hapd); 166 | + } 167 | + 168 | static int 169 | hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj, 170 | struct ubus_request_data *req, const char *method, 171 | @@ -416,6 +468,73 @@ hostapd_vendor_elements(struct ubus_cont 172 | return UBUS_STATUS_OK; 173 | } 174 | 175 | +enum { 176 | + SIGNAL_CONNECT, 177 | + SIGNAL_STAY, 178 | + SIGNAL_STRIKES, 179 | + SIGNAL_POLL, 180 | + SIGNAL_DROP_REASON, 181 | + __SIGNAL_SETTINGS_MAX 182 | +}; 183 | + 184 | +static const struct blobmsg_policy sig_policy[__SIGNAL_SETTINGS_MAX] = { 185 | + [SIGNAL_CONNECT] = {"connect", BLOBMSG_TYPE_INT32}, 186 | + [SIGNAL_STAY] = {"stay", BLOBMSG_TYPE_INT32}, 187 | + [SIGNAL_STRIKES] = {"strikes", BLOBMSG_TYPE_INT32}, 188 | + [SIGNAL_POLL] = {"poll_time", BLOBMSG_TYPE_INT32}, 189 | + [SIGNAL_DROP_REASON] = {"reason", BLOBMSG_TYPE_INT32} 190 | +}; 191 | + 192 | +static int 193 | +hostapd_bss_set_signal(struct ubus_context *ctx, struct ubus_object *obj, 194 | + struct ubus_request_data *req, const char *method, 195 | + struct blob_attr *msg) 196 | +{ 197 | + struct blob_attr *tb[__SIGNAL_SETTINGS_MAX]; 198 | + struct hostapd_data *hapd = get_hapd_from_object(obj); 199 | + int sig_stay; 200 | + 201 | + blobmsg_parse(sig_policy, __SIGNAL_SETTINGS_MAX, tb, blob_data(msg), blob_len(msg)); 202 | + 203 | + if (!tb[SIGNAL_CONNECT]) 204 | + return UBUS_STATUS_INVALID_ARGUMENT; 205 | + hapd->conf->signal_auth_min = blobmsg_get_u32(tb[SIGNAL_CONNECT]); 206 | + if (tb[SIGNAL_STAY]) { 207 | + sig_stay = blobmsg_get_u32(tb[SIGNAL_STAY]); 208 | + 209 | + } 210 | + else 211 | + sig_stay = hapd->conf->signal_auth_min - 5; // Default is 5 dB lower to stay. 212 | + hapd->conf->signal_stay_min = sig_stay; 213 | + if (tb[SIGNAL_STRIKES]) { 214 | + hapd->conf->signal_strikes = blobmsg_get_u32(tb[SIGNAL_STRIKES]); 215 | + if (hapd->conf->signal_strikes < 1) 216 | + return UBUS_STATUS_INVALID_ARGUMENT; 217 | + } 218 | + else 219 | + hapd->conf->signal_strikes = 3; 220 | + if (tb[SIGNAL_POLL]) { 221 | + hapd->conf->signal_poll_time = blobmsg_get_u32(tb[SIGNAL_POLL]); 222 | + if (hapd->conf->signal_poll_time < 3) 223 | + return UBUS_STATUS_INVALID_ARGUMENT; 224 | + } 225 | + else 226 | + hapd->conf->signal_poll_time = 10; 227 | + if (tb[SIGNAL_DROP_REASON]) { 228 | + hapd->conf->signal_drop_reason = blobmsg_get_u32(tb[SIGNAL_DROP_REASON]); 229 | + if ((hapd->conf->signal_drop_reason < 1) || (hapd->conf->signal_drop_reason > 35)) // XXX -- look up real limit 230 | + return UBUS_STATUS_INVALID_ARGUMENT; 231 | + } 232 | + else 233 | + hapd->conf->signal_drop_reason = 3; // Local choice. 5 (AP too busy) is also a good one. 234 | + 235 | + eloop_cancel_timeout(hostapd_bss_signal_check, ELOOP_ALL_CTX, ELOOP_ALL_CTX); 236 | + eloop_register_timeout(3, 0, hostapd_bss_signal_check, NULL, hapd); // Start up the poll timer. 237 | + 238 | + return UBUS_STATUS_OK; 239 | +} 240 | + 241 | + 242 | static const struct ubus_method bss_methods[] = { 243 | UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients), 244 | UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy), 245 | @@ -427,6 +546,7 @@ static const struct ubus_method bss_meth 246 | UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy), 247 | #endif 248 | UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy), 249 | + UBUS_METHOD("set_required_signal", hostapd_bss_set_signal, sig_policy), 250 | }; 251 | 252 | static struct ubus_object_type bss_object_type = 253 | @@ -456,6 +576,10 @@ void hostapd_ubus_add_bss(struct hostapd 254 | obj->n_methods = bss_object_type.n_methods; 255 | ret = ubus_add_object(ctx, obj); 256 | hostapd_ubus_ref_inc(); 257 | + /* This should run after the config file has been read, I hope. */ 258 | + if (hapd->conf->signal_stay_min > -128) 259 | + eloop_register_timeout(3, 0, hostapd_bss_signal_check, NULL, hapd); // Start up the poll timer. 260 | + 261 | } 262 | 263 | void hostapd_ubus_free_bss(struct hostapd_data *hapd) 264 | @@ -504,16 +628,26 @@ int hostapd_ubus_handle_event(struct hos 265 | addr = req->mgmt_frame->sa; 266 | else 267 | addr = req->addr; 268 | - 269 | + 270 | + if (req->type < ARRAY_SIZE(types)) 271 | + type = types[req->type]; 272 | + 273 | + if (req->frame_info && req->type != HOSTAPD_UBUS_PROBE_REQ) // don't clutter the log with probes. 274 | + hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_INFO, "%s request, signal %i %s", 275 | + type, req->frame_info->ssi_signal, 276 | + (req->frame_info->ssi_signal >= hapd->conf->signal_auth_min) ? "(Accepted)" : "(DENIED)"); 277 | +// reject weak signals. 278 | + if (req->frame_info && req->frame_info->ssi_signal < hapd->conf->signal_auth_min) 279 | + return -2; 280 | + 281 | +// reject banned MACs. 282 | ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl); 283 | - if (ban) 284 | - return -2; 285 | + if (ban) 286 | + return -2; 287 | 288 | if (!hapd->ubus.obj.has_subscribers) 289 | return 0; 290 | 291 | - if (req->type < ARRAY_SIZE(types)) 292 | - type = types[req->type]; 293 | 294 | blob_buf_init(&b, 0); 295 | blobmsg_add_macaddr(&b, "address", addr); 296 | -------------------------------------------------------------------------------- /files/hostapd.sh: -------------------------------------------------------------------------------- 1 | . /lib/functions/network.sh 2 | 3 | wpa_supplicant_add_rate() { 4 | local var="$1" 5 | local val="$(($2 / 1000))" 6 | local sub="$((($2 / 100) % 10))" 7 | append $var "$val" "," 8 | [ $sub -gt 0 ] && append $var "." 9 | } 10 | 11 | hostapd_add_rate() { 12 | local var="$1" 13 | local val="$(($2 / 100))" 14 | append $var "$val" " " 15 | } 16 | 17 | hostapd_append_wep_key() { 18 | local var="$1" 19 | 20 | wep_keyidx=0 21 | set_default key 1 22 | case "$key" in 23 | [1234]) 24 | for idx in 1 2 3 4; do 25 | local zidx 26 | zidx=$(($idx - 1)) 27 | json_get_var ckey "key${idx}" 28 | [ -n "$ckey" ] && \ 29 | append $var "wep_key${zidx}=$(prepare_key_wep "$ckey")" "$N$T" 30 | done 31 | wep_keyidx=$((key - 1)) 32 | ;; 33 | *) 34 | append $var "wep_key0=$(prepare_key_wep "$key")" "$N$T" 35 | ;; 36 | esac 37 | } 38 | 39 | hostapd_append_wpa_key_mgmt() { 40 | local auth_type="$(echo $auth_type | tr 'a-z' 'A-Z')" 41 | 42 | append wpa_key_mgmt "WPA-$auth_type" 43 | [ "${ieee80211r:-0}" -gt 0 ] && append wpa_key_mgmt "FT-${auth_type}" 44 | [ "${ieee80211w:-0}" -gt 0 ] && append wpa_key_mgmt "WPA-${auth_type}-SHA256" 45 | } 46 | 47 | hostapd_add_log_config() { 48 | config_add_boolean \ 49 | log_80211 \ 50 | log_8021x \ 51 | log_radius \ 52 | log_wpa \ 53 | log_driver \ 54 | log_iapp \ 55 | log_mlme 56 | 57 | config_add_int log_level 58 | } 59 | 60 | hostapd_common_add_device_config() { 61 | config_add_array basic_rate 62 | config_add_array supported_rates 63 | 64 | config_add_string country 65 | config_add_boolean country_ie doth 66 | config_add_string require_mode 67 | config_add_boolean legacy_rates 68 | 69 | hostapd_add_log_config 70 | } 71 | 72 | hostapd_prepare_device_config() { 73 | local config="$1" 74 | local driver="$2" 75 | 76 | local base="${config%%.conf}" 77 | local base_cfg= 78 | 79 | json_get_vars country country_ie beacon_int:100 doth require_mode legacy_rates 80 | 81 | hostapd_set_log_options base_cfg 82 | 83 | set_default country_ie 1 84 | set_default doth 1 85 | set_default legacy_rates 1 86 | 87 | [ "$hwmode" = "b" ] && legacy_rates=1 88 | 89 | [ -n "$country" ] && { 90 | append base_cfg "country_code=$country" "$N" 91 | 92 | [ "$country_ie" -gt 0 ] && append base_cfg "ieee80211d=1" "$N" 93 | [ "$hwmode" = "a" -a "$doth" -gt 0 ] && append base_cfg "ieee80211h=1" "$N" 94 | } 95 | 96 | local brlist= br 97 | json_get_values basic_rate_list basic_rate 98 | local rlist= r 99 | json_get_values rate_list supported_rates 100 | 101 | [ -n "$hwmode" ] && append base_cfg "hw_mode=$hwmode" "$N" 102 | [ "$legacy_rates" -eq 0 ] && set_default require_mode g 103 | 104 | [ "$hwmode" = "g" ] && { 105 | [ "$legacy_rates" -eq 0 ] && set_default rate_list "6000 9000 12000 18000 24000 36000 48000 54000" 106 | [ -n "$require_mode" ] && set_default basic_rate_list "6000 12000 24000" 107 | } 108 | 109 | case "$require_mode" in 110 | n) append base_cfg "require_ht=1" "$N";; 111 | ac) append base_cfg "require_vht=1" "$N";; 112 | esac 113 | 114 | for r in $rate_list; do 115 | hostapd_add_rate rlist "$r" 116 | done 117 | 118 | for br in $basic_rate_list; do 119 | hostapd_add_rate brlist "$br" 120 | done 121 | 122 | [ -n "$rlist" ] && append base_cfg "supported_rates=$rlist" "$N" 123 | [ -n "$brlist" ] && append base_cfg "basic_rates=$brlist" "$N" 124 | append base_cfg "beacon_int=$beacon_int" "$N" 125 | 126 | cat > "$config" <