Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / fst / fst_ctrl_iface.c
1 /*
2  * FST module - Control Interface implementation
3  * Copyright (c) 2014, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "utils/includes.h"
10 #include "utils/common.h"
11 #include "common/defs.h"
12 #include "list.h"
13 #include "fst/fst.h"
14 #include "fst/fst_internal.h"
15 #include "fst_ctrl_defs.h"
16 #include "fst_ctrl_iface.h"
17
18
19 static struct fst_group * get_fst_group_by_id(const char *id)
20 {
21         struct fst_group *g;
22
23         foreach_fst_group(g) {
24                 const char *group_id = fst_group_get_id(g);
25
26                 if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
27                         return g;
28         }
29
30         return NULL;
31 }
32
33
34 /* notifications */
35 static Boolean format_session_state_extra(const union fst_event_extra *extra,
36                                           char *buffer, size_t size)
37 {
38         int len;
39         char reject_str[32] = FST_CTRL_PVAL_NONE;
40         const char *initiator = FST_CTRL_PVAL_NONE;
41         const struct fst_event_extra_session_state *ss;
42
43         ss = &extra->session_state;
44         if (ss->new_state != FST_SESSION_STATE_INITIAL)
45                 return TRUE;
46
47         switch (ss->extra.to_initial.reason) {
48         case REASON_REJECT:
49                 if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
50                         os_snprintf(reject_str, sizeof(reject_str), "%u",
51                                     ss->extra.to_initial.reject_code);
52                 /* no break */
53         case REASON_TEARDOWN:
54         case REASON_SWITCH:
55                 switch (ss->extra.to_initial.initiator) {
56                 case FST_INITIATOR_LOCAL:
57                         initiator = FST_CS_PVAL_INITIATOR_LOCAL;
58                         break;
59                 case FST_INITIATOR_REMOTE:
60                         initiator = FST_CS_PVAL_INITIATOR_REMOTE;
61                         break;
62                 default:
63                         break;
64                 }
65                 break;
66         default:
67                 break;
68         }
69
70         len = os_snprintf(buffer, size,
71                           FST_CES_PNAME_REASON "=%s "
72                           FST_CES_PNAME_REJECT_CODE "=%s "
73                           FST_CES_PNAME_INITIATOR "=%s",
74                           fst_reason_name(ss->extra.to_initial.reason),
75                           reject_str, initiator);
76
77         return !os_snprintf_error(size, len);
78 }
79
80
81 static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
82                                   enum fst_event_type event_type,
83                                   const union fst_event_extra *extra)
84 {
85         struct fst_group *g;
86         char extra_str[128] = "";
87         const struct fst_event_extra_session_state *ss;
88         const struct fst_event_extra_iface_state *is;
89         const struct fst_event_extra_peer_state *ps;
90
91         /*
92          * FST can use any of interface objects as it only sends messages
93          * on global Control Interface, so we just pick the 1st one.
94          */
95
96         if (!f) {
97                 foreach_fst_group(g) {
98                         f = fst_group_first_iface(g);
99                         if (f)
100                                 break;
101                 }
102                 if (!f)
103                         return;
104         }
105
106         WPA_ASSERT(f->iface_obj.ctx);
107
108         switch (event_type) {
109         case EVENT_FST_IFACE_STATE_CHANGED:
110                 if (!extra)
111                         return;
112                 is = &extra->iface_state;
113                 wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
114                                     FST_CTRL_EVENT_IFACE " %s "
115                                     FST_CEI_PNAME_IFNAME "=%s "
116                                     FST_CEI_PNAME_GROUP "=%s",
117                                     is->attached ? FST_CEI_PNAME_ATTACHED :
118                                     FST_CEI_PNAME_DETACHED,
119                                     is->ifname, is->group_id);
120                 break;
121         case EVENT_PEER_STATE_CHANGED:
122                 if (!extra)
123                         return;
124                 ps = &extra->peer_state;
125                 wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
126                                     FST_CTRL_EVENT_PEER " %s "
127                                     FST_CEP_PNAME_IFNAME "=%s "
128                                     FST_CEP_PNAME_ADDR "=" MACSTR,
129                                     ps->connected ? FST_CEP_PNAME_CONNECTED :
130                                     FST_CEP_PNAME_DISCONNECTED,
131                                     ps->ifname, MAC2STR(ps->addr));
132                 break;
133         case EVENT_FST_SESSION_STATE_CHANGED:
134                 if (!extra)
135                         return;
136                 if (!format_session_state_extra(extra, extra_str,
137                                                 sizeof(extra_str))) {
138                         fst_printf(MSG_ERROR,
139                                    "CTRL: Cannot format STATE_CHANGE extra");
140                         extra_str[0] = 0;
141                 }
142                 ss = &extra->session_state;
143                 wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
144                                     FST_CTRL_EVENT_SESSION " "
145                                     FST_CES_PNAME_SESSION_ID "=%u "
146                                     FST_CES_PNAME_EVT_TYPE "=%s "
147                                     FST_CES_PNAME_OLD_STATE "=%s "
148                                     FST_CES_PNAME_NEW_STATE "=%s %s",
149                                     session_id,
150                                     fst_session_event_type_name(event_type),
151                                     fst_session_state_name(ss->old_state),
152                                     fst_session_state_name(ss->new_state),
153                                     extra_str);
154                 break;
155         case EVENT_FST_ESTABLISHED:
156         case EVENT_FST_SETUP:
157                 wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
158                                     FST_CTRL_EVENT_SESSION " "
159                                     FST_CES_PNAME_SESSION_ID "=%u "
160                                     FST_CES_PNAME_EVT_TYPE "=%s",
161                                     session_id,
162                                     fst_session_event_type_name(event_type));
163                 break;
164         }
165 }
166
167
168 /* command processors */
169
170 /* fst session_get */
171 static int session_get(const char *session_id, char *buf, size_t buflen)
172 {
173         struct fst_session *s;
174         struct fst_iface *new_iface, *old_iface;
175         const u8 *old_peer_addr, *new_peer_addr;
176         u32 id;
177
178         id = strtoul(session_id, NULL, 0);
179
180         s = fst_session_get_by_id(id);
181         if (!s) {
182                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
183                 return os_snprintf(buf, buflen, "FAIL\n");
184         }
185
186         old_peer_addr = fst_session_get_peer_addr(s, TRUE);
187         new_peer_addr = fst_session_get_peer_addr(s, FALSE);
188         new_iface = fst_session_get_iface(s, FALSE);
189         old_iface = fst_session_get_iface(s, TRUE);
190
191         return os_snprintf(buf, buflen,
192                            FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
193                            FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
194                            FST_CSG_PNAME_NEW_IFNAME "=%s\n"
195                            FST_CSG_PNAME_OLD_IFNAME "=%s\n"
196                            FST_CSG_PNAME_LLT "=%u\n"
197                            FST_CSG_PNAME_STATE "=%s\n",
198                            MAC2STR(old_peer_addr),
199                            MAC2STR(new_peer_addr),
200                            new_iface ? fst_iface_get_name(new_iface) :
201                            FST_CTRL_PVAL_NONE,
202                            old_iface ? fst_iface_get_name(old_iface) :
203                            FST_CTRL_PVAL_NONE,
204                            fst_session_get_llt(s),
205                            fst_session_state_name(fst_session_get_state(s)));
206 }
207
208
209 /* fst session_set */
210 static int session_set(const char *session_id, char *buf, size_t buflen)
211 {
212         struct fst_session *s;
213         char *p, *q;
214         u32 id;
215         int ret;
216
217         id = strtoul(session_id, &p, 0);
218
219         s = fst_session_get_by_id(id);
220         if (!s) {
221                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
222                 return os_snprintf(buf, buflen, "FAIL\n");
223         }
224
225         if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
226                 return os_snprintf(buf, buflen, "FAIL\n");
227         p++;
228
229         if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
230                 ret = fst_session_set_str_ifname(s, q + 1, TRUE);
231         } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
232                 ret = fst_session_set_str_ifname(s, q + 1, FALSE);
233         } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
234                 ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
235         } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
236                 ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
237         } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
238                 ret = fst_session_set_str_llt(s, q + 1);
239         } else {
240                 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
241                 return os_snprintf(buf, buflen, "FAIL\n");
242         }
243
244         return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
245 }
246
247
248 /* fst session_add/remove */
249 static int session_add(const char *group_id, char *buf, size_t buflen)
250 {
251         struct fst_group *g;
252         struct fst_session *s;
253
254         g = get_fst_group_by_id(group_id);
255         if (!g) {
256                 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
257                            group_id);
258                 return os_snprintf(buf, buflen, "FAIL\n");
259         }
260
261         s = fst_session_create(g);
262         if (!s) {
263                 fst_printf(MSG_ERROR,
264                            "CTRL: Cannot create session for group '%s'",
265                            group_id);
266                 return os_snprintf(buf, buflen, "FAIL\n");
267         }
268
269         return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
270 }
271
272
273 static int session_remove(const char *session_id, char *buf, size_t buflen)
274 {
275         struct fst_session *s;
276         struct fst_group *g;
277         u32 id;
278
279         id = strtoul(session_id, NULL, 0);
280
281         s = fst_session_get_by_id(id);
282         if (!s) {
283                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
284                 return os_snprintf(buf, buflen, "FAIL\n");
285         }
286
287         g = fst_session_get_group(s);
288         fst_session_reset(s);
289         fst_session_delete(s);
290         fst_group_delete_if_empty(g);
291
292         return os_snprintf(buf, buflen, "OK\n");
293 }
294
295
296 /* fst session_initiate */
297 static int session_initiate(const char *session_id, char *buf, size_t buflen)
298 {
299         struct fst_session *s;
300         u32 id;
301
302         id = strtoul(session_id, NULL, 0);
303
304         s = fst_session_get_by_id(id);
305         if (!s) {
306                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
307                 return os_snprintf(buf, buflen, "FAIL\n");
308         }
309
310         if (fst_session_initiate_setup(s)) {
311                 fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
312                 return os_snprintf(buf, buflen, "FAIL\n");
313         }
314
315         return os_snprintf(buf, buflen, "OK\n");
316 }
317
318
319 /* fst session_respond */
320 static int session_respond(const char *session_id, char *buf, size_t buflen)
321 {
322         struct fst_session *s;
323         char *p;
324         u32 id;
325         u8 status_code;
326
327         id = strtoul(session_id, &p, 0);
328
329         s = fst_session_get_by_id(id);
330         if (!s) {
331                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
332                 return os_snprintf(buf, buflen, "FAIL\n");
333         }
334
335         if (*p != ' ')
336                 return os_snprintf(buf, buflen, "FAIL\n");
337         p++;
338
339         if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
340                 status_code = WLAN_STATUS_SUCCESS;
341         } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
342                 status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
343         } else {
344                 fst_printf(MSG_WARNING,
345                            "CTRL: session %u: unknown response status: %s",
346                            id, p);
347                 return os_snprintf(buf, buflen, "FAIL\n");
348         }
349
350         if (fst_session_respond(s, status_code)) {
351                 fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
352                            id);
353                 return os_snprintf(buf, buflen, "FAIL\n");
354         }
355
356         fst_printf(MSG_INFO, "CTRL: session %u responded", id);
357
358         return os_snprintf(buf, buflen, "OK\n");
359 }
360
361
362 /* fst session_transfer */
363 static int session_transfer(const char *session_id, char *buf, size_t buflen)
364 {
365         struct fst_session *s;
366         u32 id;
367
368         id = strtoul(session_id, NULL, 0);
369
370         s = fst_session_get_by_id(id);
371         if (!s) {
372                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
373                 return os_snprintf(buf, buflen, "FAIL\n");
374         }
375
376         if (fst_session_initiate_switch(s)) {
377                 fst_printf(MSG_WARNING,
378                            "CTRL: Cannot initiate ST for session %u", id);
379                 return os_snprintf(buf, buflen, "FAIL\n");
380         }
381
382         return os_snprintf(buf, buflen, "OK\n");
383 }
384
385
386 /* fst session_teardown */
387 static int session_teardown(const char *session_id, char *buf, size_t buflen)
388 {
389         struct fst_session *s;
390         u32 id;
391
392         id = strtoul(session_id, NULL, 0);
393
394         s = fst_session_get_by_id(id);
395         if (!s) {
396                 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
397                 return os_snprintf(buf, buflen, "FAIL\n");
398         }
399
400         if (fst_session_tear_down_setup(s)) {
401                 fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
402                            id);
403                 return os_snprintf(buf, buflen, "FAIL\n");
404         }
405
406         return os_snprintf(buf, buflen, "OK\n");
407 }
408
409
410 #ifdef CONFIG_FST_TEST
411 /* fst test_request */
412 static int test_request(const char *request, char *buf, size_t buflen)
413 {
414         const char *p = request;
415         int ret;
416
417         if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
418                             os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
419                 ret = fst_test_req_send_fst_request(
420                         p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
421         } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
422                                    os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
423                 ret = fst_test_req_send_fst_response(
424                         p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
425         } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
426                                    os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
427                 ret = fst_test_req_send_ack_request(
428                         p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
429         } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
430                                    os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
431                 ret = fst_test_req_send_ack_response(
432                         p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
433         } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
434                                    os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
435                 ret = fst_test_req_send_tear_down(
436                         p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
437         } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
438                                    os_strlen(FST_CTR_GET_FSTS_ID))) {
439                 u32 fsts_id = fst_test_req_get_fsts_id(
440                         p + os_strlen(FST_CTR_GET_FSTS_ID));
441                 if (fsts_id != FST_FSTS_ID_NOT_FOUND)
442                         return os_snprintf(buf, buflen, "%u\n", fsts_id);
443                 return os_snprintf(buf, buflen, "FAIL\n");
444         } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
445                                    os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
446                 return fst_test_req_get_local_mbies(
447                         p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
448         } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
449                                    os_strlen(FST_CTR_IS_SUPPORTED))) {
450                 ret = 0;
451         } else {
452                 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
453                 return os_snprintf(buf, buflen, "FAIL\n");
454         }
455
456         return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
457 }
458 #endif /* CONFIG_FST_TEST */
459
460
461 /* fst list_sessions */
462 struct list_sessions_cb_ctx {
463         char *buf;
464         size_t buflen;
465         size_t reply_len;
466 };
467
468
469 static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
470                                  void *ctx)
471 {
472         struct list_sessions_cb_ctx *c = ctx;
473         int ret;
474
475         ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
476
477         c->buf += ret;
478         c->buflen -= ret;
479         c->reply_len += ret;
480 }
481
482
483 static int list_sessions(const char *group_id, char *buf, size_t buflen)
484 {
485         struct list_sessions_cb_ctx ctx;
486         struct fst_group *g;
487
488         g = get_fst_group_by_id(group_id);
489         if (!g) {
490                 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
491                            group_id);
492                 return os_snprintf(buf, buflen, "FAIL\n");
493         }
494
495         ctx.buf = buf;
496         ctx.buflen = buflen;
497         ctx.reply_len = 0;
498
499         fst_session_enum(g, list_session_enum_cb, &ctx);
500
501         ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
502
503         return ctx.reply_len;
504 }
505
506
507 /* fst iface_peers */
508 static int iface_peers(const char *group_id, char *buf, size_t buflen)
509 {
510         const char *ifname;
511         struct fst_group *g;
512         struct fst_iface *f;
513         struct fst_get_peer_ctx *ctx;
514         const u8 *addr;
515         unsigned found = 0;
516         int ret = 0;
517
518         g = get_fst_group_by_id(group_id);
519         if (!g) {
520                 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
521                            group_id);
522                 return os_snprintf(buf, buflen, "FAIL\n");
523         }
524
525         ifname = os_strchr(group_id, ' ');
526         if (!ifname)
527                 return os_snprintf(buf, buflen, "FAIL\n");
528         ifname++;
529
530         foreach_fst_group_iface(g, f) {
531                 const char *in = fst_iface_get_name(f);
532
533                 if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
534                         found = 1;
535                         break;
536                 }
537         }
538
539         if (!found)
540                 return os_snprintf(buf, buflen, "FAIL\n");
541
542         addr = fst_iface_get_peer_first(f, &ctx, FALSE);
543         for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
544                 int res;
545
546                 res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
547                                   MAC2STR(addr));
548                 if (os_snprintf_error(buflen - ret, res))
549                         break;
550                 ret += res;
551         }
552
553         return ret;
554 }
555
556
557 static int get_peer_mbies(const char *params, char *buf, size_t buflen)
558 {
559         char *endp;
560         char ifname[FST_MAX_INTERFACE_SIZE];
561         u8 peer_addr[ETH_ALEN];
562         struct fst_group *g;
563         struct fst_iface *iface = NULL;
564         const struct wpabuf *mbies;
565
566         if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
567             !*ifname)
568                 goto problem;
569
570         while (isspace(*endp))
571                 endp++;
572         if (fst_read_peer_addr(endp, peer_addr))
573                 goto problem;
574
575         foreach_fst_group(g) {
576                 iface = fst_group_get_iface_by_name(g, ifname);
577                 if (iface)
578                         break;
579         }
580         if (!iface)
581                 goto problem;
582
583         mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
584         if (!mbies)
585                 goto problem;
586
587         return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
588                                 wpabuf_len(mbies));
589
590 problem:
591         return os_snprintf(buf, buflen, "FAIL\n");
592 }
593
594
595 /* fst list_ifaces */
596 static int list_ifaces(const char *group_id, char *buf, size_t buflen)
597 {
598         struct fst_group *g;
599         struct fst_iface *f;
600         int ret = 0;
601
602         g = get_fst_group_by_id(group_id);
603         if (!g) {
604                 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
605                            group_id);
606                 return os_snprintf(buf, buflen, "FAIL\n");
607         }
608
609         foreach_fst_group_iface(g, f) {
610                 int res;
611                 const u8 *iface_addr = fst_iface_get_addr(f);
612
613                 res = os_snprintf(buf + ret, buflen - ret,
614                                   "%s|" MACSTR "|%u|%u\n",
615                                   fst_iface_get_name(f),
616                                   MAC2STR(iface_addr),
617                                   fst_iface_get_priority(f),
618                                   fst_iface_get_llt(f));
619                 if (os_snprintf_error(buflen - ret, res))
620                         break;
621                 ret += res;
622         }
623
624         return ret;
625 }
626
627
628 /* fst list_groups */
629 static int list_groups(const char *cmd, char *buf, size_t buflen)
630 {
631         struct fst_group *g;
632         int ret = 0;
633
634         foreach_fst_group(g) {
635                 int res;
636
637                 res = os_snprintf(buf + ret, buflen - ret, "%s\n",
638                                   fst_group_get_id(g));
639                 if (os_snprintf_error(buflen - ret, res))
640                         break;
641                 ret += res;
642         }
643
644         return ret;
645 }
646
647
648 static const char * band_freq(enum mb_band_id band)
649 {
650         static const char *band_names[] = {
651                 [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ",
652                 [MB_BAND_ID_WIFI_5GHZ] "5GHZ",
653                 [MB_BAND_ID_WIFI_60GHZ] "60GHZ",
654         };
655
656         return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
657 }
658
659
660 static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
661                       char *buf, size_t buflen)
662 {
663         const struct wpabuf *wpabuf;
664         enum hostapd_hw_mode hw_mode;
665         u8 channel;
666         int ret = 0;
667
668         fst_iface_get_channel_info(iface, &hw_mode, &channel);
669
670         ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
671                            num, band_freq(fst_hw_mode_to_band(hw_mode)));
672         ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
673                            num, fst_iface_get_name(iface));
674         wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
675         if (wpabuf) {
676                 ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
677                                    num);
678                 ret += wpa_snprintf_hex(buf + ret, buflen - ret,
679                                         wpabuf_head(wpabuf),
680                                         wpabuf_len(wpabuf));
681                 ret += os_snprintf(buf + ret, buflen - ret, "\n");
682         }
683         ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
684                            num, fst_iface_get_group_id(iface));
685         ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
686                            num, fst_iface_get_priority(iface));
687         ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
688                            num, fst_iface_get_llt(iface));
689
690         return ret;
691 }
692
693
694 static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
695                                                   Boolean attached)
696 {
697         union fst_event_extra extra;
698
699         os_memset(&extra, 0, sizeof(extra));
700         extra.iface_state.attached = attached;
701         os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
702                    sizeof(extra.iface_state.ifname));
703         os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
704                    sizeof(extra.iface_state.group_id));
705
706         fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
707                               EVENT_FST_IFACE_STATE_CHANGED, &extra);
708 }
709
710
711 static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
712 {
713         fst_ctrl_iface_on_iface_state_changed(i, TRUE);
714         return 0;
715 }
716
717
718 static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
719 {
720         fst_ctrl_iface_on_iface_state_changed(i, FALSE);
721 }
722
723
724 static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
725                                     struct fst_iface *i, struct fst_session *s,
726                                     const union fst_event_extra *extra)
727 {
728         u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
729
730         fst_ctrl_iface_notify(i, session_id, event_type, extra);
731 }
732
733
734 static const struct fst_ctrl ctrl_cli = {
735         .on_iface_added = fst_ctrl_iface_on_iface_added,
736         .on_iface_removed =  fst_ctrl_iface_on_iface_removed,
737         .on_event = fst_ctrl_iface_on_event,
738 };
739
740 const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
741
742
743 int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
744 {
745         struct fst_group *g;
746         struct fst_iface *f;
747         unsigned num = 0;
748         int ret = 0;
749
750         foreach_fst_group(g) {
751                 foreach_fst_group_iface(g, f) {
752                         if (fst_iface_is_connected(f, addr)) {
753                                 ret += print_band(num++, f, addr,
754                                                   buf + ret, buflen - ret);
755                         }
756                 }
757         }
758
759         return ret;
760 }
761
762
763 /* fst ctrl processor */
764 int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
765 {
766         static const struct fst_command {
767                 const char *name;
768                 unsigned has_param;
769                 int (*process)(const char *group_id, char *buf, size_t buflen);
770         } commands[] = {
771                 { FST_CMD_LIST_GROUPS, 0, list_groups},
772                 { FST_CMD_LIST_IFACES, 1, list_ifaces},
773                 { FST_CMD_IFACE_PEERS, 1, iface_peers},
774                 { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
775                 { FST_CMD_LIST_SESSIONS, 1, list_sessions},
776                 { FST_CMD_SESSION_ADD, 1, session_add},
777                 { FST_CMD_SESSION_REMOVE, 1, session_remove},
778                 { FST_CMD_SESSION_GET, 1, session_get},
779                 { FST_CMD_SESSION_SET, 1, session_set},
780                 { FST_CMD_SESSION_INITIATE, 1, session_initiate},
781                 { FST_CMD_SESSION_RESPOND, 1, session_respond},
782                 { FST_CMD_SESSION_TRANSFER, 1, session_transfer},
783                 { FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
784 #ifdef CONFIG_FST_TEST
785                 { FST_CMD_TEST_REQUEST, 1, test_request },
786 #endif /* CONFIG_FST_TEST */
787                 { NULL, 0, NULL }
788         };
789         const struct fst_command *c;
790         const char *p;
791         const char *temp;
792         Boolean non_spaces_found;
793
794         for (c = commands; c->name; c++) {
795                 if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
796                         continue;
797                 p = cmd + os_strlen(c->name);
798                 if (c->has_param) {
799                         if (!isspace(p[0]))
800                                 return os_snprintf(reply, reply_size, "FAIL\n");
801                         p++;
802                         temp = p;
803                         non_spaces_found = FALSE;
804                         while (*temp) {
805                                 if (!isspace(*temp)) {
806                                         non_spaces_found = TRUE;
807                                         break;
808                                 }
809                                 temp++;
810                         }
811                         if (!non_spaces_found)
812                                 return os_snprintf(reply, reply_size, "FAIL\n");
813                 }
814                 return c->process(p, reply, reply_size);
815         }
816
817         return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
818 }
819
820
821 int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
822 {
823         int ret = -1;
824         const char *curp;
825
826         *valid = FALSE;
827         *endp = (char *) params;
828         curp = params;
829         if (*curp) {
830                 ret = (int) strtol(curp, endp, 0);
831                 if (!**endp || isspace(**endp))
832                         *valid = TRUE;
833         }
834
835         return ret;
836 }
837
838
839 int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
840                              char **endp)
841 {
842         size_t max_chars_to_copy;
843         char *cur_dest;
844
845         *endp = (char *) params;
846         while (isspace(**endp))
847                 (*endp)++;
848         if (!**endp || buflen <= 1)
849                 return -EINVAL;
850
851         max_chars_to_copy = buflen - 1;
852         /* We need 1 byte for the terminating zero */
853         cur_dest = buf;
854         while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
855                 *cur_dest = **endp;
856                 (*endp)++;
857                 cur_dest++;
858                 max_chars_to_copy--;
859         }
860         *cur_dest = 0;
861
862         return 0;
863 }
864
865
866 int fst_read_peer_addr(const char *mac, u8 *peer_addr)
867 {
868         if (hwaddr_aton(mac, peer_addr)) {
869                 fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
870                            mac);
871                 return -1;
872         }
873
874         if (is_zero_ether_addr(peer_addr) ||
875             is_multicast_ether_addr(peer_addr)) {
876                 fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
877                            mac);
878                 return -1;
879         }
880
881         return 0;
882 }
883
884
885 int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
886                              struct fst_iface_cfg *cfg)
887 {
888         char *pos;
889         char *endp;
890         Boolean is_valid;
891         int val;
892
893         if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
894             fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
895                                      &endp))
896                 return -EINVAL;
897
898         cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
899         cfg->priority = 0;
900         pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
901         if (pos) {
902                 pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
903                 if (*pos == '=') {
904                         val = fst_read_next_int_param(pos + 1, &is_valid,
905                                                       &endp);
906                         if (is_valid)
907                                 cfg->llt = val;
908                 }
909         }
910         pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
911         if (pos) {
912                 pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
913                 if (*pos == '=') {
914                         val = fst_read_next_int_param(pos + 1, &is_valid,
915                                                       &endp);
916                         if (is_valid)
917                                 cfg->priority = (u8) val;
918                 }
919         }
920
921         return 0;
922 }
923
924
925 int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
926 {
927         char *endp;
928
929         return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
930 }
931
932
933 int fst_iface_detach(const char *ifname)
934 {
935         struct fst_group *g;
936
937         foreach_fst_group(g) {
938                 struct fst_iface *f;
939
940                 f = fst_group_get_iface_by_name(g, ifname);
941                 if (f) {
942                         fst_detach(f);
943                         return 0;
944                 }
945         }
946
947         return -EINVAL;
948 }