offchannel.c revision 252190
1252190Srpaulo/*
2252190Srpaulo * wpa_supplicant - Off-channel Action frame TX/RX
3252190Srpaulo * Copyright (c) 2009-2010, Atheros Communications
4252190Srpaulo * Copyright (c) 2011, Qualcomm Atheros
5252190Srpaulo *
6252190Srpaulo * This software may be distributed under the terms of the BSD license.
7252190Srpaulo * See README for more details.
8252190Srpaulo */
9252190Srpaulo
10252190Srpaulo#include "includes.h"
11252190Srpaulo
12252190Srpaulo#include "common.h"
13252190Srpaulo#include "utils/eloop.h"
14252190Srpaulo#include "wpa_supplicant_i.h"
15252190Srpaulo#include "driver_i.h"
16252190Srpaulo#include "offchannel.h"
17252190Srpaulo
18252190Srpaulo
19252190Srpaulo
20252190Srpaulostatic struct wpa_supplicant *
21252190Srpaulowpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
22252190Srpaulo{
23252190Srpaulo	struct wpa_supplicant *iface;
24252190Srpaulo
25252190Srpaulo	if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
26252190Srpaulo		return wpa_s;
27252190Srpaulo
28252190Srpaulo	/*
29252190Srpaulo	 * Try to find a group interface that matches with the source address.
30252190Srpaulo	 */
31252190Srpaulo	iface = wpa_s->global->ifaces;
32252190Srpaulo	while (iface) {
33252190Srpaulo		if (os_memcmp(wpa_s->pending_action_src,
34252190Srpaulo			      iface->own_addr, ETH_ALEN) == 0)
35252190Srpaulo			break;
36252190Srpaulo		iface = iface->next;
37252190Srpaulo	}
38252190Srpaulo	if (iface) {
39252190Srpaulo		wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
40252190Srpaulo			   "instead of interface %s for Action TX",
41252190Srpaulo			   iface->ifname, wpa_s->ifname);
42252190Srpaulo		return iface;
43252190Srpaulo	}
44252190Srpaulo
45252190Srpaulo	return wpa_s;
46252190Srpaulo}
47252190Srpaulo
48252190Srpaulo
49252190Srpaulostatic void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
50252190Srpaulo{
51252190Srpaulo	struct wpa_supplicant *wpa_s = eloop_ctx;
52252190Srpaulo	struct wpa_supplicant *iface;
53252190Srpaulo	int res;
54252190Srpaulo	int without_roc;
55252190Srpaulo
56252190Srpaulo	without_roc = wpa_s->pending_action_without_roc;
57252190Srpaulo	wpa_s->pending_action_without_roc = 0;
58252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
59252190Srpaulo		   "(without_roc=%d pending_action_tx=%p)",
60252190Srpaulo		   without_roc, wpa_s->pending_action_tx);
61252190Srpaulo
62252190Srpaulo	if (wpa_s->pending_action_tx == NULL)
63252190Srpaulo		return;
64252190Srpaulo
65252190Srpaulo	/*
66252190Srpaulo	 * This call is likely going to be on the P2P device instance if the
67252190Srpaulo	 * driver uses a separate interface for that purpose. However, some
68252190Srpaulo	 * Action frames are actually sent within a P2P Group and when that is
69252190Srpaulo	 * the case, we need to follow power saving (e.g., GO buffering the
70252190Srpaulo	 * frame for a client in PS mode or a client following the advertised
71252190Srpaulo	 * NoA from its GO). To make that easier for the driver, select the
72252190Srpaulo	 * correct group interface here.
73252190Srpaulo	 */
74252190Srpaulo	iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
75252190Srpaulo
76252190Srpaulo	if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
77252190Srpaulo	    wpa_s->pending_action_freq != 0 &&
78252190Srpaulo	    wpa_s->pending_action_freq != iface->assoc_freq) {
79252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
80252190Srpaulo			   "waiting for another freq=%u (off_channel_freq=%u "
81252190Srpaulo			   "assoc_freq=%u)",
82252190Srpaulo			   wpa_s->pending_action_freq,
83252190Srpaulo			   wpa_s->off_channel_freq,
84252190Srpaulo			   iface->assoc_freq);
85252190Srpaulo		if (without_roc && wpa_s->off_channel_freq == 0) {
86252190Srpaulo			/*
87252190Srpaulo			 * We may get here if wpas_send_action() found us to be
88252190Srpaulo			 * on the correct channel, but remain-on-channel cancel
89252190Srpaulo			 * event was received before getting here.
90252190Srpaulo			 */
91252190Srpaulo			wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
92252190Srpaulo				   "remain-on-channel to send Action frame");
93252190Srpaulo			if (wpa_drv_remain_on_channel(
94252190Srpaulo				    wpa_s, wpa_s->pending_action_freq, 200) <
95252190Srpaulo			    0) {
96252190Srpaulo				wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
97252190Srpaulo					   "request driver to remain on "
98252190Srpaulo					   "channel (%u MHz) for Action Frame "
99252190Srpaulo					   "TX", wpa_s->pending_action_freq);
100252190Srpaulo			} else {
101252190Srpaulo				wpa_s->off_channel_freq = 0;
102252190Srpaulo				wpa_s->roc_waiting_drv_freq =
103252190Srpaulo					wpa_s->pending_action_freq;
104252190Srpaulo			}
105252190Srpaulo		}
106252190Srpaulo		return;
107252190Srpaulo	}
108252190Srpaulo
109252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
110252190Srpaulo		   MACSTR " using interface %s",
111252190Srpaulo		   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
112252190Srpaulo	res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
113252190Srpaulo				  wpa_s->pending_action_dst,
114252190Srpaulo				  wpa_s->pending_action_src,
115252190Srpaulo				  wpa_s->pending_action_bssid,
116252190Srpaulo				  wpabuf_head(wpa_s->pending_action_tx),
117252190Srpaulo				  wpabuf_len(wpa_s->pending_action_tx),
118252190Srpaulo				  wpa_s->pending_action_no_cck);
119252190Srpaulo	if (res) {
120252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
121252190Srpaulo			   "pending Action frame");
122252190Srpaulo		/*
123252190Srpaulo		 * Use fake TX status event to allow state machines to
124252190Srpaulo		 * continue.
125252190Srpaulo		 */
126252190Srpaulo		offchannel_send_action_tx_status(
127252190Srpaulo			wpa_s, wpa_s->pending_action_dst,
128252190Srpaulo			wpabuf_head(wpa_s->pending_action_tx),
129252190Srpaulo			wpabuf_len(wpa_s->pending_action_tx),
130252190Srpaulo			OFFCHANNEL_SEND_ACTION_FAILED);
131252190Srpaulo	}
132252190Srpaulo}
133252190Srpaulo
134252190Srpaulo
135252190Srpaulo/**
136252190Srpaulo * offchannel_send_action_tx_status - TX status callback
137252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
138252190Srpaulo * @dst: Destination MAC address of the transmitted Action frame
139252190Srpaulo * @data: Transmitted frame payload
140252190Srpaulo * @data_len: Length of @data in bytes
141252190Srpaulo * @result: TX status
142252190Srpaulo *
143252190Srpaulo * This function is called whenever the driver indicates a TX status event for
144252190Srpaulo * a frame sent by offchannel_send_action() using wpa_drv_send_action().
145252190Srpaulo */
146252190Srpaulovoid offchannel_send_action_tx_status(
147252190Srpaulo	struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
148252190Srpaulo	size_t data_len, enum offchannel_send_action_result result)
149252190Srpaulo{
150252190Srpaulo	if (wpa_s->pending_action_tx == NULL) {
151252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
152252190Srpaulo			   "no pending operation");
153252190Srpaulo		return;
154252190Srpaulo	}
155252190Srpaulo
156252190Srpaulo	if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
157252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
158252190Srpaulo			   "unknown destination address");
159252190Srpaulo		return;
160252190Srpaulo	}
161252190Srpaulo
162252190Srpaulo	wpabuf_free(wpa_s->pending_action_tx);
163252190Srpaulo	wpa_s->pending_action_tx = NULL;
164252190Srpaulo
165252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
166252190Srpaulo		   result, wpa_s->pending_action_tx_status_cb);
167252190Srpaulo
168252190Srpaulo	if (wpa_s->pending_action_tx_status_cb) {
169252190Srpaulo		wpa_s->pending_action_tx_status_cb(
170252190Srpaulo			wpa_s, wpa_s->pending_action_freq,
171252190Srpaulo			wpa_s->pending_action_dst, wpa_s->pending_action_src,
172252190Srpaulo			wpa_s->pending_action_bssid,
173252190Srpaulo			data, data_len, result);
174252190Srpaulo	}
175252190Srpaulo}
176252190Srpaulo
177252190Srpaulo
178252190Srpaulo/**
179252190Srpaulo * offchannel_send_action - Request off-channel Action frame TX
180252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
181252190Srpaulo * @freq: The frequency in MHz indicating the channel on which the frame is to
182252190Srpaulo *	transmitted or 0 for the current channel (only if associated)
183252190Srpaulo * @dst: Action frame destination MAC address
184252190Srpaulo * @src: Action frame source MAC address
185252190Srpaulo * @bssid: Action frame BSSID
186252190Srpaulo * @buf: Frame to transmit starting from the Category field
187252190Srpaulo * @len: Length of @buf in bytes
188252190Srpaulo * @wait_time: Wait time for response in milliseconds
189252190Srpaulo * @tx_cb: Callback function for indicating TX status or %NULL for now callback
190252190Srpaulo * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
191252190Srpaulo * Returns: 0 on success or -1 on failure
192252190Srpaulo *
193252190Srpaulo * This function is used to request an Action frame to be transmitted on the
194252190Srpaulo * current operating channel or on another channel (off-channel). The actual
195252190Srpaulo * frame transmission will be delayed until the driver is ready on the specified
196252190Srpaulo * channel. The @wait_time parameter can be used to request the driver to remain
197252190Srpaulo * awake on the channel to wait for a response.
198252190Srpaulo */
199252190Srpauloint offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
200252190Srpaulo			   const u8 *dst, const u8 *src, const u8 *bssid,
201252190Srpaulo			   const u8 *buf, size_t len, unsigned int wait_time,
202252190Srpaulo			   void (*tx_cb)(struct wpa_supplicant *wpa_s,
203252190Srpaulo					 unsigned int freq, const u8 *dst,
204252190Srpaulo					 const u8 *src, const u8 *bssid,
205252190Srpaulo					 const u8 *data, size_t data_len,
206252190Srpaulo					 enum offchannel_send_action_result
207252190Srpaulo					 result),
208252190Srpaulo			   int no_cck)
209252190Srpaulo{
210252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
211252190Srpaulo		   MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
212252190Srpaulo		   freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
213252190Srpaulo		   (int) len);
214252190Srpaulo
215252190Srpaulo	wpa_s->pending_action_tx_status_cb = tx_cb;
216252190Srpaulo
217252190Srpaulo	if (wpa_s->pending_action_tx) {
218252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
219252190Srpaulo			   "frame TX to " MACSTR,
220252190Srpaulo			   MAC2STR(wpa_s->pending_action_dst));
221252190Srpaulo		wpabuf_free(wpa_s->pending_action_tx);
222252190Srpaulo	}
223252190Srpaulo	wpa_s->pending_action_tx = wpabuf_alloc(len);
224252190Srpaulo	if (wpa_s->pending_action_tx == NULL) {
225252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
226252190Srpaulo			   "frame TX buffer (len=%llu)",
227252190Srpaulo			   (unsigned long long) len);
228252190Srpaulo		return -1;
229252190Srpaulo	}
230252190Srpaulo	wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
231252190Srpaulo	os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
232252190Srpaulo	os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
233252190Srpaulo	os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
234252190Srpaulo	wpa_s->pending_action_freq = freq;
235252190Srpaulo	wpa_s->pending_action_no_cck = no_cck;
236252190Srpaulo
237252190Srpaulo	if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
238252190Srpaulo		struct wpa_supplicant *iface;
239252190Srpaulo
240252190Srpaulo		iface = wpas_get_tx_interface(wpa_s,
241252190Srpaulo					      wpa_s->pending_action_src);
242252190Srpaulo		wpa_s->action_tx_wait_time = wait_time;
243252190Srpaulo
244252190Srpaulo		return wpa_drv_send_action(
245252190Srpaulo			iface, wpa_s->pending_action_freq,
246252190Srpaulo			wait_time, wpa_s->pending_action_dst,
247252190Srpaulo			wpa_s->pending_action_src, wpa_s->pending_action_bssid,
248252190Srpaulo			wpabuf_head(wpa_s->pending_action_tx),
249252190Srpaulo			wpabuf_len(wpa_s->pending_action_tx),
250252190Srpaulo			wpa_s->pending_action_no_cck);
251252190Srpaulo	}
252252190Srpaulo
253252190Srpaulo	if (freq) {
254252190Srpaulo		struct wpa_supplicant *tx_iface;
255252190Srpaulo		tx_iface = wpas_get_tx_interface(wpa_s, src);
256252190Srpaulo		if (tx_iface->assoc_freq == freq) {
257252190Srpaulo			wpa_printf(MSG_DEBUG, "Off-channel: Already on "
258252190Srpaulo				   "requested channel (TX interface operating "
259252190Srpaulo				   "channel)");
260252190Srpaulo			freq = 0;
261252190Srpaulo		}
262252190Srpaulo	}
263252190Srpaulo
264252190Srpaulo	if (wpa_s->off_channel_freq == freq || freq == 0) {
265252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
266252190Srpaulo			   "channel; send Action frame immediately");
267252190Srpaulo		/* TODO: Would there ever be need to extend the current
268252190Srpaulo		 * duration on the channel? */
269252190Srpaulo		wpa_s->pending_action_without_roc = 1;
270252190Srpaulo		eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
271252190Srpaulo		eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
272252190Srpaulo		return 0;
273252190Srpaulo	}
274252190Srpaulo	wpa_s->pending_action_without_roc = 0;
275252190Srpaulo
276252190Srpaulo	if (wpa_s->roc_waiting_drv_freq == freq) {
277252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
278252190Srpaulo			   "driver to get to frequency %u MHz; continue "
279252190Srpaulo			   "waiting to send the Action frame", freq);
280252190Srpaulo		return 0;
281252190Srpaulo	}
282252190Srpaulo
283252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
284252190Srpaulo		   "transmitted once the driver gets to the requested "
285252190Srpaulo		   "channel");
286252190Srpaulo	if (wait_time > wpa_s->max_remain_on_chan)
287252190Srpaulo		wait_time = wpa_s->max_remain_on_chan;
288252190Srpaulo	if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
289252190Srpaulo		wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
290252190Srpaulo			   "to remain on channel (%u MHz) for Action "
291252190Srpaulo			   "Frame TX", freq);
292252190Srpaulo		return -1;
293252190Srpaulo	}
294252190Srpaulo	wpa_s->off_channel_freq = 0;
295252190Srpaulo	wpa_s->roc_waiting_drv_freq = freq;
296252190Srpaulo
297252190Srpaulo	return 0;
298252190Srpaulo}
299252190Srpaulo
300252190Srpaulo
301252190Srpaulo/**
302252190Srpaulo * offchannel_send_send_action_done - Notify completion of Action frame sequence
303252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
304252190Srpaulo *
305252190Srpaulo * This function can be used to cancel a wait for additional response frames on
306252190Srpaulo * the channel that was used with offchannel_send_action().
307252190Srpaulo */
308252190Srpaulovoid offchannel_send_action_done(struct wpa_supplicant *wpa_s)
309252190Srpaulo{
310252190Srpaulo	wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
311252190Srpaulo		   "notification");
312252190Srpaulo	wpabuf_free(wpa_s->pending_action_tx);
313252190Srpaulo	wpa_s->pending_action_tx = NULL;
314252190Srpaulo	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
315252190Srpaulo	    wpa_s->action_tx_wait_time)
316252190Srpaulo		wpa_drv_send_action_cancel_wait(wpa_s);
317252190Srpaulo
318252190Srpaulo	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
319252190Srpaulo		wpa_drv_cancel_remain_on_channel(wpa_s);
320252190Srpaulo		wpa_s->off_channel_freq = 0;
321252190Srpaulo		wpa_s->roc_waiting_drv_freq = 0;
322252190Srpaulo	}
323252190Srpaulo}
324252190Srpaulo
325252190Srpaulo
326252190Srpaulo/**
327252190Srpaulo * offchannel_remain_on_channel_cb - Remain-on-channel callback function
328252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
329252190Srpaulo * @freq: Frequency (in MHz) of the selected channel
330252190Srpaulo * @duration: Duration of the remain-on-channel operation in milliseconds
331252190Srpaulo *
332252190Srpaulo * This function is called whenever the driver notifies beginning of a
333252190Srpaulo * remain-on-channel operation.
334252190Srpaulo */
335252190Srpaulovoid offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
336252190Srpaulo				     unsigned int freq, unsigned int duration)
337252190Srpaulo{
338252190Srpaulo	wpa_s->roc_waiting_drv_freq = 0;
339252190Srpaulo	wpa_s->off_channel_freq = freq;
340252190Srpaulo	wpas_send_action_cb(wpa_s, NULL);
341252190Srpaulo}
342252190Srpaulo
343252190Srpaulo
344252190Srpaulo/**
345252190Srpaulo * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
346252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
347252190Srpaulo * @freq: Frequency (in MHz) of the selected channel
348252190Srpaulo *
349252190Srpaulo * This function is called whenever the driver notifies termination of a
350252190Srpaulo * remain-on-channel operation.
351252190Srpaulo */
352252190Srpaulovoid offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
353252190Srpaulo					    unsigned int freq)
354252190Srpaulo{
355252190Srpaulo	wpa_s->off_channel_freq = 0;
356252190Srpaulo}
357252190Srpaulo
358252190Srpaulo
359252190Srpaulo/**
360252190Srpaulo * offchannel_pending_action_tx - Check whether there is a pending Action TX
361252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
362252190Srpaulo * Returns: Pointer to pending frame or %NULL if no pending operation
363252190Srpaulo *
364252190Srpaulo * This function can be used to check whether there is a pending Action frame TX
365252190Srpaulo * operation. The returned pointer should be used only for checking whether it
366252190Srpaulo * is %NULL (no pending frame) or to print the pointer value in debug
367252190Srpaulo * information (i.e., the pointer should not be dereferenced).
368252190Srpaulo */
369252190Srpauloconst void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
370252190Srpaulo{
371252190Srpaulo	return wpa_s->pending_action_tx;
372252190Srpaulo}
373252190Srpaulo
374252190Srpaulo
375252190Srpaulo/**
376252190Srpaulo * offchannel_clear_pending_action_tx - Clear pending Action frame TX
377252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
378252190Srpaulo */
379252190Srpaulovoid offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
380252190Srpaulo{
381252190Srpaulo	wpabuf_free(wpa_s->pending_action_tx);
382252190Srpaulo	wpa_s->pending_action_tx = NULL;
383252190Srpaulo}
384252190Srpaulo
385252190Srpaulo
386252190Srpaulo/**
387252190Srpaulo * offchannel_deinit - Deinit off-channel operations
388252190Srpaulo * @wpa_s: Pointer to wpa_supplicant data
389252190Srpaulo *
390252190Srpaulo * This function is used to free up any allocated resources for off-channel
391252190Srpaulo * operations.
392252190Srpaulo */
393252190Srpaulovoid offchannel_deinit(struct wpa_supplicant *wpa_s)
394252190Srpaulo{
395252190Srpaulo	offchannel_clear_pending_action_tx(wpa_s);
396252190Srpaulo	eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
397252190Srpaulo}
398