3 * Copyright 2009-2014 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/socket.h>
46 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
50 wpa_open(const char *ifname, char **path)
55 struct sockaddr_un sun;
57 if ((fd = socket(AF_UNIX,
58 SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
60 memset(&sun, 0, sizeof(sun));
61 sun.sun_family = AF_UNIX;
62 snprintf(sun.sun_path, sizeof(sun.sun_path),
63 "/tmp/libdhcpcd-wpa-%d.%d", getpid(), counter++);
64 *path = strdup(sun.sun_path);
65 len = (socklen_t)SUN_LEN(&sun);
66 if (bind(fd, (struct sockaddr *)&sun, len) == -1) {
73 snprintf(sun.sun_path, sizeof(sun.sun_path),
74 WPA_CTRL_DIR "/%s", ifname);
75 len = (socklen_t)SUN_LEN(&sun);
76 if (connect(fd, (struct sockaddr *)&sun, len) == -1) {
88 wpa_cmd(int fd, const char *cmd, char *buffer, size_t len)
96 bytes = write(fd, cmd, strlen(cmd));
97 if (bytes == -1 || bytes == 0)
99 if (buffer == NULL || len == 0)
102 pfd.events = POLLIN | POLLHUP;
104 retval = poll(&pfd, 1, 2000);
107 if (retval == 0 || !(pfd.revents & (POLLIN | POLLHUP)))
110 bytes = read(fd, buffer, len == 1 ? 1 : len - 1);
112 buffer[bytes] = '\0';
117 dhcpcd_wpa_command(DHCPCD_WPA *wpa, const char *cmd)
122 bytes = wpa_cmd(wpa->command_fd, cmd, buf, sizeof(buf));
123 return (bytes == -1 || bytes == 0 ||
124 strcmp(buf, "OK\n")) ? false : true;
128 dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
132 cmdlen = strlen(cmd);
133 nlen = cmdlen + strlen(arg) + 2;
134 if (!dhcpcd_realloc(wpa->con, nlen))
136 strlcpy(wpa->con->buf, cmd, wpa->con->buflen);
137 wpa->con->buf[cmdlen] = ' ';
138 strlcpy(wpa->con->buf + cmdlen + 1, arg, wpa->con->buflen - 1 - cmdlen);
139 return dhcpcd_wpa_command(wpa, wpa->con->buf);
143 dhcpcd_attach_detach(DHCPCD_WPA *wpa, int attach)
148 if (wpa->attached == attach)
151 bytes = wpa_cmd(wpa->listen_fd, attach > 0 ? "ATTACH" : "DETACH",
153 if (bytes == -1 || bytes == 0 || strcmp(buf, "OK\n"))
156 wpa->attached = attach;
161 dhcpcd_wpa_scan(DHCPCD_WPA *wpa)
164 return dhcpcd_wpa_command(wpa, "SCAN");
168 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
180 dhcpcd_strtoi(int *val, const char *s)
184 l = strtol(s, NULL, 0);
185 if (l >= INT_MIN && l <= INT_MAX)
191 static DHCPCD_WI_SCAN *
192 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
196 DHCPCD_WI_SCAN *wis, *w, *l;
197 char *s, *p, buf[32];
199 if (!dhcpcd_realloc(wpa->con, 2048))
202 for (i = 0; i < 1000; i++) {
203 snprintf(buf, sizeof(buf), "BSS %zu", i);
204 bytes = wpa_cmd(wpa->command_fd, buf,
205 wpa->con->buf, wpa->con->buflen);
206 if (bytes == 0 || bytes == -1 ||
207 strncmp(wpa->con->buf, "FAIL", 4) == 0)
210 w = calloc(1, sizeof(*w));
218 while ((s = strsep(&p, "\n"))) {
221 if (strncmp(s, "bssid=", 6) == 0)
222 strlcpy(w->bssid, s + 6, sizeof(w->bssid));
223 else if (strncmp(s, "freq=", 5) == 0)
224 dhcpcd_strtoi(&w->frequency, s + 5);
225 // else if (strncmp(s, "beacon_int=", 11) == 0)
227 else if (strncmp(s, "qual=", 5) == 0)
228 dhcpcd_strtoi(&w->quality.value, s + 5);
229 else if (strncmp(s, "noise=", 6) == 0)
230 dhcpcd_strtoi(&w->noise.value, s + 6);
231 else if (strncmp(s, "level=", 6) == 0)
232 dhcpcd_strtoi(&w->level.value, s + 6);
233 else if (strncmp(s, "flags=", 6) == 0)
234 strlcpy(w->flags, s + 6, sizeof(w->flags));
235 else if (strncmp(s, "ssid=", 5) == 0)
236 strlcpy(w->ssid, s + 5, sizeof(w->ssid));
243 dhcpcd_wi_scans(DHCPCD_IF *i)
246 DHCPCD_WI_SCAN *wis, *w;
248 DHCPCD_WI_HIST *h, *hl;
250 wpa = dhcpcd_wpa_find(i->con, i->ifname);
253 wis = dhcpcd_wpa_scans_read(wpa);
254 for (w = wis; w; w = w->next) {
257 w->quality.average = w->quality.value;
258 w->noise.average = w->noise.value;
259 w->level.average = w->level.value;
261 for (h = wpa->con->wi_history; h; h = h->next) {
262 if (strcmp(h->ifname, i->ifname) == 0 &&
263 strcmp(h->bssid, wis->bssid) == 0)
265 w->quality.average += h->quality;
266 w->noise.average += h->noise;
267 w->level.average += h->level;
268 if (++nh == DHCPCD_WI_HIST_MAX) {
278 w->quality.average /= nh;
279 w->noise.average /= nh;
280 w->level.average /= nh;
282 h = malloc(sizeof(*h));
284 strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
285 strlcpy(h->bssid, w->bssid, sizeof(h->bssid));
286 h->quality = w->quality.value;
287 h->noise = w->noise.value;
288 h->level = w->level.value;
289 h->next = wpa->con->wi_history;
290 wpa->con->wi_history = h;
298 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
301 return dhcpcd_wpa_command(wpa, "REASSOCIATE");
305 dhcpcd_wpa_disconnect(DHCPCD_WPA *wpa)
308 return dhcpcd_wpa_command(wpa, "DISCONNECT");
312 dhcpcd_wpa_config_write(DHCPCD_WPA *wpa)
315 return dhcpcd_wpa_command(wpa, "SAVE_CONFIG");
319 dhcpcd_wpa_network(DHCPCD_WPA *wpa, const char *cmd, int id)
323 len = strlen(cmd) + 32;
324 if (!dhcpcd_realloc(wpa->con, len))
326 snprintf(wpa->con->buf, wpa->con->buflen, "%s %d", cmd, id);
327 return dhcpcd_wpa_command(wpa, wpa->con->buf);
331 dhcpcd_wpa_network_disable(DHCPCD_WPA *wpa, int id)
334 return dhcpcd_wpa_network(wpa, "DISABLE_NETWORK", id);
338 dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
341 return dhcpcd_wpa_network(wpa, "ENABLE_NETWORK", id);
345 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
348 return dhcpcd_wpa_network(wpa, "REMOVE_NETWORK", id);
352 dhcpcd_wpa_network_get(DHCPCD_WPA *wpa, int id, const char *param)
356 if (!dhcpcd_realloc(wpa->con, 2048))
358 snprintf(wpa->con->buf, wpa->con->buflen, "GET_NETWORK %d %s",
360 bytes = wpa_cmd(wpa->command_fd, wpa->con->buf,
361 wpa->con->buf, wpa->con->buflen);
362 if (bytes == 0 || bytes == -1)
364 if (strcmp(wpa->con->buf, "FAIL\n") == 0) {
368 return wpa->con->buf;
372 dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
373 const char *param, const char *value)
377 len = strlen("SET_NETWORK") + 32 + strlen(param) + strlen(value) + 3;
378 if (!dhcpcd_realloc(wpa->con, len))
380 snprintf(wpa->con->buf, wpa->con->buflen, "SET_NETWORK %d %s %s",
382 return dhcpcd_wpa_command(wpa, wpa->con->buf);
386 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
389 char *s, *t, *ssid, *bssid, *flags;
392 dhcpcd_realloc(wpa->con, 2048);
393 bytes = wpa_cmd(wpa->command_fd, "LIST_NETWORKS",
394 wpa->con->buf, wpa->con->buflen);
395 if (bytes == 0 || bytes == -1)
398 s = strchr(wpa->con->buf, '\n');
401 while ((t = strsep(&s, "\b"))) {
404 ssid = strchr(t, '\t');
408 bssid = strchr(ssid, '\t');
412 flags = strchr(bssid, '\t');
416 l = strtol(t, NULL, 0);
417 if (l < 0 || l > INT_MAX) {
421 if (strcmp(ssid, fssid) == 0)
429 dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
434 dhcpcd_realloc(wpa->con, 32);
435 bytes = wpa_cmd(wpa->command_fd, "ADD_NETWORK",
436 wpa->con->buf, sizeof(wpa->con->buf));
437 if (bytes == 0 || bytes == -1)
439 l = strtol(wpa->con->buf, NULL, 0);
440 if (l < 0 || l > INT_MAX) {
448 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
452 id = dhcpcd_wpa_network_find(wpa, ssid);
454 id = dhcpcd_wpa_network_new(wpa);
459 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
462 if (wpa->command_fd == -1)
465 dhcpcd_attach_detach(wpa, -1);
466 shutdown(wpa->command_fd, SHUT_RDWR);
467 wpa->command_fd = -1;
468 shutdown(wpa->listen_fd, SHUT_RDWR);
470 unlink(wpa->command_path);
471 free(wpa->command_path);
472 wpa->command_path = NULL;
473 unlink(wpa->listen_path);
474 free(wpa->listen_path);
475 wpa->listen_path = NULL;
477 if (wpa->con->wpa_status_cb)
478 wpa->con->wpa_status_cb(wpa, "down",
479 wpa->con->wpa_status_context);
484 dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
488 for (wpa = con->wpa; wpa; wpa = wpa->next) {
489 if (strcmp(wpa->ifname, ifname) == 0)
496 dhcpcd_wpa_new(DHCPCD_CONNECTION *con, const char *ifname)
500 wpa = dhcpcd_wpa_find(con, ifname);
504 wpa = malloc(sizeof(*wpa));
509 strlcpy(wpa->ifname, ifname, sizeof(wpa->ifname));
510 wpa->command_fd = wpa->listen_fd = -1;
511 wpa->command_path = wpa->listen_path = NULL;
512 wpa->next = con->wpa;
518 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
525 dhcpcd_wpa_if(DHCPCD_WPA *wpa)
528 return dhcpcd_get_if(wpa->con, wpa->ifname, "link");
532 dhcpcd_wpa_open(DHCPCD_WPA *wpa)
534 int cmd_fd, list_fd = -1;
535 char *cmd_path = NULL, *list_path = NULL;
537 if (wpa->listen_fd != -1)
538 return wpa->listen_fd;
540 cmd_fd = wpa_open(wpa->ifname, &cmd_path);
544 list_fd = wpa_open(wpa->ifname, &list_path);
549 wpa->command_fd = cmd_fd;
550 wpa->command_path = cmd_path;
551 wpa->listen_fd = list_fd;
552 wpa->listen_path = list_path;
553 if (!dhcpcd_attach_detach(wpa, 1)) {
554 dhcpcd_wpa_close(wpa);
558 if (wpa->con->wi_scanresults_cb)
559 wpa->con->wi_scanresults_cb(wpa,
560 wpa->con->wi_scanresults_context);
562 return wpa->listen_fd;
578 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
581 return wpa->listen_fd;
585 dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
586 void (*cb)(DHCPCD_WPA *, void *), void *context)
589 con->wi_scanresults_cb = cb;
590 con->wi_scanresults_context = context;
594 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
596 char buffer[256], *p;
599 bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
600 if ((ssize_t)bytes == -1 || bytes == 0) {
601 dhcpcd_wpa_close(wpa);
605 buffer[bytes] = '\0';
606 bytes = strlen(buffer);
607 if (buffer[bytes - 1] == ' ')
608 buffer[--bytes] = '\0';
609 for (p = buffer + 1; *p != '\0'; p++)
614 if (strcmp(p, "CTRL-EVENT-SCAN-RESULTS") == 0 &&
615 wpa->con->wi_scanresults_cb)
616 wpa->con->wi_scanresults_cb(wpa,
617 wpa->con->wi_scanresults_context);
622 dhcpcd_wpa_if_event(DHCPCD_IF *i)
627 if (strcmp(i->reason, "STOPPING") == 0) {
628 wpa = dhcpcd_wpa_find(i->con, i->ifname);
630 dhcpcd_wpa_close(wpa);
632 wpa = dhcpcd_wpa_new(i->con, i->ifname);
633 dhcpcd_wpa_open(wpa);