[OpenWrt-Devel,iwinfo,1/2] Add support for regulatory rules
diff mbox series

Message ID 72d19000530fe2b51b5a59c8ccda4d6c51c3f8ba.1556132277.git.mschiffer@universe-factory.net
State New
Delegated to: Matthias Schiffer
Headers show
Series
  • [OpenWrt-Devel,iwinfo,1/2] Add support for regulatory rules
Related show

Commit Message

Matthias Schiffer April 24, 2019, 7:03 p.m. UTC
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 <mschiffer@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.

Comments

Matthias Schiffer April 24, 2019, 11:29 p.m. UTC | #1
On 4/24/19 9:03 PM, Matthias Schiffer wrote:
> 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 <mschiffer@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.

It seems we have found a simpler solution for Gluon (country3 option in
hostapd.conf). So unless someone else considers this a useful feature for
libiwinfo, this patch can be rejected.


> 
> 
> 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
>  };
>

Patch
diff mbox series

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
 };