162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OCB mode implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright: (c) 2014 Czech Technical University in Prague 662306a36Sopenharmony_ci * (c) 2014 Volkswagen Group Research 762306a36Sopenharmony_ci * Copyright (C) 2022 - 2023 Intel Corporation 862306a36Sopenharmony_ci * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> 962306a36Sopenharmony_ci * Funded by: Volkswagen Group Research 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/if_ether.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/if_arp.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <net/mac80211.h> 1962306a36Sopenharmony_ci#include <asm/unaligned.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "ieee80211_i.h" 2262306a36Sopenharmony_ci#include "driver-ops.h" 2362306a36Sopenharmony_ci#include "rate.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ) 2662306a36Sopenharmony_ci#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ) 2762306a36Sopenharmony_ci#define IEEE80211_OCB_MAX_STA_ENTRIES 128 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks 3162306a36Sopenharmony_ci * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cienum ocb_deferred_task_flags { 3662306a36Sopenharmony_ci OCB_WORK_HOUSEKEEPING, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, 4062306a36Sopenharmony_ci const u8 *bssid, const u8 *addr, 4162306a36Sopenharmony_ci u32 supp_rates) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 4462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 4562306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 4662306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 4762306a36Sopenharmony_ci enum nl80211_bss_scan_width scan_width; 4862306a36Sopenharmony_ci struct sta_info *sta; 4962306a36Sopenharmony_ci int band; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* XXX: Consider removing the least recently used entry and 5262306a36Sopenharmony_ci * allow new one to be added. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { 5562306a36Sopenharmony_ci net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n", 5662306a36Sopenharmony_ci sdata->name, addr); 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci rcu_read_lock(); 6362306a36Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); 6462306a36Sopenharmony_ci if (WARN_ON_ONCE(!chanctx_conf)) { 6562306a36Sopenharmony_ci rcu_read_unlock(); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci band = chanctx_conf->def.chan->band; 6962306a36Sopenharmony_ci scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); 7062306a36Sopenharmony_ci rcu_read_unlock(); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); 7362306a36Sopenharmony_ci if (!sta) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Add only mandatory rates for now */ 7762306a36Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 7862306a36Sopenharmony_ci sta->sta.deflink.supp_rates[band] = 7962306a36Sopenharmony_ci ieee80211_mandatory_rates(sband, scan_width); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci spin_lock(&ifocb->incomplete_lock); 8262306a36Sopenharmony_ci list_add(&sta->list, &ifocb->incomplete_stations); 8362306a36Sopenharmony_ci spin_unlock(&ifocb->incomplete_lock); 8462306a36Sopenharmony_ci wiphy_work_queue(local->hw.wiphy, &sdata->work); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) 8862306a36Sopenharmony_ci __acquires(RCU) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 9162306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci memcpy(addr, sta->sta.addr, ETH_ALEN); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", 9662306a36Sopenharmony_ci addr, sdata->name); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_AUTH); 9962306a36Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_ASSOC); 10062306a36Sopenharmony_ci sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci rate_control_rate_init(sta); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* If it fails, maybe we raced another insertion? */ 10562306a36Sopenharmony_ci if (sta_info_insert_rcu(sta)) 10662306a36Sopenharmony_ci return sta_info_get(sdata, addr); 10762306a36Sopenharmony_ci return sta; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ocb_dbg(sdata, "Running ocb housekeeping\n"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mod_timer(&ifocb->housekeeping_timer, 11962306a36Sopenharmony_ci round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 12562306a36Sopenharmony_ci struct sta_info *sta; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (ifocb->joined != true) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci sdata_lock(sdata); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 13362306a36Sopenharmony_ci while (!list_empty(&ifocb->incomplete_stations)) { 13462306a36Sopenharmony_ci sta = list_first_entry(&ifocb->incomplete_stations, 13562306a36Sopenharmony_ci struct sta_info, list); 13662306a36Sopenharmony_ci list_del(&sta->list); 13762306a36Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ieee80211_ocb_finish_sta(sta); 14062306a36Sopenharmony_ci rcu_read_unlock(); 14162306a36Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) 14662306a36Sopenharmony_ci ieee80211_ocb_housekeeping(sdata); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci sdata_unlock(sdata); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void ieee80211_ocb_housekeeping_timer(struct timer_list *t) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 15462306a36Sopenharmony_ci from_timer(sdata, t, u.ocb.housekeeping_timer); 15562306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 15662306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci wiphy_work_queue(local->hw.wiphy, &sdata->work); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci timer_setup(&ifocb->housekeeping_timer, 16862306a36Sopenharmony_ci ieee80211_ocb_housekeeping_timer, 0); 16962306a36Sopenharmony_ci INIT_LIST_HEAD(&ifocb->incomplete_stations); 17062306a36Sopenharmony_ci spin_lock_init(&ifocb->incomplete_lock); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, 17462306a36Sopenharmony_ci struct ocb_setup *setup) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 17762306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 17862306a36Sopenharmony_ci u64 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; 17962306a36Sopenharmony_ci int err; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (ifocb->joined == true) 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci sdata->deflink.operating_11g_mode = true; 18562306a36Sopenharmony_ci sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; 18662306a36Sopenharmony_ci sdata->deflink.needed_rx_chains = sdata->local->rx_chains; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 18962306a36Sopenharmony_ci err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef, 19062306a36Sopenharmony_ci IEEE80211_CHANCTX_SHARED); 19162306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 19262306a36Sopenharmony_ci if (err) 19362306a36Sopenharmony_ci return err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ifocb->joined = true; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); 20062306a36Sopenharmony_ci wiphy_work_queue(local->hw.wiphy, &sdata->work); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci netif_carrier_on(sdata->dev); 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; 20962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 21062306a36Sopenharmony_ci struct sta_info *sta; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ifocb->joined = false; 21362306a36Sopenharmony_ci sta_info_flush(sdata); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 21662306a36Sopenharmony_ci while (!list_empty(&ifocb->incomplete_stations)) { 21762306a36Sopenharmony_ci sta = list_first_entry(&ifocb->incomplete_stations, 21862306a36Sopenharmony_ci struct sta_info, list); 21962306a36Sopenharmony_ci list_del(&sta->list); 22062306a36Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci sta_info_free(local, sta); 22362306a36Sopenharmony_ci spin_lock_bh(&ifocb->incomplete_lock); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci spin_unlock_bh(&ifocb->incomplete_lock); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci netif_carrier_off(sdata->dev); 22862306a36Sopenharmony_ci clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); 22962306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 23262306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 23362306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci skb_queue_purge(&sdata->skb_queue); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci del_timer_sync(&sdata->u.ocb.housekeeping_timer); 23862306a36Sopenharmony_ci /* If the timer fired while we waited for it, it will have 23962306a36Sopenharmony_ci * requeued the work. Now the work will be running again 24062306a36Sopenharmony_ci * but will not rearm the timer again because it checks 24162306a36Sopenharmony_ci * whether we are connected to the network or not -- at this 24262306a36Sopenharmony_ci * point we shouldn't be anymore. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 247