2 * FST module - Control Interface implementation
3 * Copyright (c) 2014, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
10 #include "utils/common.h"
11 #include "common/defs.h"
14 #include "fst/fst_internal.h"
15 #include "fst_ctrl_defs.h"
16 #include "fst_ctrl_iface.h"
19 static struct fst_group * get_fst_group_by_id(const char *id)
23 foreach_fst_group(g) {
24 const char *group_id = fst_group_get_id(g);
26 if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
35 Boolean format_session_state_extra(const union fst_event_extra *extra,
36 char *buffer, int size)
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;
46 ss = &extra->session_state;
47 if (ss->new_state != FST_SESSION_STATE_INITIAL)
50 switch (ss->extra.to_initial.reason) {
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);
58 switch (ss->extra.to_initial.initiator) {
59 case FST_INITIATOR_LOCAL:
60 initiator = FST_CS_PVAL_INITIATOR_LOCAL;
62 case FST_INITIATOR_REMOTE:
63 initiator = FST_CS_PVAL_INITIATOR_REMOTE;
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);
80 return !os_snprintf_error(size, len);
84 static void fst_ctrl_iface_notify(u32 session_id,
85 enum fst_event_type event_type,
86 const union fst_event_extra *extra)
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;
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.
100 g = fst_first_group();
104 f = fst_group_first_iface(g);
108 WPA_ASSERT(f->iface_obj.ctx);
110 switch (event_type) {
111 case EVENT_FST_IFACE_STATE_CHANGED:
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);
123 case EVENT_PEER_STATE_CHANGED:
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));
135 case EVENT_FST_SESSION_STATE_CHANGED:
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");
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",
152 fst_session_event_type_name(event_type),
153 fst_session_state_name(ss->old_state),
154 fst_session_state_name(ss->new_state),
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",
164 fst_session_event_type_name(event_type));
170 /* command processors */
172 /* fst session_get */
173 static int session_get(const char *session_id, char *buf, size_t buflen)
175 struct fst_session *s;
176 struct fst_iface *new_iface, *old_iface;
177 const u8 *old_peer_addr, *new_peer_addr;
180 id = strtoul(session_id, NULL, 0);
182 s = fst_session_get_by_id(id);
184 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
185 return os_snprintf(buf, buflen, "FAIL\n");
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);
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) :
204 old_iface ? fst_iface_get_name(old_iface) :
206 fst_session_get_llt(s),
207 fst_session_state_name(fst_session_get_state(s)));
211 /* fst session_set */
212 static int session_set(const char *session_id, char *buf, size_t buflen)
214 struct fst_session *s;
219 id = strtoul(session_id, &p, 0);
221 s = fst_session_get_by_id(id);
223 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
224 return os_snprintf(buf, buflen, "FAIL\n");
227 if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
228 return os_snprintf(buf, buflen, "FAIL\n");
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);
242 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
243 return os_snprintf(buf, buflen, "FAIL\n");
246 return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
250 /* fst session_add/remove */
251 static int session_add(const char *group_id, char *buf, size_t buflen)
254 struct fst_session *s;
256 g = get_fst_group_by_id(group_id);
258 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
260 return os_snprintf(buf, buflen, "FAIL\n");
263 s = fst_session_create(g);
265 fst_printf(MSG_ERROR,
266 "CTRL: Cannot create session for group '%s'",
268 return os_snprintf(buf, buflen, "FAIL\n");
271 return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
275 static int session_remove(const char *session_id, char *buf, size_t buflen)
277 struct fst_session *s;
281 id = strtoul(session_id, NULL, 0);
283 s = fst_session_get_by_id(id);
285 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
286 return os_snprintf(buf, buflen, "FAIL\n");
289 g = fst_session_get_group(s);
290 fst_session_reset(s);
291 fst_session_delete(s);
292 fst_group_delete_if_empty(g);
294 return os_snprintf(buf, buflen, "OK\n");
298 /* fst session_initiate */
299 static int session_initiate(const char *session_id, char *buf, size_t buflen)
301 struct fst_session *s;
304 id = strtoul(session_id, NULL, 0);
306 s = fst_session_get_by_id(id);
308 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
309 return os_snprintf(buf, buflen, "FAIL\n");
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");
317 return os_snprintf(buf, buflen, "OK\n");
321 /* fst session_respond */
322 static int session_respond(const char *session_id, char *buf, size_t buflen)
324 struct fst_session *s;
329 id = strtoul(session_id, &p, 0);
331 s = fst_session_get_by_id(id);
333 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
334 return os_snprintf(buf, buflen, "FAIL\n");
338 return os_snprintf(buf, buflen, "FAIL\n");
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;
346 fst_printf(MSG_WARNING,
347 "CTRL: session %u: unknown response status: %s",
349 return os_snprintf(buf, buflen, "FAIL\n");
352 if (fst_session_respond(s, status_code)) {
353 fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
355 return os_snprintf(buf, buflen, "FAIL\n");
358 fst_printf(MSG_INFO, "CTRL: session %u responded", id);
360 return os_snprintf(buf, buflen, "OK\n");
364 /* fst session_transfer */
365 static int session_transfer(const char *session_id, char *buf, size_t buflen)
367 struct fst_session *s;
370 id = strtoul(session_id, NULL, 0);
372 s = fst_session_get_by_id(id);
374 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
375 return os_snprintf(buf, buflen, "FAIL\n");
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");
384 return os_snprintf(buf, buflen, "OK\n");
388 /* fst session_teardown */
389 static int session_teardown(const char *session_id, char *buf, size_t buflen)
391 struct fst_session *s;
394 id = strtoul(session_id, NULL, 0);
396 s = fst_session_get_by_id(id);
398 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
399 return os_snprintf(buf, buflen, "FAIL\n");
402 if (fst_session_tear_down_setup(s)) {
403 fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
405 return os_snprintf(buf, buflen, "FAIL\n");
408 return os_snprintf(buf, buflen, "OK\n");
412 #ifdef CONFIG_FST_TEST
413 /* fst test_request */
414 static int test_request(const char *request, char *buf, size_t buflen)
416 const char *p = request;
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))) {
454 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
455 return os_snprintf(buf, buflen, "FAIL\n");
458 return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
460 #endif /* CONFIG_FST_TEST */
463 /* fst list_sessions */
464 struct list_sessions_cb_ctx {
471 static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
474 struct list_sessions_cb_ctx *c = ctx;
477 ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
485 static int list_sessions(const char *group_id, char *buf, size_t buflen)
487 struct list_sessions_cb_ctx ctx;
490 g = get_fst_group_by_id(group_id);
492 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
494 return os_snprintf(buf, buflen, "FAIL\n");
501 fst_session_enum(g, list_session_enum_cb, &ctx);
503 ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
505 return ctx.reply_len;
509 /* fst iface_peers */
510 static int iface_peers(const char *group_id, char *buf, size_t buflen)
515 struct fst_get_peer_ctx *ctx;
520 g = get_fst_group_by_id(group_id);
522 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
524 return os_snprintf(buf, buflen, "FAIL\n");
527 ifname = os_strchr(group_id, ' ');
529 return os_snprintf(buf, buflen, "FAIL\n");
532 foreach_fst_group_iface(g, f) {
533 const char *in = fst_iface_get_name(f);
535 if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
542 return os_snprintf(buf, buflen, "FAIL\n");
544 addr = fst_iface_get_peer_first(f, &ctx, FALSE);
545 for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
548 res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
550 if (os_snprintf_error(buflen - ret, res))
559 static int get_peer_mbies(const char *params, char *buf, size_t buflen)
562 char ifname[FST_MAX_INTERFACE_SIZE];
563 u8 peer_addr[ETH_ALEN];
565 struct fst_iface *iface = NULL;
566 struct wpabuf *mbies;
569 if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
573 while (isspace(*endp))
575 if (fst_read_peer_addr(endp, peer_addr))
578 foreach_fst_group(g) {
579 iface = fst_group_get_iface_by_name(g, ifname);
586 mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
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");
597 return os_snprintf(buf, buflen, "FAIL\n");
601 /* fst list_ifaces */
602 static int list_ifaces(const char *group_id, char *buf, size_t buflen)
608 g = get_fst_group_by_id(group_id);
610 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
612 return os_snprintf(buf, buflen, "FAIL\n");
615 foreach_fst_group_iface(g, f) {
617 const u8 *iface_addr = fst_iface_get_addr(f);
619 res = os_snprintf(buf + ret, buflen - ret,
620 "%s|" MACSTR "|%u|%u\n",
621 fst_iface_get_name(f),
623 fst_iface_get_priority(f),
624 fst_iface_get_llt(f));
625 if (os_snprintf_error(buflen - ret, res))
634 /* fst list_groups */
635 static int list_groups(const char *cmd, char *buf, size_t buflen)
640 foreach_fst_group(g) {
643 res = os_snprintf(buf + ret, buflen - ret, "%s\n",
644 fst_group_get_id(g));
645 if (os_snprintf_error(buflen - ret, res))
654 int print_mb_ies(struct wpabuf *mbies, char *buf, size_t buflen)
656 const u8 *p = wpabuf_head(mbies);
657 size_t s = wpabuf_len(mbies);
660 while ((size_t) ret < buflen && s--) {
663 res = os_snprintf(buf + ret, buflen - ret, "%02x", *p++);
664 if (os_snprintf_error(buflen - ret, res))
673 static const char * band_freq(enum mb_band_id band)
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",
681 return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
685 static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
686 char *buf, size_t buflen)
688 struct wpabuf *wpabuf;
689 enum hostapd_hw_mode hw_mode;
693 fst_iface_get_channel_info(iface, &hw_mode, &channel);
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);
701 ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
703 ret += print_mb_ies(wpabuf, buf + ret, buflen - ret);
704 ret += os_snprintf(buf + ret, buflen - ret, "\n");
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));
717 static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
720 union fst_event_extra extra;
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));
729 fst_ctrl_iface_notify(FST_INVALID_SESSION_ID,
730 EVENT_FST_IFACE_STATE_CHANGED, &extra);
734 static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
736 fst_ctrl_iface_on_iface_state_changed(i, TRUE);
741 static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
743 fst_ctrl_iface_on_iface_state_changed(i, FALSE);
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)
751 u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
753 fst_ctrl_iface_notify(session_id, event_type, extra);
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,
763 const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
766 int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
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);
786 /* fst ctrl processor */
787 int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
789 static const struct fst_command {
792 int (*process)(const char *group_id, char *buf, size_t buflen);
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 */
812 const struct fst_command *c;
815 Boolean non_spaces_found;
817 for (c = commands; c->name; c++) {
818 if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
820 p = cmd + os_strlen(c->name);
823 return os_snprintf(reply, reply_size, "FAIL\n");
826 non_spaces_found = FALSE;
828 if (!isspace(*temp)) {
829 non_spaces_found = TRUE;
834 if (!non_spaces_found)
835 return os_snprintf(reply, reply_size, "FAIL\n");
837 return c->process(p, reply, reply_size);
840 return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
844 int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
850 *endp = (char *) params;
853 ret = (int) strtol(curp, endp, 0);
854 if (!**endp || isspace(**endp))
862 int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
865 size_t max_chars_to_copy;
871 *endp = (char *) params;
872 while (isspace(**endp))
877 max_chars_to_copy = buflen - 1;
878 /* We need 1 byte for the terminating zero */
880 while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
892 int fst_read_peer_addr(const char *mac, u8 *peer_addr)
894 if (hwaddr_aton(mac, peer_addr)) {
895 fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
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",
911 int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
912 struct fst_iface_cfg *cfg)
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),
924 cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
926 pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
928 pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
930 val = fst_read_next_int_param(pos + 1, &is_valid,
936 pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
938 pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
940 val = fst_read_next_int_param(pos + 1, &is_valid,
943 cfg->priority = (u8) val;
951 int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
955 if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp))
962 /* fst iface_detach */
963 int fst_iface_detach(const char *ifname)
968 foreach_fst_group(g) {
969 f = fst_group_get_iface_by_name(g, ifname);