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(¶ms, 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, ¶ms); 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