]>
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]; | |
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 | }; | |
dd8eef95 PS |
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 | }; | |
febeb0c0 JB |
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]) { | |
5fe70c0e | 143 | fprintf(stderr, "sta stats missing!\n"); |
febeb0c0 JB |
144 | return NL_SKIP; |
145 | } | |
146 | if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, | |
147 | tb[NL80211_ATTR_STA_INFO], | |
148 | stats_policy)) { | |
5fe70c0e | 149 | fprintf(stderr, "failed to parse nested attributes!\n"); |
febeb0c0 JB |
150 | return NL_SKIP; |
151 | } | |
152 | ||
153 | if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS]) | |
26181415 | 154 | printf("\tRX: %u bytes (%u packets)\n", |
febeb0c0 JB |
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]) | |
26181415 | 158 | printf("\tTX: %u bytes (%u packets)\n", |
febeb0c0 JB |
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]) { | |
64179590 JB |
166 | char buf[100]; |
167 | ||
e94f0201 | 168 | parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf)); |
64179590 | 169 | printf("\ttx bitrate: %s\n", buf); |
febeb0c0 JB |
170 | } |
171 | ||
dd8eef95 PS |
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 | ||
febeb0c0 JB |
198 | return NL_SKIP; |
199 | } | |
200 | ||
201 | static int handle_link_sta(struct nl80211_state *state, | |
202 | struct nl_cb *cb, | |
203 | struct nl_msg *msg, | |
05514f95 JB |
204 | int argc, char **argv, |
205 | enum id_input id) | |
febeb0c0 JB |
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 | } | |
febeb0c0 JB |
231 | |
232 | static int handle_link(struct nl80211_state *state, struct nl_cb *cb, | |
05514f95 JB |
233 | struct nl_msg *msg, int argc, char **argv, |
234 | enum id_input id) | |
febeb0c0 JB |
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]; | |
75f4204c | 253 | err = handle_cmd(state, id, 3, link_argv); |
febeb0c0 JB |
254 | if (err) |
255 | return err; | |
256 | ||
257 | if (!lr.link_found) { | |
258 | if (!lr.anything_found) | |
bb68dbd4 | 259 | printf("Not connected.\n"); |
febeb0c0 JB |
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; | |
75f4204c | 268 | return handle_cmd(state, id, 4, station_argv); |
febeb0c0 JB |
269 | } |
270 | TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link, | |
271 | "Print information about the current link, if any."); | |
4698bfc2 JB |
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); |