]> git.ipfire.org Git - thirdparty/iw.git/commitdiff
add link command
authorJohannes Berg <johannes@sipsolutions.net>
Sat, 25 Jul 2009 15:31:08 +0000 (17:31 +0200)
committerJohannes Berg <johannes@sipsolutions.net>
Sat, 25 Jul 2009 15:31:08 +0000 (17:31 +0200)
Makefile
iw.h
link.c [new file with mode: 0644]
scan.c
util.c

index 520723cf77fd2a7819a2bd7bfb867ae509452646..e356ae49a6d5e2266ac5624f3f358ec278fe49b6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,10 @@ CC ?= "gcc"
 CFLAGS ?= -O2 -g
 CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration
 
-OBJS = iw.o genl.o event.o info.o phy.o interface.o ibss.o station.o util.o mesh.o mpath.o scan.o reg.o version.o reason.o status.o connect.o
+OBJS = iw.o genl.o event.o info.o phy.o \
+       interface.o ibss.o station.o util.o \
+       mesh.o mpath.o scan.o reg.o version.o \
+       reason.o status.o connect.o link.o
 ALL = iw
 
 NL1FOUND := $(shell $(PKG_CONFIG) --atleast-version=1 libnl-1 && echo Y)
diff --git a/iw.h b/iw.h
index a33b3535c8db7b2628f0309576db61280f313b50..10bde358f05eafad7ae70c3db736230e8108103a 100644 (file)
--- a/iw.h
+++ b/iw.h
@@ -100,7 +100,7 @@ __u32 __listen_events(struct nl80211_state *state,
 
 
 int mac_addr_a2n(unsigned char *mac_addr, char *arg);
-int mac_addr_n2a(char *mac_addr, unsigned char *arg);
+void mac_addr_n2a(char *mac_addr, unsigned char *arg);
 
 int parse_keys(struct nl_msg *msg, char **argv, int argc);
 
@@ -119,4 +119,14 @@ const char *get_status_str(uint16_t status);
 
 int set_interface_up(const char *ifname);
 
+enum print_ie_type {
+       PRINT_SCAN,
+       PRINT_LINK,
+};
+
+#define BIT(x) (1ULL<<(x))
+
+void print_ies(unsigned char *ie, int ielen, bool unknown,
+              enum print_ie_type ptype);
+
 #endif /* __IW_H */
diff --git a/link.c b/link.c
new file mode 100644 (file)
index 0000000..6e8b4d3
--- /dev/null
+++ b/link.c
@@ -0,0 +1,257 @@
+#include <net/if.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "nl80211.h"
+#include "iw.h"
+
+struct link_result {
+       uint8_t bssid[8];
+       bool link_found;
+       bool anything_found;
+};
+
+static struct link_result lr = { .link_found = false };
+
+static int link_bss_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *bss[NL80211_BSS_MAX + 1];
+       static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+               [NL80211_BSS_TSF] = { .type = NLA_U64 },
+               [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_BSS_BSSID] = { },
+               [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+               [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+               [NL80211_BSS_INFORMATION_ELEMENTS] = { },
+               [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+               [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+               [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+       };
+       struct link_result *result = arg;
+       char mac_addr[20], dev[20];
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb[NL80211_ATTR_BSS]) {
+               fprintf(stderr, "bss info missing!");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(bss, NL80211_BSS_MAX,
+                            tb[NL80211_ATTR_BSS],
+                            bss_policy)) {
+               fprintf(stderr, "failed to parse nested attributes!");
+               return NL_SKIP;
+       }
+
+       if (!bss[NL80211_BSS_BSSID])
+               return NL_SKIP;
+
+       if (!bss[NL80211_BSS_STATUS])
+               return NL_SKIP;
+
+       mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
+       if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+
+       switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
+       case NL80211_BSS_STATUS_ASSOCIATED:
+               printf("Connected to %s (on %s)\n", mac_addr, dev);
+               break;
+       case NL80211_BSS_STATUS_AUTHENTICATED:
+               printf("Authenticated with %s (on %s)\n", mac_addr, dev);
+               return NL_SKIP;
+       default:
+               return NL_SKIP;
+       }
+
+       result->anything_found = true;
+
+       if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
+               print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+                         nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+                         false, PRINT_LINK);
+
+       if (bss[NL80211_BSS_FREQUENCY])
+               printf("\tfreq: %d\n",
+                       nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
+
+       if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
+               return NL_SKIP;
+
+       /* only in the assoc case do we want more info from station get */
+       result->link_found = true;
+       memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
+       return NL_SKIP;
+}
+
+static int handle_scan_for_link(struct nl80211_state *state,
+                               struct nl_cb *cb,
+                               struct nl_msg *msg,
+                               int argc, char **argv)
+{
+       if (argc > 0)
+               return 1;
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, link_bss_handler, &lr);
+       return 0;
+}
+HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+       CIB_NETDEV, handle_scan_for_link);
+
+static int print_link_sta(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
+               [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
+               [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+       };
+
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+       };
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb[NL80211_ATTR_STA_INFO]) {
+               fprintf(stderr, "sta stats missing!");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO],
+                            stats_policy)) {
+               fprintf(stderr, "failed to parse nested attributes!");
+               return NL_SKIP;
+       }
+
+       if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
+               printf("\tRX: %d bytes (%d packets)\n",
+                       nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]),
+                       nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
+       if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS])
+               printf("\tTX: %d bytes (%d packets)\n",
+                       nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]),
+                       nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
+       if (sinfo[NL80211_STA_INFO_SIGNAL])
+               printf("\tsignal: %d dBm\n",
+                       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
+
+       if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+               if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+                                    sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) {
+                       fprintf(stderr, "failed to parse nested rate attributes!");
+               } else {
+                       printf("\ttx bitrate: ");
+                       if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+                               int rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
+                               printf("%d.%d MBit/s", rate / 10, rate % 10);
+                       }
+
+                       if (rinfo[NL80211_RATE_INFO_MCS])
+                               printf(" MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
+                       if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
+                               printf(" 40Mhz");
+                       if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+                               printf(" short GI");
+                       printf("\n");
+               }
+       }
+
+       return NL_SKIP;
+}
+
+static int handle_link_sta(struct nl80211_state *state,
+                          struct nl_cb *cb,
+                          struct nl_msg *msg,
+                          int argc, char **argv)
+{
+       unsigned char mac_addr[ETH_ALEN];
+
+       if (argc < 1)
+               return 1;
+
+       if (mac_addr_a2n(mac_addr, argv[0])) {
+               fprintf(stderr, "invalid mac address\n");
+               return 2;
+       }
+
+       argc--;
+       argv++;
+
+       if (argc)
+               return 1;
+
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_link_sta, NULL);
+
+       return 0;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0,
+       CIB_NETDEV, handle_link_sta);
+
+static int handle_link(struct nl80211_state *state, struct nl_cb *cb,
+                      struct nl_msg *msg, int argc, char **argv)
+{
+       char *link_argv[] = {
+               NULL,
+               "link",
+               "get_bss",
+               NULL,
+       };
+       char *station_argv[] = {
+               NULL,
+               "link",
+               "get_sta",
+               NULL,
+               NULL,
+       };
+       char bssid_buf[3*6];
+       int err;
+
+       link_argv[0] = argv[0];
+       err = handle_cmd(state, II_NETDEV, 3, link_argv);
+       if (err)
+               return err;
+
+       if (!lr.link_found) {
+               if (!lr.anything_found)
+                       printf("Not connected.");
+               return 0;
+       }
+
+       mac_addr_n2a(bssid_buf, lr.bssid);
+       bssid_buf[17] = '\0';
+
+       station_argv[0] = argv[0];
+       station_argv[3] = bssid_buf;
+       return handle_cmd(state, II_NETDEV, 4, station_argv);
+}
+TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
+        "Print information about the current link, if any.");
diff --git a/scan.c b/scan.c
index 64c9260e0555e7b6b396897740b84aacd35c83e3..fe3d054af0518db161253f456b0be2558cc571fe 100644 (file)
--- a/scan.c
+++ b/scan.c
@@ -32,6 +32,7 @@ static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
 
 struct scan_params {
        bool unknown;
+       enum print_ie_type type;
 };
 
 static int handle_scan(struct nl80211_state *state,
@@ -478,6 +479,7 @@ struct ie_print {
        const char *name;
        void (*print)(const uint8_t type, uint8_t len, const uint8_t *data);
        uint8_t minlen, maxlen;
+       uint8_t flags;
 };
 
 static void print_ie(const struct ie_print *p, const uint8_t type,
@@ -513,16 +515,16 @@ static void print_ie(const struct ie_print *p, const uint8_t type,
 }
 
 static const struct ie_print ieprinters[] = {
-       [0] = { "SSID", print_ssid, 0, 32, },
-       [1] = { "Supported rates", print_supprates, 0, 255, },
-       [3] = { "DS Paramater set", print_ds, 1, 1, },
+       [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
+       [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
+       [3] = { "DS Paramater set", print_ds, 1, 1, BIT(PRINT_SCAN), },
        [5] = PRINT_IGN,
-       [7] = { "Country", print_country, 3, 255, },
-       [32] = { "Power constraint", print_powerconstraint, 1, 1, },
-       [42] = { "ERP", print_erp, 1, 255, },
-       [48] = { "RSN", print_rsn, 2, 255, },
-       [50] = { "Extended supported rates", print_supprates, 0, 255, },
-       [127] = { "Extended capabilities", print_capabilities, 0, 255, },
+       [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
+       [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
+       [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
+       [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
+       [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
+       [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
 };
 
 static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data)
@@ -630,13 +632,13 @@ static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data)
 }
 
 static const struct ie_print wifiprinters[] = {
-       [1] = { "WPA", print_wifi_wpa, 2, 255, },
-       [2] = { "WMM", print_wifi_wmm, 1, 255, },
-       [4] = { "WPS", print_wifi_wps, 0, 255, },
+       [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
+       [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
+       [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
 };
 
 static void print_vendor(unsigned char len, unsigned char *data,
-                        struct scan_params *params)
+                        bool unknown, enum print_ie_type ptype)
 {
        int i;
 
@@ -649,11 +651,13 @@ static void print_vendor(unsigned char len, unsigned char *data,
        }
 
        if (len >= 4 && memcmp(data, wifi_oui, 3) == 0) {
-               if (data[3] < ARRAY_SIZE(wifiprinters) && wifiprinters[data[3]].name) {
+               if (data[3] < ARRAY_SIZE(wifiprinters) &&
+                   wifiprinters[data[3]].name &&
+                   wifiprinters[data[3]].flags & BIT(ptype)) {
                        print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4);
                        return;
                }
-               if (!params->unknown)
+               if (!unknown)
                        return;
                printf("\tWiFi OUI %#.2x, data:", data[3]);
                for(i = 0; i < len - 4; i++)
@@ -662,7 +666,7 @@ static void print_vendor(unsigned char len, unsigned char *data,
                return;
        }
 
-       if (!params->unknown)
+       if (!unknown)
                return;
 
        printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
@@ -672,14 +676,17 @@ static void print_vendor(unsigned char len, unsigned char *data,
        printf("\n");
 }
 
-static void print_ies(unsigned char *ie, int ielen, struct scan_params *params)
+void print_ies(unsigned char *ie, int ielen, bool unknown,
+              enum print_ie_type ptype)
 {
        while (ielen >= 2 && ielen >= ie[1]) {
-               if (ie[0] < ARRAY_SIZE(ieprinters) && ieprinters[ie[0]].name) {
+               if (ie[0] < ARRAY_SIZE(ieprinters) &&
+                   ieprinters[ie[0]].name &&
+                   ieprinters[ie[0]].flags & BIT(ptype)) {
                        print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2);
                } else if (ie[0] == 221 /* vendor */) {
-                       print_vendor(ie[1], ie + 2, params);
-               } else if (params->unknown) {
+                       print_vendor(ie[1], ie + 2, unknown, ptype);
+               } else if (unknown) {
                        int i;
 
                        printf("\tUnknown IE (%d):", ie[0]);
@@ -709,6 +716,7 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
                [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
                [NL80211_BSS_STATUS] = { .type = NLA_U32 },
        };
+       struct scan_params *params = arg;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -801,7 +809,7 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
        if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
                print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
                          nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
-                         arg);
+                         params->unknown, params->type);
 
        return NL_SKIP;
 }
@@ -820,6 +828,8 @@ static int handle_scan_dump(struct nl80211_state *state,
        if (argc == 1 && !strcmp(argv[0], "-u"))
                scan_params.unknown = true;
 
+       scan_params.type = PRINT_SCAN;
+
        nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler,
                  &scan_params);
        return 0;
diff --git a/util.c b/util.c
index cca0c14637414d41324ec55bad8df9fabb0ef3fb..3496c6aa98c0e8f80d45e400a3dc6da821803080 100644 (file)
--- a/util.c
+++ b/util.c
@@ -9,7 +9,7 @@
 #include "iw.h"
 #include "nl80211.h"
 
-int mac_addr_n2a(char *mac_addr, unsigned char *arg)
+void mac_addr_n2a(char *mac_addr, unsigned char *arg)
 {
        int i, l;
 
@@ -23,7 +23,6 @@ int mac_addr_n2a(char *mac_addr, unsigned char *arg)
                        l += 3;
                }
        }
-       return 0;
 }
 
 int mac_addr_a2n(unsigned char *mac_addr, char *arg)