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
30 #include <sys/socket.h>
34 #include <arpa/inet.h>
60 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
63 static const char * const dhcpcd_types[] =
64 { "link", "ipv4", "ra", "dhcp6", NULL };
67 dhcpcd_command_fd(DHCPCD_CONNECTION *con,
68 int fd, bool progname, const char *cmd, char **buffer)
78 /* Each command is \n terminated.
79 * Each argument is NULL seperated.
80 * We may need to send a space one day, so the API
81 * in this function may need to be improved */
84 pl = strlen(con->progname);
85 len = pl + 1 + cl + 1;
90 if (con->terminate_commands)
92 if (len > sizeof(buf)) {
98 memcpy(buf, con->progname, pl);
104 while ((p = strchr(p, ' ')) != NULL)
106 if (con->terminate_commands) {
111 if (write(fd, buf, len) == -1)
116 bytes = read(fd, buf, sizeof(size_t));
117 if (bytes == 0 || bytes == -1)
119 memcpy(&len, buf, sizeof(size_t));
120 nbuf = realloc(*buffer, len + 1);
124 bytes = read(fd, *buffer, len);
125 if (bytes != -1 && (size_t)bytes < len)
126 *buffer[bytes] = '\0';
131 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
135 if (!con->privileged) {
139 return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
143 dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
146 return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
150 dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
154 if (con->buflen < len) {
157 nbuf = realloc(con->buf, len);
167 dhcpcd_command_arg(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
175 cmdlen = strlen(cmd);
177 len = cmdlen + strlen(arg) + 2;
180 if (!dhcpcd_realloc(con, len))
182 strlcpy(con->buf, cmd, con->buflen);
184 con->buf[cmdlen] = ' ';
185 strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
188 return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
193 dhcpcd_connect(const char *path, int opts)
197 struct sockaddr_un sun;
200 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
204 memset(&sun, 0, sizeof(sun));
205 sun.sun_family = AF_UNIX;
206 strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
207 len = (socklen_t)SUN_LEN(&sun);
208 if (connect(fd, (struct sockaddr *)&sun, len) == 0)
215 get_value(const char *data, size_t len, const char *var)
224 while (data + vlen + 1 < end) {
225 if (strncmp(data, var, vlen) == 0) {
229 data += strlen(data) + 1;
231 if (p != NULL && *p != '\0')
237 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
242 return get_value(i->data, i->data_len, var);
246 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
257 l = strlcpy(p, prefix, plen);
258 if (l >= sizeof(pvar)) {
264 if (strlcpy(p, var, plen) >= plen) {
268 return dhcpcd_get_value(i, pvar);
272 strtobool(const char *var)
278 return (*var == '0' || *var == '\0' ||
279 strcmp(var, "false") == 0 ||
280 strcmp(var, "no") == 0) ? false : true;
284 get_status(DHCPCD_CONNECTION *con)
290 if (con->command_fd == -1)
293 if (con->listen_fd == -1)
296 if (con->interfaces == NULL)
297 return "initialised";
299 status = "disconnected";
300 for (i = con->interfaces; i; i = i->next) {
302 if (strcmp(i->type, "link")) {
303 status = "connected";
306 status = "connecting";
313 update_status(DHCPCD_CONNECTION *con, const char *nstatus)
318 nstatus = get_status(con);
319 if (con->status == NULL || strcmp(nstatus, con->status)) {
320 con->status = nstatus;
322 con->status_cb(con, con->status, con->status_context);
327 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
331 return con->interfaces;
335 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, const char *type)
343 for (i = con->interfaces; i; i = i->next)
344 if (strcmp(i->ifname, ifname) == 0 &&
345 strcmp(i->type, type) == 0)
351 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
353 const char *ifname, *ifclass, *reason, *type, *order, *flags;
354 char *orderdup, *o, *p;
355 DHCPCD_IF *e, *i, *l, *n, *nl;
359 ifname = get_value(data, len, "interface=");
360 if (ifname == NULL || *ifname == '\0') {
364 reason = get_value(data, len, "reason=");
365 if (reason == NULL || *reason == '\0') {
369 ifclass = get_value(data, len, "ifclass=");
370 /* Skip pseudo interfaces */
371 if (ifclass && *ifclass != '\0') {
375 if (strcmp(reason, "RECONFIGURE") == 0) {
379 order = get_value(data, len, "interface_order=");
380 if (order == NULL || *order == '\0') {
385 if (strcmp(reason, "PREINIT") == 0 ||
386 strcmp(reason, "UNKNOWN") == 0 ||
387 strcmp(reason, "CARRIER") == 0 ||
388 strcmp(reason, "NOCARRIER") == 0 ||
389 strcmp(reason, "DEPARTED") == 0 ||
390 strcmp(reason, "STOPPED") == 0)
392 else if (strcmp(reason, "ROUTERADVERT") == 0)
394 else if (reason[strlen(reason) - 1] == '6')
400 /* Remove all instances on carrier drop */
401 if (strcmp(reason, "NOCARRIER") == 0 ||
402 strcmp(reason, "DEPARTED") == 0 ||
403 strcmp(reason, "STOPPED") == 0)
406 for (e = con->interfaces; e; e = n) {
408 if (strcmp(e->ifname, ifname) == 0) {
409 if (strcmp(e->type, type) == 0)
415 con->interfaces = e->next;
421 } else if (strcmp(type, "link")) {
422 /* If link is down, ignore it */
423 e = dhcpcd_get_if(con, ifname, "link");
428 orderdup = strdup(order);
429 if (orderdup == NULL)
432 /* Find our pointer */
435 for (e = con->interfaces; e; e = e->next) {
436 if (strcmp(e->ifname, ifname) == 0 &&
437 strcmp(e->type, type) == 0)
446 i = malloc(sizeof(*i));
456 i->last_message = NULL;
460 /* Now fill out our interface structure */
467 flags = dhcpcd_get_value(i, "ifflags=");
469 i->flags = (unsigned int)strtoul(flags, NULL, 0);
472 if (strcmp(reason, "CARRIER") == 0)
475 i->up = strtobool(dhcpcd_get_value(i, "if_up="));
476 i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless="));
477 i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
483 while ((o = strsep(&p, " ")) != NULL) {
484 for (ti = 0; dhcpcd_types[ti]; ti++) {
486 for (e = con->interfaces; e; e = e->next) {
487 if (strcmp(e->ifname, o) == 0 &&
488 strcmp(e->type, dhcpcd_types[ti]) == 0)
499 con->interfaces = e->next;
510 /* Free any stragglers */
511 while (con->interfaces) {
512 e = con->interfaces->next;
513 free(con->interfaces->data);
514 free(con->interfaces->last_message);
515 free(con->interfaces);
520 return addedi ? i : NULL;
524 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
526 char sbuf[sizeof(size_t)], *rbuf;
531 bytes = read(fd, sbuf, sizeof(sbuf));
532 if (bytes == 0 || bytes == -1) {
536 memcpy(&len, sbuf, sizeof(len));
537 rbuf = malloc(len + 1);
540 bytes = read(fd, rbuf, len);
541 if (bytes == 0 || bytes == -1) {
546 if ((size_t)bytes != len) {
553 i = dhcpcd_new_if(con, rbuf, len);
560 dhcpcd_dispatchif(DHCPCD_IF *i)
565 i->con->if_cb(i, i->con->if_context);
566 dhcpcd_wpa_if_event(i);
570 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
575 i = dhcpcd_read_if(con, con->listen_fd);
577 dhcpcd_dispatchif(i);
579 /* Have to call update_status last as it could
580 * cause the interface to be destroyed. */
581 update_status(con, NULL);
587 DHCPCD_CONNECTION *con;
589 con = calloc(1, sizeof(*con));
590 con->command_fd = con->listen_fd = -1;
592 con->progname = "libdhcpcd";
597 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
601 con->progname = progname;
605 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
609 return con->progname;
612 #ifndef HAVE_STRVERSCMP
613 /* Good enough for our needs */
615 strverscmp(const char *s1, const char *s2)
617 int s1maj, s1min, s1part;
618 int s2maj, s2min, s2part;
622 if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
625 if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
633 return s1part - s2part;
638 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
640 const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
647 if (con->listen_fd != -1)
648 return con->listen_fd;
652 /* We need to block the command fd */
653 con->command_fd = dhcpcd_connect(path, 0);
654 if (con->command_fd == -1)
657 con->terminate_commands = false;
658 if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
660 con->terminate_commands =
661 strverscmp(con->version, "6.4.1") >= 0 ? true : false;
663 if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
667 con->privileged = privileged;
668 update_status(con, NULL);
670 con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
671 if (con->listen_fd == -1)
674 dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
675 dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
676 bytes = read(con->command_fd, cmd, sizeof(nifs));
677 if (bytes != sizeof(nifs))
679 memcpy(&nifs, cmd, sizeof(nifs));
680 /* We don't dispatch each interface here as that
681 * causes too much notification spam when the GUI starts */
682 for (n = 0; n < nifs; n++)
683 dhcpcd_read_if(con, con->command_fd);
685 update_status(con, NULL);
687 return con->listen_fd;
695 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
699 return con->listen_fd;
703 dhcpcd_privileged(DHCPCD_CONNECTION *con)
707 return con->privileged;
711 dhcpcd_status(DHCPCD_CONNECTION *con)
719 dhcpcd_version(DHCPCD_CONNECTION *con)
727 dhcpcd_cffile(DHCPCD_CONNECTION *con)
735 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
736 void (*cb)(DHCPCD_IF *, void *), void *ctx)
741 con->if_context = ctx;
745 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
746 void (*cb)(DHCPCD_CONNECTION *, const char *, void *), void *ctx)
751 con->status_context = ctx;
755 dhcpcd_close(DHCPCD_CONNECTION *con)
765 /* Shut down WPA listeners as they aren't much good without dhcpcd.
766 * They'll be restarted anyway when dhcpcd comes back up. */
768 nwpa = con->wpa->next;
769 dhcpcd_wpa_close(con->wpa);
773 while (con->wi_history) {
774 nh = con->wi_history->next;
775 free(con->wi_history);
776 con->wi_history = nh;
778 while (con->interfaces) {
779 nif = con->interfaces->next;
780 free(con->interfaces->data);
781 free(con->interfaces->last_message);
782 free(con->interfaces);
783 con->interfaces = nif;
786 if (con->command_fd != -1)
787 shutdown(con->command_fd, SHUT_RDWR);
788 if (con->listen_fd != -1)
789 shutdown(con->listen_fd, SHUT_RDWR);
791 update_status(con, "down");
793 if (con->command_fd != -1) {
794 close(con->command_fd);
795 con->command_fd = -1;
797 if (con->listen_fd != -1) {
798 close(con->listen_fd);
818 dhcpcd_free(DHCPCD_CONNECTION *con)
826 dhcpcd_if_connection(DHCPCD_IF *i)
834 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
836 const char *ip, *iplen, *pfx;
838 const char *reason = NULL;
843 /* Don't report non SLAAC configurations */
844 if (strcmp(i->type, "ra") == 0 && i->up &&
845 dhcpcd_get_value(i, "ra1_prefix=") == NULL)
849 if (strcmp(i->reason, "EXPIRE") == 0)
850 reason = _("Expired");
851 else if (strcmp(i->reason, "CARRIER") == 0) {
854 reason = _("Associated with");
856 reason = _("Cable plugged in");
857 } else if (strcmp(i->reason, "NOCARRIER") == 0) {
860 reason = _("Disassociated from");
863 reason = _("Not associated");
865 reason = _("Cable unplugged");
866 } else if (strcmp(i->reason, "DEPARTED") == 0)
867 reason = _("Departed");
868 else if (strcmp(i->reason, "UNKNOWN") == 0)
869 reason = _("Unknown link state");
870 else if (strcmp(i->reason, "FAIL") == 0)
871 reason = _("Automatic configuration not possible");
872 else if (strcmp(i->reason, "3RDPARTY") == 0)
873 reason = _("Waiting for 3rd Party configuration");
875 if (reason == NULL) {
877 reason = _("Configured");
878 else if (strcmp(i->type, "ra") == 0)
879 reason = "Expired RA";
884 pfx = i->up ? "new_" : "old_";
885 if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address=")))
886 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr=");
887 else if ((ip = dhcpcd_get_value(i, "ra1_prefix=")))
889 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
890 "dhcp6_ia_na1_ia_addr1=")))
897 len = strlen(i->ifname) + strlen(reason) + 3;
898 if (showssid && i->ssid)
899 len += strlen(i->ssid) + 1;
901 len += strlen(ip) + 1;
903 len += strlen(iplen) + 1;
904 msg = p = malloc(len);
907 p += snprintf(msg, len, "%s: %s", i->ifname, reason);
909 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
911 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
913 snprintf(p, len - (size_t)(p - msg), " %s", ip);
916 if (i->last_message == NULL || strcmp(i->last_message, msg))
921 free(i->last_message);
922 i->last_message = strdup(msg);