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))
49 #define CLAMP(x, low, high) \
50 (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
53 wpa_open(const char *ifname, char **path)
58 struct sockaddr_un sun;
60 if ((fd = socket(AF_UNIX,
61 SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
63 memset(&sun, 0, sizeof(sun));
64 sun.sun_family = AF_UNIX;
65 snprintf(sun.sun_path, sizeof(sun.sun_path),
66 "/tmp/libdhcpcd-wpa-%d.%d", getpid(), counter++);
67 *path = strdup(sun.sun_path);
68 len = (socklen_t)SUN_LEN(&sun);
69 if (bind(fd, (struct sockaddr *)&sun, len) == -1) {
76 snprintf(sun.sun_path, sizeof(sun.sun_path),
77 WPA_CTRL_DIR "/%s", ifname);
78 len = (socklen_t)SUN_LEN(&sun);
79 if (connect(fd, (struct sockaddr *)&sun, len) == -1) {
91 wpa_cmd(int fd, const char *cmd, char *buffer, size_t len)
99 bytes = write(fd, cmd, strlen(cmd));
100 if (bytes == -1 || bytes == 0)
102 if (buffer == NULL || len == 0)
105 pfd.events = POLLIN | POLLHUP;
107 retval = poll(&pfd, 1, 2000);
110 if (retval == 0 || !(pfd.revents & (POLLIN | POLLHUP)))
113 bytes = read(fd, buffer, len == 1 ? 1 : len - 1);
115 buffer[bytes] = '\0';
120 dhcpcd_wpa_command(DHCPCD_WPA *wpa, const char *cmd)
125 bytes = wpa_cmd(wpa->command_fd, cmd, buf, sizeof(buf));
126 return (bytes == -1 || bytes == 0 ||
127 strcmp(buf, "OK\n")) ? false : true;
131 dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
135 cmdlen = strlen(cmd);
136 nlen = cmdlen + strlen(arg) + 2;
137 if (!dhcpcd_realloc(wpa->con, nlen))
139 strlcpy(wpa->con->buf, cmd, wpa->con->buflen);
140 wpa->con->buf[cmdlen] = ' ';
141 strlcpy(wpa->con->buf + cmdlen + 1, arg, wpa->con->buflen - 1 - cmdlen);
142 return dhcpcd_wpa_command(wpa, wpa->con->buf);
146 dhcpcd_attach_detach(DHCPCD_WPA *wpa, bool attach)
151 if (wpa->attached == attach)
154 bytes = wpa_cmd(wpa->listen_fd, attach > 0 ? "ATTACH" : "DETACH",
156 if (bytes == -1 || bytes == 0 || strcmp(buf, "OK\n"))
159 wpa->attached = attach;
164 dhcpcd_wpa_scan(DHCPCD_WPA *wpa)
167 return dhcpcd_wpa_command(wpa, "SCAN");
171 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
183 dhcpcd_strtoi(int *val, const char *s)
187 l = strtol(s, NULL, 0);
188 if (l >= INT_MIN && l <= INT_MAX)
194 static DHCPCD_WI_SCAN *
195 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
199 DHCPCD_WI_SCAN *wis, *w, *l;
200 char *s, *p, buf[32];
202 if (!dhcpcd_realloc(wpa->con, 2048))
205 for (i = 0; i < 1000; i++) {
206 snprintf(buf, sizeof(buf), "BSS %zu", i);
207 bytes = wpa_cmd(wpa->command_fd, buf,
208 wpa->con->buf, wpa->con->buflen);
209 if (bytes == 0 || bytes == -1 ||
210 strncmp(wpa->con->buf, "FAIL", 4) == 0)
213 w = calloc(1, sizeof(*w));
221 while ((s = strsep(&p, "\n"))) {
224 if (strncmp(s, "bssid=", 6) == 0)
225 strlcpy(w->bssid, s + 6, sizeof(w->bssid));
226 else if (strncmp(s, "freq=", 5) == 0)
227 dhcpcd_strtoi(&w->frequency, s + 5);
228 // else if (strncmp(s, "beacon_int=", 11) == 0)
230 else if (strncmp(s, "qual=", 5) == 0)
231 dhcpcd_strtoi(&w->quality.value, s + 5);
232 else if (strncmp(s, "noise=", 6) == 0)
233 dhcpcd_strtoi(&w->noise.value, s + 6);
234 else if (strncmp(s, "level=", 6) == 0)
235 dhcpcd_strtoi(&w->level.value, s + 6);
236 else if (strncmp(s, "flags=", 6) == 0)
237 strlcpy(w->flags, s + 6, sizeof(w->flags));
238 else if (strncmp(s, "ssid=", 5) == 0)
239 strlcpy(w->ssid, s + 5, sizeof(w->ssid));
242 w->strength.value = w->level.value;
243 if (w->strength.value > 110 && w->strength.value < 256)
244 /* Convert WEXT level to dBm */
245 w->strength.value -= 256;
247 if (w->strength.value < 0) {
250 abs(CLAMP(w->strength.value, -100, -40) + 40);
252 100 - ((100 * w->strength.value) / 60);
254 /* Assume quality percentage */
255 w->strength.value = CLAMP(w->strength.value, 0, 100);
263 dhcpcd_wi_scans(DHCPCD_IF *i)
266 DHCPCD_WI_SCAN *wis, *w;
268 DHCPCD_WI_HIST *h, *hl;
270 wpa = dhcpcd_wpa_find(i->con, i->ifname);
273 wis = dhcpcd_wpa_scans_read(wpa);
274 for (w = wis; w; w = w->next) {
277 w->quality.average = w->quality.value;
278 w->noise.average = w->noise.value;
279 w->level.average = w->level.value;
280 w->strength.average = w->strength.value;
282 for (h = wpa->con->wi_history; h; h = h->next) {
283 if (strcmp(h->ifname, i->ifname) == 0 &&
284 strcmp(h->bssid, wis->bssid) == 0)
286 w->quality.average += h->quality;
287 w->noise.average += h->noise;
288 w->level.average += h->level;
289 w->strength.average += h->strength;
290 if (++nh == DHCPCD_WI_HIST_MAX) {
300 w->quality.average /= nh;
301 w->noise.average /= nh;
302 w->level.average /= nh;
303 w->strength.average /= nh;
305 h = malloc(sizeof(*h));
307 strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
308 strlcpy(h->bssid, w->bssid, sizeof(h->bssid));
309 h->quality = w->quality.value;
310 h->noise = w->noise.value;
311 h->level = w->level.value;
312 h->strength = w->strength.value;
313 h->next = wpa->con->wi_history;
314 wpa->con->wi_history = h;
322 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
325 return dhcpcd_wpa_command(wpa, "REASSOCIATE");
329 dhcpcd_wpa_disconnect(DHCPCD_WPA *wpa)
332 return dhcpcd_wpa_command(wpa, "DISCONNECT");
336 dhcpcd_wpa_config_write(DHCPCD_WPA *wpa)
339 return dhcpcd_wpa_command(wpa, "SAVE_CONFIG");
343 dhcpcd_wpa_network(DHCPCD_WPA *wpa, const char *cmd, int id)
347 len = strlen(cmd) + 32;
348 if (!dhcpcd_realloc(wpa->con, len))
350 snprintf(wpa->con->buf, wpa->con->buflen, "%s %d", cmd, id);
351 return dhcpcd_wpa_command(wpa, wpa->con->buf);
355 dhcpcd_wpa_network_disable(DHCPCD_WPA *wpa, int id)
358 return dhcpcd_wpa_network(wpa, "DISABLE_NETWORK", id);
362 dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
365 return dhcpcd_wpa_network(wpa, "ENABLE_NETWORK", id);
369 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
372 return dhcpcd_wpa_network(wpa, "REMOVE_NETWORK", id);
376 dhcpcd_wpa_network_get(DHCPCD_WPA *wpa, int id, const char *param)
380 if (!dhcpcd_realloc(wpa->con, 2048))
382 snprintf(wpa->con->buf, wpa->con->buflen, "GET_NETWORK %d %s",
384 bytes = wpa_cmd(wpa->command_fd, wpa->con->buf,
385 wpa->con->buf, wpa->con->buflen);
386 if (bytes == 0 || bytes == -1)
388 if (strcmp(wpa->con->buf, "FAIL\n") == 0) {
392 return wpa->con->buf;
396 dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
397 const char *param, const char *value)
401 len = strlen("SET_NETWORK") + 32 + strlen(param) + strlen(value) + 3;
402 if (!dhcpcd_realloc(wpa->con, len))
404 snprintf(wpa->con->buf, wpa->con->buflen, "SET_NETWORK %d %s %s",
406 return dhcpcd_wpa_command(wpa, wpa->con->buf);
410 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
413 char *s, *t, *ssid, *bssid, *flags;
416 dhcpcd_realloc(wpa->con, 2048);
417 bytes = wpa_cmd(wpa->command_fd, "LIST_NETWORKS",
418 wpa->con->buf, wpa->con->buflen);
419 if (bytes == 0 || bytes == -1)
422 s = strchr(wpa->con->buf, '\n');
425 while ((t = strsep(&s, "\b"))) {
428 ssid = strchr(t, '\t');
432 bssid = strchr(ssid, '\t');
436 flags = strchr(bssid, '\t');
440 l = strtol(t, NULL, 0);
441 if (l < 0 || l > INT_MAX) {
445 if (strcmp(ssid, fssid) == 0)
453 dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
458 dhcpcd_realloc(wpa->con, 32);
459 bytes = wpa_cmd(wpa->command_fd, "ADD_NETWORK",
460 wpa->con->buf, sizeof(wpa->con->buf));
461 if (bytes == 0 || bytes == -1)
463 l = strtol(wpa->con->buf, NULL, 0);
464 if (l < 0 || l > INT_MAX) {
472 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
476 id = dhcpcd_wpa_network_find(wpa, ssid);
478 id = dhcpcd_wpa_network_new(wpa);
483 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
488 if (wpa->command_fd == -1 || !wpa->open)
492 dhcpcd_attach_detach(wpa, false);
493 shutdown(wpa->command_fd, SHUT_RDWR);
494 shutdown(wpa->listen_fd, SHUT_RDWR);
496 if (wpa->con->wpa_status_cb)
497 wpa->con->wpa_status_cb(wpa, "down",
498 wpa->con->wpa_status_context);
500 close(wpa->command_fd);
501 wpa->command_fd = -1;
502 close(wpa->listen_fd);
504 unlink(wpa->command_path);
505 free(wpa->command_path);
506 wpa->command_path = NULL;
507 unlink(wpa->listen_path);
508 free(wpa->listen_path);
509 wpa->listen_path = NULL;
513 dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
517 for (wpa = con->wpa; wpa; wpa = wpa->next) {
518 if (strcmp(wpa->ifname, ifname) == 0)
526 dhcpcd_wpa_new(DHCPCD_CONNECTION *con, const char *ifname)
530 wpa = dhcpcd_wpa_find(con, ifname);
534 wpa = malloc(sizeof(*wpa));
539 strlcpy(wpa->ifname, ifname, sizeof(wpa->ifname));
540 wpa->command_fd = wpa->listen_fd = -1;
541 wpa->command_path = wpa->listen_path = NULL;
542 wpa->next = con->wpa;
548 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
556 dhcpcd_wpa_if(DHCPCD_WPA *wpa)
559 return dhcpcd_get_if(wpa->con, wpa->ifname, "link");
563 dhcpcd_wpa_open(DHCPCD_WPA *wpa)
565 int cmd_fd, list_fd = -1;
566 char *cmd_path = NULL, *list_path = NULL;
568 if (wpa->listen_fd != -1) {
573 return wpa->listen_fd;
576 cmd_fd = wpa_open(wpa->ifname, &cmd_path);
580 list_fd = wpa_open(wpa->ifname, &list_path);
585 wpa->attached = false;
586 wpa->command_fd = cmd_fd;
587 wpa->command_path = cmd_path;
588 wpa->listen_fd = list_fd;
589 wpa->listen_path = list_path;
590 if (!dhcpcd_attach_detach(wpa, true)) {
591 dhcpcd_wpa_close(wpa);
595 if (wpa->con->wi_scanresults_cb)
596 wpa->con->wi_scanresults_cb(wpa,
597 wpa->con->wi_scanresults_context);
599 return wpa->listen_fd;
615 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
619 return wpa->open ? wpa->listen_fd : -1;
623 dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
624 void (*cb)(DHCPCD_WPA *, void *), void *context)
628 con->wi_scanresults_cb = cb;
629 con->wi_scanresults_context = context;
633 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
635 char buffer[256], *p;
639 bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
640 if ((ssize_t)bytes == -1 || bytes == 0) {
641 dhcpcd_wpa_close(wpa);
645 buffer[bytes] = '\0';
646 bytes = strlen(buffer);
647 if (buffer[bytes - 1] == ' ')
648 buffer[--bytes] = '\0';
649 for (p = buffer + 1; *p != '\0'; p++)
654 if (strcmp(p, "CTRL-EVENT-SCAN-RESULTS") == 0 &&
655 wpa->con->wi_scanresults_cb)
656 wpa->con->wi_scanresults_cb(wpa,
657 wpa->con->wi_scanresults_context);
662 dhcpcd_wpa_if_event(DHCPCD_IF *i)
667 if (i->wireless && strcmp(i->type, "link") == 0) {
668 if (strcmp(i->reason, "STOPPED") == 0) {
669 wpa = dhcpcd_wpa_find(i->con, i->ifname);
671 dhcpcd_wpa_close(wpa);
672 } else if (i->con->wpa_started) {
673 wpa = dhcpcd_wpa_new(i->con, i->ifname);
674 if (wpa && wpa->listen_fd == -1)
675 dhcpcd_wpa_open(wpa);
681 dhcpcd_wpa_start(DHCPCD_CONNECTION *con)
686 con->wpa_started = true;
688 for (i = con->interfaces; i; i = i->next)
689 dhcpcd_wpa_if_event(i);
693 dhcpcd_wpa_configure_psk(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
695 const char *mgmt, *var;
704 id = dhcpcd_wpa_network_find_new(wpa, s->ssid);
706 return DHCPCD_WPA_ERR;
708 if (strcmp(s->flags, "[WEP]") == 0) {
716 if (!dhcpcd_wpa_network_set(wpa, id, "key_mgmt", mgmt))
717 return DHCPCD_WPA_ERR_SET;
720 psk_len = strlen(psk);
723 npsk = malloc(psk_len + 3);
725 return DHCPCD_WPA_ERR;
728 memcpy(npsk + 1, psk, psk_len);
729 npsk[psk_len + 1] = '"';
730 npsk[psk_len + 2] = '\0';
731 r = dhcpcd_wpa_network_set(wpa, id, var, npsk);
734 return DHCPCD_WPA_ERR_SET_PSK;
736 if (!dhcpcd_wpa_network_enable(wpa, id))
737 return DHCPCD_WPA_ERR_ENABLE;
738 if (!dhcpcd_wpa_reassociate(wpa))
739 return DHCPCD_WPA_ERR_ASSOC;
740 if (!dhcpcd_wpa_config_write(wpa))
741 return DHCPCD_WPA_ERR_WRITE;
742 return DHCPCD_WPA_SUCCESS;