LCOV - code coverage report
Current view: top level - src/fst - fst_ctrl_iface.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 393 403 97.5 %
Date: 2015-09-27 Functions: 32 32 100.0 %

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

Generated by: LCOV version 1.10