]> git.ipfire.org Git - thirdparty/iw.git/blobdiff - station.c
iw: Fix memory leak in error path
[thirdparty/iw.git] / station.c
index eb02b0c8f3a9761a3aa9f98289da9522024eefb8..b5ccf4a6f65a0435816509c2bde92be70a2fa334 100644 (file)
--- a/station.c
+++ b/station.c
@@ -23,12 +23,6 @@ enum plink_state {
        BLOCKED
 };
 
-enum plink_actions {
-       PLINK_ACTION_UNDEFINED,
-       PLINK_ACTION_OPEN,
-       PLINK_ACTION_BLOCK,
-};
-
 static void print_power_mode(struct nlattr *a)
 {
        enum nl80211_mesh_power_mode pm = nla_get_u32(a);
@@ -49,12 +43,86 @@ static void print_power_mode(struct nlattr *a)
        }
 }
 
+void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
+{
+       int rate = 0;
+       char *pos = buf;
+       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+       };
+
+       if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+                            bitrate_attr, rate_policy)) {
+               snprintf(buf, buflen, "failed to parse nested rate attributes!");
+               return;
+       }
+
+       if (rinfo[NL80211_RATE_INFO_BITRATE32])
+               rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
+       else if (rinfo[NL80211_RATE_INFO_BITRATE])
+               rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
+       if (rate > 0)
+               pos += snprintf(pos, buflen - (pos - buf),
+                               "%d.%d MBit/s", rate / 10, rate % 10);
+
+       if (rinfo[NL80211_RATE_INFO_MCS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
+       if (rinfo[NL80211_RATE_INFO_VHT_MCS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
+       if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
+       if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
+       if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
+       if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
+       if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+               pos += snprintf(pos, buflen - (pos - buf), " short GI");
+       if (rinfo[NL80211_RATE_INFO_VHT_NSS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
+}
+
+static char *get_chain_signal(struct nlattr *attr_list)
+{
+       struct nlattr *attr;
+       static char buf[64];
+       char *cur = buf;
+       int i = 0, rem;
+       const char *prefix;
+
+       if (!attr_list)
+               return "";
+
+       nla_for_each_nested(attr, attr_list, rem) {
+               if (i++ > 0)
+                       prefix = ", ";
+               else
+                       prefix = "[";
+
+               cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
+                               (int8_t) nla_get_u8(attr));
+       }
+
+       if (i)
+               snprintf(cur, sizeof(buf) - (cur - buf), "] ");
+
+       return buf;
+}
+
 static int print_sta_handler(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];
        char mac_addr[20], state_name[10], dev[20];
        struct nl80211_sta_flag_update *sta_flags;
        static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
@@ -66,6 +134,7 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
                [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
                [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
                [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_RX_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 },
@@ -76,15 +145,10 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
                [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32},
                [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32},
                [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32},
+               [NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED },
        };
-
-       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
-               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
-               [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
-               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
-               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
-               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
-       };
+       char *chain;
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
@@ -131,37 +195,46 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
        if (sinfo[NL80211_STA_INFO_TX_FAILED])
                printf("\n\ttx failed:\t%u",
                        nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
+
+       chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
        if (sinfo[NL80211_STA_INFO_SIGNAL])
-               printf("\n\tsignal:  \t%d dBm",
-                       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
+               printf("\n\tsignal:  \t%d %sdBm",
+                       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
+                       chain);
+
+       chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
        if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
-               printf("\n\tsignal avg:\t%d dBm",
-                       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]));
+               printf("\n\tsignal avg:\t%d %sdBm",
+                       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
+                       chain);
+
        if (sinfo[NL80211_STA_INFO_T_OFFSET])
                printf("\n\tToffset:\t%lld us",
                        (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]));
 
        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!\n");
-               } else {
-                       int rate = 0;
-                       printf("\n\ttx bitrate:\t");
-                       if (rinfo[NL80211_RATE_INFO_BITRATE32])
-                               rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
-                       else if (rinfo[NL80211_RATE_INFO_BITRATE])
-                               rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
-                       if (rate > 0)
-                               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");
-               }
+               char buf[100];
+
+               parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
+               printf("\n\ttx bitrate:\t%s", buf);
+       }
+
+       if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
+               char buf[100];
+
+               parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
+               printf("\n\trx bitrate:\t%s", buf);
+       }
+
+       if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
+               uint32_t thr;
+
+               thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+               /* convert in Mbps but scale by 1000 to save kbps units */
+               thr = thr * 1000 / 1024;
+
+               printf("\n\texpected throughput:\t%u.%uMbps",
+                      thr / 1000, thr % 1000);
        }
 
        if (sinfo[NL80211_STA_INFO_LLID])
@@ -257,7 +330,7 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
                }
 
                if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
-                       printf("\n\tTDLS peer:\t\t");
+                       printf("\n\tTDLS peer:\t");
                        if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                                printf("yes");
                        else
@@ -265,12 +338,15 @@ static int print_sta_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       if (sinfo[NL80211_STA_INFO_CONNECTED_TIME])
+               printf("\n\tconnected time:\t%u seconds",
+                       nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]));
+
        printf("\n");
        return NL_SKIP;
 }
 
 static int handle_station_get(struct nl80211_state *state,
-                             struct nl_cb *cb,
                              struct nl_msg *msg,
                              int argc, char **argv,
                              enum id_input id)
@@ -293,7 +369,7 @@ static int handle_station_get(struct nl80211_state *state,
 
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
-       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+       register_handler(print_sta_handler, NULL);
 
        return 0;
  nla_put_failure:
@@ -324,7 +400,6 @@ static const struct cmd *select_station_cmd(int argc, char **argv)
 }
 
 static int handle_station_set_plink(struct nl80211_state *state,
-                             struct nl_cb *cb,
                              struct nl_msg *msg,
                              int argc, char **argv,
                              enum id_input id)
@@ -348,9 +423,9 @@ static int handle_station_set_plink(struct nl80211_state *state,
        argv++;
 
        if (strcmp("open", argv[0]) == 0)
-               plink_action = PLINK_ACTION_OPEN;
+               plink_action = NL80211_PLINK_ACTION_OPEN;
        else if (strcmp("block", argv[0]) == 0)
-               plink_action = PLINK_ACTION_BLOCK;
+               plink_action = NL80211_PLINK_ACTION_BLOCK;
        else {
                fprintf(stderr, "plink action not supported\n");
                return 2;
@@ -374,7 +449,6 @@ COMMAND_ALIAS(station, set, "<MAC address> plink_action <open|block>",
        select_station_cmd, station_set_plink);
 
 static int handle_station_set_vlan(struct nl80211_state *state,
-                                  struct nl_cb *cb,
                                   struct nl_msg *msg,
                                   int argc, char **argv,
                                   enum id_input id)
@@ -422,7 +496,6 @@ COMMAND_ALIAS(station, set, "<MAC address> vlan <ifindex>",
        select_station_cmd, station_set_vlan);
 
 static int handle_station_set_mesh_power_mode(struct nl80211_state *state,
-                                             struct nl_cb *cb,
                                              struct nl_msg *msg,
                                              int argc, char **argv,
                                              enum id_input id)
@@ -475,12 +548,11 @@ COMMAND_ALIAS(station, set, "<MAC address> mesh_power_mode "
        select_station_cmd, station_set_mesh_power_mode);
 
 static int handle_station_dump(struct nl80211_state *state,
-                              struct nl_cb *cb,
                               struct nl_msg *msg,
                               int argc, char **argv,
                               enum id_input id)
 {
-       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
+       register_handler(print_sta_handler, NULL);
        return 0;
 }
 COMMAND(station, dump, NULL,