]>
Commit | Line | Data |
---|---|---|
febeb0c0 JB |
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 | ||
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]) { | |
5fe70c0e | 47 | fprintf(stderr, "bss info missing!\n"); |
febeb0c0 JB |
48 | return NL_SKIP; |
49 | } | |
50 | if (nla_parse_nested(bss, NL80211_BSS_MAX, | |
51 | tb[NL80211_ATTR_BSS], | |
52 | bss_policy)) { | |
5fe70c0e | 53 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
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; | |
28b1efb7 JB |
73 | case NL80211_BSS_STATUS_IBSS_JOINED: |
74 | printf("Joined IBSS %s (on %s)\n", mac_addr, dev); | |
75 | break; | |
febeb0c0 JB |
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 | ||
100 | static int handle_scan_for_link(struct nl80211_state *state, | |
101 | struct nl_cb *cb, | |
102 | struct nl_msg *msg, | |
05514f95 JB |
103 | int argc, char **argv, |
104 | enum id_input id) | |
febeb0c0 JB |
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 | } | |
febeb0c0 JB |
112 | |
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 *rinfo[NL80211_RATE_INFO_MAX + 1]; | |
dd8eef95 | 119 | struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1]; |
febeb0c0 JB |
120 | static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { |
121 | [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, | |
122 | [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, | |
123 | [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, | |
124 | [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, | |
125 | [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, | |
126 | [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, | |
127 | [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED }, | |
128 | [NL80211_STA_INFO_LLID] = { .type = NLA_U16 }, | |
129 | [NL80211_STA_INFO_PLID] = { .type = NLA_U16 }, | |
130 | [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, | |
131 | }; | |
132 | ||
133 | static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { | |
134 | [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, | |
f0148acd | 135 | [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 }, |
febeb0c0 JB |
136 | [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, |
137 | [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, | |
138 | [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, | |
139 | }; | |
dd8eef95 PS |
140 | static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = { |
141 | [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG }, | |
142 | [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | |
143 | [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG }, | |
144 | [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 }, | |
145 | [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 }, | |
146 | }; | |
febeb0c0 JB |
147 | |
148 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
149 | genlmsg_attrlen(gnlh, 0), NULL); | |
150 | ||
151 | if (!tb[NL80211_ATTR_STA_INFO]) { | |
5fe70c0e | 152 | fprintf(stderr, "sta stats missing!\n"); |
febeb0c0 JB |
153 | return NL_SKIP; |
154 | } | |
155 | if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, | |
156 | tb[NL80211_ATTR_STA_INFO], | |
157 | stats_policy)) { | |
5fe70c0e | 158 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
159 | return NL_SKIP; |
160 | } | |
161 | ||
162 | if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) | |
26181415 | 163 | printf("\tRX: %u bytes (%u packets)\n", |
febeb0c0 JB |
164 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]), |
165 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS])); | |
166 | if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS]) | |
26181415 | 167 | printf("\tTX: %u bytes (%u packets)\n", |
febeb0c0 JB |
168 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]), |
169 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS])); | |
170 | if (sinfo[NL80211_STA_INFO_SIGNAL]) | |
171 | printf("\tsignal: %d dBm\n", | |
172 | (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL])); | |
173 | ||
174 | if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { | |
175 | if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, | |
176 | sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) { | |
5fe70c0e | 177 | fprintf(stderr, "failed to parse nested rate attributes!\n"); |
febeb0c0 | 178 | } else { |
f0148acd | 179 | int rate = 0; |
febeb0c0 | 180 | printf("\ttx bitrate: "); |
f0148acd VK |
181 | if (rinfo[NL80211_RATE_INFO_BITRATE32]) |
182 | rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]); | |
183 | else if (rinfo[NL80211_RATE_INFO_BITRATE]) | |
184 | rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]); | |
185 | if (rate > 0) | |
febeb0c0 | 186 | printf("%d.%d MBit/s", rate / 10, rate % 10); |
febeb0c0 JB |
187 | |
188 | if (rinfo[NL80211_RATE_INFO_MCS]) | |
189 | printf(" MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS])); | |
045ead28 YAP |
190 | if (rinfo[NL80211_RATE_INFO_VHT_MCS]) |
191 | printf(" VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS])); | |
febeb0c0 | 192 | if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH]) |
045ead28 YAP |
193 | printf(" 40MHz"); |
194 | if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH]) | |
195 | printf(" 80MHz"); | |
196 | if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH]) | |
197 | printf(" 80P80MHz"); | |
198 | if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH]) | |
199 | printf(" 160MHz"); | |
febeb0c0 JB |
200 | if (rinfo[NL80211_RATE_INFO_SHORT_GI]) |
201 | printf(" short GI"); | |
045ead28 YAP |
202 | if (rinfo[NL80211_RATE_INFO_VHT_NSS]) |
203 | printf(" VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS])); | |
febeb0c0 JB |
204 | printf("\n"); |
205 | } | |
206 | } | |
207 | ||
dd8eef95 PS |
208 | if (sinfo[NL80211_STA_INFO_BSS_PARAM]) { |
209 | if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX, | |
210 | sinfo[NL80211_STA_INFO_BSS_PARAM], | |
211 | bss_policy)) { | |
212 | fprintf(stderr, "failed to parse nested bss parameters!\n"); | |
213 | } else { | |
214 | char *delim = ""; | |
215 | printf("\n\tbss flags:\t"); | |
216 | if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) { | |
217 | printf("CTS-protection"); | |
218 | delim = " "; | |
219 | } | |
220 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) { | |
221 | printf("%sshort-preamble", delim); | |
222 | delim = " "; | |
223 | } | |
224 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME]) | |
225 | printf("%sshort-slot-time", delim); | |
226 | printf("\n\tdtim period:\t%d", | |
227 | nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD])); | |
228 | printf("\n\tbeacon int:\t%d", | |
229 | nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL])); | |
230 | printf("\n"); | |
231 | } | |
232 | } | |
233 | ||
febeb0c0 JB |
234 | return NL_SKIP; |
235 | } | |
236 | ||
237 | static int handle_link_sta(struct nl80211_state *state, | |
238 | struct nl_cb *cb, | |
239 | struct nl_msg *msg, | |
05514f95 JB |
240 | int argc, char **argv, |
241 | enum id_input id) | |
febeb0c0 JB |
242 | { |
243 | unsigned char mac_addr[ETH_ALEN]; | |
244 | ||
245 | if (argc < 1) | |
246 | return 1; | |
247 | ||
248 | if (mac_addr_a2n(mac_addr, argv[0])) { | |
249 | fprintf(stderr, "invalid mac address\n"); | |
250 | return 2; | |
251 | } | |
252 | ||
253 | argc--; | |
254 | argv++; | |
255 | ||
256 | if (argc) | |
257 | return 1; | |
258 | ||
259 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | |
260 | ||
261 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_link_sta, NULL); | |
262 | ||
263 | return 0; | |
264 | nla_put_failure: | |
265 | return -ENOBUFS; | |
266 | } | |
febeb0c0 JB |
267 | |
268 | static int handle_link(struct nl80211_state *state, struct nl_cb *cb, | |
05514f95 JB |
269 | struct nl_msg *msg, int argc, char **argv, |
270 | enum id_input id) | |
febeb0c0 JB |
271 | { |
272 | char *link_argv[] = { | |
273 | NULL, | |
274 | "link", | |
275 | "get_bss", | |
276 | NULL, | |
277 | }; | |
278 | char *station_argv[] = { | |
279 | NULL, | |
280 | "link", | |
281 | "get_sta", | |
282 | NULL, | |
283 | NULL, | |
284 | }; | |
285 | char bssid_buf[3*6]; | |
286 | int err; | |
287 | ||
288 | link_argv[0] = argv[0]; | |
75f4204c | 289 | err = handle_cmd(state, id, 3, link_argv); |
febeb0c0 JB |
290 | if (err) |
291 | return err; | |
292 | ||
293 | if (!lr.link_found) { | |
294 | if (!lr.anything_found) | |
bb68dbd4 | 295 | printf("Not connected.\n"); |
febeb0c0 JB |
296 | return 0; |
297 | } | |
298 | ||
299 | mac_addr_n2a(bssid_buf, lr.bssid); | |
300 | bssid_buf[17] = '\0'; | |
301 | ||
302 | station_argv[0] = argv[0]; | |
303 | station_argv[3] = bssid_buf; | |
75f4204c | 304 | return handle_cmd(state, id, 4, station_argv); |
febeb0c0 JB |
305 | } |
306 | TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link, | |
307 | "Print information about the current link, if any."); | |
4698bfc2 JB |
308 | HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0, |
309 | CIB_NETDEV, handle_link_sta); | |
310 | HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP, | |
311 | CIB_NETDEV, handle_scan_for_link); |