]> git.ipfire.org Git - thirdparty/iw.git/blobdiff - reg.c
iw: print out mesh configuration element on scan
[thirdparty/iw.git] / reg.c
diff --git a/reg.c b/reg.c
index 37d198dcfac44e0ef6b98eed41d9930dd70f2574..9a60cec4a68e1a5ceca82c62d16701543144fe19 100644 (file)
--- a/reg.c
+++ b/reg.c
@@ -1,7 +1,7 @@
-#include <linux/nl80211.h>
 #include <net/if.h>
 #include <errno.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
@@ -9,70 +9,88 @@
 #include <netlink/msg.h>
 #include <netlink/attr.h>
 
+#include "nl80211.h"
 #include "iw.h"
 
-static int wait_handler(struct nl_msg *msg, void *arg)
-{
-       int *finished = arg;
+SECTION(reg);
 
-       *finished = 1;
-       return NL_STOP;
-}
+#define MHZ_TO_KHZ(freq) ((freq) * 1000)
+#define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define DBI_TO_MBI(gain) ((gain) * 100)
+#define MBI_TO_DBI(gain) ((gain) / 100)
+#define DBM_TO_MBM(gain) ((gain) * 100)
+#define MBM_TO_DBM(gain) ((gain) / 100)
 
-static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
-                        void *arg)
+static bool isalpha_upper(char letter)
 {
-       fprintf(stderr, "nl80211 error %d\n", err->error);
-       exit(err->error);
+       if (letter >= 65 && letter <= 90)
+               return true;
+       return false;
 }
 
-static int reg_handler(struct nl_msg *msg, void *arg)
+static bool is_alpha2(char *alpha2)
 {
-        return NL_SKIP;
+       if (isalpha_upper(alpha2[0]) && isalpha_upper(alpha2[1]))
+               return true;
+       return false;
 }
 
-int isalpha_upper(char letter)
+static bool is_world_regdom(char *alpha2)
 {
-       if (letter >= 65 && letter <= 90)
-               return 1;
-       return 0;
+       /* ASCII 0 */
+       if (alpha2[0] == 48 && alpha2[1] == 48)
+               return true;
+       return false;
 }
 
-static int is_alpha2(char *alpha2)
+char *reg_initiator_to_string(__u8 initiator)
 {
-       if (isalpha_upper(alpha2[0]) && isalpha_upper(alpha2[1]))
-               return 1;
-       return 0;
+       switch (initiator) {
+       case NL80211_REGDOM_SET_BY_CORE:
+               return "the wireless core upon initialization";
+       case NL80211_REGDOM_SET_BY_USER:
+               return "a user";
+       case NL80211_REGDOM_SET_BY_DRIVER:
+               return "a driver";
+       case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+               return "a country IE";
+       default:
+               return "BUG";
+       }
 }
 
-static int is_world_regdom(char *alpha2)
+static const char *dfs_domain_name(enum nl80211_dfs_regions region)
 {
-       /* ASCII 0 */
-       if (alpha2[0] == 48 && alpha2[1] == 48)
-               return 1;
-       return 0;
+       switch (region) {
+       case NL80211_DFS_UNSET:
+               return "DFS-UNSET";
+       case NL80211_DFS_FCC:
+               return "DFS-FCC";
+       case NL80211_DFS_ETSI:
+               return "DFS-ETSI";
+       case NL80211_DFS_JP:
+               return "DFS-JP";
+       default:
+               return "DFS-invalid";
+       }
 }
 
 static int handle_reg_set(struct nl80211_state *state,
-                       int argc, char **argv)
+                         struct nl_cb *cb,
+                         struct nl_msg *msg,
+                         int argc, char **argv,
+                         enum id_input id)
 {
-       struct nl_msg *msg;
-       struct nl_cb *cb = NULL;
-       int ret = -1;
-       int err;
-       int finished = 0;
        char alpha2[3];
 
-       if (argc < 1) {
-               fprintf(stderr, "not enough arguments\n");
-               return -1;
-       }
+       if (argc < 1)
+               return 1;
 
        if (!is_alpha2(argv[0]) && !is_world_regdom(argv[0])) {
                fprintf(stderr, "not a valid ISO/IEC 3166-1 alpha2\n");
-               fprintf(stderr, "Special non-alph2 usable entries:\n");
+               fprintf(stderr, "Special non-alpha2 usable entries:\n");
                fprintf(stderr, "\t00\tWorld Regulatory domain\n");
-               return -1;
+               return 2;
        }
 
        alpha2[0] = argv[0][0];
@@ -82,74 +100,116 @@ static int handle_reg_set(struct nl80211_state *state,
        argc--;
        argv++;
 
-       if (argc) {
-               fprintf(stderr, "too many arguments\n");
-               return -1;
-       }
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               goto out;
-
-       genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
-                   0, NL80211_CMD_REQ_SET_REG, 0);
+       if (argc)
+               return 1;
 
        NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
 
-       cb = nl_cb_alloc(NL_CB_CUSTOM);
-       if (!cb)
-               goto out;
+       return 0;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+COMMAND(reg, set, "<ISO/IEC 3166-1 alpha2>",
+       NL80211_CMD_REQ_SET_REG, 0, CIB_NONE, handle_reg_set,
+       "Notify the kernel about the current regulatory domain.");
 
-       err = nl_send_auto_complete(state->nl_handle, msg);
+static int print_reg_handler(struct nl_msg *msg, void *arg)
 
-       if (err < 0) {
-               fprintf(stderr, "failed to send reg set command\n");
-               goto out;
+{
+#define PARSE_FLAG(nl_flag, string_value)  do { \
+               if ((flags & nl_flag)) { \
+                       printf(", %s", string_value); \
+               } \
+       } while (0)
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       char *alpha2;
+       struct nlattr *nl_rule;
+       int rem_rule;
+       enum nl80211_dfs_regions dfs_domain;
+       static struct nla_policy reg_rule_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+               [NL80211_ATTR_REG_RULE_FLAGS]           = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_START]         = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_END]           = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_MAX_BW]        = { .type = NLA_U32 },
+               [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]  = { .type = NLA_U32 },
+               [NL80211_ATTR_POWER_RULE_MAX_EIRP]      = { .type = NLA_U32 },
+       };
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+               genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
+               printf("No alpha2\n");
+               return NL_SKIP;
        }
 
-       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, reg_handler, NULL);
-       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
-       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
+       if (!tb_msg[NL80211_ATTR_REG_RULES]) {
+               printf("No reg rules\n");
+               return NL_SKIP;
+       }
 
-       err = nl_recvmsgs(state->nl_handle, cb);
+       if (tb_msg[NL80211_ATTR_DFS_REGION])
+               dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+       else
+               dfs_domain = NL80211_DFS_UNSET;
 
-       if (!finished) {
-               err = nl_wait_for_ack(state->nl_handle);
-       }
+       alpha2 = nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]);
+       printf("country %c%c: %s\n", alpha2[0], alpha2[1], dfs_domain_name(dfs_domain));
 
-       if (err < 0)
-               goto out;
+       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) {
+               struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+               __u32 flags, start_freq_khz, end_freq_khz, max_bw_khz, max_ant_gain_mbi, max_eirp_mbm;
 
-       ret = 0;
+               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_rule_policy);
 
- out:
-       nl_cb_put(cb);
- nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
+               flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+               start_freq_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]);
+               end_freq_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]);
+               max_bw_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+               max_ant_gain_mbi = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
+               max_eirp_mbm = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
 
-int handle_reg(struct nl80211_state *state,
-                  int argc, char **argv)
-{
-       char *cmd = argv[0];
 
-       if (argc < 1) {
-               fprintf(stderr, "you must specify an station command\n");
-               return -1;
-       }
+               printf("\t(%d - %d @ %d), (",
+                       KHZ_TO_MHZ(start_freq_khz), KHZ_TO_MHZ(end_freq_khz), KHZ_TO_MHZ(max_bw_khz));
 
-       argc--;
-       argv++;
+               if (MBI_TO_DBI(max_ant_gain_mbi))
+                       printf("%d", MBI_TO_DBI(max_ant_gain_mbi));
+               else
+                       printf("N/A");
+
+               printf(", %d)", MBM_TO_DBM(max_eirp_mbm));
 
-       /* XXX: write support for getting the currently set regdomain
-       if (strcmp(cmd, "get") == 0)
-               return handle_reg_get(state, argc, argv);
-       */
+               if (!flags) {
+                       printf("\n");
+                       continue;
+               }
 
-       if (strcmp(cmd, "set") == 0)
-               return handle_reg_set(state, argc, argv);
+               /* Sync this output format to match that of dbparse.py from wireless-regdb.git */
+               PARSE_FLAG(NL80211_RRF_NO_OFDM, "NO-OFDM");
+               PARSE_FLAG(NL80211_RRF_NO_CCK, "NO-CCK");
+               PARSE_FLAG(NL80211_RRF_NO_INDOOR, "NO-INDOOR");
+               PARSE_FLAG(NL80211_RRF_NO_OUTDOOR, "NO-OUTDOOR");
+               PARSE_FLAG(NL80211_RRF_DFS, "DFS");
+               PARSE_FLAG(NL80211_RRF_PTP_ONLY, "PTP-ONLY");
+               PARSE_FLAG(NL80211_RRF_PASSIVE_SCAN, "PASSIVE-SCAN");
+               PARSE_FLAG(NL80211_RRF_NO_IBSS, "NO-IBSS");
 
-       printf("invalid regulatory command %s\n", cmd);
-       return -1;
+               printf("\n");
+       }
+       return NL_OK;
+#undef PARSE_FLAG
+}
+
+static int handle_reg_get(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_reg_handler, NULL);
+       return 0;
 }
+COMMAND(reg, get, NULL, NL80211_CMD_GET_REG, 0, CIB_NONE, handle_reg_get,
+       "Print out the kernel's current regulatory domain information.");