]>
Commit | Line | Data |
---|---|---|
febeb0c0 JB |
1 | #include <net/if.h> |
2 | #include <errno.h> | |
3 | #include <string.h> | |
febeb0c0 JB |
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 | ||
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]) { | |
5fe70c0e | 46 | fprintf(stderr, "bss info missing!\n"); |
febeb0c0 JB |
47 | return NL_SKIP; |
48 | } | |
49 | if (nla_parse_nested(bss, NL80211_BSS_MAX, | |
50 | tb[NL80211_ATTR_BSS], | |
51 | bss_policy)) { | |
5fe70c0e | 52 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
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; | |
28b1efb7 JB |
72 | case NL80211_BSS_STATUS_IBSS_JOINED: |
73 | printf("Joined IBSS %s (on %s)\n", mac_addr, dev); | |
74 | break; | |
febeb0c0 JB |
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 | ||
99 | static int handle_scan_for_link(struct nl80211_state *state, | |
febeb0c0 | 100 | struct nl_msg *msg, |
05514f95 JB |
101 | int argc, char **argv, |
102 | enum id_input id) | |
febeb0c0 JB |
103 | { |
104 | if (argc > 0) | |
105 | return 1; | |
106 | ||
34b23014 | 107 | register_handler(link_bss_handler, &lr); |
febeb0c0 JB |
108 | return 0; |
109 | } | |
febeb0c0 JB |
110 | |
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]; | |
dd8eef95 | 116 | struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1]; |
febeb0c0 JB |
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 }, | |
5c01d5a0 | 124 | [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED }, |
febeb0c0 JB |
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 | }; | |
dd8eef95 PS |
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 | }; | |
febeb0c0 JB |
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]) { | |
5fe70c0e | 142 | fprintf(stderr, "sta stats missing!\n"); |
febeb0c0 JB |
143 | return NL_SKIP; |
144 | } | |
145 | if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, | |
146 | tb[NL80211_ATTR_STA_INFO], | |
147 | stats_policy)) { | |
5fe70c0e | 148 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
149 | return NL_SKIP; |
150 | } | |
151 | ||
152 | if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) | |
26181415 | 153 | printf("\tRX: %u bytes (%u packets)\n", |
febeb0c0 JB |
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]) | |
26181415 | 157 | printf("\tTX: %u bytes (%u packets)\n", |
febeb0c0 JB |
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 | ||
5c01d5a0 BN |
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 | } | |
febeb0c0 | 170 | if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { |
64179590 JB |
171 | char buf[100]; |
172 | ||
e94f0201 | 173 | parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf)); |
64179590 | 174 | printf("\ttx bitrate: %s\n", buf); |
febeb0c0 JB |
175 | } |
176 | ||
dd8eef95 PS |
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 | ||
febeb0c0 JB |
203 | return NL_SKIP; |
204 | } | |
205 | ||
206 | static int handle_link_sta(struct nl80211_state *state, | |
febeb0c0 | 207 | struct nl_msg *msg, |
05514f95 JB |
208 | int argc, char **argv, |
209 | enum id_input id) | |
febeb0c0 JB |
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 | ||
34b23014 | 229 | register_handler(print_link_sta, NULL); |
febeb0c0 JB |
230 | |
231 | return 0; | |
232 | nla_put_failure: | |
233 | return -ENOBUFS; | |
234 | } | |
febeb0c0 | 235 | |
34b23014 | 236 | static int handle_link(struct nl80211_state *state, |
05514f95 JB |
237 | struct nl_msg *msg, int argc, char **argv, |
238 | enum id_input id) | |
febeb0c0 JB |
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]; | |
75f4204c | 257 | err = handle_cmd(state, id, 3, link_argv); |
febeb0c0 JB |
258 | if (err) |
259 | return err; | |
260 | ||
261 | if (!lr.link_found) { | |
262 | if (!lr.anything_found) | |
bb68dbd4 | 263 | printf("Not connected.\n"); |
febeb0c0 JB |
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; | |
75f4204c | 272 | return handle_cmd(state, id, 4, station_argv); |
febeb0c0 JB |
273 | } |
274 | TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link, | |
275 | "Print information about the current link, if any."); | |
25bb9c39 | 276 | HIDDEN(link, get_sta, "<mac-addr>", NL80211_CMD_GET_STATION, 0, |
4698bfc2 JB |
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); |