]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Improve nl80211 support as we now know how to work out the SSID we're
authorRoy Marples <roy@marples.name>
Thu, 17 Nov 2016 11:52:11 +0000 (11:52 +0000)
committerRoy Marples <roy@marples.name>
Thu, 17 Nov 2016 11:52:11 +0000 (11:52 +0000)
connected to.

configure
if-linux.c

index 5492efdf1c74418839b928b14afb5574ee8fb382..3a6304b8b4e29d80dec2f70c821563072c3c452a 100755 (executable)
--- a/configure
+++ b/configure
@@ -523,15 +523,10 @@ EOF
                echo "#define HAVE_NL80211_H" >>$CONFIG_H
        else
                echo "no"
+               echo "DHCPCD_SRCS+=     if-linux-wext.c" >>$CONFIG_MK
        fi
        rm -f _nl80211.c _nl80211
 
-       # Even though we have nl80211, we only use it as a fallback
-       # because it's currently possible to configure a kernel
-       # where the SSID associated to won't be reported by nl80211
-       # but will be via WEXT.
-       echo "DHCPCD_SRCS+=     if-linux-wext.c" >>$CONFIG_MK
-
        printf "Testing for IN6_ADDR_GEN_MODE_NONE ... "
        cat <<EOF >_IN6_ADDR_GEN_MODE_NONE.c
 #include <linux/if_link.h>
index 4836d72d6f7a3b8982e86199546b9978b525de85..c51a337010c39f26298657c576a3769031c177a1 100644 (file)
@@ -70,8 +70,9 @@
 #ifdef HAVE_NL80211_H
 #include <linux/genetlink.h>
 #include <linux/nl80211.h>
-#endif
+#else
 int if_getssid_wext(const char *ifname, uint8_t *ssid);
+#endif
 
 /* Support older kernels */
 #ifndef IFLA_WIRELESS
@@ -1000,7 +1001,7 @@ nla_next(struct nlattr *nla, size_t *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_DATA(nla) (void *)((char *)(nla) + NLA_HDRLEN)
 #define NLA_FOR_EACH_ATTR(pos, head, len, rem) \
        for (pos = head, rem = len; \
             NLA_OK(pos, rem); \
@@ -1058,17 +1059,13 @@ nla_put_string(struct nlmsghdr *n, unsigned short maxlen,
 }
 
 static int
-gnl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)
+nla_parse(struct nlattr *tb[], struct nlattr *head, size_t len, int maxtype)
 {
-       struct genlmsghdr *ghdr;
-       struct nlattr *head, *nla;
-       size_t len, rem;
+       struct nlattr *nla;
+       size_t rem;
        int type;
 
        memset(tb, 0, sizeof(*tb) * ((unsigned int)maxtype + 1));
-       ghdr = NLMSG_DATA(nlm);
-       head = (void *)((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)
@@ -1078,6 +1075,19 @@ gnl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)
        return 0;
 }
 
+static int
+genl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)
+{
+       struct genlmsghdr *ghdr;
+       struct nlattr *head;
+       size_t len;
+
+       ghdr = NLMSG_DATA(nlm);
+       head = (void *)((char *)ghdr + GENL_HDRLEN);
+       len = nlm->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
+       return nla_parse(tb, head, len, maxtype);
+}
+
 static int
 _gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
     struct nlmsghdr *nlm)
@@ -1085,13 +1095,13 @@ _gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
        struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
        uint16_t family;
 
-       if (gnl_parse(nlm, tb, CTRL_ATTR_FAMILY_ID) == -1)
+       if (genl_parse(nlm, tb, CTRL_ATTR_FAMILY_ID) == -1)
                return -1;
        if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
                errno = ENOENT;
                return -1;
        }
-       family = *(uint16_t *)(void *)NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+       memcpy(&family, NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]), sizeof(family));
        return (int)family;
 }
 
@@ -1114,31 +1124,56 @@ gnl_getfamily(struct dhcpcd_ctx *ctx, const char *name)
 }
 
 static int
-_if_getssid(__unused struct dhcpcd_ctx *ctx, struct interface *ifp,
+_if_getssid_nl80211(__unused struct dhcpcd_ctx *ctx, struct interface *ifp,
     struct nlmsghdr *nlm)
 {
-       struct nlattr *tb[NL80211_ATTR_SSID + 1];
+       struct nlattr *tb[NL80211_ATTR_BSS + 1];
+       struct nlattr *bss[NL80211_BSS_STATUS + 1];
+       uint32_t status;
+       unsigned char *ie;
+       int ie_len;
 
-       if (gnl_parse(nlm, tb, NL80211_ATTR_SSID) == -1)
-               return -1;
+       if (genl_parse(nlm, tb, NL80211_ATTR_BSS) == -1)
+               return 0;
 
-       if (tb[NL80211_ATTR_SSID] == NULL) {
-               /* If the SSID is not found then it means that
-                * we're not associated to an AP. */
-               ifp->ssid_len = 0;
-               goto out;
-       }
+       if (tb[NL80211_ATTR_BSS] == NULL)
+               return 0;
 
-       ifp->ssid_len = NLA_LEN(tb[NL80211_ATTR_SSID]);
-       if (ifp->ssid_len > IF_SSIDLEN) {
-               errno = ENOBUFS;
-               ifp->ssid_len = 0;
-               return -1;
+       if (nla_parse(bss,
+           NLA_DATA(tb[NL80211_ATTR_BSS]),
+           NLA_LEN(tb[NL80211_ATTR_BSS]),
+           NL80211_BSS_STATUS) == -1)
+               return 0;
+               
+       if (bss[NL80211_BSS_BSSID] == NULL || bss[NL80211_BSS_STATUS] == NULL)
+               return 0;
+
+       memcpy(&status, NLA_DATA(bss[NL80211_BSS_STATUS]), sizeof(status));
+       if (status != NL80211_BSS_STATUS_ASSOCIATED)
+               return 0;
+
+       if (bss[NL80211_BSS_INFORMATION_ELEMENTS] == NULL)
+               return 0;
+
+       ie = NLA_DATA(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+       ie_len = (int)NLA_LEN(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+       /* ie[0] is type, ie[1] is lenth, ie[2..] is data */
+       while (ie_len >= 2 && ie_len >= ie[1]) {
+               if (ie[0] == 0) {
+                       /* SSID */
+                       if (ie[1] > IF_SSIDLEN) {
+                               errno = ENOBUFS;
+                               return -1;
+                       }
+                       ifp->ssid_len = ie[1];
+                       memcpy(ifp->ssid, ie + 2, ifp->ssid_len);
+                       return (int)ifp->ssid_len;
+               }
+               ie_len -= ie[1] + 2;
+               ie += ie[1] + 2;
        }
-       memcpy(ifp->ssid, NLA_DATA(tb[NL80211_ATTR_SSID]), ifp->ssid_len);
 
-out:
-       return (int)ifp->ssid_len;
+       return 0;
 }
 
 static int
@@ -1151,15 +1186,28 @@ if_getssid_nl80211(struct interface *ifp)
        family = gnl_getfamily(ifp->ctx, "nl80211");
        if (family == -1)
                return -1;
+
+       /* Is this a wireless interface? */
        memset(&nlm, 0, sizeof(nlm));
        nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));
        nlm.hdr.nlmsg_type = (unsigned short)family;
        nlm.hdr.nlmsg_flags = NLM_F_REQUEST;
-       nlm.ghdr.cmd = NL80211_CMD_GET_INTERFACE;
+       nlm.ghdr.cmd = NL80211_CMD_GET_WIPHY;
+       nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);
+       if (send_netlink(ifp->ctx, ifp, NETLINK_GENERIC, &nlm.hdr, NULL) == -1)
+               return -1;
+
+       /* We need to parse out the list of scan results and find the one
+        * we are connected to. */
+       memset(&nlm, 0, sizeof(nlm));
+       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));
+       nlm.hdr.nlmsg_type = (unsigned short)family;
+       nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       nlm.ghdr.cmd = NL80211_CMD_GET_SCAN;
        nla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);
 
        return send_netlink(ifp->ctx, ifp,
-           NETLINK_GENERIC, &nlm.hdr, &_if_getssid);
+           NETLINK_GENERIC, &nlm.hdr, &_if_getssid_nl80211);
 }
 #endif
 
@@ -1168,18 +1216,16 @@ if_getssid(struct interface *ifp)
 {
        int r;
 
+#ifdef HAVE_NL80211_H
+       r = if_getssid_nl80211(ifp);
+       if (r == -1)
+               ifp->ssid_len = 0;
+#else
        r = if_getssid_wext(ifp->name, ifp->ssid);
        if (r != -1)
                ifp->ssid_len = (unsigned int)r;
-       else {
-#ifdef HAVE_NL80211_H
-               r = if_getssid_nl80211(ifp);
-               if (r == -1)
-                       ifp->ssid_len = 0;
-#else
-               ifp->ssid_len = 0;
 #endif
-       }
+
        ifp->ssid[ifp->ssid_len] = '\0';
        return r;
 }