3 * Copyright 2009-2016 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>
64 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
68 #define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
71 const char * const dhcpcd_cstates[DHC_MAX] = {
87 static const struct dhcpcd_vs dhcpcd_states[] = {
90 { DHS_STOPPED, "STOPPED" },
93 { DHS_PREINIT, "PREINIT" },
94 { DHS_DEPARTED, "DEPARTED" },
95 { DHS_NOCARRIER, "NOCARRIER" },
96 { DHS_NOCARRIER_ROAMING, "NOCARRIER_ROAMING " },
98 { DHS_EXPIRE, "EXPIRE" },
99 { DHS_RECONFIGURE, "RECONFIGURE" },
100 { DHS_CARRIER, "CARRIER" },
101 { DHS_STATIC, "STATIC" },
102 { DHS_3RDPARTY, "3RDPARTY" },
103 { DHS_IPV4LL, "IPV4LL" },
104 { DHS_INFORM, "INFORM" },
105 { DHS_BOUND, "BOUND" },
106 { DHS_RENEW, "RENEW" },
107 { DHS_REBIND, "REBIND" },
108 { DHS_REBOOT, "REBOOT" },
109 { DHS_ROUTERADVERT, "ROUTERADVERT" },
110 { DHS_BOUND, "DELEGATED" },
111 { DHS_UNKNOWN, NULL }
115 dhcpcd_command_fd(DHCPCD_CONNECTION *con,
116 int fd, bool progname, const char *cmd, char **buffer)
126 /* Each command is \n terminated.
127 * Each argument is NULL seperated.
128 * We may need to send a space one day, so the API
129 * in this function may need to be improved */
132 pl = strlen(con->progname);
133 len = pl + 1 + cl + 1;
138 if (con->terminate_commands)
140 if (len > sizeof(buf)) {
146 memcpy(buf, con->progname, pl);
152 while ((p = strchr(p, ' ')) != NULL)
154 if (con->terminate_commands) {
160 if (write(fd, buf, len) == -1)
165 bytes = read(fd, buf, sizeof(size_t));
166 if (bytes == 0 || bytes == -1)
168 memcpy(&len, buf, sizeof(size_t));
169 nbuf = realloc(*buffer, len + 1);
173 bytes = read(fd, *buffer, len);
174 if (bytes != -1 && (size_t)bytes < len)
175 *buffer[bytes] = '\0';
180 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
184 if (!con->privileged) {
188 return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
192 dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
195 return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
199 dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
203 if (con->buflen < len) {
206 nbuf = realloc(con->buf, len);
216 dhcpcd_command_arg(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
224 cmdlen = strlen(cmd);
226 len = cmdlen + strlen(arg) + 2;
229 if (!dhcpcd_realloc(con, len))
231 strlcpy(con->buf, cmd, con->buflen);
233 con->buf[cmdlen] = ' ';
234 strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
237 return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
242 dhcpcd_connect(const char *path, int opts)
246 struct sockaddr_un sun;
249 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
253 memset(&sun, 0, sizeof(sun));
254 sun.sun_family = AF_UNIX;
255 strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
256 len = (socklen_t)SUN_LEN(&sun);
257 if (connect(fd, (struct sockaddr *)&sun, len) == 0)
264 get_value(const char *data, size_t len, const char *var)
273 while (data + vlen + 1 < end) {
274 /* Skip past NUL padding */
279 if (strncmp(data, var, vlen) == 0 && data[vlen] == '=') {
283 data += strlen(data) + 1;
285 if (p != NULL && *p != '\0')
291 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
296 return get_value(i->data, i->data_len, var);
300 dhcpcd_encode_string_escape(char *dst, size_t len, const char *src, size_t slen)
309 c = (unsigned char)*src++;
310 if ((c == '\\' || !isascii(c) || !isprint(c))) {
313 if (len == 0 || len == 1) {
317 *dst++ = '\\'; *dst++ = '\\';
329 *dst++ = (char)(((c >> 6) & 03) + '0');
330 *dst++ = (char)(((c >> 3) & 07) + '0');
331 *dst++ = (char)(( c & 07) + '0');
356 return (ssize_t)bytes;
360 dhcpcd_decode_string_escape(char *dst, size_t dlen, const char *src)
371 if (dst && --dlen == 0) {
396 if (*src >= '0' && *src <='7')
397 oct = oct * 8 + (*src++ - '0');
402 if (*src >= '0' && *src <='7')
403 oct = oct * 8 + (*src++ - '0');
434 dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
442 if (dlen == 0 || dlen == 1) {
447 for (i = 0; i < 2; i++) {
449 if (c >= '0' && c <= '9')
451 else if (c >= 'a' && c <= 'f')
453 else if (c >= 'A' && c <= 'F')
467 return (ssize_t)bytes;
471 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
482 l = strlcpy(p, prefix, plen);
483 if (l >= sizeof(pvar)) {
489 if (strlcpy(p, var, plen) >= plen) {
493 return dhcpcd_get_value(i, pvar);
497 strtobool(const char *var)
503 return (*var == '0' || *var == '\0' ||
504 strcmp(var, "false") == 0 ||
505 strcmp(var, "no") == 0) ? false : true;
509 get_status(DHCPCD_CONNECTION *con)
515 if (con->command_fd == -1)
518 if (con->listen_fd == -1)
521 if (con->interfaces == NULL)
522 return DHC_INITIALISED;
524 status = DHC_DISCONNECTED;
525 for (i = con->interfaces; i; i = i->next) {
527 if (i->type == DHT_LINK) {
528 if (status == DHC_DISCONNECTED)
529 status = DHC_CONNECTING;
532 status = DHC_AF_WAITING;
534 status = DHC_CONNECTED;
544 update_status(DHCPCD_CONNECTION *con, unsigned int nstatus)
548 if (nstatus == DHC_UNKNOWN)
549 nstatus = get_status(con);
550 if (con->status != nstatus) {
551 con->status = nstatus;
553 con->status_cb(con, con->status,
554 dhcpcd_cstates[con->status], con->status_context);
559 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
563 return con->interfaces;
567 dhcpcd_interface_names(DHCPCD_CONNECTION *con, size_t *nnames)
574 if (con->interfaces == NULL)
578 for (i = con->interfaces; i; i = i->next) {
579 if (i->type == DHT_LINK)
582 names = malloc(sizeof(char *) * (n + 1));
586 for (i = con->interfaces; i; i = i->next) {
587 if (i->type == DHT_LINK) {
588 names[n] = strdup(i->ifname);
589 if (names[n] == NULL) {
604 dhcpcd_freev(char **argv)
609 for (v = argv; *v; v++)
616 dhcpcd_cmpstring(const void *p1, const void *p2)
621 s1 = *(char * const *)p1;
622 s2 = *(char * const *)p2;
623 if ((cmp = strcasecmp(s1, s2)) == 0)
624 cmp = strcmp(s1, s2);
629 dhcpcd_interface_names_sorted(DHCPCD_CONNECTION *con)
634 names = dhcpcd_interface_names(con, &nnames);
636 qsort(names, nnames, sizeof(char *), dhcpcd_cmpstring);
641 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, unsigned int type)
649 for (i = con->interfaces; i; i = i->next)
650 if (i->type == type && strcmp(i->ifname, ifname) == 0)
656 dhcpcd_reason_to_state1(const char *reason, int *isdhcp6)
663 /* Take a copy of reason and trim 6 from the end
664 * so our DHCPv6 state can match DHCP states */
665 if (strlcpy(rbuf, reason, sizeof(rbuf)) >= sizeof(rbuf))
667 p = rbuf + (strlen(rbuf) - 1);
675 for (i = 0; dhcpcd_states[i].val != DHS_UNKNOWN; i++) {
676 if (strcmp(rbuf, dhcpcd_states[i].str) == 0)
677 return dhcpcd_states[i].val;
683 dhcpcd_reason_to_statetype(const char *reason,
684 unsigned int *state, unsigned int *type)
690 *state = dhcpcd_reason_to_state1(reason, &isdhcp6);
696 case DHS_NOCARRIER_ROAMING:
701 case DHS_ROUTERADVERT:
721 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
723 const char *ifname, *ifclass, *reason, *order, *flags;
724 unsigned int state, type;
725 char *orderdup, *o, *p;
726 DHCPCD_IF *e, *i, *l, *n, *nl;
730 char *dp = data, *de = data + len;
732 printf ("XX: %s\n", dp);
733 dp += strlen(dp) + 1;
737 ifname = get_value(data, len, "interface");
738 if (ifname == NULL || *ifname == '\0') {
742 reason = get_value(data, len, "reason");
743 if (reason == NULL || *reason == '\0') {
747 dhcpcd_reason_to_statetype(reason, &state, &type);
749 ifclass = get_value(data, len, "ifclass");
750 /* Skip pseudo interfaces */
751 if (ifclass && *ifclass != '\0') {
755 if (state == DHS_RECONFIGURE || state == DHS_INFORM) {
759 order = get_value(data, len, "interface_order");
760 if (order == NULL || *order == '\0') {
766 /* Remove all instances on carrier drop */
767 if (state == DHS_NOCARRIER ||
768 state == DHS_DEPARTED || state == DHS_STOPPED)
771 for (e = con->interfaces; e; e = n) {
773 if (strcmp(e->ifname, ifname) == 0) {
780 con->interfaces = e->next;
786 } else if (type != DHT_LINK) {
787 /* If link is down, ignore it */
788 e = dhcpcd_get_if(con, ifname, DHT_LINK);
793 orderdup = strdup(order);
794 if (orderdup == NULL)
797 /* Find our pointer */
800 for (e = con->interfaces; e; e = e->next) {
801 if (e->type == type && strcmp(e->ifname, ifname) == 0) {
809 i = malloc(sizeof(*i));
819 i->last_message = NULL;
823 /* Now fill out our interface structure */
831 flags = dhcpcd_get_value(i, "ifflags");
833 i->ifflags = (unsigned int)strtoul(flags, NULL, 0);
836 if (state == DHS_CARRIER || state == DHS_DELEGATED)
839 i->up = strtobool(dhcpcd_get_value(i, "if_up"));
840 i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless"));
841 i->ssid = dhcpcd_get_value(i, "ifssid");
842 i->freq = 0; /* wpa_supplicant will set this when opened */
843 if (i->ssid == NULL && i->wireless)
844 i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid" : "old_ssid");
846 /* Work out if we're waiting for any other addresses */
847 if (dhcpcd_get_value(i, "af_waiting") == NULL)
848 con->af_waiting = false;
850 con->af_waiting = true;
856 while ((o = strsep(&p, " ")) != NULL) {
857 for (type = 0; type < DHT_MAX; type++) {
859 for (e = con->interfaces; e; e = e->next) {
860 if (e->type == type && strcmp(e->ifname, o) == 0)
871 con->interfaces = e->next;
882 /* Free any stragglers */
883 while (con->interfaces) {
884 e = con->interfaces->next;
885 free(con->interfaces->data);
886 free(con->interfaces->last_message);
887 free(con->interfaces);
892 return addedi ? i : NULL;
896 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
903 bytes = read(fd, &len, sizeof(len));
904 if (bytes == 0 || bytes == -1) {
908 if (len >= SSIZE_MAX) {
909 /* Even this is probably too big! */
913 rbuf = malloc(len + 1);
918 bytes = read(fd, rbufp, len);
919 if (bytes == 0 || bytes == -1) {
924 if ((size_t)bytes < len) {
926 len -= (size_t)bytes;
929 if ((size_t)bytes != len) {
936 i = dhcpcd_new_if(con, rbuf, (size_t)((rbufp - rbuf) + bytes));
943 dhcpcd_dispatchif(DHCPCD_IF *i)
948 i->con->if_cb(i, i->con->if_context);
949 dhcpcd_wpa_if_event(i);
953 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
958 i = dhcpcd_read_if(con, con->listen_fd);
961 dhcpcd_dispatchif(i);
963 /* Have to call update_status last as it could
964 * cause the interface to be destroyed. */
965 update_status(con, DHC_UNKNOWN);
971 DHCPCD_CONNECTION *con;
973 con = calloc(1, sizeof(*con));
974 con->command_fd = con->listen_fd = -1;
976 con->progname = "libdhcpcd";
977 con->af_waiting = false;
982 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
986 con->progname = progname;
990 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
994 return con->progname;
997 #ifndef HAVE_STRVERSCMP
998 /* Good enough for our needs */
1000 strverscmp(const char *s1, const char *s2)
1002 int s1maj, s1min, s1part;
1003 int s2maj, s2min, s2part;
1007 if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
1010 if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
1018 return s1part - s2part;
1023 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
1025 const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
1032 if (con->listen_fd != -1)
1033 return con->listen_fd;
1038 /* We need to block the command fd */
1039 con->command_fd = dhcpcd_connect(path, 0);
1040 if (con->command_fd == -1) {
1041 if (errno == ENOENT) {
1043 DHCPCD_OSOCKET : DHCPCD_UNPRIV_OSOCKET;
1044 con->command_fd = dhcpcd_connect(path, 0);
1046 if (con->command_fd == -1)
1050 con->terminate_commands = false;
1051 if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
1053 con->terminate_commands =
1054 strverscmp(con->version, "6.4.1") >= 0 ? true : false;
1056 if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
1060 con->privileged = privileged;
1061 update_status(con, DHC_UNKNOWN);
1063 con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
1064 if (con->listen_fd == -1)
1067 dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
1068 dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
1069 bytes = read(con->command_fd, cmd, sizeof(nifs));
1070 if (bytes != sizeof(nifs))
1072 memcpy(&nifs, cmd, sizeof(nifs));
1073 /* We don't dispatch each interface here as that
1074 * causes too much notification spam when the GUI starts */
1075 for (n = 0; n < nifs; n++) {
1076 /* Some interface states we do not create an interface for
1077 * such as DHS_INFORM. */
1078 dhcpcd_read_if(con, con->command_fd);
1081 update_status(con, DHC_UNKNOWN);
1083 return con->listen_fd;
1091 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
1095 return con->listen_fd;
1099 dhcpcd_privileged(DHCPCD_CONNECTION *con)
1103 return con->privileged;
1107 dhcpcd_status(DHCPCD_CONNECTION *con, const char **status)
1112 *status = dhcpcd_cstates[con->status];
1117 dhcpcd_af_waiting(const DHCPCD_CONNECTION *con)
1121 return con->af_waiting;
1125 dhcpcd_version(DHCPCD_CONNECTION *con)
1129 return con->version;
1133 dhcpcd_cffile(DHCPCD_CONNECTION *con)
1141 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
1142 void (*cb)(DHCPCD_IF *, void *), void *ctx)
1147 con->if_context = ctx;
1151 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
1152 void (*cb)(DHCPCD_CONNECTION *, unsigned int, const char *, void *),
1157 con->status_cb = cb;
1158 con->status_context = ctx;
1162 dhcpcd_close(DHCPCD_CONNECTION *con)
1171 if (con->command_fd != -1)
1172 shutdown(con->command_fd, SHUT_RDWR);
1173 if (con->listen_fd != -1)
1174 shutdown(con->listen_fd, SHUT_RDWR);
1178 /* Shut down WPA listeners as they aren't much good without dhcpcd.
1179 * They'll be restarted anyway when dhcpcd comes back up. */
1181 nwpa = con->wpa->next;
1182 dhcpcd_wpa_close(con->wpa);
1186 while (con->wi_history) {
1187 nh = con->wi_history->next;
1188 free(con->wi_history);
1189 con->wi_history = nh;
1191 while (con->interfaces) {
1192 nif = con->interfaces->next;
1193 free(con->interfaces->data);
1194 free(con->interfaces->last_message);
1195 free(con->interfaces);
1196 con->interfaces = nif;
1199 update_status(con, DHC_DOWN);
1201 if (con->command_fd != -1) {
1202 close(con->command_fd);
1203 con->command_fd = -1;
1205 if (con->listen_fd != -1) {
1206 close(con->listen_fd);
1207 con->listen_fd = -1;
1216 con->version = NULL;
1226 dhcpcd_free(DHCPCD_CONNECTION *con)
1234 dhcpcd_if_connection(DHCPCD_IF *i)
1242 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
1244 const char *ip, *iplen, *pfx;
1246 const char *reason = NULL;
1251 /* Don't report non SLAAC configurations */
1252 if (i->type == DHT_RA && i->up &&
1253 dhcpcd_get_value(i, "nd1_addr1") == NULL &&
1254 dhcpcd_get_value(i, "ra1_prefix") == NULL)
1260 reason = _("Expired");
1265 reason = _("Associated with");
1267 /* Don't report able in if we have addresses */
1268 const DHCPCD_IF *ci;
1270 for (ci = i->con->interfaces; ci; ci = ci->next) {
1272 strcmp(i->ifname, ci->ifname) == 0 &&
1278 reason = _("Link is up, configuring");
1284 reason = _("Disassociated from");
1287 reason = _("Not associated");
1289 reason = _("Link is down");
1291 case DHS_NOCARRIER_ROAMING:
1292 reason = _("Link is down, roaming");
1295 reason = _("Departed");
1298 reason = _("Unknown link state");
1301 reason = _("Automatic configuration not possible");
1304 reason = _("Waiting for 3rd Party configuration");
1308 if (reason == NULL) {
1310 if (i->state == DHS_DELEGATED)
1311 reason = _("Delegated");
1313 reason = _("Configured");
1314 } else if (i->type == DHT_RA)
1315 reason = "Expired RA";
1316 else if (i->type == DHT_IPV4LL)
1317 reason = "Expired IPv4LL";
1322 pfx = i->up ? "new_" : "old_";
1323 if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address")))
1324 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr");
1325 else if ((ip = dhcpcd_get_value(i, "nd1_addr1")))
1327 else if ((ip = dhcpcd_get_value(i, "ra1_addr")))
1329 else if ((ip = dhcpcd_get_value(i, "ra1_prefix")))
1331 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1332 "dhcp6_ia_na1_ia_addr1")))
1334 else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1335 "delegated_dhcp6_prefix")))
1337 else if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip6_address")))
1344 len = strlen(i->ifname) + strlen(reason) + 3;
1345 if (showssid && i->ssid)
1346 len += strlen(i->ssid) + 1;
1348 len += strlen(ip) + 1;
1350 len += strlen(iplen) + 1;
1351 msg = p = malloc(len);
1354 p += snprintf(msg, len, "%s: %s", i->ifname, reason);
1356 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
1358 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
1360 snprintf(p, len - (size_t)(p - msg), " %s", ip);
1363 if (i->last_message == NULL || strcmp(i->last_message, msg))
1368 free(i->last_message);
1369 i->last_message = strdup(msg);