]>
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 }, | |
124 | [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED }, | |
125 | [NL80211_STA_INFO_LLID] = { .type = NLA_U16 }, | |
126 | [NL80211_STA_INFO_PLID] = { .type = NLA_U16 }, | |
127 | [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, | |
128 | }; | |
dd8eef95 PS |
129 | static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = { |
130 | [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG }, | |
131 | [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | |
132 | [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG }, | |
133 | [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 }, | |
134 | [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 }, | |
135 | }; | |
febeb0c0 JB |
136 | |
137 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
138 | genlmsg_attrlen(gnlh, 0), NULL); | |
139 | ||
140 | if (!tb[NL80211_ATTR_STA_INFO]) { | |
5fe70c0e | 141 | fprintf(stderr, "sta stats missing!\n"); |
febeb0c0 JB |
142 | return NL_SKIP; |
143 | } | |
144 | if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, | |
145 | tb[NL80211_ATTR_STA_INFO], | |
146 | stats_policy)) { | |
5fe70c0e | 147 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
148 | return NL_SKIP; |
149 | } | |
150 | ||
151 | if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) | |
26181415 | 152 | printf("\tRX: %u bytes (%u packets)\n", |
febeb0c0 JB |
153 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]), |
154 | nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS])); | |
155 | if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS]) | |
26181415 | 156 | printf("\tTX: %u bytes (%u packets)\n", |
febeb0c0 JB |
157 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]), |
158 | nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS])); | |
159 | if (sinfo[NL80211_STA_INFO_SIGNAL]) | |
160 | printf("\tsignal: %d dBm\n", | |
161 | (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL])); | |
162 | ||
163 | if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { | |
64179590 JB |
164 | char buf[100]; |
165 | ||
e94f0201 | 166 | parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf)); |
64179590 | 167 | printf("\ttx bitrate: %s\n", buf); |
febeb0c0 JB |
168 | } |
169 | ||
dd8eef95 PS |
170 | if (sinfo[NL80211_STA_INFO_BSS_PARAM]) { |
171 | if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX, | |
172 | sinfo[NL80211_STA_INFO_BSS_PARAM], | |
173 | bss_policy)) { | |
174 | fprintf(stderr, "failed to parse nested bss parameters!\n"); | |
175 | } else { | |
176 | char *delim = ""; | |
177 | printf("\n\tbss flags:\t"); | |
178 | if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) { | |
179 | printf("CTS-protection"); | |
180 | delim = " "; | |
181 | } | |
182 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) { | |
183 | printf("%sshort-preamble", delim); | |
184 | delim = " "; | |
185 | } | |
186 | if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME]) | |
187 | printf("%sshort-slot-time", delim); | |
188 | printf("\n\tdtim period:\t%d", | |
189 | nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD])); | |
190 | printf("\n\tbeacon int:\t%d", | |
191 | nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL])); | |
192 | printf("\n"); | |
193 | } | |
194 | } | |
195 | ||
febeb0c0 JB |
196 | return NL_SKIP; |
197 | } | |
198 | ||
199 | static int handle_link_sta(struct nl80211_state *state, | |
febeb0c0 | 200 | struct nl_msg *msg, |
05514f95 JB |
201 | int argc, char **argv, |
202 | enum id_input id) | |
febeb0c0 JB |
203 | { |
204 | unsigned char mac_addr[ETH_ALEN]; | |
205 | ||
206 | if (argc < 1) | |
207 | return 1; | |
208 | ||
209 | if (mac_addr_a2n(mac_addr, argv[0])) { | |
210 | fprintf(stderr, "invalid mac address\n"); | |
211 | return 2; | |
212 | } | |
213 | ||
214 | argc--; | |
215 | argv++; | |
216 | ||
217 | if (argc) | |
218 | return 1; | |
219 | ||
220 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | |
221 | ||
34b23014 | 222 | register_handler(print_link_sta, NULL); |
febeb0c0 JB |
223 | |
224 | return 0; | |
225 | nla_put_failure: | |
226 | return -ENOBUFS; | |
227 | } | |
febeb0c0 | 228 | |
34b23014 | 229 | static int handle_link(struct nl80211_state *state, |
05514f95 JB |
230 | struct nl_msg *msg, int argc, char **argv, |
231 | enum id_input id) | |
febeb0c0 JB |
232 | { |
233 | char *link_argv[] = { | |
234 | NULL, | |
235 | "link", | |
236 | "get_bss", | |
237 | NULL, | |
238 | }; | |
239 | char *station_argv[] = { | |
240 | NULL, | |
241 | "link", | |
242 | "get_sta", | |
243 | NULL, | |
244 | NULL, | |
245 | }; | |
246 | char bssid_buf[3*6]; | |
247 | int err; | |
248 | ||
249 | link_argv[0] = argv[0]; | |
75f4204c | 250 | err = handle_cmd(state, id, 3, link_argv); |
febeb0c0 JB |
251 | if (err) |
252 | return err; | |
253 | ||
254 | if (!lr.link_found) { | |
255 | if (!lr.anything_found) | |
bb68dbd4 | 256 | printf("Not connected.\n"); |
febeb0c0 JB |
257 | return 0; |
258 | } | |
259 | ||
260 | mac_addr_n2a(bssid_buf, lr.bssid); | |
261 | bssid_buf[17] = '\0'; | |
262 | ||
263 | station_argv[0] = argv[0]; | |
264 | station_argv[3] = bssid_buf; | |
75f4204c | 265 | return handle_cmd(state, id, 4, station_argv); |
febeb0c0 JB |
266 | } |
267 | TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link, | |
268 | "Print information about the current link, if any."); | |
4698bfc2 JB |
269 | HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0, |
270 | CIB_NETDEV, handle_link_sta); | |
271 | HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP, | |
272 | CIB_NETDEV, handle_scan_for_link); |