diff options
Diffstat (limited to 'src/drivers/driver_nl80211.c')
-rw-r--r-- | src/drivers/driver_nl80211.c | 418 |
1 files changed, 240 insertions, 178 deletions
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index f7b56b2..a1a09a5 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -167,6 +167,10 @@ struct nl80211_global { int ioctl_sock; /* socket for ioctl() use */ struct nl_handle *nl_event; +#ifdef ANDROID + int wowlan_triggers; + int wowlan_enabled; +#endif }; struct nl80211_wiphy_data { @@ -203,6 +207,10 @@ struct i802_bss { struct nl80211_wiphy_data *wiphy_data; struct dl_list wiphy_list; + +#ifdef ANDROID + int rx_filter_idx; +#endif }; struct wpa_driver_nl80211_data { @@ -285,10 +293,6 @@ struct wpa_driver_nl80211_data { int auth_wep_tx_keyidx; int auth_local_state_change; int auth_p2p; -#ifdef ANDROID - u8 wowlan_triggers; - u8 wowlan_enabled; -#endif }; @@ -312,6 +316,10 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report); static int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params); static int android_pno_stop(struct i802_bss *bss); +static int nl80211_register_rx_filter(struct i802_bss *bss, char *name, + u8 *pattern, int len, u8 *mask, + u8 action); +static int nl80211_unregister_rx_filter(struct i802_bss *bss, int filter_idx); #endif /* ANDROID */ #ifdef ANDROID_BRCM_P2P_PATCH static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, @@ -1209,13 +1217,14 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, struct nlattr *type) + struct nlattr *freq, struct nlattr *type, + enum wpa_event_type event) { union wpa_event_data data; int ht_enabled = 1; int chan_offset = 0; - wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + wpa_printf(MSG_INFO, "nl80211: Channel switch event"); if (!freq || !type) return; @@ -1238,7 +1247,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, data.ch_switch.ht_enabled = ht_enabled; data.ch_switch.ch_offset = chan_offset; - wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); + wpa_supplicant_event(drv->ctx, event, &data); } @@ -2102,6 +2111,22 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } +static void nl80211_req_ch_sw_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + if (!tb[NL80211_ATTR_WIPHY_FREQ]) + return; + + os_memset(&data, 0, sizeof(data)); + data.ch_switch.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]); + + wpa_printf(MSG_DEBUG, "nl80211: Requst to switch to %d freq", + data.ch_switch.freq); + + wpa_supplicant_event(drv->ctx, EVENT_REQ_CH_SW, &data); +} static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, int wds) @@ -2188,7 +2213,11 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv, break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ], - tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + EVENT_CH_SWITCH); + break; + case NL80211_CMD_REQ_CH_SW: + nl80211_req_ch_sw_event(drv, tb); break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], @@ -3007,6 +3036,9 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, bss = &drv->first_bss; bss->drv = drv; os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); +#ifdef ANDROID + bss->rx_filter_idx = -1; +#endif drv->monitor_ifidx = -1; drv->monitor_sock = -1; drv->eapol_tx_sock = -1; @@ -7957,6 +7989,10 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ifidx; +#ifdef ANDROID + int filter_idx; +#endif + #ifdef HOSTAPD struct i802_bss *new_bss = NULL; @@ -8017,6 +8053,26 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } #endif /* CONFIG_P2P */ +#if defined(ANDROID) && !defined(HOSTAPD) + static u8 eth_addr_mask[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + filter_idx = nl80211_register_rx_filter(bss, "unicast", + if_addr, ETH_ALEN, + eth_addr_mask, + NL80211_WOWLAN_ACTION_ALLOW); + if (filter_idx < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + + if (bss->rx_filter_idx != -1) + wpa_printf(MSG_WARNING, "nl80211: Rx filter is already " + "configured when it shouldn't be (idx=%d)", + bss->rx_filter_idx); + + bss->rx_filter_idx = filter_idx; +#endif /* ANDROID && !HOSTAPD */ + #ifdef HOSTAPD if (bridge && i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { @@ -8089,6 +8145,10 @@ static int wpa_driver_nl80211_if_remove(void *priv, nl80211_remove_iface(drv, ifindex); +#ifdef ANDROID + nl80211_unregister_rx_filter(bss, bss->rx_filter_idx); +#endif + #ifdef HOSTAPD if (type != WPA_IF_AP_BSS) return 0; @@ -8883,13 +8943,13 @@ static u8 *nl80211_rx_filter_get_pattern(struct rx_filter *filter, void *arg) } static int -nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg) +nl80211_sta_unicast_filter_get_pattern_handler(u8 *buf, int buflen, void *arg) { int ret; struct i802_bss *bss = (struct i802_bss *)arg; ret = linux_get_ifhwaddr(bss->drv->global->ioctl_sock, - bss->ifname, buf); + "wlan0", buf); if (ret) { wpa_printf(MSG_ERROR, "Failed to get own HW addr (%d)", ret); return -1; @@ -8897,14 +8957,16 @@ nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg) return 0; } -static struct rx_filter rx_filters[] = { +#define NUM_RX_FILTERS 15 + +static struct rx_filter rx_filters[NUM_RX_FILTERS] = { /* ID 0 */ - {.name = "self", + {.name = "sta_unicast", .pattern = {}, .pattern_len = 6, .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) }, .mask_len = 1, - .get_pattern_handler = nl80211_self_filter_get_pattern_handler, + .get_pattern_handler = nl80211_sta_unicast_filter_get_pattern_handler, .action = NL80211_WOWLAN_ACTION_ALLOW, }, @@ -8999,22 +9061,28 @@ static struct rx_filter rx_filters[] = { }; -#define NR_RX_FILTERS (int)(sizeof(rx_filters) / sizeof(struct rx_filter)) +#define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y)) + +static inline int nl80211_rx_filter_configured(struct rx_filter *rx_filter) +{ + return (rx_filter->name != NULL); +} static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable) { + struct nl80211_global *global = bss->drv->global; struct nl_msg *msg, *pats = NULL; struct nlattr *wowtrig, *pat; int i, ret = -1; int filters; - bss->drv->wowlan_enabled = !!enable; + global->wowlan_enabled = !!enable; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, bss->drv->global->nl80211_id, 0, + genlmsg_put(msg, 0, 0, global->nl80211_id, 0, 0, NL80211_CMD_SET_WOWLAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->drv->first_bss.ifindex); @@ -9040,13 +9108,16 @@ static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable) * so unicast traffic won't be dropped in any case. */ - filters = bss->drv->wowlan_triggers |= 1; + filters = global->wowlan_triggers |= 1; - for (i = 0; i < NR_RX_FILTERS; i++) { + for (i = 0; i < NUM_RX_FILTERS; i++) { struct rx_filter *rx_filter = &rx_filters[i]; int patnr = 1; u8 *pattern; + if (!nl80211_rx_filter_configured(rx_filter)) + continue; + if (!(filters & (1 << i))) continue; @@ -9093,20 +9164,32 @@ nla_put_failure: static int nl80211_toggle_wowlan_trigger(struct i802_bss *bss, int nr, int enabled) { - if (nr >= NR_RX_FILTERS) { - wpa_printf(MSG_ERROR, "Unknown filter: %d\n", nr); + struct nl80211_global *global = bss->drv->global; + int prev_triggers; + int ret = 0; + + if (nr >= NUM_RX_FILTERS) { + wpa_printf(MSG_ERROR, "nl80211: Invalid RX filter: %d\n", nr); return -1; } + prev_triggers = global->wowlan_triggers; + if (enabled) - bss->drv->wowlan_triggers |= 1 << nr; + global->wowlan_triggers |= 1 << nr; else - bss->drv->wowlan_triggers &= ~(1 << nr); + global->wowlan_triggers &= ~(1 << nr); - if (bss->drv->wowlan_enabled) - nl80211_set_wowlan_triggers(bss, 1); + if (global->wowlan_enabled) + ret = nl80211_set_wowlan_triggers(bss, 1); - return 0; + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set wowlan triggers " + "(%d)", ret); + global->wowlan_triggers = prev_triggers; + } + + return ret; } static int nl80211_parse_wowlan_trigger_nr(char *s) @@ -9121,60 +9204,112 @@ static int nl80211_parse_wowlan_trigger_nr(char *s) return i; } -static int nl80211_toggle_dropbcast(int enable) +/* Helper for nl80211_register_rx_filter. Don't call directly */ +static int nl80211_add_rx_filter(char *name, u8 *pattern, int len, + u8 *mask, u8 action) { - char filename[90]; - int rv; - FILE *f; + int i, j, pos; - snprintf(filename, sizeof(filename) - 1, - "/sys/bus/platform/devices/wl12xx/drop_bcast"); - f = fopen(filename, "w"); - if (!f) { - wpa_printf(MSG_DEBUG, "Could not open file %s: %s", - filename, strerror(errno)); + if (name == NULL || pattern == NULL || mask == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: " + "invalid params"); return -1; } - rv = fprintf(f, "%d", enable); - fclose(f); - if (rv < 1) { - wpa_printf(MSG_DEBUG, "Could not write to file %s: %s", - filename, strerror(errno)); + if (len > MAX_PATTERN_SIZE) { + wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: " + "Pattern too big (len=%d)", len); return -1; } - return 0; + if (action > MAX_NL80211_WOWLAN_ACTION) { + wpa_printf(MSG_ERROR, "nl80211: Add RX filter failed: " + "bad action (action=%d)", action); + return -1; + } + + for (i = 0; i < NUM_RX_FILTERS; i++) { + struct rx_filter *filter = &rx_filters[i]; + + if (filter->name) + continue; + + filter->name = name; + filter->pattern_len = len; + memcpy(filter->pattern, pattern, len); + for (j = 0; j < len; j++) + if (mask[j]) { + pos = j / 8; + filter->mask[pos] |= 1 << (j % 8); + } + + filter->mask_len = DIV_ROUND_UP(len, 8); + filter->action = action; + break; + } + + if (i == NUM_RX_FILTERS) { + wpa_printf(MSG_ERROR, "nl80211: Out of RX filters"); + return -1; + } + + return i; } -static int nl80211_dropbcast_get(char *buf, size_t buf_len) +/* Helper for nl80211_unregister_rx_filter. Don't call directly */ +static int nl80211_remove_rx_filter(int filter_idx) { - char filename[90], value[10], *pos; - int f, rv; + struct rx_filter *filter; - snprintf(filename, sizeof(filename) - 1, - "/sys/bus/platform/devices/wl12xx/drop_bcast"); - f = open(filename, O_RDONLY); - if (f < 0) { - wpa_printf(MSG_DEBUG, "Could not open file %s: %s", - filename, strerror(errno)); + if (filter_idx < 0 || filter_idx >= NUM_RX_FILTERS) { + wpa_printf(MSG_ERROR, "nl80211: Failed to remove RX filter: " + "bad filter (idx=%d)", filter_idx); return -1; } - rv = read(f, value, sizeof(value) - 1); - close(f); - if (rv < 0) { - wpa_printf(MSG_DEBUG, "Could not read file %s: %s", - filename, strerror(errno)); + filter = &rx_filters[filter_idx]; + filter->name = NULL; + memset(filter->pattern, 0, MAX_PATTERN_SIZE); + memset(filter->mask, 0, MAX_MASK_SIZE); + filter->mask_len = 0; + filter->pattern_len = 0; + filter->action = 0; + + return 0; +} + +static int nl80211_register_rx_filter(struct i802_bss *bss, char *name, + u8 *pattern, int len, u8 *mask, u8 action) +{ + int filter_idx, ret; + + filter_idx = nl80211_add_rx_filter(name, pattern, len, mask, action); + + if (filter_idx < 0) return -1; - } - value[rv] = '\0'; - pos = os_strchr(value, '\n'); - if (pos) - *pos = '\0'; + ret = nl80211_toggle_wowlan_trigger(bss, filter_idx, 1); + if (ret < 0) + goto fail; + + return filter_idx; - return snprintf(buf, buf_len, "Drop bcast = %s\n", value); +fail: + nl80211_remove_rx_filter(filter_idx); + return -1; +} + +static int nl80211_unregister_rx_filter(struct i802_bss *bss, int filter_idx) +{ + int ret; + + ret = nl80211_toggle_wowlan_trigger(bss, filter_idx, 0); + if (ret < 0) + return ret; + + ret = nl80211_remove_rx_filter(filter_idx); + + return ret; } #endif /* ANDROID */ @@ -9352,6 +9487,49 @@ static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, } +static int nl80211_ap_channel_switch(void *priv, + struct hostapd_channel_switch *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_INFO, "nl80211: Channel switch, " + "ch_switch_count = %d, tx_block = %d, " + "freq = %d, post_switch_block_tx = %d.", + params->ch_switch_count, params->tx_block, + params->freq, params->post_switch_block_tx); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_AP_CH_SWITCH); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, params->ch_switch_count); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + + /* only HT20 is supported at this point */ + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); + + if (params->tx_block) + NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); + + if (params->post_switch_block_tx) + NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_POST_BLOCK_TX); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) { + bss->freq = params->freq; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to channel switch: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + return -1; +} #ifdef CONFIG_TDLS static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, @@ -9460,65 +9638,6 @@ static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) } } - -#define WPA_PS_ENABLED 0 -#define WPA_PS_DISABLED 1 - -static int wpa_driver_set_power_save(void *priv, int state) -{ - return nl80211_set_power_save(priv, state == WPA_PS_ENABLED); -} - - -static int get_power_mode_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - int *state = arg; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[NL80211_ATTR_PS_STATE]) - return NL_SKIP; - - if (state) { - int s = (int) nla_get_u32(tb[NL80211_ATTR_PS_STATE]); - wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", s); - *state = (s == NL80211_PS_ENABLED) ? - WPA_PS_ENABLED : WPA_PS_DISABLED; - } - - return NL_SKIP; -} - - -static int wpa_driver_get_power_save(void *priv, int *state) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -1; - enum nl80211_ps_state ps_state; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_POWER_SAVE); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state); - msg = NULL; - if (ret < 0) - wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret); -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - static int android_priv_cmd(struct i802_bss *bss, const char *cmd) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -9656,19 +9775,6 @@ static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, MAC2STR(macaddr)); } else if (os_strcasecmp(cmd, "RELOAD") == 0) { wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); - } else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) { - int state = atoi(cmd + 10); - ret = wpa_driver_set_power_save(priv, state); - if (ret < 0) - wpa_driver_send_hang_msg(drv); - else - drv_errors = 0; - } else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) { - int state = -1; - ret = wpa_driver_get_power_save(priv, &state); - if (!ret && (state != -1)) - ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n", - state); } else if( os_strncasecmp(cmd, "RXFILTER-ADD ", 13) == 0 ) { int i = nl80211_parse_wowlan_trigger_nr(cmd + 13); if(i < 0) @@ -9683,51 +9789,6 @@ static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, return nl80211_set_wowlan_triggers(bss, 1); } else if (os_strcasecmp(cmd, "RXFILTER-STOP") == 0) { return nl80211_set_wowlan_triggers(bss, 0); - } else if (os_strncasecmp(cmd, "DROPBCAST", 9) == 0) { - char *value = cmd + 10; - - if (!os_strcasecmp(value, "ENABLE") || - !os_strcasecmp(value, "1")) { - ret = nl80211_toggle_dropbcast(1); - } else if (!os_strcasecmp(value, "DISABLE") || - !os_strcasecmp(value, "0")) { - ret = nl80211_toggle_dropbcast(0); - } else if (!os_strcasecmp(value, "GET") || - !os_strlen(value)) { - ret = nl80211_dropbcast_get(buf, buf_len); - } else { - wpa_printf(MSG_ERROR, - "Invalid parameter for DROPBCAST: %s", - value); - ret = -1; - } - } else if( os_strcasecmp(cmd, "LINKSPEED") == 0 ) { - struct wpa_signal_info sig; - int linkspeed; - - if (!drv->associated) - return -1; - - ret = nl80211_get_link_signal(drv, &sig); - if (ret == 0) { - linkspeed = sig.current_txrate / 1000; - ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n", - linkspeed); - } - } else if (os_strcasecmp(cmd, "RSSI") == 0 || - os_strcasecmp(cmd, "RSSI-APPROX") == 0) { - struct wpa_signal_info sig; - int rssi; - - if (!drv->associated) - return -1; - - ret = nl80211_get_link_signal(drv, &sig); - if (ret == 0) { - rssi = sig.current_signal; - ret = os_snprintf(buf, buf_len, "%s rssi %d\n", - drv->ssid, rssi); - } } else { wpa_printf(MSG_ERROR, "Unsupported command: %s", cmd); ret = -1; @@ -9894,6 +9955,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_rekey_info = nl80211_set_rekey_info, .poll_client = nl80211_poll_client, .set_p2p_powersave = nl80211_set_p2p_powersave, + .hapd_channel_switch = nl80211_ap_channel_switch, #ifdef CONFIG_TDLS .send_tdls_mgmt = nl80211_send_tdls_mgmt, .tdls_oper = nl80211_tdls_oper, |