18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OCB mode implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright: (c) 2014 Czech Technical University in Prague 68c2ecf20Sopenharmony_ci * (c) 2014 Volkswagen Group Research 78c2ecf20Sopenharmony_ci * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> 88c2ecf20Sopenharmony_ci * Funded by: Volkswagen Group Research 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 158c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 178c2ecf20Sopenharmony_ci#include <net/mac80211.h> 188c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 218c2ecf20Sopenharmony_ci#include "driver-ops.h" 228c2ecf20Sopenharmony_ci#include "rate.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ) 258c2ecf20Sopenharmony_ci#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ) 268c2ecf20Sopenharmony_ci#define IEEE80211_OCB_MAX_STA_ENTRIES 128 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/** 298c2ecf20Sopenharmony_ci * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks 308c2ecf20Sopenharmony_ci * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cienum ocb_deferred_task_flags { 358c2ecf20Sopenharmony_ci OCB_WORK_HOUSEKEEPING, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_civoid ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, 398c2ecf20Sopenharmony_ci const u8 *bssid, const u8 *addr, 408c2ecf20Sopenharmony_ci u32 supp_rates) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 438c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 448c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 458c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 468c2ecf20Sopenharmony_ci enum nl80211_bss_scan_width scan_width; 478c2ecf20Sopenharmony_ci struct sta_info *sta; 488c2ecf20Sopenharmony_ci int band; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* XXX: Consider removing the least recently used entry and 518c2ecf20Sopenharmony_ci * allow new one to be added. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { 548c2ecf20Sopenharmony_ci net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n", 558c2ecf20Sopenharmony_ci sdata->name, addr); 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci rcu_read_lock(); 628c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 638c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!chanctx_conf)) { 648c2ecf20Sopenharmony_ci rcu_read_unlock(); 658c2ecf20Sopenharmony_ci return; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci band = chanctx_conf->def.chan->band; 688c2ecf20Sopenharmony_ci scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); 698c2ecf20Sopenharmony_ci rcu_read_unlock(); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); 728c2ecf20Sopenharmony_ci if (!sta) 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Add only mandatory rates for now */ 768c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 778c2ecf20Sopenharmony_ci sta->sta.supp_rates[band] = 788c2ecf20Sopenharmony_ci ieee80211_mandatory_rates(sband, scan_width); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci spin_lock(&ifocb->incomplete_lock); 818c2ecf20Sopenharmony_ci list_add(&sta->list, &ifocb->incomplete_stations); 828c2ecf20Sopenharmony_ci spin_unlock(&ifocb->incomplete_lock); 838c2ecf20Sopenharmony_ci ieee80211_queue_work(&local->hw, &sdata->work); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) 878c2ecf20Sopenharmony_ci __acquires(RCU) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 908c2ecf20Sopenharmony_ci u8 addr[ETH_ALEN]; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci memcpy(addr, sta->sta.addr, ETH_ALEN); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", 958c2ecf20Sopenharmony_ci addr, sdata->name); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_AUTH); 988c2ecf20Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_ASSOC); 998c2ecf20Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci rate_control_rate_init(sta); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* If it fails, maybe we raced another insertion? */ 1048c2ecf20Sopenharmony_ci if (sta_info_insert_rcu(sta)) 1058c2ecf20Sopenharmony_ci return sta_info_get(sdata, addr); 1068c2ecf20Sopenharmony_ci return sta; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ocb_dbg(sdata, "Running ocb housekeeping\n"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci mod_timer(&ifocb->housekeeping_timer, 1188c2ecf20Sopenharmony_ci round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 1248c2ecf20Sopenharmony_ci struct sta_info *sta; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (ifocb->joined != true) 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci sdata_lock(sdata); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 1328c2ecf20Sopenharmony_ci while (!list_empty(&ifocb->incomplete_stations)) { 1338c2ecf20Sopenharmony_ci sta = list_first_entry(&ifocb->incomplete_stations, 1348c2ecf20Sopenharmony_ci struct sta_info, list); 1358c2ecf20Sopenharmony_ci list_del(&sta->list); 1368c2ecf20Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ieee80211_ocb_finish_sta(sta); 1398c2ecf20Sopenharmony_ci rcu_read_unlock(); 1408c2ecf20Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) 1458c2ecf20Sopenharmony_ci ieee80211_ocb_housekeeping(sdata); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci sdata_unlock(sdata); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void ieee80211_ocb_housekeeping_timer(struct timer_list *t) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 1538c2ecf20Sopenharmony_ci from_timer(sdata, t, u.ocb.housekeeping_timer); 1548c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 1558c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ieee80211_queue_work(&local->hw, &sdata->work); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_civoid ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci timer_setup(&ifocb->housekeeping_timer, 1678c2ecf20Sopenharmony_ci ieee80211_ocb_housekeeping_timer, 0); 1688c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ifocb->incomplete_stations); 1698c2ecf20Sopenharmony_ci spin_lock_init(&ifocb->incomplete_lock); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, 1738c2ecf20Sopenharmony_ci struct ocb_setup *setup) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 1768c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 1778c2ecf20Sopenharmony_ci u32 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; 1788c2ecf20Sopenharmony_ci int err; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (ifocb->joined == true) 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; 1848c2ecf20Sopenharmony_ci sdata->smps_mode = IEEE80211_SMPS_OFF; 1858c2ecf20Sopenharmony_ci sdata->needed_rx_chains = sdata->local->rx_chains; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci mutex_lock(&sdata->local->mtx); 1888c2ecf20Sopenharmony_ci err = ieee80211_vif_use_channel(sdata, &setup->chandef, 1898c2ecf20Sopenharmony_ci IEEE80211_CHANCTX_SHARED); 1908c2ecf20Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 1918c2ecf20Sopenharmony_ci if (err) 1928c2ecf20Sopenharmony_ci return err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ifocb->joined = true; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); 1998c2ecf20Sopenharmony_ci ieee80211_queue_work(&local->hw, &sdata->work); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci netif_carrier_on(sdata->dev); 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciint ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 2088c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 2098c2ecf20Sopenharmony_ci struct sta_info *sta; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ifocb->joined = false; 2128c2ecf20Sopenharmony_ci sta_info_flush(sdata); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 2158c2ecf20Sopenharmony_ci while (!list_empty(&ifocb->incomplete_stations)) { 2168c2ecf20Sopenharmony_ci sta = list_first_entry(&ifocb->incomplete_stations, 2178c2ecf20Sopenharmony_ci struct sta_info, list); 2188c2ecf20Sopenharmony_ci list_del(&sta->list); 2198c2ecf20Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci sta_info_free(local, sta); 2228c2ecf20Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci netif_carrier_off(sdata->dev); 2278c2ecf20Sopenharmony_ci clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); 2288c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_lock(&sdata->local->mtx); 2318c2ecf20Sopenharmony_ci ieee80211_vif_release_channel(sdata); 2328c2ecf20Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci skb_queue_purge(&sdata->skb_queue); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci del_timer_sync(&sdata->u.ocb.housekeeping_timer); 2378c2ecf20Sopenharmony_ci /* If the timer fired while we waited for it, it will have 2388c2ecf20Sopenharmony_ci * requeued the work. Now the work will be running again 2398c2ecf20Sopenharmony_ci * but will not rearm the timer again because it checks 2408c2ecf20Sopenharmony_ci * whether we are connected to the network or not -- at this 2418c2ecf20Sopenharmony_ci * point we shouldn't be anymore. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 246