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