5 #include <netlink/genl/genl.h>
6 #include <netlink/genl/family.h>
7 #include <netlink/genl/ctrl.h>
8 #include <netlink/msg.h>
9 #include <netlink/attr.h>
16 #define MHZ_TO_KHZ(freq) ((freq) * 1000)
17 #define KHZ_TO_MHZ(freq) ((freq) / 1000)
18 #define DBI_TO_MBI(gain) ((gain) * 100)
19 #define MBI_TO_DBI(gain) ((gain) / 100)
20 #define DBM_TO_MBM(gain) ((gain) * 100)
21 #define MBM_TO_DBM(gain) ((gain) / 100)
23 static bool isalpha_upper(char letter
)
25 if (letter
>= 65 && letter
<= 90)
30 static bool is_alpha2(char *alpha2
)
32 if (isalpha_upper(alpha2
[0]) && isalpha_upper(alpha2
[1]))
37 static bool is_world_regdom(char *alpha2
)
40 if (alpha2
[0] == 48 && alpha2
[1] == 48)
45 char *reg_initiator_to_string(__u8 initiator
)
48 case NL80211_REGDOM_SET_BY_CORE
:
49 return "the wireless core upon initialization";
50 case NL80211_REGDOM_SET_BY_USER
:
52 case NL80211_REGDOM_SET_BY_DRIVER
:
54 case NL80211_REGDOM_SET_BY_COUNTRY_IE
:
55 return "a country IE";
61 static const char *dfs_domain_name(enum nl80211_dfs_regions region
)
64 case NL80211_DFS_UNSET
:
68 case NL80211_DFS_ETSI
:
77 static int handle_reg_set(struct nl80211_state
*state
,
79 int argc
, char **argv
,
87 if (!is_alpha2(argv
[0]) && !is_world_regdom(argv
[0])) {
88 fprintf(stderr
, "not a valid ISO/IEC 3166-1 alpha2\n");
89 fprintf(stderr
, "Special non-alpha2 usable entries:\n");
90 fprintf(stderr
, "\t00\tWorld Regulatory domain\n");
94 alpha2
[0] = argv
[0][0];
95 alpha2
[1] = argv
[0][1];
104 NLA_PUT_STRING(msg
, NL80211_ATTR_REG_ALPHA2
, alpha2
);
110 COMMAND(reg
, set
, "<ISO/IEC 3166-1 alpha2>",
111 NL80211_CMD_REQ_SET_REG
, 0, CIB_NONE
, handle_reg_set
,
112 "Notify the kernel about the current regulatory domain.");
114 static int print_reg_handler(struct nl_msg
*msg
, void *arg
)
116 #define PARSE_FLAG(nl_flag, string_value) do { \
117 if ((flags & nl_flag)) { \
118 printf(", %s", string_value); \
121 struct nlattr
*tb_msg
[NL80211_ATTR_MAX
+ 1];
122 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
124 struct nlattr
*nl_rule
;
126 enum nl80211_dfs_regions dfs_domain
;
127 static struct nla_policy reg_rule_policy
[NL80211_REG_RULE_ATTR_MAX
+ 1] = {
128 [NL80211_ATTR_REG_RULE_FLAGS
] = { .type
= NLA_U32
},
129 [NL80211_ATTR_FREQ_RANGE_START
] = { .type
= NLA_U32
},
130 [NL80211_ATTR_FREQ_RANGE_END
] = { .type
= NLA_U32
},
131 [NL80211_ATTR_FREQ_RANGE_MAX_BW
] = { .type
= NLA_U32
},
132 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN
] = { .type
= NLA_U32
},
133 [NL80211_ATTR_POWER_RULE_MAX_EIRP
] = { .type
= NLA_U32
},
134 [NL80211_ATTR_DFS_CAC_TIME
] = { .type
= NLA_U32
},
137 nla_parse(tb_msg
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
138 genlmsg_attrlen(gnlh
, 0), NULL
);
140 if (!tb_msg
[NL80211_ATTR_REG_ALPHA2
]) {
141 printf("No alpha2\n");
145 if (!tb_msg
[NL80211_ATTR_REG_RULES
]) {
146 printf("No reg rules\n");
150 if (tb_msg
[NL80211_ATTR_WIPHY
])
151 printf("phy#%d%s\n", nla_get_u32(tb_msg
[NL80211_ATTR_WIPHY
]),
152 tb_msg
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG
] ?
153 " (self-managed)" : "");
157 if (tb_msg
[NL80211_ATTR_DFS_REGION
])
158 dfs_domain
= nla_get_u8(tb_msg
[NL80211_ATTR_DFS_REGION
]);
160 dfs_domain
= NL80211_DFS_UNSET
;
162 alpha2
= nla_data(tb_msg
[NL80211_ATTR_REG_ALPHA2
]);
163 printf("country %c%c: %s\n", alpha2
[0], alpha2
[1], dfs_domain_name(dfs_domain
));
165 nla_for_each_nested(nl_rule
, tb_msg
[NL80211_ATTR_REG_RULES
], rem_rule
) {
166 struct nlattr
*tb_rule
[NL80211_REG_RULE_ATTR_MAX
+ 1];
167 __u32 flags
, start_freq_khz
, end_freq_khz
, max_bw_khz
, max_ant_gain_mbi
, max_eirp_mbm
;
169 nla_parse(tb_rule
, NL80211_REG_RULE_ATTR_MAX
, nla_data(nl_rule
), nla_len(nl_rule
), reg_rule_policy
);
171 flags
= nla_get_u32(tb_rule
[NL80211_ATTR_REG_RULE_FLAGS
]);
172 start_freq_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_START
]);
173 end_freq_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_END
]);
174 max_bw_khz
= nla_get_u32(tb_rule
[NL80211_ATTR_FREQ_RANGE_MAX_BW
]);
175 max_ant_gain_mbi
= nla_get_u32(tb_rule
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN
]);
176 max_eirp_mbm
= nla_get_u32(tb_rule
[NL80211_ATTR_POWER_RULE_MAX_EIRP
]);
179 printf("\t(%d - %d @ %d), (",
180 KHZ_TO_MHZ(start_freq_khz
), KHZ_TO_MHZ(end_freq_khz
), KHZ_TO_MHZ(max_bw_khz
));
182 if (MBI_TO_DBI(max_ant_gain_mbi
))
183 printf("%d", MBI_TO_DBI(max_ant_gain_mbi
));
187 printf(", %d)", MBM_TO_DBM(max_eirp_mbm
));
189 if ((flags
& NL80211_RRF_DFS
) && tb_rule
[NL80211_ATTR_DFS_CAC_TIME
])
190 printf(", (%u ms)", nla_get_u32(tb_rule
[NL80211_ATTR_DFS_CAC_TIME
]));
199 /* Sync this output format to match that of dbparse.py from wireless-regdb.git */
200 PARSE_FLAG(NL80211_RRF_NO_OFDM
, "NO-OFDM");
201 PARSE_FLAG(NL80211_RRF_NO_CCK
, "NO-CCK");
202 PARSE_FLAG(NL80211_RRF_NO_INDOOR
, "NO-INDOOR");
203 PARSE_FLAG(NL80211_RRF_NO_OUTDOOR
, "NO-OUTDOOR");
204 PARSE_FLAG(NL80211_RRF_DFS
, "DFS");
205 PARSE_FLAG(NL80211_RRF_PTP_ONLY
, "PTP-ONLY");
206 PARSE_FLAG(NL80211_RRF_AUTO_BW
, "AUTO-BW");
207 PARSE_FLAG(NL80211_RRF_IR_CONCURRENT
, "IR-CONCURRENT");
208 PARSE_FLAG(NL80211_RRF_NO_HT40MINUS
, "NO-HT40MINUS");
209 PARSE_FLAG(NL80211_RRF_NO_HT40PLUS
, "NO-HT40PLUS");
210 PARSE_FLAG(NL80211_RRF_NO_80MHZ
, "NO-80MHZ");
211 PARSE_FLAG(NL80211_RRF_NO_160MHZ
, "NO-160MHZ");
212 PARSE_FLAG(NL80211_RRF_NO_HE
, "NO-HE");
214 /* Kernels that support NO_IR always turn on both flags */
215 if ((flags
& NL80211_RRF_NO_IR
) && (flags
& __NL80211_RRF_NO_IBSS
)) {
218 PARSE_FLAG(NL80211_RRF_PASSIVE_SCAN
, "PASSIVE-SCAN");
219 PARSE_FLAG(__NL80211_RRF_NO_IBSS
, "NO-IBSS");
230 static int handle_reg_dump(struct nl80211_state
*state
,
232 int argc
, char **argv
,
235 register_handler(print_reg_handler
, NULL
);
239 static int handle_reg_get(struct nl80211_state
*state
,
241 int argc
, char **argv
,
244 char *dump_args
[] = { "reg", "dump" };
247 err
= handle_cmd(state
, II_NONE
, 2, dump_args
);
249 * dump might fail since it's not supported on older kernels,
250 * in that case the handler is still registered already
252 if (err
== -EOPNOTSUPP
)
255 return err
?: HANDLER_RET_DONE
;
257 COMMAND(reg
, get
, NULL
, NL80211_CMD_GET_REG
, 0, CIB_NONE
, handle_reg_get
,
258 "Print out the kernel's current regulatory domain information.");
259 COMMAND(reg
, get
, NULL
, NL80211_CMD_GET_REG
, 0, CIB_PHY
, handle_reg_get
,
260 "Print out the devices' current regulatory domain information.");
261 HIDDEN(reg
, dump
, NULL
, NL80211_CMD_GET_REG
, NLM_F_DUMP
, CIB_NONE
,
264 static int handle_reg_reload(struct nl80211_state
*state
,
266 int argc
, char **argv
,
271 COMMAND(reg
, reload
, NULL
, NL80211_CMD_RELOAD_REGDB
, 0, CIB_NONE
,
272 handle_reg_reload
, "Reload the kernel's regulatory database.");