9 static int put_preamble(struct nl_msg
*msg
, char *s
)
15 { .name
= "legacy", .val
= NL80211_PREAMBLE_LEGACY
, },
16 { .name
= "ht", .val
= NL80211_PREAMBLE_HT
, },
17 { .name
= "vht", .val
= NL80211_PREAMBLE_VHT
, },
18 { .name
= "dmg", .val
= NL80211_PREAMBLE_DMG
, },
22 for (i
= 0; i
< ARRAY_SIZE(preamble_map
); i
++) {
23 if (strcasecmp(preamble_map
[i
].name
, s
) == 0) {
24 NLA_PUT_U32(msg
, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE
,
34 static int parse_ftm_target(struct nl_msg
*msg
, char *str
, int peer_index
)
36 unsigned char addr
[ETH_ALEN
];
38 char *bw
= NULL
, *pos
, *tmp
, *save_ptr
, *delims
= " \t\n";
39 struct nlattr
*peer
, *req
, *reqdata
, *ftm
, *chan
;
40 bool report_ap_tsf
= false, preamble
= false;
41 unsigned int freq
= 0, cf1
= 0, cf2
= 0;
43 res
= sscanf(str
, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n",
44 &addr
[0], &addr
[1], &addr
[2], &addr
[3], &addr
[4], &addr
[5],
47 if (res
!= ETH_ALEN
) {
48 printf("Invalid MAC address\n");
49 return HANDLER_RET_USAGE
;
52 peer
= nla_nest_start(msg
, peer_index
);
54 NLA_PUT(msg
, NL80211_PMSR_PEER_ATTR_ADDR
, ETH_ALEN
, addr
);
56 req
= nla_nest_start(msg
, NL80211_PMSR_PEER_ATTR_REQ
);
59 reqdata
= nla_nest_start(msg
, NL80211_PMSR_REQ_ATTR_DATA
);
62 ftm
= nla_nest_start(msg
, NL80211_PMSR_TYPE_FTM
);
67 pos
= strtok_r(str
, delims
, &save_ptr
);
70 if (strncmp(pos
, "cf=", 3) == 0) {
71 freq
= strtol(pos
+ 3, &tmp
, 0);
73 printf("Invalid cf value!\n");
74 return HANDLER_RET_USAGE
;
76 } else if (strncmp(pos
, "bw=", 3) == 0) {
78 } else if (strncmp(pos
, "cf1=", 4) == 0) {
79 cf1
= strtol(pos
+ 4, &tmp
, 0);
81 printf("Invalid cf1 value!\n");
82 return HANDLER_RET_USAGE
;
84 } else if (strncmp(pos
, "cf2=", 4) == 0) {
85 cf2
= strtol(pos
+ 4, &tmp
, 0);
87 printf("Invalid cf2 value!\n");
88 return HANDLER_RET_USAGE
;
90 } else if (strncmp(pos
, "bursts_exp=", 11) == 0) {
92 NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP
,
93 strtol(pos
+ 11, &tmp
, 0));
95 printf("Invalid bursts_exp value!\n");
96 return HANDLER_RET_USAGE
;
98 } else if (strncmp(pos
, "burst_period=", 13) == 0) {
99 NLA_PUT_U16(msg
, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD
,
100 strtol(pos
+ 13, &tmp
, 0));
102 printf("Invalid burst_period value!\n");
103 return HANDLER_RET_USAGE
;
105 } else if (strncmp(pos
, "retries=", 8) == 0) {
107 NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES
,
108 strtol(pos
+ 8, &tmp
, 0));
110 printf("Invalid retries value!\n");
111 return HANDLER_RET_USAGE
;
113 } else if (strncmp(pos
, "burst_duration=", 15) == 0) {
115 NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION
,
116 strtol(pos
+ 15, &tmp
, 0));
118 printf("Invalid burst_duration value!\n");
119 return HANDLER_RET_USAGE
;
121 } else if (strncmp(pos
, "ftms_per_burst=", 15) == 0) {
123 NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST
,
124 strtol(pos
+ 15, &tmp
, 0));
126 printf("Invalid ftms_per_burst value!\n");
127 return HANDLER_RET_USAGE
;
129 } else if (strcmp(pos
, "asap") == 0) {
130 NLA_PUT_FLAG(msg
, NL80211_PMSR_FTM_REQ_ATTR_ASAP
);
131 } else if (strcmp(pos
, "ap-tsf") == 0) {
132 report_ap_tsf
= true;
133 } else if (strcmp(pos
, "civic") == 0) {
134 NLA_PUT_FLAG(msg
, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC
);
135 } else if (strcmp(pos
, "lci") == 0) {
136 NLA_PUT_FLAG(msg
, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI
);
137 } else if (strncmp(pos
, "preamble=", 9) == 0) {
138 if (put_preamble(msg
, pos
+ 9)) {
139 printf("Invalid preamble %s\n", pos
+ 9);
140 return HANDLER_RET_USAGE
;
144 printf("Unknown parameter %s\n", pos
);
145 return HANDLER_RET_USAGE
;
148 pos
= strtok_r(NULL
, delims
, &save_ptr
);
154 switch (str_to_bw(bw
)) {
155 case NL80211_CHAN_WIDTH_20_NOHT
:
156 case NL80211_CHAN_WIDTH_5
:
157 case NL80211_CHAN_WIDTH_10
:
158 preamble
= NL80211_PREAMBLE_LEGACY
;
160 case NL80211_CHAN_WIDTH_20
:
161 case NL80211_CHAN_WIDTH_40
:
162 preamble
= NL80211_PREAMBLE_HT
;
164 case NL80211_CHAN_WIDTH_80
:
165 case NL80211_CHAN_WIDTH_80P80
:
166 case NL80211_CHAN_WIDTH_160
:
167 preamble
= NL80211_PREAMBLE_VHT
;
171 NLA_PUT_U32(msg
, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE
, preamble
);
174 nla_nest_end(msg
, ftm
);
176 NLA_PUT_FLAG(msg
, NL80211_PMSR_REQ_ATTR_GET_AP_TSF
);
177 nla_nest_end(msg
, reqdata
);
178 nla_nest_end(msg
, req
);
180 /* set the channel */
181 chan
= nla_nest_start(msg
, NL80211_PMSR_PEER_ATTR_CHAN
);
183 goto nla_put_failure
;
185 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY_FREQ
, freq
);
187 NLA_PUT_U32(msg
, NL80211_ATTR_CENTER_FREQ1
, cf1
);
189 NLA_PUT_U32(msg
, NL80211_ATTR_CENTER_FREQ2
, cf2
);
191 NLA_PUT_U32(msg
, NL80211_ATTR_CHANNEL_WIDTH
,
193 nla_nest_end(msg
, chan
);
195 nla_nest_end(msg
, peer
);
201 static int parse_ftm_config(struct nl_msg
*msg
, const char *file
)
207 input
= fopen(file
, "r");
211 printf("Failed to open file: %s\n", strerror(err
));
215 for (line_num
= 1; fgets(line
, sizeof(line
), input
); line_num
++) {
219 if (parse_ftm_target(msg
, line
, line_num
)) {
220 printf("Invalid FTM configuration at line %d!\n",
222 return HANDLER_RET_USAGE
;
229 static int handle_ftm_req(struct nl80211_state
*state
, struct nl_msg
*msg
,
230 int argc
, char **argv
, enum id_input id
)
233 static char **req_argv
;
234 static const __u32 wait
[] = {
235 NL80211_CMD_PEER_MEASUREMENT_COMPLETE
,
237 static const __u32 print
[] = {
238 NL80211_CMD_PEER_MEASUREMENT_RESULT
,
239 NL80211_CMD_PEER_MEASUREMENT_COMPLETE
,
241 struct print_event_args printargs
= { };
243 req_argv
= calloc(argc
+ 1, sizeof(req_argv
[0]));
244 req_argv
[0] = argv
[0];
245 req_argv
[1] = "measurement";
246 req_argv
[2] = "ftm_request_send";
247 for (i
= 3; i
< argc
; i
++)
248 req_argv
[i
] = argv
[i
];
250 err
= handle_cmd(state
, id
, argc
, req_argv
);
257 __do_listen_events(state
,
258 ARRAY_SIZE(wait
), wait
,
259 ARRAY_SIZE(print
), print
,
264 static int handle_ftm_req_send(struct nl80211_state
*state
, struct nl_msg
*msg
,
265 int argc
, char **argv
, enum id_input id
)
267 struct nlattr
*pmsr
, *peers
;
275 if (strncmp(argv
[0], "randomise", 9) == 0 ||
276 strncmp(argv
[0], "randomize", 9) == 0) {
277 err
= parse_random_mac_addr(msg
, argv
[0] + 9);
280 } else if (strncmp(argv
[0], "timeout=", 8) == 0) {
283 NLA_PUT_U32(msg
, NL80211_ATTR_TIMEOUT
,
284 strtoul(argv
[0] + 8, &end
, 0));
286 return HANDLER_RET_USAGE
;
288 return HANDLER_RET_USAGE
;
295 pmsr
= nla_nest_start(msg
, NL80211_ATTR_PEER_MEASUREMENTS
);
297 goto nla_put_failure
;
298 peers
= nla_nest_start(msg
, NL80211_PMSR_ATTR_PEERS
);
300 goto nla_put_failure
;
302 err
= parse_ftm_config(msg
, file
);
306 nla_nest_end(msg
, peers
);
307 nla_nest_end(msg
, pmsr
);
314 COMMAND(measurement
, ftm_request
, "<config-file> [timeout=<seconds>] [randomise[=<addr>/<mask>]]", 0, 0,
315 CIB_NETDEV
, handle_ftm_req
,
316 "Send an FTM request to the targets supplied in the config file.\n"
317 "Each line in the file represents a target, with the following format:\n"
318 "<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]");
319 HIDDEN(measurement
, ftm_request_send
, "", NL80211_CMD_PEER_MEASUREMENT_START
,
320 0, CIB_NETDEV
, handle_ftm_req_send
);