source: rtems-libbsd/freebsd/contrib/wpa/wpa_supplicant/offchannel.c @ 8f2267b

55-freebsd-126-freebsd-12
Last change on this file since 8f2267b was 9c9d11b, checked in by Sichen Zhao <1473996754@…>, on 08/01/17 at 12:43:41

Import wpa from FreeBSD

  • Property mode set to 100644
File size: 14.6 KB
Line 
1#include <machine/rtems-bsd-user-space.h>
2
3/*
4 * wpa_supplicant - Off-channel Action frame TX/RX
5 * Copyright (c) 2009-2010, Atheros Communications
6 * Copyright (c) 2011, Qualcomm Atheros
7 *
8 * This software may be distributed under the terms of the BSD license.
9 * See README for more details.
10 */
11
12#include "includes.h"
13
14#include "common.h"
15#include "utils/eloop.h"
16#include "wpa_supplicant_i.h"
17#include "p2p_supplicant.h"
18#include "driver_i.h"
19#include "offchannel.h"
20
21
22
23static struct wpa_supplicant *
24wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
25{
26        struct wpa_supplicant *iface;
27
28        if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
29                return wpa_s;
30
31        /*
32         * Try to find a group interface that matches with the source address.
33         */
34        iface = wpa_s->global->ifaces;
35        while (iface) {
36                if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
37                        break;
38                iface = iface->next;
39        }
40        if (iface) {
41                wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
42                           "instead of interface %s for Action TX",
43                           iface->ifname, wpa_s->ifname);
44                return iface;
45        }
46
47        return wpa_s;
48}
49
50
51static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
52{
53        struct wpa_supplicant *wpa_s = eloop_ctx;
54        struct wpa_supplicant *iface;
55        int res;
56        int without_roc;
57
58        without_roc = wpa_s->pending_action_without_roc;
59        wpa_s->pending_action_without_roc = 0;
60        wpa_printf(MSG_DEBUG,
61                   "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
62                   without_roc, wpa_s->pending_action_tx,
63                   !!wpa_s->pending_action_tx_done);
64
65        if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
66                return;
67
68        /*
69         * This call is likely going to be on the P2P device instance if the
70         * driver uses a separate interface for that purpose. However, some
71         * Action frames are actually sent within a P2P Group and when that is
72         * the case, we need to follow power saving (e.g., GO buffering the
73         * frame for a client in PS mode or a client following the advertised
74         * NoA from its GO). To make that easier for the driver, select the
75         * correct group interface here.
76         */
77        iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
78
79        if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
80            wpa_s->pending_action_freq != 0 &&
81            wpa_s->pending_action_freq != iface->assoc_freq) {
82                wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
83                           "waiting for another freq=%u (off_channel_freq=%u "
84                           "assoc_freq=%u)",
85                           wpa_s->pending_action_freq,
86                           wpa_s->off_channel_freq,
87                           iface->assoc_freq);
88                if (without_roc && wpa_s->off_channel_freq == 0) {
89                        unsigned int duration = 200;
90                        /*
91                         * We may get here if wpas_send_action() found us to be
92                         * on the correct channel, but remain-on-channel cancel
93                         * event was received before getting here.
94                         */
95                        wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
96                                   "remain-on-channel to send Action frame");
97#ifdef CONFIG_TESTING_OPTIONS
98                        if (wpa_s->extra_roc_dur) {
99                                wpa_printf(MSG_DEBUG,
100                                           "TESTING: Increase ROC duration %u -> %u",
101                                           duration,
102                                           duration + wpa_s->extra_roc_dur);
103                                duration += wpa_s->extra_roc_dur;
104        }
105#endif /* CONFIG_TESTING_OPTIONS */
106                        if (wpa_drv_remain_on_channel(
107                                    wpa_s, wpa_s->pending_action_freq,
108                                    duration) < 0) {
109                                wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
110                                           "request driver to remain on "
111                                           "channel (%u MHz) for Action Frame "
112                                           "TX", wpa_s->pending_action_freq);
113                        } else {
114                                wpa_s->off_channel_freq = 0;
115                                wpa_s->roc_waiting_drv_freq =
116                                        wpa_s->pending_action_freq;
117                        }
118                }
119                return;
120        }
121
122        wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
123                   MACSTR " using interface %s",
124                   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
125        res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
126                                  wpa_s->pending_action_dst,
127                                  wpa_s->pending_action_src,
128                                  wpa_s->pending_action_bssid,
129                                  wpabuf_head(wpa_s->pending_action_tx),
130                                  wpabuf_len(wpa_s->pending_action_tx),
131                                  wpa_s->pending_action_no_cck);
132        if (res) {
133                wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
134                           "pending Action frame");
135                /*
136                 * Use fake TX status event to allow state machines to
137                 * continue.
138                 */
139                offchannel_send_action_tx_status(
140                        wpa_s, wpa_s->pending_action_dst,
141                        wpabuf_head(wpa_s->pending_action_tx),
142                        wpabuf_len(wpa_s->pending_action_tx),
143                        OFFCHANNEL_SEND_ACTION_FAILED);
144        }
145}
146
147
148/**
149 * offchannel_send_action_tx_status - TX status callback
150 * @wpa_s: Pointer to wpa_supplicant data
151 * @dst: Destination MAC address of the transmitted Action frame
152 * @data: Transmitted frame payload
153 * @data_len: Length of @data in bytes
154 * @result: TX status
155 *
156 * This function is called whenever the driver indicates a TX status event for
157 * a frame sent by offchannel_send_action() using wpa_drv_send_action().
158 */
159void offchannel_send_action_tx_status(
160        struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
161        size_t data_len, enum offchannel_send_action_result result)
162{
163        if (wpa_s->pending_action_tx == NULL) {
164                wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
165                           "no pending operation");
166                return;
167        }
168
169        if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
170                wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
171                           "unknown destination address");
172                return;
173        }
174
175        /* Accept report only if the contents of the frame matches */
176        if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
177            os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
178                      wpabuf_len(wpa_s->pending_action_tx)) != 0) {
179                wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
180                                   "mismatching contents with pending frame");
181                wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
182                            data, data_len);
183                wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
184                                wpa_s->pending_action_tx);
185                return;
186        }
187
188        wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
189
190        wpabuf_free(wpa_s->pending_action_tx);
191        wpa_s->pending_action_tx = NULL;
192
193        wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
194                   result, wpa_s->pending_action_tx_status_cb);
195
196        if (wpa_s->pending_action_tx_status_cb) {
197                wpa_s->pending_action_tx_status_cb(
198                        wpa_s, wpa_s->pending_action_freq,
199                        wpa_s->pending_action_dst, wpa_s->pending_action_src,
200                        wpa_s->pending_action_bssid,
201                        data, data_len, result);
202        }
203
204#ifdef CONFIG_P2P
205        if (wpa_s->p2p_long_listen > 0) {
206                /* Continue the listen */
207                wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
208                wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
209        }
210#endif /* CONFIG_P2P */
211}
212
213
214/**
215 * offchannel_send_action - Request off-channel Action frame TX
216 * @wpa_s: Pointer to wpa_supplicant data
217 * @freq: The frequency in MHz indicating the channel on which the frame is to
218 *      transmitted or 0 for the current channel (only if associated)
219 * @dst: Action frame destination MAC address
220 * @src: Action frame source MAC address
221 * @bssid: Action frame BSSID
222 * @buf: Frame to transmit starting from the Category field
223 * @len: Length of @buf in bytes
224 * @wait_time: Wait time for response in milliseconds
225 * @tx_cb: Callback function for indicating TX status or %NULL for now callback
226 * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
227 * Returns: 0 on success or -1 on failure
228 *
229 * This function is used to request an Action frame to be transmitted on the
230 * current operating channel or on another channel (off-channel). The actual
231 * frame transmission will be delayed until the driver is ready on the specified
232 * channel. The @wait_time parameter can be used to request the driver to remain
233 * awake on the channel to wait for a response.
234 */
235int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
236                           const u8 *dst, const u8 *src, const u8 *bssid,
237                           const u8 *buf, size_t len, unsigned int wait_time,
238                           void (*tx_cb)(struct wpa_supplicant *wpa_s,
239                                         unsigned int freq, const u8 *dst,
240                                         const u8 *src, const u8 *bssid,
241                                         const u8 *data, size_t data_len,
242                                         enum offchannel_send_action_result
243                                         result),
244                           int no_cck)
245{
246        wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
247                   MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
248                   freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
249                   (int) len);
250
251        wpa_s->pending_action_tx_status_cb = tx_cb;
252
253        if (wpa_s->pending_action_tx) {
254                wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
255                           "frame TX to " MACSTR,
256                           MAC2STR(wpa_s->pending_action_dst));
257                wpabuf_free(wpa_s->pending_action_tx);
258        }
259        wpa_s->pending_action_tx_done = 0;
260        wpa_s->pending_action_tx = wpabuf_alloc(len);
261        if (wpa_s->pending_action_tx == NULL) {
262                wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
263                           "frame TX buffer (len=%llu)",
264                           (unsigned long long) len);
265                return -1;
266        }
267        wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
268        os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
269        os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
270        os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
271        wpa_s->pending_action_freq = freq;
272        wpa_s->pending_action_no_cck = no_cck;
273
274        if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
275                struct wpa_supplicant *iface;
276                int ret;
277
278                iface = wpas_get_tx_interface(wpa_s, src);
279                wpa_s->action_tx_wait_time = wait_time;
280
281                ret = wpa_drv_send_action(
282                        iface, wpa_s->pending_action_freq,
283                        wait_time, wpa_s->pending_action_dst,
284                        wpa_s->pending_action_src, wpa_s->pending_action_bssid,
285                        wpabuf_head(wpa_s->pending_action_tx),
286                        wpabuf_len(wpa_s->pending_action_tx),
287                        wpa_s->pending_action_no_cck);
288                if (ret == 0)
289                        wpa_s->pending_action_tx_done = 1;
290                return ret;
291        }
292
293        if (freq) {
294                struct wpa_supplicant *tx_iface;
295                tx_iface = wpas_get_tx_interface(wpa_s, src);
296                if (tx_iface->assoc_freq == freq) {
297                        wpa_printf(MSG_DEBUG, "Off-channel: Already on "
298                                   "requested channel (TX interface operating "
299                                   "channel)");
300                        freq = 0;
301                }
302        }
303
304        if (wpa_s->off_channel_freq == freq || freq == 0) {
305                wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
306                           "channel; send Action frame immediately");
307                /* TODO: Would there ever be need to extend the current
308                 * duration on the channel? */
309                wpa_s->pending_action_without_roc = 1;
310                eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
311                eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
312                return 0;
313        }
314        wpa_s->pending_action_without_roc = 0;
315
316        if (wpa_s->roc_waiting_drv_freq == freq) {
317                wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
318                           "driver to get to frequency %u MHz; continue "
319                           "waiting to send the Action frame", freq);
320                return 0;
321        }
322
323        wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
324                   "transmitted once the driver gets to the requested "
325                   "channel");
326        if (wait_time > wpa_s->max_remain_on_chan)
327                wait_time = wpa_s->max_remain_on_chan;
328        else if (wait_time == 0)
329                wait_time = 20;
330#ifdef CONFIG_TESTING_OPTIONS
331        if (wpa_s->extra_roc_dur) {
332                wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
333                           wait_time, wait_time + wpa_s->extra_roc_dur);
334                wait_time += wpa_s->extra_roc_dur;
335        }
336#endif /* CONFIG_TESTING_OPTIONS */
337        if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
338                wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
339                           "to remain on channel (%u MHz) for Action "
340                           "Frame TX", freq);
341                return -1;
342        }
343        wpa_s->off_channel_freq = 0;
344        wpa_s->roc_waiting_drv_freq = freq;
345
346        return 0;
347}
348
349
350/**
351 * offchannel_send_send_action_done - Notify completion of Action frame sequence
352 * @wpa_s: Pointer to wpa_supplicant data
353 *
354 * This function can be used to cancel a wait for additional response frames on
355 * the channel that was used with offchannel_send_action().
356 */
357void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
358{
359        wpa_printf(MSG_DEBUG,
360                   "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
361                   wpa_s->pending_action_tx,
362                   !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
363                   wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
364                   wpa_s->roc_waiting_drv_freq);
365        wpabuf_free(wpa_s->pending_action_tx);
366        wpa_s->pending_action_tx = NULL;
367        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
368            wpa_s->action_tx_wait_time)
369                wpa_drv_send_action_cancel_wait(wpa_s);
370        else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
371                wpa_drv_cancel_remain_on_channel(wpa_s);
372                wpa_s->off_channel_freq = 0;
373                wpa_s->roc_waiting_drv_freq = 0;
374        }
375}
376
377
378/**
379 * offchannel_remain_on_channel_cb - Remain-on-channel callback function
380 * @wpa_s: Pointer to wpa_supplicant data
381 * @freq: Frequency (in MHz) of the selected channel
382 * @duration: Duration of the remain-on-channel operation in milliseconds
383 *
384 * This function is called whenever the driver notifies beginning of a
385 * remain-on-channel operation.
386 */
387void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
388                                     unsigned int freq, unsigned int duration)
389{
390        wpa_s->roc_waiting_drv_freq = 0;
391        wpa_s->off_channel_freq = freq;
392        wpas_send_action_cb(wpa_s, NULL);
393}
394
395
396/**
397 * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
398 * @wpa_s: Pointer to wpa_supplicant data
399 * @freq: Frequency (in MHz) of the selected channel
400 *
401 * This function is called whenever the driver notifies termination of a
402 * remain-on-channel operation.
403 */
404void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
405                                            unsigned int freq)
406{
407        wpa_s->off_channel_freq = 0;
408}
409
410
411/**
412 * offchannel_pending_action_tx - Check whether there is a pending Action TX
413 * @wpa_s: Pointer to wpa_supplicant data
414 * Returns: Pointer to pending frame or %NULL if no pending operation
415 *
416 * This function can be used to check whether there is a pending Action frame TX
417 * operation. The returned pointer should be used only for checking whether it
418 * is %NULL (no pending frame) or to print the pointer value in debug
419 * information (i.e., the pointer should not be dereferenced).
420 */
421const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
422{
423        return wpa_s->pending_action_tx;
424}
425
426
427/**
428 * offchannel_clear_pending_action_tx - Clear pending Action frame TX
429 * @wpa_s: Pointer to wpa_supplicant data
430 */
431void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
432{
433        wpabuf_free(wpa_s->pending_action_tx);
434        wpa_s->pending_action_tx = NULL;
435}
436
437
438/**
439 * offchannel_deinit - Deinit off-channel operations
440 * @wpa_s: Pointer to wpa_supplicant data
441 *
442 * This function is used to free up any allocated resources for off-channel
443 * operations.
444 */
445void offchannel_deinit(struct wpa_supplicant *wpa_s)
446{
447        offchannel_clear_pending_action_tx(wpa_s);
448        eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
449}
Note: See TracBrowser for help on using the repository browser.