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