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 static Boolean format_session_state_extra(const union fst_event_extra *extra,
36 char *buffer, size_t 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;
43 ss = &extra->session_state;
44 if (ss->new_state != FST_SESSION_STATE_INITIAL)
47 switch (ss->extra.to_initial.reason) {
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);
55 switch (ss->extra.to_initial.initiator) {
56 case FST_INITIATOR_LOCAL:
57 initiator = FST_CS_PVAL_INITIATOR_LOCAL;
59 case FST_INITIATOR_REMOTE:
60 initiator = FST_CS_PVAL_INITIATOR_REMOTE;
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);
77 return !os_snprintf_error(size, len);
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)
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;
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.
97 foreach_fst_group(g) {
98 f = fst_group_first_iface(g);
106 WPA_ASSERT(f->iface_obj.ctx);
108 switch (event_type) {
109 case EVENT_FST_IFACE_STATE_CHANGED:
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);
121 case EVENT_PEER_STATE_CHANGED:
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));
133 case EVENT_FST_SESSION_STATE_CHANGED:
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");
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",
150 fst_session_event_type_name(event_type),
151 fst_session_state_name(ss->old_state),
152 fst_session_state_name(ss->new_state),
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",
162 fst_session_event_type_name(event_type));
168 /* command processors */
170 /* fst session_get */
171 static int session_get(const char *session_id, char *buf, size_t buflen)
173 struct fst_session *s;
174 struct fst_iface *new_iface, *old_iface;
175 const u8 *old_peer_addr, *new_peer_addr;
178 id = strtoul(session_id, NULL, 0);
180 s = fst_session_get_by_id(id);
182 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
183 return os_snprintf(buf, buflen, "FAIL\n");
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);
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) :
202 old_iface ? fst_iface_get_name(old_iface) :
204 fst_session_get_llt(s),
205 fst_session_state_name(fst_session_get_state(s)));
209 /* fst session_set */
210 static int session_set(const char *session_id, char *buf, size_t buflen)
212 struct fst_session *s;
217 id = strtoul(session_id, &p, 0);
219 s = fst_session_get_by_id(id);
221 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
222 return os_snprintf(buf, buflen, "FAIL\n");
225 if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
226 return os_snprintf(buf, buflen, "FAIL\n");
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);
240 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
241 return os_snprintf(buf, buflen, "FAIL\n");
244 return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
248 /* fst session_add/remove */
249 static int session_add(const char *group_id, char *buf, size_t buflen)
252 struct fst_session *s;
254 g = get_fst_group_by_id(group_id);
256 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
258 return os_snprintf(buf, buflen, "FAIL\n");
261 s = fst_session_create(g);
263 fst_printf(MSG_ERROR,
264 "CTRL: Cannot create session for group '%s'",
266 return os_snprintf(buf, buflen, "FAIL\n");
269 return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
273 static int session_remove(const char *session_id, char *buf, size_t buflen)
275 struct fst_session *s;
279 id = strtoul(session_id, NULL, 0);
281 s = fst_session_get_by_id(id);
283 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
284 return os_snprintf(buf, buflen, "FAIL\n");
287 g = fst_session_get_group(s);
288 fst_session_reset(s);
289 fst_session_delete(s);
290 fst_group_delete_if_empty(g);
292 return os_snprintf(buf, buflen, "OK\n");
296 /* fst session_initiate */
297 static int session_initiate(const char *session_id, char *buf, size_t buflen)
299 struct fst_session *s;
302 id = strtoul(session_id, NULL, 0);
304 s = fst_session_get_by_id(id);
306 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
307 return os_snprintf(buf, buflen, "FAIL\n");
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");
315 return os_snprintf(buf, buflen, "OK\n");
319 /* fst session_respond */
320 static int session_respond(const char *session_id, char *buf, size_t buflen)
322 struct fst_session *s;
327 id = strtoul(session_id, &p, 0);
329 s = fst_session_get_by_id(id);
331 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
332 return os_snprintf(buf, buflen, "FAIL\n");
336 return os_snprintf(buf, buflen, "FAIL\n");
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;
344 fst_printf(MSG_WARNING,
345 "CTRL: session %u: unknown response status: %s",
347 return os_snprintf(buf, buflen, "FAIL\n");
350 if (fst_session_respond(s, status_code)) {
351 fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
353 return os_snprintf(buf, buflen, "FAIL\n");
356 fst_printf(MSG_INFO, "CTRL: session %u responded", id);
358 return os_snprintf(buf, buflen, "OK\n");
362 /* fst session_transfer */
363 static int session_transfer(const char *session_id, char *buf, size_t buflen)
365 struct fst_session *s;
368 id = strtoul(session_id, NULL, 0);
370 s = fst_session_get_by_id(id);
372 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
373 return os_snprintf(buf, buflen, "FAIL\n");
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");
382 return os_snprintf(buf, buflen, "OK\n");
386 /* fst session_teardown */
387 static int session_teardown(const char *session_id, char *buf, size_t buflen)
389 struct fst_session *s;
392 id = strtoul(session_id, NULL, 0);
394 s = fst_session_get_by_id(id);
396 fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
397 return os_snprintf(buf, buflen, "FAIL\n");
400 if (fst_session_tear_down_setup(s)) {
401 fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
403 return os_snprintf(buf, buflen, "FAIL\n");
406 return os_snprintf(buf, buflen, "OK\n");
410 #ifdef CONFIG_FST_TEST
411 /* fst test_request */
412 static int test_request(const char *request, char *buf, size_t buflen)
414 const char *p = request;
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))) {
452 fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
453 return os_snprintf(buf, buflen, "FAIL\n");
456 return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
458 #endif /* CONFIG_FST_TEST */
461 /* fst list_sessions */
462 struct list_sessions_cb_ctx {
469 static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
472 struct list_sessions_cb_ctx *c = ctx;
475 ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
483 static int list_sessions(const char *group_id, char *buf, size_t buflen)
485 struct list_sessions_cb_ctx ctx;
488 g = get_fst_group_by_id(group_id);
490 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
492 return os_snprintf(buf, buflen, "FAIL\n");
499 fst_session_enum(g, list_session_enum_cb, &ctx);
501 ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
503 return ctx.reply_len;
507 /* fst iface_peers */
508 static int iface_peers(const char *group_id, char *buf, size_t buflen)
513 struct fst_get_peer_ctx *ctx;
518 g = get_fst_group_by_id(group_id);
520 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
522 return os_snprintf(buf, buflen, "FAIL\n");
525 ifname = os_strchr(group_id, ' ');
527 return os_snprintf(buf, buflen, "FAIL\n");
530 foreach_fst_group_iface(g, f) {
531 const char *in = fst_iface_get_name(f);
533 if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
540 return os_snprintf(buf, buflen, "FAIL\n");
542 addr = fst_iface_get_peer_first(f, &ctx, FALSE);
543 for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
546 res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
548 if (os_snprintf_error(buflen - ret, res))
557 static int get_peer_mbies(const char *params, char *buf, size_t buflen)
560 char ifname[FST_MAX_INTERFACE_SIZE];
561 u8 peer_addr[ETH_ALEN];
563 struct fst_iface *iface = NULL;
564 const struct wpabuf *mbies;
566 if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
570 while (isspace(*endp))
572 if (fst_read_peer_addr(endp, peer_addr))
575 foreach_fst_group(g) {
576 iface = fst_group_get_iface_by_name(g, ifname);
583 mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
587 return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
591 return os_snprintf(buf, buflen, "FAIL\n");
595 /* fst list_ifaces */
596 static int list_ifaces(const char *group_id, char *buf, size_t buflen)
602 g = get_fst_group_by_id(group_id);
604 fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
606 return os_snprintf(buf, buflen, "FAIL\n");
609 foreach_fst_group_iface(g, f) {
611 const u8 *iface_addr = fst_iface_get_addr(f);
613 res = os_snprintf(buf + ret, buflen - ret,
614 "%s|" MACSTR "|%u|%u\n",
615 fst_iface_get_name(f),
617 fst_iface_get_priority(f),
618 fst_iface_get_llt(f));
619 if (os_snprintf_error(buflen - ret, res))
628 /* fst list_groups */
629 static int list_groups(const char *cmd, char *buf, size_t buflen)
634 foreach_fst_group(g) {
637 res = os_snprintf(buf + ret, buflen - ret, "%s\n",
638 fst_group_get_id(g));
639 if (os_snprintf_error(buflen - ret, res))
648 static const char * band_freq(enum mb_band_id band)
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",
656 return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
660 static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
661 char *buf, size_t buflen)
663 const struct wpabuf *wpabuf;
664 enum hostapd_hw_mode hw_mode;
668 fst_iface_get_channel_info(iface, &hw_mode, &channel);
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);
676 ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
678 ret += wpa_snprintf_hex(buf + ret, buflen - ret,
681 ret += os_snprintf(buf + ret, buflen - ret, "\n");
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));
694 static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
697 union fst_event_extra extra;
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));
706 fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
707 EVENT_FST_IFACE_STATE_CHANGED, &extra);
711 static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
713 fst_ctrl_iface_on_iface_state_changed(i, TRUE);
718 static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
720 fst_ctrl_iface_on_iface_state_changed(i, FALSE);
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)
728 u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
730 fst_ctrl_iface_notify(i, session_id, event_type, extra);
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,
740 const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
743 int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
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);
763 /* fst ctrl processor */
764 int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
766 static const struct fst_command {
769 int (*process)(const char *group_id, char *buf, size_t buflen);
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 */
789 const struct fst_command *c;
792 Boolean non_spaces_found;
794 for (c = commands; c->name; c++) {
795 if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
797 p = cmd + os_strlen(c->name);
800 return os_snprintf(reply, reply_size, "FAIL\n");
803 non_spaces_found = FALSE;
805 if (!isspace(*temp)) {
806 non_spaces_found = TRUE;
811 if (!non_spaces_found)
812 return os_snprintf(reply, reply_size, "FAIL\n");
814 return c->process(p, reply, reply_size);
817 return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
821 int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
827 *endp = (char *) params;
830 ret = (int) strtol(curp, endp, 0);
831 if (!**endp || isspace(**endp))
839 int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
842 size_t max_chars_to_copy;
845 *endp = (char *) params;
846 while (isspace(**endp))
848 if (!**endp || buflen <= 1)
851 max_chars_to_copy = buflen - 1;
852 /* We need 1 byte for the terminating zero */
854 while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
866 int fst_read_peer_addr(const char *mac, u8 *peer_addr)
868 if (hwaddr_aton(mac, peer_addr)) {
869 fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
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",
885 int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
886 struct fst_iface_cfg *cfg)
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),
898 cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
900 pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
902 pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
904 val = fst_read_next_int_param(pos + 1, &is_valid,
910 pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
912 pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
914 val = fst_read_next_int_param(pos + 1, &is_valid,
917 cfg->priority = (u8) val;
925 int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
929 return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
933 int fst_iface_detach(const char *ifname)
937 foreach_fst_group(g) {
940 f = fst_group_get_iface_by_name(g, ifname);