wpa_priv.c revision 209158
1189251Ssam/*
2189251Ssam * WPA Supplicant / privileged helper program
3189251Ssam * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam#ifdef __linux__
17189251Ssam#include <fcntl.h>
18189251Ssam#endif /* __linux__ */
19189251Ssam#include <sys/un.h>
20189251Ssam#include <sys/stat.h>
21189251Ssam
22189251Ssam#include "common.h"
23189251Ssam#include "eloop.h"
24189251Ssam#include "version.h"
25189251Ssam#include "drivers/driver.h"
26189251Ssam#include "l2_packet/l2_packet.h"
27189251Ssam#include "privsep_commands.h"
28189251Ssam#include "ieee802_11_defs.h"
29189251Ssam
30189251Ssam#ifndef ETH_P_EAPOL
31189251Ssam#define ETH_P_EAPOL 0x888e
32189251Ssam#endif
33189251Ssam
34189251Ssam#ifndef ETH_P_RSN_PREAUTH
35189251Ssam#define ETH_P_RSN_PREAUTH 0x88c7
36189251Ssam#endif
37189251Ssam
38189251Ssam
39189251Ssamstruct wpa_priv_interface {
40189251Ssam	struct wpa_priv_interface *next;
41189251Ssam	char *driver_name;
42189251Ssam	char *ifname;
43189251Ssam	char *sock_name;
44189251Ssam	int fd;
45189251Ssam
46189251Ssam	struct wpa_driver_ops *driver;
47189251Ssam	void *drv_priv;
48189251Ssam	struct sockaddr_un drv_addr;
49189251Ssam	int wpas_registered;
50189251Ssam
51189251Ssam	/* TODO: add support for multiple l2 connections */
52189251Ssam	struct l2_packet_data *l2;
53189251Ssam	struct sockaddr_un l2_addr;
54189251Ssam};
55189251Ssam
56189251Ssam
57189251Ssamstatic void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
58189251Ssam				  struct sockaddr_un *from)
59189251Ssam{
60189251Ssam	if (iface->drv_priv) {
61189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
62189251Ssam		if (iface->driver->set_wpa)
63189251Ssam			iface->driver->set_wpa(iface->drv_priv, 0);
64189251Ssam		if (iface->driver->deinit)
65189251Ssam			iface->driver->deinit(iface->drv_priv);
66189251Ssam		iface->drv_priv = NULL;
67189251Ssam		iface->wpas_registered = 0;
68189251Ssam	}
69189251Ssam
70189251Ssam	if (iface->l2) {
71189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
72189251Ssam			   "instance");
73189251Ssam		l2_packet_deinit(iface->l2);
74189251Ssam		iface->l2 = NULL;
75189251Ssam	}
76189251Ssam
77189251Ssam	if (iface->driver->init == NULL)
78189251Ssam		return;
79189251Ssam
80189251Ssam	iface->drv_priv = iface->driver->init(iface, iface->ifname);
81189251Ssam	if (iface->drv_priv == NULL) {
82189251Ssam		wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
83189251Ssam		return;
84189251Ssam	}
85189251Ssam
86189251Ssam	wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
87189251Ssam		   "'%s'", iface->driver_name, iface->ifname);
88189251Ssam
89189251Ssam	os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
90189251Ssam	iface->wpas_registered = 1;
91189251Ssam
92189251Ssam	if (iface->driver->set_param &&
93189251Ssam	    iface->driver->set_param(iface->drv_priv, NULL) < 0) {
94189251Ssam		wpa_printf(MSG_ERROR, "Driver interface rejected param");
95189251Ssam	}
96189251Ssam
97189251Ssam	if (iface->driver->set_wpa)
98189251Ssam		iface->driver->set_wpa(iface->drv_priv, 1);
99189251Ssam}
100189251Ssam
101189251Ssam
102189251Ssamstatic void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
103189251Ssam				    struct sockaddr_un *from)
104189251Ssam{
105189251Ssam	if (iface->drv_priv) {
106189251Ssam		if (iface->driver->set_wpa)
107189251Ssam			iface->driver->set_wpa(iface->drv_priv, 0);
108189251Ssam		if (iface->driver->deinit)
109189251Ssam			iface->driver->deinit(iface->drv_priv);
110189251Ssam		iface->drv_priv = NULL;
111189251Ssam		iface->wpas_registered = 0;
112189251Ssam	}
113189251Ssam}
114189251Ssam
115189251Ssam
116189251Ssamstatic void wpa_priv_cmd_set_wpa(struct wpa_priv_interface *iface,
117189251Ssam				 char *buf, size_t len)
118189251Ssam{
119189251Ssam	if (iface->drv_priv == NULL || len != sizeof(int))
120189251Ssam		return;
121189251Ssam
122189251Ssam	if (iface->driver->set_wpa)
123189251Ssam		iface->driver->set_wpa(iface->drv_priv, *((int *) buf));
124189251Ssam}
125189251Ssam
126189251Ssam
127189251Ssamstatic void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
128189251Ssam			      char *buf, size_t len)
129189251Ssam{
130189251Ssam	if (iface->drv_priv == NULL)
131189251Ssam		return;
132189251Ssam
133189251Ssam	if (iface->driver->scan)
134189251Ssam		iface->driver->scan(iface->drv_priv, len ? (u8 *) buf : NULL,
135189251Ssam				    len);
136189251Ssam}
137189251Ssam
138189251Ssam
139189251Ssamstatic void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
140189251Ssam				       struct sockaddr_un *from)
141189251Ssam{
142189251Ssam	struct wpa_scan_results *res;
143189251Ssam	u8 *buf = NULL, *pos, *end;
144189251Ssam	int val;
145189251Ssam	size_t i;
146189251Ssam
147189251Ssam	res = iface->driver->get_scan_results2(iface->drv_priv);
148189251Ssam	if (res == NULL)
149189251Ssam		goto fail;
150189251Ssam
151189251Ssam	buf = os_malloc(60000);
152189251Ssam	if (buf == NULL)
153189251Ssam		goto fail;
154189251Ssam	pos = buf;
155189251Ssam	end = buf + 60000;
156189251Ssam	val = res->num;
157189251Ssam	os_memcpy(pos, &val, sizeof(int));
158189251Ssam	pos += sizeof(int);
159189251Ssam
160189251Ssam	for (i = 0; i < res->num; i++) {
161189251Ssam		struct wpa_scan_res *r = res->res[i];
162189251Ssam		val = sizeof(*r) + r->ie_len;
163189251Ssam		if (end - pos < (int) sizeof(int) + val)
164189251Ssam			break;
165189251Ssam		os_memcpy(pos, &val, sizeof(int));
166189251Ssam		pos += sizeof(int);
167189251Ssam		os_memcpy(pos, r, val);
168189251Ssam		pos += val;
169189251Ssam	}
170189251Ssam
171189251Ssam	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
172189251Ssam	       sizeof(*from));
173189251Ssam
174189251Ssam	os_free(buf);
175209158Srpaulo	wpa_scan_results_free(res);
176189251Ssam	return;
177189251Ssam
178189251Ssamfail:
179189251Ssam	os_free(buf);
180209158Srpaulo	wpa_scan_results_free(res);
181189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
182189251Ssam}
183189251Ssam
184189251Ssam
185189251Ssamstatic void wpa_priv_send_old_scan_results(struct wpa_priv_interface *iface,
186189251Ssam					   struct sockaddr_un *from)
187189251Ssam{
188189251Ssam#define SCAN_AP_LIMIT 128
189189251Ssam	int i, res, val;
190189251Ssam	struct wpa_scan_result *results = NULL;
191189251Ssam	u8 *buf = NULL, *pos, *end;
192189251Ssam	struct wpa_scan_res nres;
193189251Ssam
194189251Ssam	results = os_malloc(SCAN_AP_LIMIT * sizeof(*results));
195189251Ssam	if (results == NULL)
196189251Ssam		goto fail;
197189251Ssam
198189251Ssam	res = iface->driver->get_scan_results(iface->drv_priv, results,
199189251Ssam					      SCAN_AP_LIMIT);
200189251Ssam	if (res < 0 || res > SCAN_AP_LIMIT)
201189251Ssam		goto fail;
202189251Ssam
203189251Ssam	buf = os_malloc(60000);
204189251Ssam	if (buf == NULL)
205189251Ssam		goto fail;
206189251Ssam	pos = buf;
207189251Ssam	end = buf + 60000;
208189251Ssam	os_memcpy(pos, &res, sizeof(int));
209189251Ssam	pos += sizeof(int);
210189251Ssam
211189251Ssam	os_memset(&nres, 0, sizeof(nres));
212189251Ssam	for (i = 0; i < res; i++) {
213189251Ssam		struct wpa_scan_result *r = &results[i];
214189251Ssam		size_t ie_len;
215189251Ssam
216189251Ssam		ie_len = 2 + r->ssid_len + r->rsn_ie_len + r->wpa_ie_len;
217189251Ssam		if (r->maxrate)
218189251Ssam			ie_len += 3;
219189251Ssam		if (r->mdie_present)
220189251Ssam			ie_len += 5;
221189251Ssam
222189251Ssam		val = sizeof(nres) + ie_len;
223189251Ssam		if (end - pos < (int) sizeof(int) + val)
224189251Ssam			break;
225189251Ssam		os_memcpy(pos, &val, sizeof(int));
226189251Ssam		pos += sizeof(int);
227189251Ssam
228189251Ssam		os_memcpy(nres.bssid, r->bssid, ETH_ALEN);
229189251Ssam		nres.freq = r->freq;
230189251Ssam		nres.caps = r->caps;
231189251Ssam		nres.qual = r->qual;
232189251Ssam		nres.noise = r->noise;
233189251Ssam		nres.level = r->level;
234189251Ssam		nres.tsf = r->tsf;
235189251Ssam		nres.ie_len = ie_len;
236189251Ssam
237189251Ssam		os_memcpy(pos, &nres, sizeof(nres));
238189251Ssam		pos += sizeof(nres);
239189251Ssam
240189251Ssam		/* SSID IE */
241189251Ssam		*pos++ = WLAN_EID_SSID;
242189251Ssam		*pos++ = r->ssid_len;
243189251Ssam		os_memcpy(pos, r->ssid, r->ssid_len);
244189251Ssam		pos += r->ssid_len;
245189251Ssam
246189251Ssam		if (r->maxrate) {
247189251Ssam			/* Fake Supported Rate IE to include max rate */
248189251Ssam			*pos++ = WLAN_EID_SUPP_RATES;
249189251Ssam			*pos++ = 1;
250189251Ssam			*pos++ = r->maxrate;
251189251Ssam		}
252189251Ssam
253189251Ssam		if (r->rsn_ie_len) {
254189251Ssam			os_memcpy(pos, r->rsn_ie, r->rsn_ie_len);
255189251Ssam			pos += r->rsn_ie_len;
256189251Ssam		}
257189251Ssam
258189251Ssam		if (r->mdie_present) {
259189251Ssam			os_memcpy(pos, r->mdie, 5);
260189251Ssam			pos += 5;
261189251Ssam		}
262189251Ssam
263189251Ssam		if (r->wpa_ie_len) {
264189251Ssam			os_memcpy(pos, r->wpa_ie, r->wpa_ie_len);
265189251Ssam			pos += r->wpa_ie_len;
266189251Ssam		}
267189251Ssam	}
268189251Ssam
269189251Ssam	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
270189251Ssam	       sizeof(*from));
271189251Ssam
272189251Ssam	os_free(buf);
273189251Ssam	os_free(results);
274189251Ssam	return;
275189251Ssam
276189251Ssamfail:
277189251Ssam	os_free(buf);
278189251Ssam	os_free(results);
279189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
280189251Ssam}
281189251Ssam
282189251Ssam
283189251Ssamstatic void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
284189251Ssam					  struct sockaddr_un *from)
285189251Ssam{
286189251Ssam	if (iface->drv_priv == NULL)
287189251Ssam		return;
288189251Ssam
289189251Ssam	if (iface->driver->get_scan_results2)
290189251Ssam		wpa_priv_get_scan_results2(iface, from);
291189251Ssam	else if (iface->driver->get_scan_results)
292189251Ssam		wpa_priv_send_old_scan_results(iface, from);
293189251Ssam	else
294189251Ssam		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
295189251Ssam		       sizeof(*from));
296189251Ssam}
297189251Ssam
298189251Ssam
299189251Ssamstatic void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
300189251Ssam				   void *buf, size_t len)
301189251Ssam{
302189251Ssam	struct wpa_driver_associate_params params;
303189251Ssam	struct privsep_cmd_associate *assoc;
304189251Ssam	u8 *bssid;
305189251Ssam	int res;
306189251Ssam
307189251Ssam	if (iface->drv_priv == NULL || iface->driver->associate == NULL)
308189251Ssam		return;
309189251Ssam
310189251Ssam	if (len < sizeof(*assoc)) {
311189251Ssam		wpa_printf(MSG_DEBUG, "Invalid association request");
312189251Ssam		return;
313189251Ssam	}
314189251Ssam
315189251Ssam	assoc = buf;
316189251Ssam	if (sizeof(*assoc) + assoc->wpa_ie_len > len) {
317189251Ssam		wpa_printf(MSG_DEBUG, "Association request overflow");
318189251Ssam		return;
319189251Ssam	}
320189251Ssam
321189251Ssam	os_memset(&params, 0, sizeof(params));
322189251Ssam	bssid = assoc->bssid;
323189251Ssam	if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
324189251Ssam		params.bssid = bssid;
325189251Ssam	params.ssid = assoc->ssid;
326189251Ssam	if (assoc->ssid_len > 32)
327189251Ssam		return;
328189251Ssam	params.ssid_len = assoc->ssid_len;
329189251Ssam	params.freq = assoc->freq;
330189251Ssam	if (assoc->wpa_ie_len) {
331189251Ssam		params.wpa_ie = (u8 *) (assoc + 1);
332189251Ssam		params.wpa_ie_len = assoc->wpa_ie_len;
333189251Ssam	}
334189251Ssam	params.pairwise_suite = assoc->pairwise_suite;
335189251Ssam	params.group_suite = assoc->group_suite;
336189251Ssam	params.key_mgmt_suite = assoc->key_mgmt_suite;
337189251Ssam	params.auth_alg = assoc->auth_alg;
338189251Ssam	params.mode = assoc->mode;
339189251Ssam
340189251Ssam	res = iface->driver->associate(iface->drv_priv, &params);
341189251Ssam	wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res);
342189251Ssam}
343189251Ssam
344189251Ssam
345189251Ssamstatic void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
346189251Ssam				   struct sockaddr_un *from)
347189251Ssam{
348189251Ssam	u8 bssid[ETH_ALEN];
349189251Ssam
350189251Ssam	if (iface->drv_priv == NULL)
351189251Ssam		goto fail;
352189251Ssam
353189251Ssam	if (iface->driver->get_bssid == NULL ||
354189251Ssam	    iface->driver->get_bssid(iface->drv_priv, bssid) < 0)
355189251Ssam		goto fail;
356189251Ssam
357189251Ssam	sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
358189251Ssam	       sizeof(*from));
359189251Ssam	return;
360189251Ssam
361189251Ssamfail:
362189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
363189251Ssam}
364189251Ssam
365189251Ssam
366189251Ssamstatic void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
367189251Ssam				  struct sockaddr_un *from)
368189251Ssam{
369189251Ssam	u8 ssid[sizeof(int) + 32];
370189251Ssam	int res;
371189251Ssam
372189251Ssam	if (iface->drv_priv == NULL)
373189251Ssam		goto fail;
374189251Ssam
375189251Ssam	if (iface->driver->get_ssid == NULL)
376189251Ssam		goto fail;
377189251Ssam
378189251Ssam	res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
379189251Ssam	if (res < 0 || res > 32)
380189251Ssam		goto fail;
381189251Ssam	os_memcpy(ssid, &res, sizeof(int));
382189251Ssam
383189251Ssam	sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
384189251Ssam	       sizeof(*from));
385189251Ssam	return;
386189251Ssam
387189251Ssamfail:
388189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
389189251Ssam}
390189251Ssam
391189251Ssam
392189251Ssamstatic void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
393189251Ssam				 void *buf, size_t len)
394189251Ssam{
395189251Ssam	struct privsep_cmd_set_key *params;
396189251Ssam	int res;
397189251Ssam
398189251Ssam	if (iface->drv_priv == NULL || iface->driver->set_key == NULL)
399189251Ssam		return;
400189251Ssam
401189251Ssam	if (len != sizeof(*params)) {
402189251Ssam		wpa_printf(MSG_DEBUG, "Invalid set_key request");
403189251Ssam		return;
404189251Ssam	}
405189251Ssam
406189251Ssam	params = buf;
407189251Ssam
408189251Ssam	res = iface->driver->set_key(iface->drv_priv, params->alg,
409189251Ssam				     params->addr, params->key_idx,
410189251Ssam				     params->set_tx,
411189251Ssam				     params->seq_len ? params->seq : NULL,
412189251Ssam				     params->seq_len,
413189251Ssam				     params->key_len ? params->key : NULL,
414189251Ssam				     params->key_len);
415189251Ssam	wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
416189251Ssam}
417189251Ssam
418189251Ssam
419189251Ssamstatic void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
420189251Ssam				  struct sockaddr_un *from)
421189251Ssam{
422189251Ssam	struct wpa_driver_capa capa;
423189251Ssam
424189251Ssam	if (iface->drv_priv == NULL)
425189251Ssam		goto fail;
426189251Ssam
427189251Ssam	if (iface->driver->get_capa == NULL ||
428189251Ssam	    iface->driver->get_capa(iface->drv_priv, &capa) < 0)
429189251Ssam		goto fail;
430189251Ssam
431189251Ssam	sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
432189251Ssam	       sizeof(*from));
433189251Ssam	return;
434189251Ssam
435189251Ssamfail:
436189251Ssam	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
437189251Ssam}
438189251Ssam
439189251Ssam
440189251Ssamstatic void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
441189251Ssam			   size_t len)
442189251Ssam{
443189251Ssam	struct wpa_priv_interface *iface = ctx;
444189251Ssam	struct msghdr msg;
445189251Ssam	struct iovec io[2];
446189251Ssam
447189251Ssam	io[0].iov_base = (u8 *) src_addr;
448189251Ssam	io[0].iov_len = ETH_ALEN;
449189251Ssam	io[1].iov_base = (u8 *) buf;
450189251Ssam	io[1].iov_len = len;
451189251Ssam
452189251Ssam	os_memset(&msg, 0, sizeof(msg));
453189251Ssam	msg.msg_iov = io;
454189251Ssam	msg.msg_iovlen = 2;
455189251Ssam	msg.msg_name = &iface->l2_addr;
456189251Ssam	msg.msg_namelen = sizeof(iface->l2_addr);
457189251Ssam
458189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0) {
459189251Ssam		perror("sendmsg(l2 rx)");
460189251Ssam	}
461189251Ssam}
462189251Ssam
463189251Ssam
464189251Ssamstatic void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
465189251Ssam				     struct sockaddr_un *from,
466189251Ssam				     void *buf, size_t len)
467189251Ssam{
468189251Ssam	int *reg_cmd = buf;
469189251Ssam	u8 own_addr[ETH_ALEN];
470189251Ssam	int res;
471189251Ssam	u16 proto;
472189251Ssam
473189251Ssam	if (len != 2 * sizeof(int)) {
474189251Ssam		wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
475189251Ssam			   (unsigned long) len);
476189251Ssam		return;
477189251Ssam	}
478189251Ssam
479189251Ssam	proto = reg_cmd[0];
480189251Ssam	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
481189251Ssam		wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
482189251Ssam			   "ethertype 0x%x", proto);
483189251Ssam		return;
484189251Ssam	}
485189251Ssam
486189251Ssam	if (iface->l2) {
487189251Ssam		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
488189251Ssam			   "instance");
489189251Ssam		l2_packet_deinit(iface->l2);
490189251Ssam		iface->l2 = NULL;
491189251Ssam	}
492189251Ssam
493189251Ssam	os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
494189251Ssam
495189251Ssam	iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
496189251Ssam				   wpa_priv_l2_rx, iface, reg_cmd[1]);
497189251Ssam	if (iface->l2 == NULL) {
498189251Ssam		wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
499189251Ssam			   "instance for protocol %d", proto);
500189251Ssam		return;
501189251Ssam	}
502189251Ssam
503189251Ssam	if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
504189251Ssam		wpa_printf(MSG_DEBUG, "Failed to get own address from "
505189251Ssam			   "l2_packet");
506189251Ssam		l2_packet_deinit(iface->l2);
507189251Ssam		iface->l2 = NULL;
508189251Ssam		return;
509189251Ssam	}
510189251Ssam
511189251Ssam	res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
512189251Ssam		     (struct sockaddr *) from, sizeof(*from));
513189251Ssam	wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
514189251Ssam}
515189251Ssam
516189251Ssam
517189251Ssamstatic void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
518189251Ssam				       struct sockaddr_un *from)
519189251Ssam{
520189251Ssam	if (iface->l2) {
521189251Ssam		l2_packet_deinit(iface->l2);
522189251Ssam		iface->l2 = NULL;
523189251Ssam	}
524189251Ssam}
525189251Ssam
526189251Ssam
527189251Ssamstatic void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
528189251Ssam					      struct sockaddr_un *from)
529189251Ssam{
530189251Ssam	if (iface->l2)
531189251Ssam		l2_packet_notify_auth_start(iface->l2);
532189251Ssam}
533189251Ssam
534189251Ssam
535189251Ssamstatic void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
536189251Ssam				 struct sockaddr_un *from,
537189251Ssam				 void *buf, size_t len)
538189251Ssam{
539189251Ssam	u8 *dst_addr;
540189251Ssam	u16 proto;
541189251Ssam	int res;
542189251Ssam
543189251Ssam	if (iface->l2 == NULL)
544189251Ssam		return;
545189251Ssam
546189251Ssam	if (len < ETH_ALEN + 2) {
547189251Ssam		wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)",
548189251Ssam			   (unsigned long) len);
549189251Ssam		return;
550189251Ssam	}
551189251Ssam
552189251Ssam	dst_addr = buf;
553189251Ssam	os_memcpy(&proto, buf + ETH_ALEN, 2);
554189251Ssam
555189251Ssam	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
556189251Ssam		wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
557189251Ssam			   "0x%x", proto);
558189251Ssam		return;
559189251Ssam	}
560189251Ssam
561189251Ssam	res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
562189251Ssam			     len - ETH_ALEN - 2);
563189251Ssam	wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
564189251Ssam}
565189251Ssam
566189251Ssam
567189251Ssamstatic void wpa_priv_cmd_set_mode(struct wpa_priv_interface *iface,
568189251Ssam				  void *buf, size_t len)
569189251Ssam{
570189251Ssam	if (iface->drv_priv == NULL || iface->driver->set_mode == NULL ||
571189251Ssam	    len != sizeof(int))
572189251Ssam		return;
573189251Ssam
574189251Ssam	iface->driver->set_mode(iface->drv_priv, *((int *) buf));
575189251Ssam}
576189251Ssam
577189251Ssam
578189251Ssamstatic void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
579189251Ssam				     char *buf)
580189251Ssam{
581189251Ssam	if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
582189251Ssam	    *buf == '\0')
583189251Ssam		return;
584189251Ssam
585189251Ssam	iface->driver->set_country(iface->drv_priv, buf);
586189251Ssam}
587189251Ssam
588189251Ssam
589189251Ssamstatic void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
590189251Ssam{
591189251Ssam	struct wpa_priv_interface *iface = eloop_ctx;
592189251Ssam	char buf[2000], *pos;
593189251Ssam	void *cmd_buf;
594189251Ssam	size_t cmd_len;
595189251Ssam	int res, cmd;
596189251Ssam	struct sockaddr_un from;
597189251Ssam	socklen_t fromlen = sizeof(from);
598189251Ssam
599189251Ssam	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
600189251Ssam		       &fromlen);
601189251Ssam	if (res < 0) {
602189251Ssam		perror("recvfrom");
603189251Ssam		return;
604189251Ssam	}
605189251Ssam
606189251Ssam	if (res < (int) sizeof(int)) {
607189251Ssam		wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res);
608189251Ssam		return;
609189251Ssam	}
610189251Ssam
611189251Ssam	os_memcpy(&cmd, buf, sizeof(int));
612189251Ssam	wpa_printf(MSG_DEBUG, "Command %d for interface %s",
613189251Ssam		   cmd, iface->ifname);
614189251Ssam	cmd_buf = &buf[sizeof(int)];
615189251Ssam	cmd_len = res - sizeof(int);
616189251Ssam
617189251Ssam	switch (cmd) {
618189251Ssam	case PRIVSEP_CMD_REGISTER:
619189251Ssam		wpa_priv_cmd_register(iface, &from);
620189251Ssam		break;
621189251Ssam	case PRIVSEP_CMD_UNREGISTER:
622189251Ssam		wpa_priv_cmd_unregister(iface, &from);
623189251Ssam		break;
624189251Ssam	case PRIVSEP_CMD_SET_WPA:
625189251Ssam		wpa_priv_cmd_set_wpa(iface, cmd_buf, cmd_len);
626189251Ssam		break;
627189251Ssam	case PRIVSEP_CMD_SCAN:
628189251Ssam		wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
629189251Ssam		break;
630189251Ssam	case PRIVSEP_CMD_GET_SCAN_RESULTS:
631189251Ssam		wpa_priv_cmd_get_scan_results(iface, &from);
632189251Ssam		break;
633189251Ssam	case PRIVSEP_CMD_ASSOCIATE:
634189251Ssam		wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
635189251Ssam		break;
636189251Ssam	case PRIVSEP_CMD_GET_BSSID:
637189251Ssam		wpa_priv_cmd_get_bssid(iface, &from);
638189251Ssam		break;
639189251Ssam	case PRIVSEP_CMD_GET_SSID:
640189251Ssam		wpa_priv_cmd_get_ssid(iface, &from);
641189251Ssam		break;
642189251Ssam	case PRIVSEP_CMD_SET_KEY:
643189251Ssam		wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
644189251Ssam		break;
645189251Ssam	case PRIVSEP_CMD_GET_CAPA:
646189251Ssam		wpa_priv_cmd_get_capa(iface, &from);
647189251Ssam		break;
648189251Ssam	case PRIVSEP_CMD_L2_REGISTER:
649189251Ssam		wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
650189251Ssam		break;
651189251Ssam	case PRIVSEP_CMD_L2_UNREGISTER:
652189251Ssam		wpa_priv_cmd_l2_unregister(iface, &from);
653189251Ssam		break;
654189251Ssam	case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
655189251Ssam		wpa_priv_cmd_l2_notify_auth_start(iface, &from);
656189251Ssam		break;
657189251Ssam	case PRIVSEP_CMD_L2_SEND:
658189251Ssam		wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
659189251Ssam		break;
660189251Ssam	case PRIVSEP_CMD_SET_MODE:
661189251Ssam		wpa_priv_cmd_set_mode(iface, cmd_buf, cmd_len);
662189251Ssam		break;
663189251Ssam	case PRIVSEP_CMD_SET_COUNTRY:
664189251Ssam		pos = cmd_buf;
665189251Ssam		if (pos + cmd_len >= buf + sizeof(buf))
666189251Ssam			break;
667189251Ssam		pos[cmd_len] = '\0';
668189251Ssam		wpa_priv_cmd_set_country(iface, pos);
669189251Ssam		break;
670189251Ssam	}
671189251Ssam}
672189251Ssam
673189251Ssam
674189251Ssamstatic void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
675189251Ssam{
676189251Ssam	if (iface->drv_priv && iface->driver->deinit)
677189251Ssam		iface->driver->deinit(iface->drv_priv);
678189251Ssam
679189251Ssam	if (iface->fd >= 0) {
680189251Ssam		eloop_unregister_read_sock(iface->fd);
681189251Ssam		close(iface->fd);
682189251Ssam		unlink(iface->sock_name);
683189251Ssam	}
684189251Ssam
685189251Ssam	if (iface->l2)
686189251Ssam		l2_packet_deinit(iface->l2);
687189251Ssam
688189251Ssam	os_free(iface->ifname);
689189251Ssam	os_free(iface->driver_name);
690189251Ssam	os_free(iface->sock_name);
691189251Ssam	os_free(iface);
692189251Ssam}
693189251Ssam
694189251Ssam
695189251Ssamextern struct wpa_driver_ops *wpa_supplicant_drivers[];
696189251Ssam
697189251Ssamstatic struct wpa_priv_interface *
698189251Ssamwpa_priv_interface_init(const char *dir, const char *params)
699189251Ssam{
700189251Ssam	struct wpa_priv_interface *iface;
701189251Ssam	char *pos;
702189251Ssam	size_t len;
703189251Ssam	struct sockaddr_un addr;
704189251Ssam	int i;
705189251Ssam
706189251Ssam	pos = os_strchr(params, ':');
707189251Ssam	if (pos == NULL)
708189251Ssam		return NULL;
709189251Ssam
710189251Ssam	iface = os_zalloc(sizeof(*iface));
711189251Ssam	if (iface == NULL)
712189251Ssam		return NULL;
713189251Ssam	iface->fd = -1;
714189251Ssam
715189251Ssam	len = pos - params;
716189251Ssam	iface->driver_name = os_malloc(len + 1);
717189251Ssam	if (iface->driver_name == NULL) {
718189251Ssam		wpa_priv_interface_deinit(iface);
719189251Ssam		return NULL;
720189251Ssam	}
721189251Ssam	os_memcpy(iface->driver_name, params, len);
722189251Ssam	iface->driver_name[len] = '\0';
723189251Ssam
724189251Ssam	for (i = 0; wpa_supplicant_drivers[i]; i++) {
725189251Ssam		if (os_strcmp(iface->driver_name,
726189251Ssam			      wpa_supplicant_drivers[i]->name) == 0) {
727189251Ssam			iface->driver = wpa_supplicant_drivers[i];
728189251Ssam			break;
729189251Ssam		}
730189251Ssam	}
731189251Ssam	if (iface->driver == NULL) {
732189251Ssam		wpa_printf(MSG_ERROR, "Unsupported driver '%s'",
733189251Ssam			   iface->driver_name);
734189251Ssam		wpa_priv_interface_deinit(iface);
735189251Ssam		return NULL;
736189251Ssam	}
737189251Ssam
738189251Ssam	pos++;
739189251Ssam	iface->ifname = os_strdup(pos);
740189251Ssam	if (iface->ifname == NULL) {
741189251Ssam		wpa_priv_interface_deinit(iface);
742189251Ssam		return NULL;
743189251Ssam	}
744189251Ssam
745189251Ssam	len = os_strlen(dir) + 1 + os_strlen(iface->ifname);
746189251Ssam	iface->sock_name = os_malloc(len + 1);
747189251Ssam	if (iface->sock_name == NULL) {
748189251Ssam		wpa_priv_interface_deinit(iface);
749189251Ssam		return NULL;
750189251Ssam	}
751189251Ssam
752189251Ssam	os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname);
753189251Ssam	if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) {
754189251Ssam		wpa_priv_interface_deinit(iface);
755189251Ssam		return NULL;
756189251Ssam	}
757189251Ssam
758189251Ssam	iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
759189251Ssam	if (iface->fd < 0) {
760189251Ssam		perror("socket(PF_UNIX)");
761189251Ssam		wpa_priv_interface_deinit(iface);
762189251Ssam		return NULL;
763189251Ssam	}
764189251Ssam
765189251Ssam	os_memset(&addr, 0, sizeof(addr));
766189251Ssam	addr.sun_family = AF_UNIX;
767189251Ssam	os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path));
768189251Ssam
769189251Ssam	if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
770189251Ssam		wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s",
771189251Ssam			   strerror(errno));
772189251Ssam		if (connect(iface->fd, (struct sockaddr *) &addr,
773189251Ssam			    sizeof(addr)) < 0) {
774189251Ssam			wpa_printf(MSG_DEBUG, "Socket exists, but does not "
775189251Ssam				   "allow connections - assuming it was "
776189251Ssam				   "leftover from forced program termination");
777189251Ssam			if (unlink(iface->sock_name) < 0) {
778189251Ssam				perror("unlink[ctrl_iface]");
779189251Ssam				wpa_printf(MSG_ERROR, "Could not unlink "
780189251Ssam					   "existing ctrl_iface socket '%s'",
781189251Ssam					   iface->sock_name);
782189251Ssam				goto fail;
783189251Ssam			}
784189251Ssam			if (bind(iface->fd, (struct sockaddr *) &addr,
785189251Ssam				 sizeof(addr)) < 0) {
786189251Ssam				perror("bind(PF_UNIX)");
787189251Ssam				goto fail;
788189251Ssam			}
789189251Ssam			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
790189251Ssam				   "socket '%s'", iface->sock_name);
791189251Ssam		} else {
792189251Ssam			wpa_printf(MSG_INFO, "Socket exists and seems to be "
793189251Ssam				   "in use - cannot override it");
794189251Ssam			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
795189251Ssam				   "not used anymore", iface->sock_name);
796189251Ssam			goto fail;
797189251Ssam		}
798189251Ssam	}
799189251Ssam
800189251Ssam	if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
801189251Ssam		perror("chmod");
802189251Ssam		goto fail;
803189251Ssam	}
804189251Ssam
805189251Ssam	eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL);
806189251Ssam
807189251Ssam	return iface;
808189251Ssam
809189251Ssamfail:
810189251Ssam	wpa_priv_interface_deinit(iface);
811189251Ssam	return NULL;
812189251Ssam}
813189251Ssam
814189251Ssam
815189251Ssamstatic int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
816189251Ssam			       const void *data, size_t data_len)
817189251Ssam{
818189251Ssam	struct msghdr msg;
819189251Ssam	struct iovec io[2];
820189251Ssam
821189251Ssam	io[0].iov_base = &event;
822189251Ssam	io[0].iov_len = sizeof(event);
823189251Ssam	io[1].iov_base = (u8 *) data;
824189251Ssam	io[1].iov_len = data_len;
825189251Ssam
826189251Ssam	os_memset(&msg, 0, sizeof(msg));
827189251Ssam	msg.msg_iov = io;
828189251Ssam	msg.msg_iovlen = data ? 2 : 1;
829189251Ssam	msg.msg_name = &iface->drv_addr;
830189251Ssam	msg.msg_namelen = sizeof(iface->drv_addr);
831189251Ssam
832189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0) {
833189251Ssam		perror("sendmsg(wpas_socket)");
834189251Ssam		return -1;
835189251Ssam	}
836189251Ssam
837189251Ssam	return 0;
838189251Ssam}
839189251Ssam
840189251Ssam
841189251Ssamstatic void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
842189251Ssam				union wpa_event_data *data)
843189251Ssam{
844189251Ssam	size_t buflen = 3 * sizeof(int);
845189251Ssam	u8 *buf, *pos;
846189251Ssam	int len;
847189251Ssam
848189251Ssam	if (data) {
849189251Ssam		buflen += data->assoc_info.req_ies_len +
850189251Ssam			data->assoc_info.resp_ies_len +
851189251Ssam			data->assoc_info.beacon_ies_len;
852189251Ssam	}
853189251Ssam
854189251Ssam	buf = os_malloc(buflen);
855189251Ssam	if (buf == NULL)
856189251Ssam		return;
857189251Ssam
858189251Ssam	pos = buf;
859189251Ssam
860189251Ssam	if (data && data->assoc_info.req_ies) {
861189251Ssam		len = data->assoc_info.req_ies_len;
862189251Ssam		os_memcpy(pos, &len, sizeof(int));
863189251Ssam		pos += sizeof(int);
864189251Ssam		os_memcpy(pos, data->assoc_info.req_ies, len);
865189251Ssam		pos += len;
866189251Ssam	} else {
867189251Ssam		len = 0;
868189251Ssam		os_memcpy(pos, &len, sizeof(int));
869189251Ssam		pos += sizeof(int);
870189251Ssam	}
871189251Ssam
872189251Ssam	if (data && data->assoc_info.resp_ies) {
873189251Ssam		len = data->assoc_info.resp_ies_len;
874189251Ssam		os_memcpy(pos, &len, sizeof(int));
875189251Ssam		pos += sizeof(int);
876189251Ssam		os_memcpy(pos, data->assoc_info.resp_ies, len);
877189251Ssam		pos += len;
878189251Ssam	} else {
879189251Ssam		len = 0;
880189251Ssam		os_memcpy(pos, &len, sizeof(int));
881189251Ssam		pos += sizeof(int);
882189251Ssam	}
883189251Ssam
884189251Ssam	if (data && data->assoc_info.beacon_ies) {
885189251Ssam		len = data->assoc_info.beacon_ies_len;
886189251Ssam		os_memcpy(pos, &len, sizeof(int));
887189251Ssam		pos += sizeof(int);
888189251Ssam		os_memcpy(pos, data->assoc_info.beacon_ies, len);
889189251Ssam		pos += len;
890189251Ssam	} else {
891189251Ssam		len = 0;
892189251Ssam		os_memcpy(pos, &len, sizeof(int));
893189251Ssam		pos += sizeof(int);
894189251Ssam	}
895189251Ssam
896189251Ssam	wpa_priv_send_event(iface, event, buf, buflen);
897189251Ssam
898189251Ssam	os_free(buf);
899189251Ssam}
900189251Ssam
901189251Ssam
902189251Ssamstatic void wpa_priv_send_interface_status(struct wpa_priv_interface *iface,
903189251Ssam					   union wpa_event_data *data)
904189251Ssam{
905189251Ssam	int ievent;
906189251Ssam	size_t len, maxlen;
907189251Ssam	u8 *buf;
908189251Ssam	char *ifname;
909189251Ssam
910189251Ssam	if (data == NULL)
911189251Ssam		return;
912189251Ssam
913189251Ssam	ievent = data->interface_status.ievent;
914189251Ssam	maxlen = sizeof(data->interface_status.ifname);
915189251Ssam	ifname = data->interface_status.ifname;
916189251Ssam	for (len = 0; len < maxlen && ifname[len]; len++)
917189251Ssam		;
918189251Ssam
919189251Ssam	buf = os_malloc(sizeof(int) + len);
920189251Ssam	if (buf == NULL)
921189251Ssam		return;
922189251Ssam
923189251Ssam	os_memcpy(buf, &ievent, sizeof(int));
924189251Ssam	os_memcpy(buf + sizeof(int), ifname, len);
925189251Ssam
926189251Ssam	wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS,
927189251Ssam			    buf, sizeof(int) + len);
928189251Ssam
929189251Ssam	os_free(buf);
930189251Ssam
931189251Ssam}
932189251Ssam
933189251Ssam
934189251Ssamstatic void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
935189251Ssam				      union wpa_event_data *data)
936189251Ssam{
937189251Ssam	size_t len;
938189251Ssam	u8 *buf, *pos;
939189251Ssam
940189251Ssam	if (data == NULL || data->ft_ies.ies == NULL)
941189251Ssam		return;
942189251Ssam
943189251Ssam	len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len;
944189251Ssam	buf = os_malloc(len);
945189251Ssam	if (buf == NULL)
946189251Ssam		return;
947189251Ssam
948189251Ssam	pos = buf;
949189251Ssam	os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int));
950189251Ssam	pos += sizeof(int);
951189251Ssam	os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN);
952189251Ssam	pos += ETH_ALEN;
953189251Ssam	os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len);
954189251Ssam
955189251Ssam	wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len);
956189251Ssam
957189251Ssam	os_free(buf);
958189251Ssam
959189251Ssam}
960189251Ssam
961189251Ssam
962189251Ssamvoid wpa_supplicant_event(void *ctx, wpa_event_type event,
963189251Ssam			  union wpa_event_data *data)
964189251Ssam{
965189251Ssam	struct wpa_priv_interface *iface = ctx;
966189251Ssam
967189251Ssam	wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event);
968189251Ssam
969189251Ssam	if (!iface->wpas_registered) {
970189251Ssam		wpa_printf(MSG_DEBUG, "Driver event received, but "
971189251Ssam			   "wpa_supplicant not registered");
972189251Ssam		return;
973189251Ssam	}
974189251Ssam
975189251Ssam	switch (event) {
976189251Ssam	case EVENT_ASSOC:
977189251Ssam		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data);
978189251Ssam		break;
979189251Ssam	case EVENT_DISASSOC:
980189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0);
981189251Ssam		break;
982189251Ssam	case EVENT_ASSOCINFO:
983189251Ssam		if (data == NULL)
984189251Ssam			return;
985189251Ssam		wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data);
986189251Ssam		break;
987189251Ssam	case EVENT_MICHAEL_MIC_FAILURE:
988189251Ssam		if (data == NULL)
989189251Ssam			return;
990189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
991189251Ssam				    &data->michael_mic_failure.unicast,
992189251Ssam				    sizeof(int));
993189251Ssam		break;
994189251Ssam	case EVENT_SCAN_RESULTS:
995189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
996189251Ssam				    0);
997189251Ssam		break;
998189251Ssam	case EVENT_INTERFACE_STATUS:
999189251Ssam		wpa_priv_send_interface_status(iface, data);
1000189251Ssam		break;
1001189251Ssam	case EVENT_PMKID_CANDIDATE:
1002189251Ssam		if (data == NULL)
1003189251Ssam			return;
1004189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE,
1005189251Ssam				    &data->pmkid_candidate,
1006189251Ssam				    sizeof(struct pmkid_candidate));
1007189251Ssam		break;
1008189251Ssam	case EVENT_STKSTART:
1009189251Ssam		if (data == NULL)
1010189251Ssam			return;
1011189251Ssam		wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
1012189251Ssam				    &data->stkstart.peer, ETH_ALEN);
1013189251Ssam		break;
1014189251Ssam	case EVENT_FT_RESPONSE:
1015189251Ssam		wpa_priv_send_ft_response(iface, data);
1016189251Ssam		break;
1017189251Ssam	default:
1018189251Ssam		wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
1019189251Ssam			   event);
1020189251Ssam		break;
1021189251Ssam	}
1022189251Ssam}
1023189251Ssam
1024189251Ssam
1025189251Ssamvoid wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
1026189251Ssam			     const u8 *buf, size_t len)
1027189251Ssam{
1028189251Ssam	struct wpa_priv_interface *iface = ctx;
1029189251Ssam	struct msghdr msg;
1030189251Ssam	struct iovec io[3];
1031189251Ssam	int event = PRIVSEP_EVENT_RX_EAPOL;
1032189251Ssam
1033189251Ssam	wpa_printf(MSG_DEBUG, "RX EAPOL from driver");
1034189251Ssam	io[0].iov_base = &event;
1035189251Ssam	io[0].iov_len = sizeof(event);
1036189251Ssam	io[1].iov_base = (u8 *) src_addr;
1037189251Ssam	io[1].iov_len = ETH_ALEN;
1038189251Ssam	io[2].iov_base = (u8 *) buf;
1039189251Ssam	io[2].iov_len = len;
1040189251Ssam
1041189251Ssam	os_memset(&msg, 0, sizeof(msg));
1042189251Ssam	msg.msg_iov = io;
1043189251Ssam	msg.msg_iovlen = 3;
1044189251Ssam	msg.msg_name = &iface->drv_addr;
1045189251Ssam	msg.msg_namelen = sizeof(iface->drv_addr);
1046189251Ssam
1047189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0)
1048189251Ssam		perror("sendmsg(wpas_socket)");
1049189251Ssam}
1050189251Ssam
1051189251Ssam
1052189251Ssam#ifdef CONFIG_CLIENT_MLME
1053189251Ssamvoid wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features,
1054189251Ssam					 size_t num_hw_features)
1055189251Ssam{
1056189251Ssam	size_t i;
1057189251Ssam
1058189251Ssam	if (hw_features == NULL)
1059189251Ssam		return;
1060189251Ssam
1061189251Ssam	for (i = 0; i < num_hw_features; i++) {
1062189251Ssam		os_free(hw_features[i].channels);
1063189251Ssam		os_free(hw_features[i].rates);
1064189251Ssam	}
1065189251Ssam
1066189251Ssam	os_free(hw_features);
1067189251Ssam}
1068189251Ssam
1069189251Ssam
1070189251Ssamvoid wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len,
1071189251Ssam			   struct ieee80211_rx_status *rx_status)
1072189251Ssam{
1073189251Ssam	struct wpa_priv_interface *iface = ctx;
1074189251Ssam	struct msghdr msg;
1075189251Ssam	struct iovec io[3];
1076189251Ssam	int event = PRIVSEP_EVENT_STA_RX;
1077189251Ssam
1078189251Ssam	wpa_printf(MSG_DEBUG, "STA RX from driver");
1079189251Ssam	io[0].iov_base = &event;
1080189251Ssam	io[0].iov_len = sizeof(event);
1081189251Ssam	io[1].iov_base = (u8 *) rx_status;
1082189251Ssam	io[1].iov_len = sizeof(*rx_status);
1083189251Ssam	io[2].iov_base = (u8 *) buf;
1084189251Ssam	io[2].iov_len = len;
1085189251Ssam
1086189251Ssam	os_memset(&msg, 0, sizeof(msg));
1087189251Ssam	msg.msg_iov = io;
1088189251Ssam	msg.msg_iovlen = 3;
1089189251Ssam	msg.msg_name = &iface->drv_addr;
1090189251Ssam	msg.msg_namelen = sizeof(iface->drv_addr);
1091189251Ssam
1092189251Ssam	if (sendmsg(iface->fd, &msg, 0) < 0)
1093189251Ssam		perror("sendmsg(wpas_socket)");
1094189251Ssam}
1095189251Ssam#endif /* CONFIG_CLIENT_MLME */
1096189251Ssam
1097189251Ssam
1098189251Ssamstatic void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
1099189251Ssam{
1100189251Ssam	wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
1101189251Ssam	eloop_terminate();
1102189251Ssam}
1103189251Ssam
1104189251Ssam
1105189251Ssamstatic void wpa_priv_fd_workaround(void)
1106189251Ssam{
1107189251Ssam#ifdef __linux__
1108189251Ssam	int s, i;
1109189251Ssam	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
1110189251Ssam	 * fd 0, 1, and 2 closed. This will cause some issues because many
1111189251Ssam	 * places in wpa_supplicant are still printing out to stdout. As a
1112189251Ssam	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
1113189251Ssam	 * sockets. */
1114189251Ssam	for (i = 0; i < 3; i++) {
1115189251Ssam		s = open("/dev/null", O_RDWR);
1116189251Ssam		if (s > 2) {
1117189251Ssam			close(s);
1118189251Ssam			break;
1119189251Ssam		}
1120189251Ssam	}
1121189251Ssam#endif /* __linux__ */
1122189251Ssam}
1123189251Ssam
1124189251Ssam
1125189251Ssamstatic void usage(void)
1126189251Ssam{
1127189251Ssam	printf("wpa_priv v" VERSION_STR "\n"
1128189251Ssam	       "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
1129189251Ssam	       "contributors\n"
1130189251Ssam	       "\n"
1131189251Ssam	       "usage:\n"
1132189251Ssam	       "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
1133189251Ssam	       "[driver:ifname ...]\n");
1134189251Ssam}
1135189251Ssam
1136189251Ssam
1137189251Ssamextern int wpa_debug_level;
1138189251Ssam
1139189251Ssamint main(int argc, char *argv[])
1140189251Ssam{
1141189251Ssam	int c, i;
1142189251Ssam	int ret = -1;
1143189251Ssam	char *pid_file = NULL;
1144189251Ssam	int daemonize = 0;
1145189251Ssam	char *ctrl_dir = "/var/run/wpa_priv";
1146189251Ssam	struct wpa_priv_interface *interfaces = NULL, *iface;
1147189251Ssam
1148189251Ssam	if (os_program_init())
1149189251Ssam		return -1;
1150189251Ssam
1151189251Ssam	wpa_priv_fd_workaround();
1152189251Ssam
1153189251Ssam	for (;;) {
1154189251Ssam		c = getopt(argc, argv, "Bc:dP:");
1155189251Ssam		if (c < 0)
1156189251Ssam			break;
1157189251Ssam		switch (c) {
1158189251Ssam		case 'B':
1159189251Ssam			daemonize++;
1160189251Ssam			break;
1161189251Ssam		case 'c':
1162189251Ssam			ctrl_dir = optarg;
1163189251Ssam			break;
1164189251Ssam		case 'd':
1165189251Ssam			wpa_debug_level--;
1166189251Ssam			break;
1167189251Ssam		case 'P':
1168189251Ssam			pid_file = os_rel2abs_path(optarg);
1169189251Ssam			break;
1170189251Ssam		default:
1171189251Ssam			usage();
1172189251Ssam			goto out;
1173189251Ssam		}
1174189251Ssam	}
1175189251Ssam
1176189251Ssam	if (optind >= argc) {
1177189251Ssam		usage();
1178189251Ssam		goto out;
1179189251Ssam	}
1180189251Ssam
1181189251Ssam	wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
1182189251Ssam
1183189251Ssam	if (eloop_init(NULL)) {
1184189251Ssam		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
1185189251Ssam		goto out;
1186189251Ssam	}
1187189251Ssam
1188189251Ssam	for (i = optind; i < argc; i++) {
1189189251Ssam		wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
1190189251Ssam		iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
1191189251Ssam		if (iface == NULL)
1192189251Ssam			goto out;
1193189251Ssam		iface->next = interfaces;
1194189251Ssam		interfaces = iface;
1195189251Ssam	}
1196189251Ssam
1197189251Ssam	if (daemonize && os_daemonize(pid_file))
1198189251Ssam		goto out;
1199189251Ssam
1200189251Ssam	eloop_register_signal_terminate(wpa_priv_terminate, NULL);
1201189251Ssam	eloop_run();
1202189251Ssam
1203189251Ssam	ret = 0;
1204189251Ssam
1205189251Ssamout:
1206189251Ssam	iface = interfaces;
1207189251Ssam	while (iface) {
1208189251Ssam		struct wpa_priv_interface *prev = iface;
1209189251Ssam		iface = iface->next;
1210189251Ssam		wpa_priv_interface_deinit(prev);
1211189251Ssam	}
1212189251Ssam
1213189251Ssam	eloop_destroy();
1214189251Ssam
1215189251Ssam	os_daemonize_terminate(pid_file);
1216189251Ssam	os_free(pid_file);
1217189251Ssam	os_program_deinit();
1218189251Ssam
1219189251Ssam	return ret;
1220189251Ssam}
1221