]> git.ipfire.org Git - thirdparty/iw.git/blob - link.c
update nl80211.h
[thirdparty/iw.git] / link.c
1 #include <net/if.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <stdbool.h>
5
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>
11
12 #include "nl80211.h"
13 #include "iw.h"
14
15 struct link_result {
16 uint8_t sta_addr[8];
17 bool link_found;
18 bool anything_found;
19 bool mld;
20 };
21
22 static struct link_result lr = { .link_found = false };
23
24 static int link_bss_handler(struct nl_msg *msg, void *arg)
25 {
26 struct nlattr *tb[NL80211_ATTR_MAX + 1];
27 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
28 struct nlattr *bss[NL80211_BSS_MAX + 1];
29 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
30 [NL80211_BSS_TSF] = { .type = NLA_U64 },
31 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
32 [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
33 [NL80211_BSS_BSSID] = { },
34 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
35 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
36 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
37 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
38 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
39 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
40 };
41 struct link_result *result = arg;
42 char mac_addr[20], dev[20], link_addr[20];
43 int link_id = -1;
44 const char *indent = "\t";
45 int freq_offset = 0;
46
47 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
48 genlmsg_attrlen(gnlh, 0), NULL);
49
50 if (!tb[NL80211_ATTR_BSS]) {
51 fprintf(stderr, "bss info missing!\n");
52 return NL_SKIP;
53 }
54 if (nla_parse_nested(bss, NL80211_BSS_MAX,
55 tb[NL80211_ATTR_BSS],
56 bss_policy)) {
57 fprintf(stderr, "failed to parse nested attributes!\n");
58 return NL_SKIP;
59 }
60
61 if (!bss[NL80211_BSS_BSSID])
62 return NL_SKIP;
63
64 if (!bss[NL80211_BSS_STATUS])
65 return NL_SKIP;
66
67 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
68 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
69
70 if (bss[NL80211_BSS_MLO_LINK_ID])
71 link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
72
73 if (bss[NL80211_BSS_MLD_ADDR]) {
74 mac_addr_n2a(link_addr, nla_data(bss[NL80211_BSS_BSSID]));
75 indent = "\t\t";
76
77 if (result->mld) {
78 if (memcmp(result->sta_addr,
79 nla_data(bss[NL80211_BSS_MLD_ADDR]), 6)) {
80 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_MLD_ADDR]));
81 printf("!! inconsistent MLD address information (%s)\n",
82 mac_addr);
83 }
84 } else {
85 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_MLD_ADDR]));
86 result->mld = true;
87 memcpy(result->sta_addr,
88 nla_data(bss[NL80211_BSS_MLD_ADDR]), 6);
89 if (nla_get_u32(bss[NL80211_BSS_STATUS]) == NL80211_BSS_STATUS_ASSOCIATED) {
90 printf("Connected to %s (on %s)\n", mac_addr, dev);
91 }
92
93 if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
94 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
95 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
96 false, PRINT_LINK_MLO_MLD);
97 }
98 } else {
99 memcpy(result->sta_addr, nla_data(bss[NL80211_BSS_BSSID]), 6);
100 }
101
102 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
103 case NL80211_BSS_STATUS_ASSOCIATED:
104 if (result->mld)
105 printf("\tLink %d BSSID %s\n", link_id, link_addr);
106 else
107 printf("Connected to %s (on %s)\n", mac_addr, dev);
108 break;
109 case NL80211_BSS_STATUS_AUTHENTICATED:
110 printf("Authenticated with %s (on %s)\n", mac_addr, dev);
111 return NL_SKIP;
112 case NL80211_BSS_STATUS_IBSS_JOINED:
113 printf("Joined IBSS %s (on %s)\n", mac_addr, dev);
114 break;
115 default:
116 return NL_SKIP;
117 }
118
119 result->anything_found = true;
120
121 if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
122 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
123 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
124 false, result->mld ? PRINT_LINK_MLO_LINK : PRINT_LINK);
125
126 if (bss[NL80211_BSS_FREQUENCY_OFFSET])
127 freq_offset = nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]);
128
129 if (bss[NL80211_BSS_FREQUENCY])
130 printf("%sfreq: %d.%d\n", indent,
131 nla_get_u32(bss[NL80211_BSS_FREQUENCY]), freq_offset);
132
133 if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
134 return NL_SKIP;
135
136 /* only in the assoc case do we want more info from station get */
137 result->link_found = true;
138 return NL_SKIP;
139 }
140
141 static int handle_scan_for_link(struct nl80211_state *state,
142 struct nl_msg *msg,
143 int argc, char **argv,
144 enum id_input id)
145 {
146 if (argc > 0)
147 return 1;
148
149 register_handler(link_bss_handler, &lr);
150 return 0;
151 }
152
153 static int print_link_sta(struct nl_msg *msg, void *arg)
154 {
155 struct nlattr *tb[NL80211_ATTR_MAX + 1];
156 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
157 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
158 struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1];
159 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
160 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
161 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
162 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
163 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
164 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
165 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
166 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
167 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
168 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
169 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
170 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
171 };
172 static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = {
173 [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
174 [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
175 [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
176 [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
177 [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
178 };
179
180 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
181 genlmsg_attrlen(gnlh, 0), NULL);
182
183 if (!tb[NL80211_ATTR_STA_INFO]) {
184 fprintf(stderr, "sta stats missing!\n");
185 return NL_SKIP;
186 }
187 if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
188 tb[NL80211_ATTR_STA_INFO],
189 stats_policy)) {
190 fprintf(stderr, "failed to parse nested attributes!\n");
191 return NL_SKIP;
192 }
193
194 if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
195 printf("\tRX: %u bytes (%u packets)\n",
196 nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]),
197 nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
198 if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS])
199 printf("\tTX: %u bytes (%u packets)\n",
200 nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]),
201 nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
202 if (sinfo[NL80211_STA_INFO_SIGNAL])
203 printf("\tsignal: %d dBm\n",
204 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
205
206 if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
207 char buf[100];
208
209 parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
210 printf("\trx bitrate: %s\n", buf);
211 }
212 if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
213 char buf[100];
214
215 parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
216 printf("\ttx bitrate: %s\n", buf);
217 }
218
219 if (sinfo[NL80211_STA_INFO_BSS_PARAM]) {
220 if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX,
221 sinfo[NL80211_STA_INFO_BSS_PARAM],
222 bss_policy)) {
223 fprintf(stderr, "failed to parse nested bss parameters!\n");
224 } else {
225 char *delim = "";
226 printf("\tbss flags: ");
227 if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) {
228 printf("CTS-protection");
229 delim = " ";
230 }
231 if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) {
232 printf("%sshort-preamble", delim);
233 delim = " ";
234 }
235 if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME])
236 printf("%sshort-slot-time", delim);
237 printf("\n\tdtim period: %d",
238 nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
239 printf("\n\tbeacon int: %d",
240 nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
241 printf("\n");
242 }
243 }
244
245 return NL_SKIP;
246 }
247
248 static int handle_link_sta(struct nl80211_state *state,
249 struct nl_msg *msg,
250 int argc, char **argv,
251 enum id_input id)
252 {
253 unsigned char mac_addr[ETH_ALEN];
254
255 if (argc < 1)
256 return 1;
257
258 if (mac_addr_a2n(mac_addr, argv[0])) {
259 fprintf(stderr, "invalid mac address\n");
260 return 2;
261 }
262
263 argc--;
264 argv++;
265
266 if (argc)
267 return 1;
268
269 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
270
271 register_handler(print_link_sta, NULL);
272
273 return 0;
274 nla_put_failure:
275 return -ENOBUFS;
276 }
277
278 static int handle_link(struct nl80211_state *state,
279 struct nl_msg *msg, int argc, char **argv,
280 enum id_input id)
281 {
282 char *link_argv[] = {
283 NULL,
284 "link",
285 "get_bss",
286 NULL,
287 };
288 char *station_argv[] = {
289 NULL,
290 "link",
291 "get_sta",
292 NULL,
293 NULL,
294 };
295 char addr_buf[3*6];
296 int err;
297
298 link_argv[0] = argv[0];
299 err = handle_cmd(state, id, 3, link_argv);
300 if (err)
301 return err;
302
303 if (!lr.link_found) {
304 if (!lr.anything_found)
305 printf("Not connected.\n");
306 return 0;
307 }
308
309 mac_addr_n2a(addr_buf, lr.sta_addr);
310 addr_buf[17] = '\0';
311
312 if (lr.mld)
313 printf("MLD %s stats:\n", addr_buf);
314
315 station_argv[0] = argv[0];
316 station_argv[3] = addr_buf;
317 return handle_cmd(state, id, 4, station_argv);
318 }
319 TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
320 "Print information about the current connection, if any.");
321 HIDDEN(link, get_sta, "<mac-addr>", NL80211_CMD_GET_STATION, 0,
322 CIB_NETDEV, handle_link_sta);
323 HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
324 CIB_NETDEV, handle_scan_for_link);