fst_ctrl_iface.c 23 KB

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