]> git.ipfire.org Git - thirdparty/iw.git/blame - link.c
iw: S1G: add 802.11ah support for link command display
[thirdparty/iw.git] / link.c
CommitLineData
febeb0c0
JB
1#include <net/if.h>
2#include <errno.h>
3#include <string.h>
febeb0c0
JB
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
15struct link_result {
221875eb 16 uint8_t sta_addr[8];
febeb0c0
JB
17 bool link_found;
18 bool anything_found;
221875eb 19 bool mld;
febeb0c0
JB
20};
21
22static struct link_result lr = { .link_found = false };
23
24static 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 },
e2224c72 32 [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
febeb0c0
JB
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;
221875eb
JB
42 char mac_addr[20], dev[20], link_addr[20];
43 int link_id = -1;
44 const char *indent = "\t";
e2224c72 45 int freq_offset = 0;
febeb0c0
JB
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]) {
5fe70c0e 51 fprintf(stderr, "bss info missing!\n");
febeb0c0
JB
52 return NL_SKIP;
53 }
54 if (nla_parse_nested(bss, NL80211_BSS_MAX,
55 tb[NL80211_ATTR_BSS],
56 bss_policy)) {
5fe70c0e 57 fprintf(stderr, "failed to parse nested attributes!\n");
febeb0c0
JB
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
221875eb
JB
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
febeb0c0
JB
102 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
103 case NL80211_BSS_STATUS_ASSOCIATED:
221875eb
JB
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);
febeb0c0
JB
108 break;
109 case NL80211_BSS_STATUS_AUTHENTICATED:
110 printf("Authenticated with %s (on %s)\n", mac_addr, dev);
111 return NL_SKIP;
28b1efb7
JB
112 case NL80211_BSS_STATUS_IBSS_JOINED:
113 printf("Joined IBSS %s (on %s)\n", mac_addr, dev);
114 break;
febeb0c0
JB
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]),
221875eb 124 false, result->mld ? PRINT_LINK_MLO_LINK : PRINT_LINK);
febeb0c0 125
e2224c72
BD
126 if (bss[NL80211_BSS_FREQUENCY_OFFSET])
127 freq_offset = nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]);
128
febeb0c0 129 if (bss[NL80211_BSS_FREQUENCY])
e2224c72
BD
130 printf("%sfreq: %d.%d\n", indent,
131 nla_get_u32(bss[NL80211_BSS_FREQUENCY]), freq_offset);
febeb0c0
JB
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;
febeb0c0
JB
138 return NL_SKIP;
139}
140
141static int handle_scan_for_link(struct nl80211_state *state,
febeb0c0 142 struct nl_msg *msg,
05514f95
JB
143 int argc, char **argv,
144 enum id_input id)
febeb0c0
JB
145{
146 if (argc > 0)
147 return 1;
148
34b23014 149 register_handler(link_bss_handler, &lr);
febeb0c0
JB
150 return 0;
151}
febeb0c0
JB
152
153static 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];
dd8eef95 158 struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1];
febeb0c0
JB
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 },
5c01d5a0 166 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
febeb0c0
JB
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 };
dd8eef95
PS
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 };
febeb0c0
JB
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]) {
5fe70c0e 184 fprintf(stderr, "sta stats missing!\n");
febeb0c0
JB
185 return NL_SKIP;
186 }
187 if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
188 tb[NL80211_ATTR_STA_INFO],
189 stats_policy)) {
5fe70c0e 190 fprintf(stderr, "failed to parse nested attributes!\n");
febeb0c0
JB
191 return NL_SKIP;
192 }
193
194 if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
26181415 195 printf("\tRX: %u bytes (%u packets)\n",
febeb0c0
JB
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])
26181415 199 printf("\tTX: %u bytes (%u packets)\n",
febeb0c0
JB
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
5c01d5a0
BN
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 }
febeb0c0 212 if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
64179590
JB
213 char buf[100];
214
e94f0201 215 parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
64179590 216 printf("\ttx bitrate: %s\n", buf);
febeb0c0
JB
217 }
218
dd8eef95
PS
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 = "";
5f64b702 226 printf("\tbss flags: ");
dd8eef95
PS
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);
5f64b702 237 printf("\n\tdtim period: %d",
dd8eef95 238 nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
5f64b702 239 printf("\n\tbeacon int: %d",
dd8eef95
PS
240 nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
241 printf("\n");
242 }
243 }
244
febeb0c0
JB
245 return NL_SKIP;
246}
247
248static int handle_link_sta(struct nl80211_state *state,
febeb0c0 249 struct nl_msg *msg,
05514f95
JB
250 int argc, char **argv,
251 enum id_input id)
febeb0c0
JB
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
34b23014 271 register_handler(print_link_sta, NULL);
febeb0c0
JB
272
273 return 0;
274 nla_put_failure:
275 return -ENOBUFS;
276}
febeb0c0 277
34b23014 278static int handle_link(struct nl80211_state *state,
05514f95
JB
279 struct nl_msg *msg, int argc, char **argv,
280 enum id_input id)
febeb0c0
JB
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 };
221875eb 295 char addr_buf[3*6];
febeb0c0
JB
296 int err;
297
298 link_argv[0] = argv[0];
75f4204c 299 err = handle_cmd(state, id, 3, link_argv);
febeb0c0
JB
300 if (err)
301 return err;
302
303 if (!lr.link_found) {
304 if (!lr.anything_found)
bb68dbd4 305 printf("Not connected.\n");
febeb0c0
JB
306 return 0;
307 }
308
221875eb
JB
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);
febeb0c0
JB
314
315 station_argv[0] = argv[0];
221875eb 316 station_argv[3] = addr_buf;
75f4204c 317 return handle_cmd(state, id, 4, station_argv);
febeb0c0
JB
318}
319TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
221875eb 320 "Print information about the current connection, if any.");
25bb9c39 321HIDDEN(link, get_sta, "<mac-addr>", NL80211_CMD_GET_STATION, 0,
4698bfc2
JB
322 CIB_NETDEV, handle_link_sta);
323HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
324 CIB_NETDEV, handle_scan_for_link);