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