1*92022041SSam Saccone #include <net/if.h>
2*92022041SSam Saccone #include <errno.h>
3*92022041SSam Saccone #include <string.h>
4*92022041SSam Saccone #include <stdbool.h>
5*92022041SSam Saccone
6*92022041SSam Saccone #include <netlink/genl/genl.h>
7*92022041SSam Saccone #include <netlink/genl/family.h>
8*92022041SSam Saccone #include <netlink/genl/ctrl.h>
9*92022041SSam Saccone #include <netlink/msg.h>
10*92022041SSam Saccone #include <netlink/attr.h>
11*92022041SSam Saccone
12*92022041SSam Saccone #include "nl80211.h"
13*92022041SSam Saccone #include "iw.h"
14*92022041SSam Saccone
15*92022041SSam Saccone struct link_result {
16*92022041SSam Saccone uint8_t bssid[8];
17*92022041SSam Saccone bool link_found;
18*92022041SSam Saccone bool anything_found;
19*92022041SSam Saccone };
20*92022041SSam Saccone
21*92022041SSam Saccone static struct link_result lr = { .link_found = false };
22*92022041SSam Saccone
link_bss_handler(struct nl_msg * msg,void * arg)23*92022041SSam Saccone static int link_bss_handler(struct nl_msg *msg, void *arg)
24*92022041SSam Saccone {
25*92022041SSam Saccone struct nlattr *tb[NL80211_ATTR_MAX + 1];
26*92022041SSam Saccone struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
27*92022041SSam Saccone struct nlattr *bss[NL80211_BSS_MAX + 1];
28*92022041SSam Saccone static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
29*92022041SSam Saccone [NL80211_BSS_TSF] = { .type = NLA_U64 },
30*92022041SSam Saccone [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
31*92022041SSam Saccone [NL80211_BSS_BSSID] = { },
32*92022041SSam Saccone [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
33*92022041SSam Saccone [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
34*92022041SSam Saccone [NL80211_BSS_INFORMATION_ELEMENTS] = { },
35*92022041SSam Saccone [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
36*92022041SSam Saccone [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
37*92022041SSam Saccone [NL80211_BSS_STATUS] = { .type = NLA_U32 },
38*92022041SSam Saccone };
39*92022041SSam Saccone struct link_result *result = arg;
40*92022041SSam Saccone char mac_addr[20], dev[20];
41*92022041SSam Saccone
42*92022041SSam Saccone nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
43*92022041SSam Saccone genlmsg_attrlen(gnlh, 0), NULL);
44*92022041SSam Saccone
45*92022041SSam Saccone if (!tb[NL80211_ATTR_BSS]) {
46*92022041SSam Saccone fprintf(stderr, "bss info missing!\n");
47*92022041SSam Saccone return NL_SKIP;
48*92022041SSam Saccone }
49*92022041SSam Saccone if (nla_parse_nested(bss, NL80211_BSS_MAX,
50*92022041SSam Saccone tb[NL80211_ATTR_BSS],
51*92022041SSam Saccone bss_policy)) {
52*92022041SSam Saccone fprintf(stderr, "failed to parse nested attributes!\n");
53*92022041SSam Saccone return NL_SKIP;
54*92022041SSam Saccone }
55*92022041SSam Saccone
56*92022041SSam Saccone if (!bss[NL80211_BSS_BSSID])
57*92022041SSam Saccone return NL_SKIP;
58*92022041SSam Saccone
59*92022041SSam Saccone if (!bss[NL80211_BSS_STATUS])
60*92022041SSam Saccone return NL_SKIP;
61*92022041SSam Saccone
62*92022041SSam Saccone mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
63*92022041SSam Saccone if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
64*92022041SSam Saccone
65*92022041SSam Saccone switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
66*92022041SSam Saccone case NL80211_BSS_STATUS_ASSOCIATED:
67*92022041SSam Saccone printf("Connected to %s (on %s)\n", mac_addr, dev);
68*92022041SSam Saccone break;
69*92022041SSam Saccone case NL80211_BSS_STATUS_AUTHENTICATED:
70*92022041SSam Saccone printf("Authenticated with %s (on %s)\n", mac_addr, dev);
71*92022041SSam Saccone return NL_SKIP;
72*92022041SSam Saccone case NL80211_BSS_STATUS_IBSS_JOINED:
73*92022041SSam Saccone printf("Joined IBSS %s (on %s)\n", mac_addr, dev);
74*92022041SSam Saccone break;
75*92022041SSam Saccone default:
76*92022041SSam Saccone return NL_SKIP;
77*92022041SSam Saccone }
78*92022041SSam Saccone
79*92022041SSam Saccone result->anything_found = true;
80*92022041SSam Saccone
81*92022041SSam Saccone if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
82*92022041SSam Saccone print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
83*92022041SSam Saccone nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
84*92022041SSam Saccone false, PRINT_LINK);
85*92022041SSam Saccone
86*92022041SSam Saccone if (bss[NL80211_BSS_FREQUENCY])
87*92022041SSam Saccone printf("\tfreq: %d\n",
88*92022041SSam Saccone nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
89*92022041SSam Saccone
90*92022041SSam Saccone if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
91*92022041SSam Saccone return NL_SKIP;
92*92022041SSam Saccone
93*92022041SSam Saccone /* only in the assoc case do we want more info from station get */
94*92022041SSam Saccone result->link_found = true;
95*92022041SSam Saccone memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
96*92022041SSam Saccone return NL_SKIP;
97*92022041SSam Saccone }
98*92022041SSam Saccone
handle_scan_for_link(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)99*92022041SSam Saccone static int handle_scan_for_link(struct nl80211_state *state,
100*92022041SSam Saccone struct nl_msg *msg,
101*92022041SSam Saccone int argc, char **argv,
102*92022041SSam Saccone enum id_input id)
103*92022041SSam Saccone {
104*92022041SSam Saccone if (argc > 0)
105*92022041SSam Saccone return 1;
106*92022041SSam Saccone
107*92022041SSam Saccone register_handler(link_bss_handler, &lr);
108*92022041SSam Saccone return 0;
109*92022041SSam Saccone }
110*92022041SSam Saccone
print_link_sta(struct nl_msg * msg,void * arg)111*92022041SSam Saccone static int print_link_sta(struct nl_msg *msg, void *arg)
112*92022041SSam Saccone {
113*92022041SSam Saccone struct nlattr *tb[NL80211_ATTR_MAX + 1];
114*92022041SSam Saccone struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
115*92022041SSam Saccone struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
116*92022041SSam Saccone struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1];
117*92022041SSam Saccone static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
118*92022041SSam Saccone [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
119*92022041SSam Saccone [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
120*92022041SSam Saccone [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
121*92022041SSam Saccone [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
122*92022041SSam Saccone [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
123*92022041SSam Saccone [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
124*92022041SSam Saccone [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
125*92022041SSam Saccone [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
126*92022041SSam Saccone [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
127*92022041SSam Saccone [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
128*92022041SSam Saccone [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
129*92022041SSam Saccone };
130*92022041SSam Saccone static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = {
131*92022041SSam Saccone [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
132*92022041SSam Saccone [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
133*92022041SSam Saccone [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
134*92022041SSam Saccone [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
135*92022041SSam Saccone [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
136*92022041SSam Saccone };
137*92022041SSam Saccone
138*92022041SSam Saccone nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
139*92022041SSam Saccone genlmsg_attrlen(gnlh, 0), NULL);
140*92022041SSam Saccone
141*92022041SSam Saccone if (!tb[NL80211_ATTR_STA_INFO]) {
142*92022041SSam Saccone fprintf(stderr, "sta stats missing!\n");
143*92022041SSam Saccone return NL_SKIP;
144*92022041SSam Saccone }
145*92022041SSam Saccone if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
146*92022041SSam Saccone tb[NL80211_ATTR_STA_INFO],
147*92022041SSam Saccone stats_policy)) {
148*92022041SSam Saccone fprintf(stderr, "failed to parse nested attributes!\n");
149*92022041SSam Saccone return NL_SKIP;
150*92022041SSam Saccone }
151*92022041SSam Saccone
152*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
153*92022041SSam Saccone printf("\tRX: %u bytes (%u packets)\n",
154*92022041SSam Saccone nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]),
155*92022041SSam Saccone nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
156*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS])
157*92022041SSam Saccone printf("\tTX: %u bytes (%u packets)\n",
158*92022041SSam Saccone nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]),
159*92022041SSam Saccone nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
160*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_SIGNAL])
161*92022041SSam Saccone printf("\tsignal: %d dBm\n",
162*92022041SSam Saccone (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
163*92022041SSam Saccone
164*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
165*92022041SSam Saccone char buf[100];
166*92022041SSam Saccone
167*92022041SSam Saccone parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
168*92022041SSam Saccone printf("\trx bitrate: %s\n", buf);
169*92022041SSam Saccone }
170*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
171*92022041SSam Saccone char buf[100];
172*92022041SSam Saccone
173*92022041SSam Saccone parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
174*92022041SSam Saccone printf("\ttx bitrate: %s\n", buf);
175*92022041SSam Saccone }
176*92022041SSam Saccone
177*92022041SSam Saccone if (sinfo[NL80211_STA_INFO_BSS_PARAM]) {
178*92022041SSam Saccone if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX,
179*92022041SSam Saccone sinfo[NL80211_STA_INFO_BSS_PARAM],
180*92022041SSam Saccone bss_policy)) {
181*92022041SSam Saccone fprintf(stderr, "failed to parse nested bss parameters!\n");
182*92022041SSam Saccone } else {
183*92022041SSam Saccone char *delim = "";
184*92022041SSam Saccone printf("\n\tbss flags:\t");
185*92022041SSam Saccone if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) {
186*92022041SSam Saccone printf("CTS-protection");
187*92022041SSam Saccone delim = " ";
188*92022041SSam Saccone }
189*92022041SSam Saccone if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) {
190*92022041SSam Saccone printf("%sshort-preamble", delim);
191*92022041SSam Saccone delim = " ";
192*92022041SSam Saccone }
193*92022041SSam Saccone if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME])
194*92022041SSam Saccone printf("%sshort-slot-time", delim);
195*92022041SSam Saccone printf("\n\tdtim period:\t%d",
196*92022041SSam Saccone nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
197*92022041SSam Saccone printf("\n\tbeacon int:\t%d",
198*92022041SSam Saccone nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
199*92022041SSam Saccone printf("\n");
200*92022041SSam Saccone }
201*92022041SSam Saccone }
202*92022041SSam Saccone
203*92022041SSam Saccone return NL_SKIP;
204*92022041SSam Saccone }
205*92022041SSam Saccone
handle_link_sta(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)206*92022041SSam Saccone static int handle_link_sta(struct nl80211_state *state,
207*92022041SSam Saccone struct nl_msg *msg,
208*92022041SSam Saccone int argc, char **argv,
209*92022041SSam Saccone enum id_input id)
210*92022041SSam Saccone {
211*92022041SSam Saccone unsigned char mac_addr[ETH_ALEN];
212*92022041SSam Saccone
213*92022041SSam Saccone if (argc < 1)
214*92022041SSam Saccone return 1;
215*92022041SSam Saccone
216*92022041SSam Saccone if (mac_addr_a2n(mac_addr, argv[0])) {
217*92022041SSam Saccone fprintf(stderr, "invalid mac address\n");
218*92022041SSam Saccone return 2;
219*92022041SSam Saccone }
220*92022041SSam Saccone
221*92022041SSam Saccone argc--;
222*92022041SSam Saccone argv++;
223*92022041SSam Saccone
224*92022041SSam Saccone if (argc)
225*92022041SSam Saccone return 1;
226*92022041SSam Saccone
227*92022041SSam Saccone NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
228*92022041SSam Saccone
229*92022041SSam Saccone register_handler(print_link_sta, NULL);
230*92022041SSam Saccone
231*92022041SSam Saccone return 0;
232*92022041SSam Saccone nla_put_failure:
233*92022041SSam Saccone return -ENOBUFS;
234*92022041SSam Saccone }
235*92022041SSam Saccone
handle_link(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)236*92022041SSam Saccone static int handle_link(struct nl80211_state *state,
237*92022041SSam Saccone struct nl_msg *msg, int argc, char **argv,
238*92022041SSam Saccone enum id_input id)
239*92022041SSam Saccone {
240*92022041SSam Saccone char *link_argv[] = {
241*92022041SSam Saccone NULL,
242*92022041SSam Saccone "link",
243*92022041SSam Saccone "get_bss",
244*92022041SSam Saccone NULL,
245*92022041SSam Saccone };
246*92022041SSam Saccone char *station_argv[] = {
247*92022041SSam Saccone NULL,
248*92022041SSam Saccone "link",
249*92022041SSam Saccone "get_sta",
250*92022041SSam Saccone NULL,
251*92022041SSam Saccone NULL,
252*92022041SSam Saccone };
253*92022041SSam Saccone char bssid_buf[3*6];
254*92022041SSam Saccone int err;
255*92022041SSam Saccone
256*92022041SSam Saccone link_argv[0] = argv[0];
257*92022041SSam Saccone err = handle_cmd(state, id, 3, link_argv);
258*92022041SSam Saccone if (err)
259*92022041SSam Saccone return err;
260*92022041SSam Saccone
261*92022041SSam Saccone if (!lr.link_found) {
262*92022041SSam Saccone if (!lr.anything_found)
263*92022041SSam Saccone printf("Not connected.\n");
264*92022041SSam Saccone return 0;
265*92022041SSam Saccone }
266*92022041SSam Saccone
267*92022041SSam Saccone mac_addr_n2a(bssid_buf, lr.bssid);
268*92022041SSam Saccone bssid_buf[17] = '\0';
269*92022041SSam Saccone
270*92022041SSam Saccone station_argv[0] = argv[0];
271*92022041SSam Saccone station_argv[3] = bssid_buf;
272*92022041SSam Saccone return handle_cmd(state, id, 4, station_argv);
273*92022041SSam Saccone }
274*92022041SSam Saccone TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
275*92022041SSam Saccone "Print information about the current link, if any.");
276*92022041SSam Saccone HIDDEN(link, get_sta, "<mac-addr>", NL80211_CMD_GET_STATION, 0,
277*92022041SSam Saccone CIB_NETDEV, handle_link_sta);
278*92022041SSam Saccone HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
279*92022041SSam Saccone CIB_NETDEV, handle_scan_for_link);
280