6 #include <netlink/genl/genl.h>
7 #include <netlink/genl/family.h>
8 #include <netlink/genl/ctrl.h>
9 #include <netlink/msg.h>
10 #include <netlink/attr.h>
17 #define MHZ_TO_KHZ(freq) ((freq) * 1000)
18 #define KHZ_TO_MHZ(freq) ((freq) / 1000)
19 #define DBI_TO_MBI(gain) ((gain) * 100)
20 #define MBI_TO_DBI(gain) ((gain) / 100)
21 #define DBM_TO_MBM(gain) ((gain) * 100)
22 #define MBM_TO_DBM(gain) ((gain) / 100)
24 static bool isalpha_upper(char letter
)
26 if (letter
>= 65 && letter
<= 90)
31 static bool is_alpha2(char *alpha2
)
33 if (isalpha_upper(alpha2
[0]) && isalpha_upper(alpha2
[1]))
38 static bool is_world_regdom(char *alpha2
)
41 if (alpha2
[0] == 48 && alpha2
[1] == 48)
46 char *reg_initiator_to_string(__u8 initiator
)
49 case NL80211_REGDOM_SET_BY_CORE
:
50 return "the wireless core upon initialization";
51 case NL80211_REGDOM_SET_BY_USER
:
53 case NL80211_REGDOM_SET_BY_DRIVER
:
55 case NL80211_REGDOM_SET_BY_COUNTRY_IE
:
56 return "a country IE";
62 static const char *dfs_domain_name(enum nl80211_dfs_regions region
)
65 case NL80211_DFS_UNSET
:
69 case NL80211_DFS_ETSI
:
78 static int handle_reg_set(struct nl80211_state
*state
,
81 int argc
, char **argv
,
89 if (!is_alpha2(argv
[0]) && !is_world_regdom(argv
[0])) {
90 fprintf(stderr
, "not a valid ISO/IEC 3166-1 alpha2\n");
91 fprintf(stderr
, "Special non-alpha2 usable entries:\n");
92 fprintf(stderr
, "\t00\tWorld Regulatory domain\n");
96 alpha2
[0] = argv
[0][0];
97 alpha2
[1] = argv
[0][1];
106 NLA_PUT_STRING(msg
, NL80211_ATTR_REG_ALPHA2
, alpha2
);
112 COMMAND(reg
, set
, "<ISO/IEC 3166-1 alpha2>",
113 NL80211_CMD_REQ_SET_REG
, 0, CIB_NONE
, handle_reg_set
,
114 "Notify the kernel about the current regulatory domain.");
116 static int print_reg_handler(struct nl_msg
*msg
, void *arg
)
119 #define PARSE_FLAG(nl_flag, string_value) do { \
120 if ((flags & nl_flag)) { \
121 printf(", %s", string_value); \
124 struct nlattr
*tb_msg
[NL80211_ATTR_MAX
+ 1];
125 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
127 struct nlattr
*nl_rule
;
129 enum nl80211_dfs_regions dfs_domain
;
130 static struct nla_policy reg_rule_policy
[NL80211_FREQUENCY_ATTR_MAX
+ 1] = {
131 [NL80211_ATTR_REG_RULE_FLAGS
] = { .type
= NLA_U32
},
132 [NL80211_ATTR_FREQ_RANGE_START
] = { .type
= NLA_U32
},
133 [NL80211_ATTR_FREQ_RANGE_END
] = { .type
= NLA_U32
},
134 [NL80211_ATTR_FREQ_RANGE_MAX_BW
] = { .type
= NLA_U32
},
135 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN
] = { .type
= NLA_U32
},
136 [NL80211_ATTR_POWER_RULE_MAX_EIRP
] = { .type
= NLA_U32
},
139 nla_parse(tb_msg
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
140 genlmsg_attrlen(gnlh
, 0), NULL
);
142 if (!tb_msg
[NL80211_ATTR_REG_ALPHA2
]) {
143 printf("No alpha2\n");
147 if (!tb_msg
[NL80211_ATTR_REG_RULES
]) {
148 printf("No reg rules\n");
152 if (tb_msg
[NL80211_ATTR_DFS_REGION
])
153 dfs_domain
= nla_get_u8(tb_msg
[NL80211_ATTR_DFS_REGION
]);
155 dfs_domain
= NL80211_DFS_UNSET
;
157 alpha2
= nla_data(tb_msg
[NL80211_ATTR_REG_ALPHA2
]);
158 printf("country %c%c: %s\n", alpha2
[0], alpha2
[1], dfs_domain_name(dfs_domain
));
160 nla_for_each_nested(nl_rule
, tb_msg
[NL80211_ATTR_REG_RULES
], rem_rule
) {
161 struct nlattr
*tb_rule
[NL80211_FREQUENCY_ATTR_MAX
+ 1];
162 __u32 flags
, start_freq_khz
, end_freq_khz
, max_bw_khz
, max_ant_gain_mbi
, max_eirp_mbm
;
164 nla_parse(tb_rule
, NL80211_FREQUENCY_ATTR_MAX
, nla_data(nl_rule
), nla_len(nl_rule
), reg_rule_policy
);
166 flags
= nla_get_u32(tb_rule
[NL80211_ATTR_REG_RULE_FLAGS
]);
167 start_freq_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_START
]);
168 end_freq_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_END
]);
169 max_bw_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_MAX_BW
]);
170 max_ant_gain_mbi
= nla_get_u32(tb_rule
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN
]);
171 max_eirp_mbm
= nla_get_u32(tb_rule
[NL80211_ATTR_POWER_RULE_MAX_EIRP
]);
174 printf("\t(%d - %d @ %d), (",
175 KHZ_TO_MHZ(start_freq_khz
), KHZ_TO_MHZ(end_freq_khz
), KHZ_TO_MHZ(max_bw_khz
));
177 if (MBI_TO_DBI(max_ant_gain_mbi
))
178 printf("%d", MBI_TO_DBI(max_ant_gain_mbi
));
182 printf(", %d)", MBM_TO_DBM(max_eirp_mbm
));
189 /* Sync this output format to match that of dbparse.py from wireless-regdb.git */
190 PARSE_FLAG(NL80211_RRF_NO_OFDM
, "NO-OFDM");
191 PARSE_FLAG(NL80211_RRF_NO_CCK
, "NO-CCK");
192 PARSE_FLAG(NL80211_RRF_NO_INDOOR
, "NO-INDOOR");
193 PARSE_FLAG(NL80211_RRF_NO_OUTDOOR
, "NO-OUTDOOR");
194 PARSE_FLAG(NL80211_RRF_DFS
, "DFS");
195 PARSE_FLAG(NL80211_RRF_PTP_ONLY
, "PTP-ONLY");
197 /* Kernels that support NO_IR always turn on both flags */
198 if ((flags
& NL80211_RRF_NO_IR
) && (flags
& __NL80211_RRF_NO_IBSS
)) {
201 PARSE_FLAG(NL80211_RRF_PASSIVE_SCAN
, "PASSIVE-SCAN");
202 PARSE_FLAG(__NL80211_RRF_NO_IBSS
, "NO-IBSS");
211 static int handle_reg_get(struct nl80211_state
*state
,
214 int argc
, char **argv
,
217 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, print_reg_handler
, NULL
);
220 COMMAND(reg
, get
, NULL
, NL80211_CMD_GET_REG
, 0, CIB_NONE
, handle_reg_get
,
221 "Print out the kernel's current regulatory domain information.");