]>
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, | |
103 | int argc, char **argv) | |
104 | { | |
105 | if (argc > 0) | |
106 | return 1; | |
107 | ||
108 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, link_bss_handler, &lr); | |
109 | return 0; | |
110 | } | |
febeb0c0 JB |
111 | |
112 | static int print_link_sta(struct nl_msg *msg, void *arg) | |
113 | { | |
114 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; | |
115 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
116 | struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; | |
117 | struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; | |
dd8eef95 | 118 | struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1]; |
febeb0c0 JB |
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 | ||
132 | static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { | |
133 | [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, | |
134 | [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, | |
135 | [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, | |
136 | [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, | |
137 | }; | |
dd8eef95 PS |
138 | static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = { |
139 | [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG }, | |
140 | [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | |
141 | [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG }, | |
142 | [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 }, | |
143 | [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 }, | |
144 | }; | |
febeb0c0 JB |
145 | |
146 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
147 | genlmsg_attrlen(gnlh, 0), NULL); | |
148 | ||
149 | if (!tb[NL80211_ATTR_STA_INFO]) { | |
5fe70c0e | 150 | fprintf(stderr, "sta stats missing!\n"); |
febeb0c0 JB |
151 | return NL_SKIP; |
152 | } | |
153 | if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, | |
154 | tb[NL80211_ATTR_STA_INFO], | |
155 | stats_policy)) { | |
5fe70c0e | 156 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
157 | return NL_SKIP; |
158 | } | |
159 | ||
160 | if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) | |
26181415 | 161 | printf("\tRX: %u bytes (%u packets)\n", |
febeb0c0 JB |
162 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]), |
163 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS])); | |
164 | if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS]) | |
26181415 | 165 | printf("\tTX: %u bytes (%u packets)\n", |
febeb0c0 JB |
166 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]), |
167 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS])); | |
168 | if (sinfo[NL80211_STA_INFO_SIGNAL]) | |
169 | printf("\tsignal: %d dBm\n", | |
170 | (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL])); | |
171 | ||
172 | if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { | |
173 | if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, | |
174 | sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) { | |
5fe70c0e | 175 | fprintf(stderr, "failed to parse nested rate attributes!\n"); |
febeb0c0 JB |
176 | } else { |
177 | printf("\ttx bitrate: "); | |
178 | if (rinfo[NL80211_RATE_INFO_BITRATE]) { | |
179 | int rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]); | |
180 | printf("%d.%d MBit/s", rate / 10, rate % 10); | |
181 | } | |
182 | ||
183 | if (rinfo[NL80211_RATE_INFO_MCS]) | |
184 | printf(" MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS])); | |
185 | if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH]) | |
186 | printf(" 40Mhz"); | |
187 | if (rinfo[NL80211_RATE_INFO_SHORT_GI]) | |
188 | printf(" short GI"); | |
189 | printf("\n"); | |
190 | } | |
191 | } | |
192 | ||
dd8eef95 PS |
193 | if (sinfo[NL80211_STA_INFO_BSS_PARAM]) { |
194 | if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX, | |
195 | sinfo[NL80211_STA_INFO_BSS_PARAM], | |
196 | bss_policy)) { | |
197 | fprintf(stderr, "failed to parse nested bss parameters!\n"); | |
198 | } else { | |
199 | char *delim = ""; | |
200 | printf("\n\tbss flags:\t"); | |
201 | if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) { | |
202 | printf("CTS-protection"); | |
203 | delim = " "; | |
204 | } | |
205 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) { | |
206 | printf("%sshort-preamble", delim); | |
207 | delim = " "; | |
208 | } | |
209 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME]) | |
210 | printf("%sshort-slot-time", delim); | |
211 | printf("\n\tdtim period:\t%d", | |
212 | nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD])); | |
213 | printf("\n\tbeacon int:\t%d", | |
214 | nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL])); | |
215 | printf("\n"); | |
216 | } | |
217 | } | |
218 | ||
febeb0c0 JB |
219 | return NL_SKIP; |
220 | } | |
221 | ||
222 | static int handle_link_sta(struct nl80211_state *state, | |
223 | struct nl_cb *cb, | |
224 | struct nl_msg *msg, | |
225 | int argc, char **argv) | |
226 | { | |
227 | unsigned char mac_addr[ETH_ALEN]; | |
228 | ||
229 | if (argc < 1) | |
230 | return 1; | |
231 | ||
232 | if (mac_addr_a2n(mac_addr, argv[0])) { | |
233 | fprintf(stderr, "invalid mac address\n"); | |
234 | return 2; | |
235 | } | |
236 | ||
237 | argc--; | |
238 | argv++; | |
239 | ||
240 | if (argc) | |
241 | return 1; | |
242 | ||
243 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | |
244 | ||
245 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_link_sta, NULL); | |
246 | ||
247 | return 0; | |
248 | nla_put_failure: | |
249 | return -ENOBUFS; | |
250 | } | |
febeb0c0 JB |
251 | |
252 | static int handle_link(struct nl80211_state *state, struct nl_cb *cb, | |
253 | struct nl_msg *msg, int argc, char **argv) | |
254 | { | |
255 | char *link_argv[] = { | |
256 | NULL, | |
257 | "link", | |
258 | "get_bss", | |
259 | NULL, | |
260 | }; | |
261 | char *station_argv[] = { | |
262 | NULL, | |
263 | "link", | |
264 | "get_sta", | |
265 | NULL, | |
266 | NULL, | |
267 | }; | |
268 | char bssid_buf[3*6]; | |
269 | int err; | |
270 | ||
271 | link_argv[0] = argv[0]; | |
272 | err = handle_cmd(state, II_NETDEV, 3, link_argv); | |
273 | if (err) | |
274 | return err; | |
275 | ||
276 | if (!lr.link_found) { | |
277 | if (!lr.anything_found) | |
bb68dbd4 | 278 | printf("Not connected.\n"); |
febeb0c0 JB |
279 | return 0; |
280 | } | |
281 | ||
282 | mac_addr_n2a(bssid_buf, lr.bssid); | |
283 | bssid_buf[17] = '\0'; | |
284 | ||
285 | station_argv[0] = argv[0]; | |
286 | station_argv[3] = bssid_buf; | |
287 | return handle_cmd(state, II_NETDEV, 4, station_argv); | |
288 | } | |
289 | TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link, | |
290 | "Print information about the current link, if any."); | |
4698bfc2 JB |
291 | HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0, |
292 | CIB_NETDEV, handle_link_sta); | |
293 | HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP, | |
294 | CIB_NETDEV, handle_scan_for_link); |