| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * OCB mode implementation | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright: (c) 2014 Czech Technical University in Prague | 
|---|
| 6 | *            (c) 2014 Volkswagen Group Research | 
|---|
| 7 | * Copyright (C) 2022 - 2024 Intel Corporation | 
|---|
| 8 | * Author:    Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> | 
|---|
| 9 | * Funded by: Volkswagen Group Research | 
|---|
| 10 | */ | 
|---|
| 11 |  | 
|---|
| 12 | #include <linux/delay.h> | 
|---|
| 13 | #include <linux/if_ether.h> | 
|---|
| 14 | #include <linux/skbuff.h> | 
|---|
| 15 | #include <linux/if_arp.h> | 
|---|
| 16 | #include <linux/etherdevice.h> | 
|---|
| 17 | #include <linux/rtnetlink.h> | 
|---|
| 18 | #include <net/mac80211.h> | 
|---|
| 19 | #include <linux/unaligned.h> | 
|---|
| 20 |  | 
|---|
| 21 | #include "ieee80211_i.h" | 
|---|
| 22 | #include "driver-ops.h" | 
|---|
| 23 | #include "rate.h" | 
|---|
| 24 |  | 
|---|
| 25 | #define IEEE80211_OCB_HOUSEKEEPING_INTERVAL		(60 * HZ) | 
|---|
| 26 | #define IEEE80211_OCB_PEER_INACTIVITY_LIMIT		(240 * HZ) | 
|---|
| 27 | #define IEEE80211_OCB_MAX_STA_ENTRIES			128 | 
|---|
| 28 |  | 
|---|
| 29 | /** | 
|---|
| 30 | * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks | 
|---|
| 31 | * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks | 
|---|
| 32 | * | 
|---|
| 33 | * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb | 
|---|
| 34 | */ | 
|---|
| 35 | enum ocb_deferred_task_flags { | 
|---|
| 36 | OCB_WORK_HOUSEKEEPING, | 
|---|
| 37 | }; | 
|---|
| 38 |  | 
|---|
| 39 | void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, | 
|---|
| 40 | const u8 *bssid, const u8 *addr, | 
|---|
| 41 | u32 supp_rates) | 
|---|
| 42 | { | 
|---|
| 43 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 44 | struct ieee80211_local *local = sdata->local; | 
|---|
| 45 | struct ieee80211_chanctx_conf *chanctx_conf; | 
|---|
| 46 | struct ieee80211_supported_band *sband; | 
|---|
| 47 | struct sta_info *sta; | 
|---|
| 48 | int band; | 
|---|
| 49 |  | 
|---|
| 50 | /* XXX: Consider removing the least recently used entry and | 
|---|
| 51 | *      allow new one to be added. | 
|---|
| 52 | */ | 
|---|
| 53 | if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { | 
|---|
| 54 | net_info_ratelimited( "%s: No room for a new OCB STA entry %pM\n", | 
|---|
| 55 | sdata->name, addr); | 
|---|
| 56 | return; | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); | 
|---|
| 60 |  | 
|---|
| 61 | rcu_read_lock(); | 
|---|
| 62 | chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); | 
|---|
| 63 | if (WARN_ON_ONCE(!chanctx_conf)) { | 
|---|
| 64 | rcu_read_unlock(); | 
|---|
| 65 | return; | 
|---|
| 66 | } | 
|---|
| 67 | band = chanctx_conf->def.chan->band; | 
|---|
| 68 | rcu_read_unlock(); | 
|---|
| 69 |  | 
|---|
| 70 | sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); | 
|---|
| 71 | if (!sta) | 
|---|
| 72 | return; | 
|---|
| 73 |  | 
|---|
| 74 | /* Add only mandatory rates for now */ | 
|---|
| 75 | sband = local->hw.wiphy->bands[band]; | 
|---|
| 76 | sta->sta.deflink.supp_rates[band] = ieee80211_mandatory_rates(sband); | 
|---|
| 77 |  | 
|---|
| 78 | spin_lock(lock: &ifocb->incomplete_lock); | 
|---|
| 79 | list_add(new: &sta->list, head: &ifocb->incomplete_stations); | 
|---|
| 80 | spin_unlock(lock: &ifocb->incomplete_lock); | 
|---|
| 81 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &sdata->work); | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 | static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) | 
|---|
| 85 | __acquires(RCU) | 
|---|
| 86 | { | 
|---|
| 87 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 
|---|
| 88 | u8 addr[ETH_ALEN]; | 
|---|
| 89 |  | 
|---|
| 90 | memcpy(to: addr, from: sta->sta.addr, ETH_ALEN); | 
|---|
| 91 |  | 
|---|
| 92 | ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", | 
|---|
| 93 | addr, sdata->name); | 
|---|
| 94 |  | 
|---|
| 95 | sta_info_move_state(sta, new_state: IEEE80211_STA_AUTH); | 
|---|
| 96 | sta_info_move_state(sta, new_state: IEEE80211_STA_ASSOC); | 
|---|
| 97 | sta_info_move_state(sta, new_state: IEEE80211_STA_AUTHORIZED); | 
|---|
| 98 |  | 
|---|
| 99 | rate_control_rate_init(link_sta: &sta->deflink); | 
|---|
| 100 |  | 
|---|
| 101 | /* If it fails, maybe we raced another insertion? */ | 
|---|
| 102 | if (sta_info_insert_rcu(sta)) | 
|---|
| 103 | return sta_info_get(sdata, addr); | 
|---|
| 104 | return sta; | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) | 
|---|
| 108 | { | 
|---|
| 109 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 110 |  | 
|---|
| 111 | ocb_dbg(sdata, "Running ocb housekeeping\n"); | 
|---|
| 112 |  | 
|---|
| 113 | ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); | 
|---|
| 114 |  | 
|---|
| 115 | mod_timer(timer: &ifocb->housekeeping_timer, | 
|---|
| 116 | expires: round_jiffies(j: jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) | 
|---|
| 120 | { | 
|---|
| 121 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 122 | struct sta_info *sta; | 
|---|
| 123 |  | 
|---|
| 124 | lockdep_assert_wiphy(sdata->local->hw.wiphy); | 
|---|
| 125 |  | 
|---|
| 126 | if (ifocb->joined != true) | 
|---|
| 127 | return; | 
|---|
| 128 |  | 
|---|
| 129 | spin_lock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 130 | while (!list_empty(head: &ifocb->incomplete_stations)) { | 
|---|
| 131 | sta = list_first_entry(&ifocb->incomplete_stations, | 
|---|
| 132 | struct sta_info, list); | 
|---|
| 133 | list_del(entry: &sta->list); | 
|---|
| 134 | spin_unlock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 135 |  | 
|---|
| 136 | ieee80211_ocb_finish_sta(sta); | 
|---|
| 137 | rcu_read_unlock(); | 
|---|
| 138 | spin_lock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 139 | } | 
|---|
| 140 | spin_unlock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 141 |  | 
|---|
| 142 | if (test_and_clear_bit(nr: OCB_WORK_HOUSEKEEPING, addr: &ifocb->wrkq_flags)) | 
|---|
| 143 | ieee80211_ocb_housekeeping(sdata); | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | static void ieee80211_ocb_housekeeping_timer(struct timer_list *t) | 
|---|
| 147 | { | 
|---|
| 148 | struct ieee80211_sub_if_data *sdata = | 
|---|
| 149 | timer_container_of(sdata, t, u.ocb.housekeeping_timer); | 
|---|
| 150 | struct ieee80211_local *local = sdata->local; | 
|---|
| 151 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 152 |  | 
|---|
| 153 | set_bit(nr: OCB_WORK_HOUSEKEEPING, addr: &ifocb->wrkq_flags); | 
|---|
| 154 |  | 
|---|
| 155 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &sdata->work); | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) | 
|---|
| 159 | { | 
|---|
| 160 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 161 |  | 
|---|
| 162 | timer_setup(&ifocb->housekeeping_timer, | 
|---|
| 163 | ieee80211_ocb_housekeeping_timer, 0); | 
|---|
| 164 | INIT_LIST_HEAD(list: &ifocb->incomplete_stations); | 
|---|
| 165 | spin_lock_init(&ifocb->incomplete_lock); | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, | 
|---|
| 169 | struct ocb_setup *setup) | 
|---|
| 170 | { | 
|---|
| 171 | struct ieee80211_chan_req chanreq = { .oper = setup->chandef }; | 
|---|
| 172 | struct ieee80211_local *local = sdata->local; | 
|---|
| 173 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 174 | u64 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; | 
|---|
| 175 | int err; | 
|---|
| 176 |  | 
|---|
| 177 | lockdep_assert_wiphy(sdata->local->hw.wiphy); | 
|---|
| 178 |  | 
|---|
| 179 | if (ifocb->joined == true) | 
|---|
| 180 | return -EINVAL; | 
|---|
| 181 |  | 
|---|
| 182 | sdata->deflink.operating_11g_mode = true; | 
|---|
| 183 | sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; | 
|---|
| 184 | sdata->deflink.needed_rx_chains = sdata->local->rx_chains; | 
|---|
| 185 |  | 
|---|
| 186 | err = ieee80211_link_use_channel(link: &sdata->deflink, req: &chanreq, | 
|---|
| 187 | mode: IEEE80211_CHANCTX_SHARED); | 
|---|
| 188 | if (err) | 
|---|
| 189 | return err; | 
|---|
| 190 |  | 
|---|
| 191 | ieee80211_bss_info_change_notify(sdata, changed); | 
|---|
| 192 |  | 
|---|
| 193 | ifocb->joined = true; | 
|---|
| 194 |  | 
|---|
| 195 | set_bit(nr: OCB_WORK_HOUSEKEEPING, addr: &ifocb->wrkq_flags); | 
|---|
| 196 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &sdata->work); | 
|---|
| 197 |  | 
|---|
| 198 | netif_carrier_on(dev: sdata->dev); | 
|---|
| 199 | return 0; | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) | 
|---|
| 203 | { | 
|---|
| 204 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | 
|---|
| 205 | struct ieee80211_local *local = sdata->local; | 
|---|
| 206 | struct sta_info *sta; | 
|---|
| 207 |  | 
|---|
| 208 | lockdep_assert_wiphy(sdata->local->hw.wiphy); | 
|---|
| 209 |  | 
|---|
| 210 | ifocb->joined = false; | 
|---|
| 211 | sta_info_flush(sdata, link_id: -1); | 
|---|
| 212 |  | 
|---|
| 213 | spin_lock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 214 | while (!list_empty(head: &ifocb->incomplete_stations)) { | 
|---|
| 215 | sta = list_first_entry(&ifocb->incomplete_stations, | 
|---|
| 216 | struct sta_info, list); | 
|---|
| 217 | list_del(entry: &sta->list); | 
|---|
| 218 | spin_unlock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 219 |  | 
|---|
| 220 | sta_info_free(local, sta); | 
|---|
| 221 | spin_lock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 222 | } | 
|---|
| 223 | spin_unlock_bh(lock: &ifocb->incomplete_lock); | 
|---|
| 224 |  | 
|---|
| 225 | netif_carrier_off(dev: sdata->dev); | 
|---|
| 226 | clear_bit(nr: SDATA_STATE_OFFCHANNEL, addr: &sdata->state); | 
|---|
| 227 | ieee80211_bss_info_change_notify(sdata, changed: BSS_CHANGED_OCB); | 
|---|
| 228 |  | 
|---|
| 229 | ieee80211_link_release_channel(link: &sdata->deflink); | 
|---|
| 230 |  | 
|---|
| 231 | skb_queue_purge(list: &sdata->skb_queue); | 
|---|
| 232 |  | 
|---|
| 233 | timer_delete_sync(timer: &sdata->u.ocb.housekeeping_timer); | 
|---|
| 234 | /* If the timer fired while we waited for it, it will have | 
|---|
| 235 | * requeued the work. Now the work will be running again | 
|---|
| 236 | * but will not rearm the timer again because it checks | 
|---|
| 237 | * whether we are connected to the network or not -- at this | 
|---|
| 238 | * point we shouldn't be anymore. | 
|---|
| 239 | */ | 
|---|
| 240 |  | 
|---|
| 241 | return 0; | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|