| #include <errno.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 <inttypes.h> |
| |
| #include "nl80211.h" |
| #include "iw.h" |
| |
| SECTION(ftm); |
| |
| static int handle_ftm_stats(struct nl_msg *msg, void *arg) |
| { |
| struct nlattr *tb[NL80211_ATTR_MAX + 1]; |
| struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| struct nlattr *info[NL80211_FTM_STATS_MAX + 1]; |
| static struct nla_policy info_policy[NL80211_FTM_STATS_MAX + 1] = { |
| [NL80211_FTM_STATS_SUCCESS_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_PARTIAL_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_FAILED_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_ASAP_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_NON_ASAP_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_TOTAL_DURATION_MSEC] = { .type = NLA_U64 }, |
| [NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM] = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM] |
| = { .type = NLA_U32 }, |
| [NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM] |
| = { .type = NLA_U32 }, |
| }; |
| |
| nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| genlmsg_attrlen(gnlh, 0), NULL); |
| |
| if (!tb[NL80211_ATTR_FTM_RESPONDER_STATS]) { |
| fprintf(stderr, "FTM responder statistics are missing"); |
| return NL_SKIP; |
| } |
| |
| nla_parse(info, NL80211_REG_RULE_ATTR_MAX, |
| nla_data(tb[NL80211_ATTR_FTM_RESPONDER_STATS]), |
| nla_len(tb[NL80211_ATTR_FTM_RESPONDER_STATS]), |
| info_policy); |
| |
| printf("FTM responder stats:\n"); |
| |
| if (info[NL80211_FTM_STATS_SUCCESS_NUM]) |
| printf("\tSuccess num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_SUCCESS_NUM])); |
| |
| if (info[NL80211_FTM_STATS_PARTIAL_NUM]) |
| printf("\tPartial success num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_PARTIAL_NUM])); |
| |
| if (info[NL80211_FTM_STATS_FAILED_NUM]) |
| printf("\tFailed num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_FAILED_NUM])); |
| |
| if (info[NL80211_FTM_STATS_ASAP_NUM]) |
| printf("\tASAP success num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_ASAP_NUM])); |
| |
| if (info[NL80211_FTM_STATS_NON_ASAP_NUM]) |
| printf("\tNon ASAP num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_NON_ASAP_NUM])); |
| |
| if (info[NL80211_FTM_STATS_TOTAL_DURATION_MSEC]) |
| printf("\tTotal duration %" PRIu64 "\n", |
| nla_get_u64(info[NL80211_FTM_STATS_TOTAL_DURATION_MSEC])); |
| |
| if (info[NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM]) |
| printf("\tUnknown triggers num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM])); |
| |
| if (info[NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM]) |
| printf("\tRescheduled requests num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM])); |
| |
| if (info[NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM]) |
| printf("\tOut of window num %u\n", |
| nla_get_u32(info[NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM])); |
| |
| return NL_SKIP; |
| } |
| |
| static int handle_ftm_get_stats(struct nl80211_state *state, |
| struct nl_msg *msg, int argc, char **argv, |
| enum id_input id) |
| { |
| register_handler(handle_ftm_stats, NULL); |
| return 0; |
| } |
| |
| COMMAND(ftm, get_stats, "", |
| NL80211_CMD_GET_FTM_RESPONDER_STATS, 0, CIB_NETDEV, handle_ftm_get_stats, |
| "Get FTM responder statistics.\n"); |
| |
| static int handle_ftm_start_responder(struct nl80211_state *state, |
| struct nl_msg *msg, int argc, char **argv, |
| enum id_input id) |
| { |
| int i; |
| char buf[256]; |
| bool lci_present = false, civic_present = false; |
| struct nlattr *ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER); |
| |
| if (!ftm) |
| return -ENOBUFS; |
| |
| nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED); |
| |
| for (i = 0; i < argc; i++) { |
| if (strncmp(argv[i], "lci=", 4) == 0) { |
| size_t lci_len = strlen(argv[i] + 4); |
| |
| if (lci_present || !lci_len || lci_len % 2 || |
| !hex2bin(argv[i] + 4, buf)) { |
| printf("Illegal LCI buffer!\n"); |
| return HANDLER_RET_USAGE; |
| } |
| |
| lci_present = true; |
| NLA_PUT(msg, NL80211_FTM_RESP_ATTR_LCI, |
| lci_len / 2, buf); |
| } else if (strncmp(argv[i], "civic=", 6) == 0) { |
| size_t civic_len = strlen(argv[i] + 6); |
| |
| if (civic_present || !civic_len || civic_len % 2 || |
| !hex2bin(argv[i] + 6, buf)) { |
| printf("Illegal CIVIC buffer!\n"); |
| return HANDLER_RET_USAGE; |
| } |
| |
| civic_present = true; |
| NLA_PUT(msg, NL80211_FTM_RESP_ATTR_CIVICLOC, |
| civic_len / 2, buf); |
| } else { |
| printf("Illegal argument: %s\n", argv[i]); |
| return HANDLER_RET_USAGE; |
| } |
| } |
| |
| nla_nest_end(msg, ftm); |
| |
| return 0; |
| |
| nla_put_failure: |
| return -ENOMEM; |
| } |
| |
| COMMAND(ftm, start_responder, |
| "[lci=<lci buffer in hex>] [civic=<civic buffer in hex>]", |
| NL80211_CMD_SET_BEACON, 0, CIB_NETDEV, |
| handle_ftm_start_responder, |
| "Start an FTM responder. Needs a running ap interface\n"); |