The new regrulelist operation returns the list of regulatory rules, similar
to the `iw reg get` command. The passed ifname must either refer to a PHY,
or be NULL to retrieve the rules for the global regdomain.

The new operation is implemented for nl80211 only.

Signed-off-by: Matthias Schiffer <mschif...@universe-factory.net>
---
 include/iwinfo.h |  27 ++++++++++
 iwinfo_nl80211.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)

Usecase: In Gluon we would like to use the regulatory data to filter the
channel list for channels that are valid for indoor / outdoor use.


diff --git a/include/iwinfo.h b/include/iwinfo.h
index 9b2ffd1ea111..f7e53c599e5f 100644
--- a/include/iwinfo.h
+++ b/include/iwinfo.h
@@ -57,6 +57,22 @@
 #define IWINFO_FREQ_NO_160MHZ          (1 << 5)
 #define IWINFO_FREQ_NO_2160MHZ         (1 << 6)
 
+#define IWINFO_REGRULE_NO_OFDM         (1 << 0)
+#define IWINFO_REGRULE_NO_CCK          (1 << 1)
+#define IWINFO_REGRULE_NO_INDOOR       (1 << 2)
+#define IWINFO_REGRULE_NO_OUTDOOR      (1 << 3)
+#define IWINFO_REGRULE_DFS             (1 << 4)
+#define IWINFO_REGRULE_PTP_ONLY                (1 << 5)
+#define IWINFO_REGRULE_AUTO_BW         (1 << 6)
+#define IWINFO_REGRULE_IR_CONCURRENT   (1 << 7)
+#define IWINFO_REGRULE_NO_HT40MINUS    (1 << 8)
+#define IWINFO_REGRULE_NO_HT40PLUS     (1 << 9)
+#define IWINFO_REGRULE_NO_80MHZ                (1 << 10)
+#define IWINFO_REGRULE_NO_160MHZ       (1 << 11)
+#define IWINFO_REGRULE_NO_IR           (1 << 12)
+#define IWINFO_REGRULE_PASSIVE_SCAN    (1 << 13)
+#define IWINFO_REGRULE_NO_IBSS         (1 << 14)
+
 extern const char *IWINFO_CIPHER_NAMES[IWINFO_CIPHER_COUNT];
 extern const char *IWINFO_KMGMT_NAMES[IWINFO_KMGMT_COUNT];
 extern const char *IWINFO_AUTH_NAMES[IWINFO_AUTH_COUNT];
@@ -183,6 +199,16 @@ struct iwinfo_country_entry {
        char ccode[4];
 };
 
+struct iwinfo_regrule_entry {
+       uint32_t start_freq_khz;
+       uint32_t end_freq_khz;
+       uint32_t max_bw_khz;
+       uint32_t max_ant_gain_mbi;
+       uint32_t max_eirp_mbm;
+       uint32_t dfs_cac_time_ms;
+       uint32_t flags;
+};
+
 struct iwinfo_iso3166_label {
        uint16_t iso3166;
        char name[28];
@@ -242,6 +268,7 @@ struct iwinfo_ops {
        int (*freqlist)(const char *, char *, int *);
        int (*countrylist)(const char *, char *, int *);
        int (*survey)(const char *, char *, int *);
+       int (*regrulelist)(const char *, char *, int *);
        int (*lookup_phy)(const char *, char *);
        void (*close)(void);
 };
diff --git a/iwinfo_nl80211.c b/iwinfo_nl80211.c
index 200be28d9a44..9b1efea2f4b5 100644
--- a/iwinfo_nl80211.c
+++ b/iwinfo_nl80211.c
@@ -2732,6 +2732,131 @@ static int nl80211_get_countrylist(const char *ifname, 
char *buf, int *len)
        return 0;
 }
 
+static int nl80211_get_regrulelist_cb(struct nl_msg *msg, void *arg) {
+       struct nl80211_array_buf *arr = arg;
+       struct iwinfo_regrule_entry *e;
+       const char *const end = (char *)arr->buf + IWINFO_BUFSIZE;
+
+       uint32_t flags;
+       int rule_rem;
+
+       struct nlattr **attr = nl80211_parse(msg);
+       struct nlattr *rule;
+       struct nlattr *rule_attr[NL80211_REG_RULE_ATTR_MAX + 1];
+
+       e = arr->buf;
+       e += arr->count;
+
+       if (!attr[NL80211_ATTR_REG_RULES])
+               return NL_SKIP;
+
+       nla_for_each_nested(rule, attr[NL80211_ATTR_REG_RULES], rule_rem)
+       {
+               if ((char *)(e+1) > end)
+                       break; // We're out of buffer space...
+
+               nla_parse(rule_attr, NL80211_REG_RULE_ATTR_MAX, nla_data(rule), 
nla_len(rule), NULL);
+
+               if (
+                       !rule_attr[NL80211_ATTR_REG_RULE_FLAGS] ||
+                       !rule_attr[NL80211_ATTR_FREQ_RANGE_START] ||
+                       !rule_attr[NL80211_ATTR_FREQ_RANGE_END] ||
+                       !rule_attr[NL80211_ATTR_FREQ_RANGE_MAX_BW] ||
+                       !rule_attr[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] ||
+                       !rule_attr[NL80211_ATTR_POWER_RULE_MAX_EIRP]
+               )
+                       continue;
+
+               flags = nla_get_u32(rule_attr[NL80211_ATTR_REG_RULE_FLAGS]);
+
+               e->flags = 0;
+               e->start_freq_khz = 
nla_get_u32(rule_attr[NL80211_ATTR_FREQ_RANGE_START]);
+               e->end_freq_khz = 
nla_get_u32(rule_attr[NL80211_ATTR_FREQ_RANGE_END]);
+               e->max_bw_khz = 
nla_get_u32(rule_attr[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+               e->max_ant_gain_mbi = 
nla_get_u32(rule_attr[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
+               e->max_eirp_mbm = 
nla_get_u32(rule_attr[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
+
+               if ((flags & NL80211_RRF_DFS) && 
rule_attr[NL80211_ATTR_DFS_CAC_TIME]) {
+                       e->dfs_cac_time_ms = 
nla_get_u32(rule_attr[NL80211_ATTR_DFS_CAC_TIME]);
+               }
+
+#define HANDLE_FLAG(flag) \
+       do { \
+               if (flags & NL80211_RRF_##flag) \
+                       e->flags |= IWINFO_REGRULE_##flag; \
+       } while (0)
+
+               HANDLE_FLAG(NO_OFDM);
+               HANDLE_FLAG(NO_CCK);
+               HANDLE_FLAG(NO_INDOOR);
+               HANDLE_FLAG(NO_OUTDOOR);
+               HANDLE_FLAG(DFS);
+               HANDLE_FLAG(PTP_ONLY);
+               HANDLE_FLAG(AUTO_BW);
+               HANDLE_FLAG(IR_CONCURRENT);
+               HANDLE_FLAG(NO_HT40MINUS);
+               HANDLE_FLAG(NO_HT40PLUS);
+               HANDLE_FLAG(NO_80MHZ);
+               HANDLE_FLAG(NO_160MHZ);
+
+               /* Logic taken from iw */
+               if ((flags & NL80211_RRF_NO_IR) && (flags & 
__NL80211_RRF_NO_IBSS)) {
+                       e->flags |= IWINFO_REGRULE_NO_IR;
+               } else {
+                       HANDLE_FLAG(PASSIVE_SCAN);
+
+                       if (flags & __NL80211_RRF_NO_IBSS)
+                               e->flags |= IWINFO_REGRULE_NO_IBSS;
+               }
+
+#undef HANDLE_FLAG
+
+               e++;
+               arr->count++;
+       }
+
+       return NL_SKIP;
+}
+
+static int nl80211_get_regrulelist(const char *ifname, char *buf, int *len)
+{
+       struct nl80211_msg_conveyor *cv;
+       struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
+       int phyidx = -1;
+
+       if (nl80211_init() < 0)
+               goto out;
+
+       if (ifname) {
+               if (!strncmp(ifname, "phy", 3))
+                       phyidx = atoi(&ifname[3]);
+               else if (!strncmp(ifname, "radio", 5))
+                       phyidx = nl80211_phy_idx_from_uci(ifname);
+
+               if (phyidx < 0)
+                       goto out;
+       }
+
+       cv = nl80211_new(nls->nl80211, NL80211_CMD_GET_REG, 0);
+       if (!cv)
+               goto out;
+
+       if (ifname)
+               NLA_PUT_U32(cv->msg, NL80211_ATTR_WIPHY, phyidx);
+
+       if (nl80211_send(cv, nl80211_get_regrulelist_cb, &arr))
+               goto out;
+
+       *len = arr.count * sizeof(struct iwinfo_regrule_entry);
+       return 0;
+
+nla_put_failure:
+       nl80211_free(cv);
+out:
+       *len = 0;
+       return -1;
+}
+
 
 struct nl80211_modes
 {
@@ -3045,6 +3170,7 @@ const struct iwinfo_ops nl80211_ops = {
        .freqlist         = nl80211_get_freqlist,
        .countrylist      = nl80211_get_countrylist,
        .survey           = nl80211_get_survey,
+       .regrulelist      = nl80211_get_regrulelist,
        .lookup_phy       = nl80211_lookup_phyname,
        .close            = nl80211_close
 };
-- 
2.21.0


_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to