From: Roy Marples Date: Wed, 10 Sep 2014 01:46:58 +0000 (+0000) Subject: Use the nl80211 interface on Linux to get the wireless SSID. X-Git-Tag: v6.4.4~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67ed6c5fdc76fa237307750d84251de7857f7e99;p=thirdparty%2Fdhcpcd.git Use the nl80211 interface on Linux to get the wireless SSID. If this is not found in the system headers, fall back to assuming WEXT. This has been done because the WEXT module has been marked as deprecated for a while now and I'm getting bored of people asking me why their wireless wasn't working. Bloated dhcpcd by another 600 bytes as a result. --- diff --git a/configure b/configure index fccfb470..40bd01f8 100755 --- a/configure +++ b/configure @@ -382,7 +382,7 @@ fi case "$OS" in linux*) echo "CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700" >>$CONFIG_MK - echo "DHCPCD_SRCS+= if-linux.c if-linux-wireless.c" >>$CONFIG_MK + echo "DHCPCD_SRCS+= if-linux.c" >>$CONFIG_MK ;; kfreebsd*) echo "CPPFLAGS+= -D_GNU_SOURCE" >>$CONFIG_MK @@ -1024,6 +1024,24 @@ EOF $abort && exit 1 fi +if [ "$OS" = linux ]; then + printf "Testing for nl80211 ... " + cat <_nl80211.c +#include +int main(void) { + return 0; +} +EOF + if $XCC _nl80211.c -o _nl80211 2>/dev/null; then + echo "yes" + echo "#define HAVE_NL80211_H" >>$CONFIG_H + else + echo "no" + echo "DHCPCD_SRCS+= if-linux-wireless.c" >>$CONFIG_MK + fi + rm -f _nl80211.c _nl80211 +fi + # Transform for a make file SERVICEEXISTS=$(echo "$SERVICEEXISTS" | $SED \ -e 's:\\:\\\\:g' \ diff --git a/dhcpcd.c b/dhcpcd.c index 85a0db45..dd684147 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -574,7 +574,7 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, dhcpcd_handleinterface(ctx, 0, ifp->name); #endif if (ifp->wireless) - if_getssid(ifp->name, ifp->ssid); + if_getssid(ifp); configure_interface(ifp, ctx->argc, ctx->argv); script_runreason(ifp, "CARRIER"); dhcpcd_startinterface(ifp); diff --git a/dhcpcd.h b/dhcpcd.h index a45d9a90..cedab5fb 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -65,6 +65,7 @@ struct interface { int carrier; int wireless; char ssid[IF_SSIDSIZE]; + unsigned int ssid_len; char profile[PROFILE_LEN]; struct if_options *options; diff --git a/if-bsd.c b/if-bsd.c index 5bc30fd4..baf7c355 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -138,7 +138,7 @@ if_openlinksocket(void) } int -if_getssid(const char *ifname, char *ssid) +if_getssid(struct interface *ifp) { int s, retval = -1; #if defined(SIOCG80211NWID) @@ -154,14 +154,18 @@ if_getssid(const char *ifname, char *ssid) #if defined(SIOCG80211NWID) /* NetBSD */ memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); memset(&nwid, 0, sizeof(nwid)); ifr.ifr_data = (void *)&nwid; if (ioctl(s, SIOCG80211NWID, &ifr) == 0) { - retval = nwid.i_len; - if (ssid) { - memcpy(ssid, nwid.i_nwid, nwid.i_len); + if (nwid.i_len > sizeof(ifp->ssid)) { + errno = ENOBUFS; + retval = -1; + } else { + retval = nwid.i_len; + memcpy(ifp->ssid, nwid.i_nwid, nwid.i_len); ssid[nwid.i_len] = '\0'; + ifp->ssid_len = nwid.i_len; } } #elif defined(IEEE80211_IOC_SSID) /* FreeBSD */ @@ -172,10 +176,14 @@ if_getssid(const char *ifname, char *ssid) memset(nwid, 0, sizeof(nwid)); ireq.i_data = &nwid; if (ioctl(s, SIOCG80211, &ireq) == 0) { - retval = ireq.i_len; - if (ssid) { - memcpy(ssid, nwid, ireq.i_len); - ssid[ireq.i_len] = '\0'; + if (ireq.i_len > sizeof(ifp->ssid)) { + errno = ENOBUFS; + retval = -1; + } else { + retval = ireq.i_len; + memcpy(ifp->ssid, nwid, ireq.i_len); + ifp->ssid[ireq.i_len] = '\0'; + ifp->ssid_len = ireq.i_len; } } #endif diff --git a/if-linux-wireless.c b/if-linux-wireless.c index 028b682a..117eb1a5 100644 --- a/if-linux-wireless.c +++ b/if-linux-wireless.c @@ -60,10 +60,10 @@ /* We can't include if.h or dhcpcd.h because * they would pull in net/if.h, which defeats the purpose of this hack. */ #define IF_SSIDSIZE 33 -int if_getssid(const char *ifname, char *ssid); +int if_getssid_wext(const char *ifname, char *ssid); int -if_getssid(const char *ifname, char *ssid) +if_getssid_wext(const char *ifname, char *ssid) { #ifdef SIOCGIWESSID int s, retval; diff --git a/if-linux.c b/if-linux.c index 5bbac9b1..a528f06f 100644 --- a/if-linux.c +++ b/if-linux.c @@ -83,6 +83,13 @@ #include "ipv6.h" #include "ipv6nd.h" +#ifdef HAVE_NL80211_H +#include +#include +#else +int if_getssid_wext(const char *ifname, char *ssid); +#endif + #define bpf_insn sock_filter #define BPF_SKIPTYPE #define BPF_ETHCOOK -ETH_HLEN @@ -243,21 +250,22 @@ if_vimaster(__unused const char *ifname) } static int -_open_link_socket(struct sockaddr_nl *nl) +_open_link_socket(struct sockaddr_nl *nl, int flags, int protocol) { int fd; #ifdef SOCK_CLOEXEC - fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if (flags) + flags = SOCK_CLOEXEC; + fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol); if (fd == -1) return -1; #else - int flags; - fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd == -1) return -1; - if ((flags = fcntl(fd, F_GETFD, 0)) == -1 || + if (flags && + (flags = fcntl(fd, F_GETFD, 0)) == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { close(fd); @@ -287,12 +295,32 @@ if_openlinksocket(void) snl.nl_groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_NEIGH; #endif - return _open_link_socket(&snl); + return _open_link_socket(&snl, 1, NETLINK_ROUTE); +} + +static int +err_netlink(struct nlmsghdr *nlm) +{ + struct nlmsgerr *err; + size_t len; + + if (nlm->nlmsg_type != NLMSG_ERROR) + return 0; + len = nlm->nlmsg_len - sizeof(*nlm); + if (len < sizeof(*err)) { + errno = EBADMSG; + return -1; + } + err = (struct nlmsgerr *)NLMSG_DATA(nlm); + if (err->error == 0) + return (int)len; + errno = -err->error; + return -1; } static int -get_netlink(struct dhcpcd_ctx *ctx, int fd, int flags, - int (*callback)(struct dhcpcd_ctx *, struct nlmsghdr *)) +get_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, int fd, int flags, + int (*callback)(struct dhcpcd_ctx *, struct interface *,struct nlmsghdr *)) { char *buf = NULL, *nbuf; ssize_t bytes; @@ -342,7 +370,12 @@ get_netlink(struct dhcpcd_ctx *ctx, int fd, int flags, nlm && NLMSG_OK(nlm, (size_t)bytes); nlm = NLMSG_NEXT(nlm, bytes)) { - r = callback(ctx, nlm); + r = err_netlink(nlm); + if (r == -1) + goto eexit; + if (r) + continue; + r = callback(ctx, ifp, nlm); if (r != 0) goto eexit; } @@ -353,26 +386,6 @@ eexit: return r; } -static int -err_netlink(__unused struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) -{ - struct nlmsgerr *err; - size_t len; - - if (nlm->nlmsg_type != NLMSG_ERROR) - return 0; - len = nlm->nlmsg_len - sizeof(*nlm); - if (len < sizeof(*err)) { - errno = EBADMSG; - return -1; - } - err = (struct nlmsgerr *)NLMSG_DATA(nlm); - if (err->error == 0) - return (int)len; - errno = -err->error; - return -1; -} - /* Work out the maximum pid size */ static inline long long get_max_pid_t() @@ -386,7 +399,8 @@ get_max_pid_t() } static int -link_route(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) +link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, + struct nlmsghdr *nlm) { size_t len; unsigned int idx, metric; @@ -455,7 +469,8 @@ link_route(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) } static int -link_addr(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) +link_addr(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, + struct nlmsghdr *nlm) { size_t len; struct rtattr *rta; @@ -568,7 +583,8 @@ handle_rename(struct dhcpcd_ctx *ctx, unsigned int ifindex, const char *ifname) #ifdef INET6 static int -link_neigh(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) +link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, + struct nlmsghdr *nlm) { struct ndmsg *r; struct rtattr *rta; @@ -611,7 +627,8 @@ link_neigh(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) #endif static int -link_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) +link_netlink(struct dhcpcd_ctx *ctx, __unused struct interface *_ifp, + struct nlmsghdr *nlm) { int r; size_t len; @@ -620,14 +637,14 @@ link_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) char ifn[IF_NAMESIZE + 1]; struct interface *ifp; - r = link_route(ctx, nlm); + r = link_route(ctx, ifp, nlm); if (r != 0) return r; - r = link_addr(ctx, nlm); + r = link_addr(ctx, ifp, nlm); if (r != 0) return r; #ifdef INET6 - r = link_neigh(ctx, nlm); + r = link_neigh(ctx, ifp, nlm); if (r != 0) return r; #endif @@ -712,11 +729,14 @@ int if_managelink(struct dhcpcd_ctx *ctx) { - return get_netlink(ctx, ctx->link_fd, MSG_DONTWAIT, &link_netlink); + return get_netlink(ctx, NULL, + ctx->link_fd, MSG_DONTWAIT, &link_netlink); } static int -send_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *hdr) +send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, + int protocol, struct nlmsghdr *hdr, + int (*callback)(struct dhcpcd_ctx *, struct interface *,struct nlmsghdr *)) { int s, r; struct sockaddr_nl snl; @@ -725,7 +745,7 @@ send_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *hdr) static unsigned int seq; memset(&snl, 0, sizeof(snl)); - if ((s = _open_link_socket(&snl)) == -1) + if ((s = _open_link_socket(&snl, 0, protocol)) == -1) return -1; memset(&iov, 0, sizeof(iov)); iov.iov_base = hdr; @@ -740,7 +760,7 @@ send_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *hdr) hdr->nlmsg_seq = ++seq; if (sendmsg(s, &msg, 0) != -1) - r = get_netlink(ctx, s, 0, &err_netlink); + r = get_netlink(ctx, ifp, s, 0, callback); else r = -1; close(s); @@ -792,6 +812,187 @@ add_attr_32(struct nlmsghdr *n, unsigned short maxlen, unsigned short type, return 0; } +#ifdef HAVE_NL80211_H +static struct nlattr * +nla_next(struct nlattr *nla, size_t *rem) +{ + + *rem -= NLA_ALIGN(nla->nla_len); + return (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); +} + +#define NLA_TYPE(nla) ((nla)->nla_type & NLA_TYPE_MASK) +#define NLA_LEN(nla) ((nla)->nla_len - NLA_HDRLEN) +#define NLA_OK(nla, rem) \ + ((rem) >= sizeof(struct nlattr) && \ + (nla)->nla_len >= sizeof(struct nlattr) && \ + (nla)->nla_len <= rem) +#define NLA_DATA(nla) ((char *)(nla) + NLA_HDRLEN) +#define NLA_FOR_EACH_ATTR(pos, head, len, rem) \ + for (pos = head, rem = len; NLA_OK(pos, rem); pos = nla_next(pos, &(rem))) + +struct nlmg +{ + struct nlmsghdr hdr; + struct genlmsghdr ghdr; + char buffer[64]; +}; + +static int +nla_put_32(struct nlmsghdr *n, unsigned short maxlen, + unsigned short type, uint32_t data) +{ + unsigned short len; + struct nlattr *nla; + + len = NLA_ALIGN(NLA_HDRLEN + sizeof(data)); + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + errno = ENOBUFS; + return -1; + } + + nla = (struct nlattr *)NLMSG_TAIL(n); + nla->nla_type = type; + nla->nla_len = len; + memcpy(NLA_DATA(nla), &data, sizeof(data)); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + + return 0; +} + +static int +nla_put_string(struct nlmsghdr *n, unsigned short maxlen, + unsigned short type, const char *data) +{ + unsigned short len; + struct nlattr *nla; + size_t sl; + + sl = strlen(data) + 1; + len = NLA_ALIGN(NLA_HDRLEN + sl); + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + errno = ENOBUFS; + return -1; + } + + nla = (struct nlattr *)NLMSG_TAIL(n); + nla->nla_type = type; + nla->nla_len = len; + memcpy(NLA_DATA(nla), data, sl); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +static int +gnl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype) +{ + struct genlmsghdr *ghdr; + struct nlattr *head, *nla; + size_t len, rem; + int type; + + ghdr = NLMSG_DATA(nlm); + head = (struct nlattr *)((char *) ghdr + GENL_HDRLEN); + len = nlm->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN; + NLA_FOR_EACH_ATTR(nla, head, len, rem) { + type = NLA_TYPE(nla); + if (type > maxtype) + continue; + tb[type] = nla; + } + return 0; +} + +static int +_gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp, + struct nlmsghdr *nlm) +{ + struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1]; + uint16_t family; + + if (gnl_parse(nlm, tb, CTRL_ATTR_FAMILY_ID) == -1) + return -1; + if (tb[CTRL_ATTR_FAMILY_ID] == NULL) { + errno = ENOENT; + return -1; + } + family = *(uint16_t *)NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + return (int)family; +} + +static int +gnl_getfamily(struct dhcpcd_ctx *ctx, const char *name) +{ + struct nlmg nlm; + + memset(&nlm, 0, sizeof(nlm)); + nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr)); + nlm.hdr.nlmsg_type = GENL_ID_CTRL; + nlm.hdr.nlmsg_flags = NLM_F_REQUEST; + nlm.ghdr.cmd = CTRL_CMD_GETFAMILY; + nlm.ghdr.version = 1; + if (nla_put_string(&nlm.hdr, sizeof(nlm), CTRL_ATTR_FAMILY_NAME, name) + == -1) + return -1; + return send_netlink(ctx, NULL, NETLINK_GENERIC, &nlm.hdr, + &_gnl_getfamily); +} + +static int +_if_getssid(__unused struct dhcpcd_ctx *ctx, struct interface *ifp, + struct nlmsghdr *nlm) +{ + struct nlattr *tb[NL80211_ATTR_SSID + 1]; + + if (gnl_parse(nlm, tb, NL80211_ATTR_SSID) == -1) + return -1; + if (tb[NL80211_ATTR_SSID] == NULL) { + errno = ENOENT; + return -1; + } + ifp->ssid_len = NLA_LEN(tb[NL80211_ATTR_SSID]); + if (ifp->ssid_len > sizeof(ifp->ssid)) { + errno = ENOBUFS; + ifp->ssid_len = 0; + return -1; + } + memcpy(ifp->ssid, NLA_DATA(tb[NL80211_ATTR_SSID]), ifp->ssid_len); + ifp->ssid[ifp->ssid_len] = '\0'; + return ifp->ssid_len; +} + +int +if_getssid(struct interface *ifp) +{ + int family; + struct nlmg nlm; + + errno = 0; + family = gnl_getfamily(ifp->ctx, "nl80211"); + if (family == -1) + return -1; + memset(&nlm, 0, sizeof(nlm)); + nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr)); + nlm.hdr.nlmsg_type = family; + nlm.hdr.nlmsg_flags = NLM_F_REQUEST; + nlm.ghdr.cmd = NL80211_CMD_GET_INTERFACE; + nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index); + + return send_netlink(ifp->ctx, ifp, NETLINK_GENERIC, &nlm.hdr, + &_if_getssid); +} +#else +int +if_getssid(struct interface *ifp) +{ + int r; + + r = if_getssid_wext(ifp->name, ifp->ssid); + if (r != -1) + ifp->ssid_len = r; +} +#endif + struct nlma { struct nlmsghdr hdr; @@ -990,7 +1191,7 @@ if_address(const struct interface *iface, add_attr_l(&nlm.hdr, sizeof(nlm), IFA_BROADCAST, &broadcast->s_addr, sizeof(broadcast->s_addr)); - if (send_netlink(iface->ctx, &nlm.hdr) == -1) + if (send_netlink(iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) == -1) retval = -1; return retval; } @@ -1053,7 +1254,8 @@ if_route(const struct rt *rt, int action) add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index); add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric); - if (send_netlink(rt->iface->ctx, &nlm.hdr) == -1) + if (send_netlink(rt->iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) + == -1) retval = -1; return retval; } @@ -1102,7 +1304,8 @@ if_address6(const struct ipv6_addr *ap, int action) } #endif - if (send_netlink(ap->iface->ctx, &nlm.hdr) == -1) + if (send_netlink(ap->iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) + == -1) retval = -1; return retval; } @@ -1185,7 +1388,8 @@ if_route6(const struct rt6 *rt, int action) RTA_DATA(metrics), RTA_PAYLOAD(metrics)); } - if (send_netlink(rt->iface->ctx, &nlm.hdr) == -1) + if (send_netlink(rt->iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL) + == -1) retval = -1; return retval; } @@ -1280,7 +1484,7 @@ if_disable_autolinklocal(struct dhcpcd_ctx *ctx, const char *ifname) add_attr_nest_end(&nlm.hdr, afs6); add_attr_nest_end(&nlm.hdr, afs); - return send_netlink(ctx, &nlm.hdr); + return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, NULL); } static const char *prefix = "/proc/sys/net/ipv6/conf"; diff --git a/if.c b/if.c index ba374452..7057419a 100644 --- a/if.c +++ b/if.c @@ -421,7 +421,7 @@ if_discover(struct dhcpcd_ctx *ctx, int argc, char * const *argv) /* We reserve the 100 range for virtual interfaces, if and when * we can work them out. */ ifp->metric = 200 + ifp->index; - if (if_getssid(ifp->name, ifp->ssid) != -1) { + if (if_getssid(ifp) != -1) { ifp->wireless = 1; ifp->metric += 100; } diff --git a/if.h b/if.h index c829b7ea..4fcbab7a 100644 --- a/if.h +++ b/if.h @@ -103,7 +103,7 @@ int if_carrier(struct interface *); /* The below functions are provided by if-KERNEL.c */ int if_conf(struct interface *); int if_init(struct interface *); -int if_getssid(const char *, char *); +int if_getssid(struct interface *); int if_vimaster(const char *); int if_openlinksocket(void); int if_managelink(struct dhcpcd_ctx *); diff --git a/script.c b/script.c index db203f4f..ea094b6e 100644 --- a/script.c +++ b/script.c @@ -374,21 +374,28 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) snprintf(env[elen++], e, "profile=%s", ifp->profile); } if (ifp->wireless) { - e = strlen("new_ssid=") + strlen(ifp->ssid) + 2; - if (strcmp(reason, "CARRIER") == 0) { - nenv = realloc(env, sizeof(char *) * (elen + 2)); - if (nenv == NULL) - goto eexit; - env = nenv; - EMALLOC(elen, e); - snprintf(env[elen++], e, "new_ssid=%s", ifp->ssid); - } else if (strcmp(reason, "NOCARRIER") == 0) { - nenv = realloc(env, sizeof(char *) * (elen + 2)); - if (nenv == NULL) - goto eexit; - env = nenv; - EMALLOC(elen, e); - snprintf(env[elen++], e, "old_ssid=%s", ifp->ssid); + const char *pfx; + + if (strcmp(reason, "CARRIER") == 0) + pfx = "new_ssid="; + else if (strcmp(reason, "NOCARRIER") == 0) + pfx = "old_ssid="; + else + pfx = NULL; + if (pfx) { + int pfx_len; + ssize_t psl; + + pfx_len = strlen(pfx); + psl = print_string(NULL, 0, + (const uint8_t *)ifp->ssid, ifp->ssid_len); + if (psl != -1) { + EMALLOC(elen, pfx_len + psl + 1); + memcpy(env[elen], pfx, pfx_len); + print_string(env[elen] + pfx_len, psl, + (const uint8_t *)ifp->ssid, ifp->ssid_len); + elen++; + } } } #ifdef INET