xref: /freebsd/contrib/wpa/src/ap/ap_drv_ops.c (revision b1f9167f94059fd55c630891d359bcff987bd7eb)
1 /*
2  * hostapd - Driver operations
3  * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "drivers/driver.h"
13 #include "common/ieee802_11_defs.h"
14 #include "wps/wps.h"
15 #include "p2p/p2p.h"
16 #include "hostapd.h"
17 #include "ieee802_11.h"
18 #include "sta_info.h"
19 #include "ap_config.h"
20 #include "p2p_hostapd.h"
21 #include "hs20.h"
22 #include "ap_drv_ops.h"
23 
24 
25 u32 hostapd_sta_flags_to_drv(u32 flags)
26 {
27 	int res = 0;
28 	if (flags & WLAN_STA_AUTHORIZED)
29 		res |= WPA_STA_AUTHORIZED;
30 	if (flags & WLAN_STA_WMM)
31 		res |= WPA_STA_WMM;
32 	if (flags & WLAN_STA_SHORT_PREAMBLE)
33 		res |= WPA_STA_SHORT_PREAMBLE;
34 	if (flags & WLAN_STA_MFP)
35 		res |= WPA_STA_MFP;
36 	return res;
37 }
38 
39 
40 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
41 			       struct wpabuf **beacon_ret,
42 			       struct wpabuf **proberesp_ret,
43 			       struct wpabuf **assocresp_ret)
44 {
45 	struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
46 	u8 buf[200], *pos;
47 
48 	*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
49 
50 	pos = buf;
51 	pos = hostapd_eid_time_adv(hapd, pos);
52 	if (pos != buf) {
53 		if (wpabuf_resize(&beacon, pos - buf) != 0)
54 			goto fail;
55 		wpabuf_put_data(beacon, buf, pos - buf);
56 	}
57 	pos = hostapd_eid_time_zone(hapd, pos);
58 	if (pos != buf) {
59 		if (wpabuf_resize(&proberesp, pos - buf) != 0)
60 			goto fail;
61 		wpabuf_put_data(proberesp, buf, pos - buf);
62 	}
63 
64 	pos = buf;
65 	pos = hostapd_eid_ext_capab(hapd, pos);
66 	if (pos != buf) {
67 		if (wpabuf_resize(&assocresp, pos - buf) != 0)
68 			goto fail;
69 		wpabuf_put_data(assocresp, buf, pos - buf);
70 	}
71 	pos = hostapd_eid_interworking(hapd, pos);
72 	pos = hostapd_eid_adv_proto(hapd, pos);
73 	pos = hostapd_eid_roaming_consortium(hapd, pos);
74 	if (pos != buf) {
75 		if (wpabuf_resize(&beacon, pos - buf) != 0)
76 			goto fail;
77 		wpabuf_put_data(beacon, buf, pos - buf);
78 
79 		if (wpabuf_resize(&proberesp, pos - buf) != 0)
80 			goto fail;
81 		wpabuf_put_data(proberesp, buf, pos - buf);
82 	}
83 
84 	if (hapd->wps_beacon_ie) {
85 		if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
86 		    0)
87 			goto fail;
88 		wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
89 	}
90 
91 	if (hapd->wps_probe_resp_ie) {
92 		if (wpabuf_resize(&proberesp,
93 				  wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
94 			goto fail;
95 		wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
96 	}
97 
98 #ifdef CONFIG_P2P
99 	if (hapd->p2p_beacon_ie) {
100 		if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
101 		    0)
102 			goto fail;
103 		wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
104 	}
105 
106 	if (hapd->p2p_probe_resp_ie) {
107 		if (wpabuf_resize(&proberesp,
108 				  wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
109 			goto fail;
110 		wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
111 	}
112 #endif /* CONFIG_P2P */
113 
114 #ifdef CONFIG_P2P_MANAGER
115 	if (hapd->conf->p2p & P2P_MANAGE) {
116 		if (wpabuf_resize(&beacon, 100) == 0) {
117 			u8 *start, *p;
118 			start = wpabuf_put(beacon, 0);
119 			p = hostapd_eid_p2p_manage(hapd, start);
120 			wpabuf_put(beacon, p - start);
121 		}
122 
123 		if (wpabuf_resize(&proberesp, 100) == 0) {
124 			u8 *start, *p;
125 			start = wpabuf_put(proberesp, 0);
126 			p = hostapd_eid_p2p_manage(hapd, start);
127 			wpabuf_put(proberesp, p - start);
128 		}
129 	}
130 #endif /* CONFIG_P2P_MANAGER */
131 
132 #ifdef CONFIG_WPS2
133 	if (hapd->conf->wps_state) {
134 		struct wpabuf *a = wps_build_assoc_resp_ie();
135 		if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
136 			wpabuf_put_buf(assocresp, a);
137 		wpabuf_free(a);
138 	}
139 #endif /* CONFIG_WPS2 */
140 
141 #ifdef CONFIG_P2P_MANAGER
142 	if (hapd->conf->p2p & P2P_MANAGE) {
143 		if (wpabuf_resize(&assocresp, 100) == 0) {
144 			u8 *start, *p;
145 			start = wpabuf_put(assocresp, 0);
146 			p = hostapd_eid_p2p_manage(hapd, start);
147 			wpabuf_put(assocresp, p - start);
148 		}
149 	}
150 #endif /* CONFIG_P2P_MANAGER */
151 
152 #ifdef CONFIG_WIFI_DISPLAY
153 	if (hapd->p2p_group) {
154 		struct wpabuf *a;
155 		a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
156 		if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
157 			wpabuf_put_buf(assocresp, a);
158 		wpabuf_free(a);
159 	}
160 #endif /* CONFIG_WIFI_DISPLAY */
161 
162 #ifdef CONFIG_HS20
163 	pos = buf;
164 	pos = hostapd_eid_hs20_indication(hapd, pos);
165 	if (pos != buf) {
166 		if (wpabuf_resize(&beacon, pos - buf) != 0)
167 			goto fail;
168 		wpabuf_put_data(beacon, buf, pos - buf);
169 
170 		if (wpabuf_resize(&proberesp, pos - buf) != 0)
171 			goto fail;
172 		wpabuf_put_data(proberesp, buf, pos - buf);
173 	}
174 #endif /* CONFIG_HS20 */
175 
176 	*beacon_ret = beacon;
177 	*proberesp_ret = proberesp;
178 	*assocresp_ret = assocresp;
179 
180 	return 0;
181 
182 fail:
183 	wpabuf_free(beacon);
184 	wpabuf_free(proberesp);
185 	wpabuf_free(assocresp);
186 	return -1;
187 }
188 
189 
190 void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
191 			       struct wpabuf *beacon,
192 			       struct wpabuf *proberesp,
193 			       struct wpabuf *assocresp)
194 {
195 	wpabuf_free(beacon);
196 	wpabuf_free(proberesp);
197 	wpabuf_free(assocresp);
198 }
199 
200 
201 int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
202 {
203 	struct wpabuf *beacon, *proberesp, *assocresp;
204 	int ret;
205 
206 	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
207 		return 0;
208 
209 	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
210 	    0)
211 		return -1;
212 
213 	ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
214 					  assocresp);
215 
216 	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
217 
218 	return ret;
219 }
220 
221 
222 int hostapd_set_authorized(struct hostapd_data *hapd,
223 			   struct sta_info *sta, int authorized)
224 {
225 	if (authorized) {
226 		return hostapd_sta_set_flags(hapd, sta->addr,
227 					     hostapd_sta_flags_to_drv(
228 						     sta->flags),
229 					     WPA_STA_AUTHORIZED, ~0);
230 	}
231 
232 	return hostapd_sta_set_flags(hapd, sta->addr,
233 				     hostapd_sta_flags_to_drv(sta->flags),
234 				     0, ~WPA_STA_AUTHORIZED);
235 }
236 
237 
238 int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
239 {
240 	int set_flags, total_flags, flags_and, flags_or;
241 	total_flags = hostapd_sta_flags_to_drv(sta->flags);
242 	set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
243 	if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
244 	     sta->auth_alg == WLAN_AUTH_FT) &&
245 	    sta->flags & WLAN_STA_AUTHORIZED)
246 		set_flags |= WPA_STA_AUTHORIZED;
247 	flags_or = total_flags & set_flags;
248 	flags_and = total_flags | ~set_flags;
249 	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
250 				     flags_or, flags_and);
251 }
252 
253 
254 int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
255 			      int enabled)
256 {
257 	struct wpa_bss_params params;
258 	os_memset(&params, 0, sizeof(params));
259 	params.ifname = ifname;
260 	params.enabled = enabled;
261 	if (enabled) {
262 		params.wpa = hapd->conf->wpa;
263 		params.ieee802_1x = hapd->conf->ieee802_1x;
264 		params.wpa_group = hapd->conf->wpa_group;
265 		params.wpa_pairwise = hapd->conf->wpa_pairwise;
266 		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
267 		params.rsn_preauth = hapd->conf->rsn_preauth;
268 #ifdef CONFIG_IEEE80211W
269 		params.ieee80211w = hapd->conf->ieee80211w;
270 #endif /* CONFIG_IEEE80211W */
271 	}
272 	return hostapd_set_ieee8021x(hapd, &params);
273 }
274 
275 
276 int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
277 {
278 	char force_ifname[IFNAMSIZ];
279 	u8 if_addr[ETH_ALEN];
280 	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
281 			      NULL, NULL, force_ifname, if_addr, NULL);
282 }
283 
284 
285 int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
286 {
287 	return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
288 }
289 
290 
291 int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
292 			int val)
293 {
294 	const char *bridge = NULL;
295 
296 	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
297 		return 0;
298 	if (hapd->conf->wds_bridge[0])
299 		bridge = hapd->conf->wds_bridge;
300 	else if (hapd->conf->bridge[0])
301 		bridge = hapd->conf->bridge;
302 	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
303 					 bridge);
304 }
305 
306 
307 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
308 			 u16 auth_alg)
309 {
310 	if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
311 		return 0;
312 	return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
313 }
314 
315 
316 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
317 		     u16 seq, u16 status, const u8 *ie, size_t len)
318 {
319 	if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
320 		return 0;
321 	return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
322 				      seq, status, ie, len);
323 }
324 
325 
326 int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
327 		      int reassoc, u16 status, const u8 *ie, size_t len)
328 {
329 	if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
330 		return 0;
331 	return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
332 				       reassoc, status, ie, len);
333 }
334 
335 
336 int hostapd_sta_add(struct hostapd_data *hapd,
337 		    const u8 *addr, u16 aid, u16 capability,
338 		    const u8 *supp_rates, size_t supp_rates_len,
339 		    u16 listen_interval,
340 		    const struct ieee80211_ht_capabilities *ht_capab,
341 		    u32 flags, u8 qosinfo)
342 {
343 	struct hostapd_sta_add_params params;
344 
345 	if (hapd->driver == NULL)
346 		return 0;
347 	if (hapd->driver->sta_add == NULL)
348 		return 0;
349 
350 	os_memset(&params, 0, sizeof(params));
351 	params.addr = addr;
352 	params.aid = aid;
353 	params.capability = capability;
354 	params.supp_rates = supp_rates;
355 	params.supp_rates_len = supp_rates_len;
356 	params.listen_interval = listen_interval;
357 	params.ht_capabilities = ht_capab;
358 	params.flags = hostapd_sta_flags_to_drv(flags);
359 	params.qosinfo = qosinfo;
360 	return hapd->driver->sta_add(hapd->drv_priv, &params);
361 }
362 
363 
364 int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
365 		      u8 *tspec_ie, size_t tspec_ielen)
366 {
367 	if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
368 		return 0;
369 	return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
370 				       tspec_ielen);
371 }
372 
373 
374 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
375 {
376 	if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
377 		return 0;
378 	return hapd->driver->set_privacy(hapd->drv_priv, enabled);
379 }
380 
381 
382 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
383 			     size_t elem_len)
384 {
385 	if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
386 		return 0;
387 	return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
388 }
389 
390 
391 int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
392 {
393 	if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
394 		return 0;
395 	return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
396 }
397 
398 
399 int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
400 {
401 	if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
402 		return 0;
403 	return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
404 }
405 
406 
407 int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
408 		   const char *ifname, const u8 *addr, void *bss_ctx,
409 		   void **drv_priv, char *force_ifname, u8 *if_addr,
410 		   const char *bridge)
411 {
412 	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
413 		return -1;
414 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
415 				    bss_ctx, drv_priv, force_ifname, if_addr,
416 				    bridge);
417 }
418 
419 
420 int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
421 		      const char *ifname)
422 {
423 	if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
424 		return -1;
425 	return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
426 }
427 
428 
429 int hostapd_set_ieee8021x(struct hostapd_data *hapd,
430 			  struct wpa_bss_params *params)
431 {
432 	if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
433 		return 0;
434 	return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
435 }
436 
437 
438 int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
439 		       const u8 *addr, int idx, u8 *seq)
440 {
441 	if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
442 		return 0;
443 	return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
444 					seq);
445 }
446 
447 
448 int hostapd_flush(struct hostapd_data *hapd)
449 {
450 	if (hapd->driver == NULL || hapd->driver->flush == NULL)
451 		return 0;
452 	return hapd->driver->flush(hapd->drv_priv);
453 }
454 
455 
456 int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
457 		     int channel, int ht_enabled, int sec_channel_offset)
458 {
459 	struct hostapd_freq_params data;
460 	if (hapd->driver == NULL)
461 		return 0;
462 	if (hapd->driver->set_freq == NULL)
463 		return 0;
464 	os_memset(&data, 0, sizeof(data));
465 	data.mode = mode;
466 	data.freq = freq;
467 	data.channel = channel;
468 	data.ht_enabled = ht_enabled;
469 	data.sec_channel_offset = sec_channel_offset;
470 	return hapd->driver->set_freq(hapd->drv_priv, &data);
471 }
472 
473 int hostapd_set_rts(struct hostapd_data *hapd, int rts)
474 {
475 	if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
476 		return 0;
477 	return hapd->driver->set_rts(hapd->drv_priv, rts);
478 }
479 
480 
481 int hostapd_set_frag(struct hostapd_data *hapd, int frag)
482 {
483 	if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
484 		return 0;
485 	return hapd->driver->set_frag(hapd->drv_priv, frag);
486 }
487 
488 
489 int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
490 			  int total_flags, int flags_or, int flags_and)
491 {
492 	if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
493 		return 0;
494 	return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
495 					   flags_or, flags_and);
496 }
497 
498 
499 int hostapd_set_country(struct hostapd_data *hapd, const char *country)
500 {
501 	if (hapd->driver == NULL ||
502 	    hapd->driver->set_country == NULL)
503 		return 0;
504 	return hapd->driver->set_country(hapd->drv_priv, country);
505 }
506 
507 
508 int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
509 				int cw_min, int cw_max, int burst_time)
510 {
511 	if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
512 		return 0;
513 	return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
514 						 cw_min, cw_max, burst_time);
515 }
516 
517 
518 struct hostapd_hw_modes *
519 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
520 			    u16 *flags)
521 {
522 	if (hapd->driver == NULL ||
523 	    hapd->driver->get_hw_feature_data == NULL)
524 		return NULL;
525 	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
526 						 flags);
527 }
528 
529 
530 int hostapd_driver_commit(struct hostapd_data *hapd)
531 {
532 	if (hapd->driver == NULL || hapd->driver->commit == NULL)
533 		return 0;
534 	return hapd->driver->commit(hapd->drv_priv);
535 }
536 
537 
538 int hostapd_drv_none(struct hostapd_data *hapd)
539 {
540 	return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
541 }
542 
543 
544 int hostapd_driver_scan(struct hostapd_data *hapd,
545 			struct wpa_driver_scan_params *params)
546 {
547 	if (hapd->driver && hapd->driver->scan2)
548 		return hapd->driver->scan2(hapd->drv_priv, params);
549 	return -1;
550 }
551 
552 
553 struct wpa_scan_results * hostapd_driver_get_scan_results(
554 	struct hostapd_data *hapd)
555 {
556 	if (hapd->driver && hapd->driver->get_scan_results2)
557 		return hapd->driver->get_scan_results2(hapd->drv_priv);
558 	return NULL;
559 }
560 
561 
562 int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
563 			   int duration)
564 {
565 	if (hapd->driver && hapd->driver->set_noa)
566 		return hapd->driver->set_noa(hapd->drv_priv, count, start,
567 					     duration);
568 	return -1;
569 }
570 
571 
572 int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
573 			enum wpa_alg alg, const u8 *addr,
574 			int key_idx, int set_tx,
575 			const u8 *seq, size_t seq_len,
576 			const u8 *key, size_t key_len)
577 {
578 	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
579 		return 0;
580 	return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
581 				     key_idx, set_tx, seq, seq_len, key,
582 				     key_len);
583 }
584 
585 
586 int hostapd_drv_send_mlme(struct hostapd_data *hapd,
587 			  const void *msg, size_t len, int noack)
588 {
589 	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
590 		return 0;
591 	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack);
592 }
593 
594 
595 int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
596 			   const u8 *addr, int reason)
597 {
598 	if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
599 		return 0;
600 	return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
601 					reason);
602 }
603 
604 
605 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
606 			     const u8 *addr, int reason)
607 {
608 	if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
609 		return 0;
610 	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
611 					  reason);
612 }
613 
614 
615 int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
616 			 const u8 *peer, u8 *buf, u16 *buf_len)
617 {
618 	if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
619 		return 0;
620 	return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
621 				      buf_len);
622 }
623 
624 
625 int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
626 			    unsigned int wait, const u8 *dst, const u8 *data,
627 			    size_t len)
628 {
629 	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
630 		return 0;
631 	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
632 					 hapd->own_addr, hapd->own_addr, data,
633 					 len, 0);
634 }
635