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 - 2023 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 <asm/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(addr, 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(sta); |
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 | from_timer(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 | del_timer_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 | |