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