1*92022041SSam Saccone #include <errno.h>
2*92022041SSam Saccone
3*92022041SSam Saccone #include "nl80211.h"
4*92022041SSam Saccone #include "iw.h"
5*92022041SSam Saccone #include <unistd.h>
6*92022041SSam Saccone
7*92022041SSam Saccone SECTION(measurement);
8*92022041SSam Saccone
put_preamble(struct nl_msg * msg,char * s)9*92022041SSam Saccone static int put_preamble(struct nl_msg *msg, char *s)
10*92022041SSam Saccone {
11*92022041SSam Saccone static const struct {
12*92022041SSam Saccone const char *name;
13*92022041SSam Saccone unsigned int val;
14*92022041SSam Saccone } preamble_map[] = {
15*92022041SSam Saccone { .name = "legacy", .val = NL80211_PREAMBLE_LEGACY, },
16*92022041SSam Saccone { .name = "ht", .val = NL80211_PREAMBLE_HT, },
17*92022041SSam Saccone { .name = "vht", .val = NL80211_PREAMBLE_VHT, },
18*92022041SSam Saccone { .name = "dmg", .val = NL80211_PREAMBLE_DMG, },
19*92022041SSam Saccone };
20*92022041SSam Saccone unsigned int i;
21*92022041SSam Saccone
22*92022041SSam Saccone for (i = 0; i < ARRAY_SIZE(preamble_map); i++) {
23*92022041SSam Saccone if (strcasecmp(preamble_map[i].name, s) == 0) {
24*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
25*92022041SSam Saccone preamble_map[i].val);
26*92022041SSam Saccone return 0;
27*92022041SSam Saccone }
28*92022041SSam Saccone }
29*92022041SSam Saccone
30*92022041SSam Saccone nla_put_failure:
31*92022041SSam Saccone return -1;
32*92022041SSam Saccone }
33*92022041SSam Saccone
parse_ftm_target(struct nl_msg * msg,char * str,int peer_index)34*92022041SSam Saccone static int parse_ftm_target(struct nl_msg *msg, char *str, int peer_index)
35*92022041SSam Saccone {
36*92022041SSam Saccone unsigned char addr[ETH_ALEN];
37*92022041SSam Saccone int res, consumed;
38*92022041SSam Saccone char *bw = NULL, *pos, *tmp, *save_ptr, *delims = " \t\n";
39*92022041SSam Saccone struct nlattr *peer, *req, *reqdata, *ftm, *chan;
40*92022041SSam Saccone bool report_ap_tsf = false, preamble = false;
41*92022041SSam Saccone unsigned int freq = 0, cf1 = 0, cf2 = 0;
42*92022041SSam Saccone
43*92022041SSam Saccone res = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n",
44*92022041SSam Saccone &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5],
45*92022041SSam Saccone &consumed);
46*92022041SSam Saccone
47*92022041SSam Saccone if (res != ETH_ALEN) {
48*92022041SSam Saccone printf("Invalid MAC address\n");
49*92022041SSam Saccone return HANDLER_RET_USAGE;
50*92022041SSam Saccone }
51*92022041SSam Saccone
52*92022041SSam Saccone peer = nla_nest_start(msg, peer_index);
53*92022041SSam Saccone
54*92022041SSam Saccone NLA_PUT(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, addr);
55*92022041SSam Saccone
56*92022041SSam Saccone req = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_REQ);
57*92022041SSam Saccone if (!req)
58*92022041SSam Saccone goto nla_put_failure;
59*92022041SSam Saccone reqdata = nla_nest_start(msg, NL80211_PMSR_REQ_ATTR_DATA);
60*92022041SSam Saccone if (!reqdata)
61*92022041SSam Saccone goto nla_put_failure;
62*92022041SSam Saccone ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
63*92022041SSam Saccone if (!ftm)
64*92022041SSam Saccone goto nla_put_failure;
65*92022041SSam Saccone
66*92022041SSam Saccone str += consumed;
67*92022041SSam Saccone pos = strtok_r(str, delims, &save_ptr);
68*92022041SSam Saccone
69*92022041SSam Saccone while (pos) {
70*92022041SSam Saccone if (strncmp(pos, "cf=", 3) == 0) {
71*92022041SSam Saccone freq = strtol(pos + 3, &tmp, 0);
72*92022041SSam Saccone if (*tmp) {
73*92022041SSam Saccone printf("Invalid cf value!\n");
74*92022041SSam Saccone return HANDLER_RET_USAGE;
75*92022041SSam Saccone }
76*92022041SSam Saccone } else if (strncmp(pos, "bw=", 3) == 0) {
77*92022041SSam Saccone bw = pos + 3;
78*92022041SSam Saccone } else if (strncmp(pos, "cf1=", 4) == 0) {
79*92022041SSam Saccone cf1 = strtol(pos + 4, &tmp, 0);
80*92022041SSam Saccone if (*tmp) {
81*92022041SSam Saccone printf("Invalid cf1 value!\n");
82*92022041SSam Saccone return HANDLER_RET_USAGE;
83*92022041SSam Saccone }
84*92022041SSam Saccone } else if (strncmp(pos, "cf2=", 4) == 0) {
85*92022041SSam Saccone cf2 = strtol(pos + 4, &tmp, 0);
86*92022041SSam Saccone if (*tmp) {
87*92022041SSam Saccone printf("Invalid cf2 value!\n");
88*92022041SSam Saccone return HANDLER_RET_USAGE;
89*92022041SSam Saccone }
90*92022041SSam Saccone } else if (strncmp(pos, "bursts_exp=", 11) == 0) {
91*92022041SSam Saccone NLA_PUT_U8(msg,
92*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
93*92022041SSam Saccone strtol(pos + 11, &tmp, 0));
94*92022041SSam Saccone if (*tmp) {
95*92022041SSam Saccone printf("Invalid bursts_exp value!\n");
96*92022041SSam Saccone return HANDLER_RET_USAGE;
97*92022041SSam Saccone }
98*92022041SSam Saccone } else if (strncmp(pos, "burst_period=", 13) == 0) {
99*92022041SSam Saccone NLA_PUT_U16(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
100*92022041SSam Saccone strtol(pos + 13, &tmp, 0));
101*92022041SSam Saccone if (*tmp) {
102*92022041SSam Saccone printf("Invalid burst_period value!\n");
103*92022041SSam Saccone return HANDLER_RET_USAGE;
104*92022041SSam Saccone }
105*92022041SSam Saccone } else if (strncmp(pos, "retries=", 8) == 0) {
106*92022041SSam Saccone NLA_PUT_U8(msg,
107*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
108*92022041SSam Saccone strtol(pos + 8, &tmp, 0));
109*92022041SSam Saccone if (*tmp) {
110*92022041SSam Saccone printf("Invalid retries value!\n");
111*92022041SSam Saccone return HANDLER_RET_USAGE;
112*92022041SSam Saccone }
113*92022041SSam Saccone } else if (strncmp(pos, "burst_duration=", 15) == 0) {
114*92022041SSam Saccone NLA_PUT_U8(msg,
115*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
116*92022041SSam Saccone strtol(pos + 15, &tmp, 0));
117*92022041SSam Saccone if (*tmp) {
118*92022041SSam Saccone printf("Invalid burst_duration value!\n");
119*92022041SSam Saccone return HANDLER_RET_USAGE;
120*92022041SSam Saccone }
121*92022041SSam Saccone } else if (strncmp(pos, "ftms_per_burst=", 15) == 0) {
122*92022041SSam Saccone NLA_PUT_U8(msg,
123*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
124*92022041SSam Saccone strtol(pos + 15, &tmp, 0));
125*92022041SSam Saccone if (*tmp) {
126*92022041SSam Saccone printf("Invalid ftms_per_burst value!\n");
127*92022041SSam Saccone return HANDLER_RET_USAGE;
128*92022041SSam Saccone }
129*92022041SSam Saccone } else if (strcmp(pos, "asap") == 0) {
130*92022041SSam Saccone NLA_PUT_FLAG(msg, NL80211_PMSR_FTM_REQ_ATTR_ASAP);
131*92022041SSam Saccone } else if (strcmp(pos, "ap-tsf") == 0) {
132*92022041SSam Saccone report_ap_tsf = true;
133*92022041SSam Saccone } else if (strcmp(pos, "civic") == 0) {
134*92022041SSam Saccone NLA_PUT_FLAG(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC);
135*92022041SSam Saccone } else if (strcmp(pos, "lci") == 0) {
136*92022041SSam Saccone NLA_PUT_FLAG(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI);
137*92022041SSam Saccone } else if (strncmp(pos, "preamble=", 9) == 0) {
138*92022041SSam Saccone if (put_preamble(msg, pos + 9)) {
139*92022041SSam Saccone printf("Invalid preamble %s\n", pos + 9);
140*92022041SSam Saccone return HANDLER_RET_USAGE;
141*92022041SSam Saccone }
142*92022041SSam Saccone preamble = true;
143*92022041SSam Saccone } else if (strncmp(pos, "tb", 2) == 0) {
144*92022041SSam Saccone NLA_PUT_FLAG(msg,
145*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED);
146*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
147*92022041SSam Saccone NL80211_PREAMBLE_HE);
148*92022041SSam Saccone preamble = true;
149*92022041SSam Saccone } else if (strncmp(pos, "non_tb", 6) == 0) {
150*92022041SSam Saccone NLA_PUT_FLAG(msg,
151*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED);
152*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
153*92022041SSam Saccone NL80211_PREAMBLE_HE);
154*92022041SSam Saccone preamble = true;
155*92022041SSam Saccone } else if (strncmp(pos, "lmr_feedback", 12) == 0) {
156*92022041SSam Saccone NLA_PUT_FLAG(msg,
157*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK);
158*92022041SSam Saccone } else if (strncmp(pos, "bss_color=", 10) == 0) {
159*92022041SSam Saccone NLA_PUT_U8(msg,
160*92022041SSam Saccone NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
161*92022041SSam Saccone strtol(pos + 10, &tmp, 0));
162*92022041SSam Saccone if (*tmp) {
163*92022041SSam Saccone printf("Invalid bss_color value!\n");
164*92022041SSam Saccone return HANDLER_RET_USAGE;
165*92022041SSam Saccone }
166*92022041SSam Saccone } else {
167*92022041SSam Saccone printf("Unknown parameter %s\n", pos);
168*92022041SSam Saccone return HANDLER_RET_USAGE;
169*92022041SSam Saccone }
170*92022041SSam Saccone
171*92022041SSam Saccone pos = strtok_r(NULL, delims, &save_ptr);
172*92022041SSam Saccone }
173*92022041SSam Saccone
174*92022041SSam Saccone if (!preamble) {
175*92022041SSam Saccone int preamble = -1;
176*92022041SSam Saccone
177*92022041SSam Saccone switch (str_to_bw(bw)) {
178*92022041SSam Saccone case NL80211_CHAN_WIDTH_20_NOHT:
179*92022041SSam Saccone case NL80211_CHAN_WIDTH_5:
180*92022041SSam Saccone case NL80211_CHAN_WIDTH_10:
181*92022041SSam Saccone preamble = NL80211_PREAMBLE_LEGACY;
182*92022041SSam Saccone break;
183*92022041SSam Saccone case NL80211_CHAN_WIDTH_20:
184*92022041SSam Saccone case NL80211_CHAN_WIDTH_40:
185*92022041SSam Saccone preamble = NL80211_PREAMBLE_HT;
186*92022041SSam Saccone break;
187*92022041SSam Saccone case NL80211_CHAN_WIDTH_80:
188*92022041SSam Saccone case NL80211_CHAN_WIDTH_80P80:
189*92022041SSam Saccone case NL80211_CHAN_WIDTH_160:
190*92022041SSam Saccone preamble = NL80211_PREAMBLE_VHT;
191*92022041SSam Saccone break;
192*92022041SSam Saccone default:
193*92022041SSam Saccone return HANDLER_RET_USAGE;
194*92022041SSam Saccone }
195*92022041SSam Saccone
196*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, preamble);
197*92022041SSam Saccone }
198*92022041SSam Saccone
199*92022041SSam Saccone nla_nest_end(msg, ftm);
200*92022041SSam Saccone if (report_ap_tsf)
201*92022041SSam Saccone NLA_PUT_FLAG(msg, NL80211_PMSR_REQ_ATTR_GET_AP_TSF);
202*92022041SSam Saccone nla_nest_end(msg, reqdata);
203*92022041SSam Saccone nla_nest_end(msg, req);
204*92022041SSam Saccone
205*92022041SSam Saccone /* set the channel */
206*92022041SSam Saccone chan = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_CHAN);
207*92022041SSam Saccone if (!chan)
208*92022041SSam Saccone goto nla_put_failure;
209*92022041SSam Saccone if (freq)
210*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
211*92022041SSam Saccone if (cf1)
212*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, cf1);
213*92022041SSam Saccone if (cf2)
214*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, cf2);
215*92022041SSam Saccone if (bw)
216*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
217*92022041SSam Saccone str_to_bw(bw));
218*92022041SSam Saccone nla_nest_end(msg, chan);
219*92022041SSam Saccone
220*92022041SSam Saccone nla_nest_end(msg, peer);
221*92022041SSam Saccone return 0;
222*92022041SSam Saccone nla_put_failure:
223*92022041SSam Saccone return -ENOBUFS;
224*92022041SSam Saccone }
225*92022041SSam Saccone
parse_ftm_config(struct nl_msg * msg,const char * file)226*92022041SSam Saccone static int parse_ftm_config(struct nl_msg *msg, const char *file)
227*92022041SSam Saccone {
228*92022041SSam Saccone FILE *input;
229*92022041SSam Saccone char line[256];
230*92022041SSam Saccone int line_num;
231*92022041SSam Saccone
232*92022041SSam Saccone input = fopen(file, "r");
233*92022041SSam Saccone if (!input) {
234*92022041SSam Saccone int err = errno;
235*92022041SSam Saccone
236*92022041SSam Saccone printf("Failed to open file: %s\n", strerror(err));
237*92022041SSam Saccone return -err;
238*92022041SSam Saccone }
239*92022041SSam Saccone
240*92022041SSam Saccone for (line_num = 1; fgets(line, sizeof(line), input); line_num++) {
241*92022041SSam Saccone if (line[0] == '#')
242*92022041SSam Saccone continue;
243*92022041SSam Saccone
244*92022041SSam Saccone if (parse_ftm_target(msg, line, line_num)) {
245*92022041SSam Saccone printf("Invalid FTM configuration at line %d!\n",
246*92022041SSam Saccone line_num);
247*92022041SSam Saccone return HANDLER_RET_USAGE;
248*92022041SSam Saccone }
249*92022041SSam Saccone }
250*92022041SSam Saccone
251*92022041SSam Saccone return 0;
252*92022041SSam Saccone }
253*92022041SSam Saccone
handle_ftm_req(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)254*92022041SSam Saccone static int handle_ftm_req(struct nl80211_state *state, struct nl_msg *msg,
255*92022041SSam Saccone int argc, char **argv, enum id_input id)
256*92022041SSam Saccone {
257*92022041SSam Saccone int err, i;
258*92022041SSam Saccone static char **req_argv;
259*92022041SSam Saccone static const __u32 wait[] = {
260*92022041SSam Saccone NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
261*92022041SSam Saccone };
262*92022041SSam Saccone static const __u32 print[] = {
263*92022041SSam Saccone NL80211_CMD_PEER_MEASUREMENT_RESULT,
264*92022041SSam Saccone NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
265*92022041SSam Saccone };
266*92022041SSam Saccone struct print_event_args printargs = { };
267*92022041SSam Saccone
268*92022041SSam Saccone req_argv = calloc(argc + 1, sizeof(req_argv[0]));
269*92022041SSam Saccone req_argv[0] = argv[0];
270*92022041SSam Saccone req_argv[1] = "measurement";
271*92022041SSam Saccone req_argv[2] = "ftm_request_send";
272*92022041SSam Saccone for (i = 3; i < argc; i++)
273*92022041SSam Saccone req_argv[i] = argv[i];
274*92022041SSam Saccone
275*92022041SSam Saccone err = handle_cmd(state, id, argc, req_argv);
276*92022041SSam Saccone
277*92022041SSam Saccone free(req_argv);
278*92022041SSam Saccone
279*92022041SSam Saccone if (err)
280*92022041SSam Saccone return err;
281*92022041SSam Saccone
282*92022041SSam Saccone __do_listen_events(state,
283*92022041SSam Saccone ARRAY_SIZE(wait), wait,
284*92022041SSam Saccone ARRAY_SIZE(print), print,
285*92022041SSam Saccone &printargs);
286*92022041SSam Saccone return 0;
287*92022041SSam Saccone }
288*92022041SSam Saccone
handle_ftm_req_send(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)289*92022041SSam Saccone static int handle_ftm_req_send(struct nl80211_state *state, struct nl_msg *msg,
290*92022041SSam Saccone int argc, char **argv, enum id_input id)
291*92022041SSam Saccone {
292*92022041SSam Saccone struct nlattr *pmsr, *peers;
293*92022041SSam Saccone const char *file;
294*92022041SSam Saccone int err;
295*92022041SSam Saccone
296*92022041SSam Saccone if (argc < 1)
297*92022041SSam Saccone return HANDLER_RET_USAGE;
298*92022041SSam Saccone
299*92022041SSam Saccone file = argv[0];
300*92022041SSam Saccone argc--;
301*92022041SSam Saccone argv++;
302*92022041SSam Saccone while (argc) {
303*92022041SSam Saccone if (strncmp(argv[0], "randomise", 9) == 0 ||
304*92022041SSam Saccone strncmp(argv[0], "randomize", 9) == 0) {
305*92022041SSam Saccone err = parse_random_mac_addr(msg, argv[0] + 9);
306*92022041SSam Saccone if (err)
307*92022041SSam Saccone return err;
308*92022041SSam Saccone } else if (strncmp(argv[0], "timeout=", 8) == 0) {
309*92022041SSam Saccone char *end;
310*92022041SSam Saccone
311*92022041SSam Saccone NLA_PUT_U32(msg, NL80211_ATTR_TIMEOUT,
312*92022041SSam Saccone strtoul(argv[0] + 8, &end, 0));
313*92022041SSam Saccone if (*end)
314*92022041SSam Saccone return HANDLER_RET_USAGE;
315*92022041SSam Saccone } else {
316*92022041SSam Saccone return HANDLER_RET_USAGE;
317*92022041SSam Saccone }
318*92022041SSam Saccone
319*92022041SSam Saccone argc--;
320*92022041SSam Saccone argv++;
321*92022041SSam Saccone }
322*92022041SSam Saccone
323*92022041SSam Saccone pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
324*92022041SSam Saccone if (!pmsr)
325*92022041SSam Saccone goto nla_put_failure;
326*92022041SSam Saccone peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
327*92022041SSam Saccone if (!peers)
328*92022041SSam Saccone goto nla_put_failure;
329*92022041SSam Saccone
330*92022041SSam Saccone err = parse_ftm_config(msg, file);
331*92022041SSam Saccone if (err)
332*92022041SSam Saccone return err;
333*92022041SSam Saccone
334*92022041SSam Saccone nla_nest_end(msg, peers);
335*92022041SSam Saccone nla_nest_end(msg, pmsr);
336*92022041SSam Saccone
337*92022041SSam Saccone return 0;
338*92022041SSam Saccone
339*92022041SSam Saccone nla_put_failure:
340*92022041SSam Saccone return -ENOBUFS;
341*92022041SSam Saccone }
342*92022041SSam Saccone COMMAND(measurement, ftm_request, "<config-file> [timeout=<seconds>] [randomise[=<addr>/<mask>]]", 0, 0,
343*92022041SSam Saccone CIB_NETDEV, handle_ftm_req,
344*92022041SSam Saccone "Send an FTM request to the targets supplied in the config file.\n"
345*92022041SSam Saccone "Each line in the file represents a target, with the following format:\n"
346*92022041SSam Saccone "<addr> bw=<[20|40|80|80+80|160]> cf=<center_freq> [cf1=<center_freq1>] [cf2=<center_freq2>] [ftms_per_burst=<samples per burst>] [ap-tsf] [asap] [bursts_exp=<num of bursts exponent>] [burst_period=<burst period>] [retries=<num of retries>] [burst_duration=<burst duration>] [preamble=<legacy,ht,vht,dmg>] [lci] [civic] [tb] [non_tb]");
347*92022041SSam Saccone HIDDEN(measurement, ftm_request_send, "", NL80211_CMD_PEER_MEASUREMENT_START,
348*92022041SSam Saccone 0, CIB_NETDEV, handle_ftm_req_send);
349