| /* |
| * wl anqpo command module |
| * |
| * Broadcom Proprietary and Confidential. Copyright (C) 2017, |
| * All Rights Reserved. |
| * |
| * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; |
| * the contents of this file may not be disclosed to third parties, copied |
| * or duplicated in any form, in whole or in part, without the prior |
| * written permission of Broadcom. |
| * |
| * |
| * <<Broadcom-WL-IPTag/Proprietary:>> |
| * |
| * $Id: wluc_anqpo.c 458728 2014-02-27 18:15:25Z $ |
| */ |
| |
| #ifdef WIN32 |
| #include <windows.h> |
| #endif |
| |
| #include <wlioctl.h> |
| |
| #if defined(DONGLEBUILD) |
| #include <typedefs.h> |
| #include <osl.h> |
| #endif |
| |
| /* Because IL_BIGENDIAN was removed there are few warnings that need |
| * to be fixed. Windows was not compiled earlier with IL_BIGENDIAN. |
| * Hence these warnings were not seen earlier. |
| * For now ignore the following warnings |
| */ |
| #ifdef WIN32 |
| #pragma warning(push) |
| #pragma warning(disable : 4244) |
| #pragma warning(disable : 4761) |
| #endif |
| |
| #include <bcmutils.h> |
| #include <bcmendian.h> |
| #include "wlu_common.h" |
| #include "wlu.h" |
| |
| #ifdef LINUX |
| #include <unistd.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/ioctl.h> |
| #include <netinet/in.h> |
| #include <net/if.h> |
| #include <linux/if_packet.h> |
| #endif /* LINUX */ |
| |
| static cmd_func_t wl_anqpo_set; |
| static cmd_func_t wl_anqpo_stop_query; |
| static cmd_func_t wl_anqpo_start_query; |
| static cmd_func_t wl_anqpo_ignore_ssid_list; |
| static cmd_func_t wl_anqpo_ignore_bssid_list; |
| #if defined(linux) |
| static cmd_func_t wl_anqpo_results; |
| #endif /* linux */ |
| |
| static cmd_t wl_anqpo_cmds[] = { |
| { "anqpo_set", wl_anqpo_set, -1, -1, |
| "set ANQP offload parameters\n" |
| "\tusage: anqpo_set [max_retransmit <number>]\n" |
| "\t\t[response_timeout <msec>] [max_comeback_delay <msec>]\n" |
| "\t\t[max_retries <number>] [query \"encoded ANQP query\"]" |
| }, |
| { "anqpo_stop_query", wl_anqpo_stop_query, -1, WLC_SET_VAR, |
| "stop ANQP query\n" |
| "\tusage: anqpo_stop_query" |
| }, |
| { "anqpo_start_query", wl_anqpo_start_query, -1, WLC_SET_VAR, |
| "start ANQP query to peer(s)\n" |
| "\tusage: anqpo_start_query <channel> <xx:xx:xx:xx:xx:xx>\n" |
| "\t\t[<channel> <xx:xx:xx:xx:xx:xx>]>" |
| }, |
| { "anqpo_auto_hotspot", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "automatic ANQP query to maximum number of hotspot APs, default 0 (disabled)\n" |
| "\tusage: anqpo_auto_hotspot [max]" |
| }, |
| { "anqpo_ignore_mode", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "ignore duplicate SSIDs or BSSIDs, default 0 (SSID)\n" |
| "\tusage: anqpo_ignore_mode [0 (SSID) | 1 (BSSID)]" |
| }, |
| { "anqpo_ignore_ssid_list", wl_anqpo_ignore_ssid_list, WLC_GET_VAR, WLC_SET_VAR, |
| "get, clear, set, or append to ANQP offload ignore SSID list\n" |
| "\tusage: wl anqpo_ignore_ssid_list [clear |\n" |
| "\t\tset <ssid1> [ssid2] |\n" |
| "\t\tappend <ssid3> [ssid4]>" |
| }, |
| { "anqpo_ignore_bssid_list", wl_anqpo_ignore_bssid_list, WLC_GET_VAR, WLC_SET_VAR, |
| "get, clear, set, or append to ANQP offload ignore BSSID list\n" |
| "\tusage: wl anqpo_ignore_bssid_list [clear |\n" |
| "\t\tset <xx:xx:xx:xx:xx:xx> [xx:xx:xx:xx:xx:xx] |\n" |
| "\t\tappend <xx:xx:xx:xx:xx:xx> [xx:xx:xx:xx:xx:xx]]>" |
| }, |
| #if defined(linux) |
| { "anqpo_results", wl_anqpo_results, -1, WLC_SET_VAR, |
| "Listens and displays ANQP results." |
| }, |
| #endif /* linux */ |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_anqpo_module_init(void) |
| { |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register anqpo commands */ |
| wl_module_cmds_register(wl_anqpo_cmds); |
| } |
| |
| #define ANQPO_EVENTS_BUFFER_SIZE 2048 |
| |
| static int |
| wl_anqpo_set(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err = BCME_USAGE_ERROR, malloc_size; |
| char *buffer; |
| wl_anqpo_set_t *set; |
| int length; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| malloc_size = OFFSETOF(wl_anqpo_set_t, query_data) + |
| (ANQPO_MAX_QUERY_SIZE * sizeof(uint8)); |
| buffer = malloc(malloc_size); |
| if (buffer == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| memset(buffer, 0, malloc_size); |
| set = (wl_anqpo_set_t *)buffer; |
| /* parameters not configured by default */ |
| set->max_retransmit = -1; |
| set->response_timeout = -1; |
| set->max_comeback_delay = -1; |
| set->max_retries = -1; |
| |
| while (*++argv) { |
| if (!stricmp(*argv, "max_retransmit")) { |
| if (*++argv) |
| set->max_retransmit = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing max retransmit\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| } |
| else if (!stricmp(*argv, "response_timeout")) { |
| if (*++argv) |
| set->response_timeout = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing response timeout\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| } |
| else if (!stricmp(*argv, "max_comeback_delay")) { |
| if (*++argv) |
| set->max_comeback_delay = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing max comeback delay\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| } |
| else if (!stricmp(*argv, "max_retries")) { |
| if (*++argv) |
| set->max_retries = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing retries\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| } |
| else if (!stricmp(*argv, "query")) { |
| if (*++argv) { |
| if ((set->query_len = hexstr2hex(*argv)) == 0) { |
| fprintf(stderr, "Invalid ANQP query\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (set->query_len > ANQPO_MAX_QUERY_SIZE) { |
| fprintf(stderr, "ANQP query size %d exceeds %d\n", |
| set->query_len, ANQPO_MAX_QUERY_SIZE); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| memcpy(set->query_data, *argv, set->query_len); |
| } |
| else { |
| fprintf(stderr, "Missing ANQP query\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| } |
| else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| |
| } |
| |
| length = OFFSETOF(wl_anqpo_set_t, query_data) + set->query_len; |
| set->max_retransmit = htod16(set->max_retransmit); |
| set->response_timeout = htod16(set->response_timeout); |
| set->max_comeback_delay = htod16(set->max_comeback_delay); |
| set->max_retries = htod16(set->max_retries); |
| set->query_len = htod16(set->query_len); |
| |
| err = wlu_iovar_set(wl, cmd->name, set, length); |
| |
| done: |
| free(buffer); |
| return err; |
| } |
| |
| static int |
| wl_anqpo_stop_query(void *wl, cmd_t *cmd, char **argv) |
| { |
| UNUSED_PARAMETER(cmd); |
| UNUSED_PARAMETER(argv); |
| return wlu_iovar_set(wl, cmd->name, 0, 0); |
| } |
| |
| static int |
| wl_anqpo_start_query(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err = BCME_USAGE_ERROR, malloc_size; |
| char *buffer; |
| wl_anqpo_peer_list_t *list; |
| wl_anqpo_peer_t *peer; |
| int c; |
| int length; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| malloc_size = OFFSETOF(wl_anqpo_peer_list_t, peer) + |
| (ANQPO_MAX_PEER_LIST * sizeof(wl_anqpo_peer_t)); |
| buffer = malloc(malloc_size); |
| if (buffer == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| memset(buffer, 0, malloc_size); |
| list = (wl_anqpo_peer_list_t *)buffer; |
| peer = &list->peer[0]; |
| |
| c = 1; |
| while (argv[c] && argv[c + 1]) { |
| if ((peer->channel = htod16(atoi(argv[c]))) == 0) { |
| fprintf(stderr, "Invalid channel\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (!wl_ether_atoe(argv[c + 1], &peer->addr)) { |
| fprintf(stderr, "Invalid address\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| list->count++; |
| c += 2; |
| peer++; |
| } |
| |
| length = OFFSETOF(wl_anqpo_peer_list_t, peer) + |
| (list->count * sizeof(wl_anqpo_peer_t)); |
| list->count = htod16(list->count); |
| |
| err = wlu_iovar_set(wl, cmd->name, list, length); |
| |
| done: |
| free(buffer); |
| return err; |
| } |
| |
| static int |
| wl_anqpo_ignore_ssid_list(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err = BCME_USAGE_ERROR, malloc_size; |
| char *buffer; |
| wl_anqpo_ignore_ssid_list_t *list; |
| int length; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| malloc_size = OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid) + |
| (ANQPO_MAX_IGNORE_SSID * (sizeof(wl_anqpo_ignore_ssid_list_t) - |
| OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid))); |
| buffer = malloc(malloc_size); |
| if (buffer == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| memset(buffer, 0, malloc_size); |
| list = (wl_anqpo_ignore_ssid_list_t *)buffer; |
| |
| /* get */ |
| if (!argv[1]) { |
| int i; |
| |
| if ((err = wlu_iovar_get(wl, cmd->name, list, malloc_size)) < 0) |
| goto done; |
| list->count = dtoh16(list->count); |
| for (i = 0; i < list->count; i++) { |
| char ssidbuf[SSID_FMT_BUF_LEN]; |
| |
| wl_format_ssid(ssidbuf, list->ssid[i].SSID, list->ssid[i].SSID_len); |
| printf("%s\n", ssidbuf); |
| } |
| goto done; |
| } |
| |
| /* set */ |
| argv++; |
| if (!stricmp(*argv, "clear")) { |
| list->is_clear = TRUE; |
| } |
| else if (!stricmp(*argv, "set")) { |
| list->is_clear = TRUE; |
| while (*++argv) { |
| int len = strlen(*argv); |
| if (list->count > ANQPO_MAX_IGNORE_SSID) { |
| fprintf(stderr, "Too many BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (len > 32) { |
| fprintf(stderr, "SSID too long\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| list->ssid[list->count].SSID_len = len; |
| memcpy(list->ssid[list->count].SSID, *argv, len); |
| list->count++; |
| } |
| } |
| else if (!stricmp(*argv, "append")) { |
| while (*++argv) { |
| int len = strlen(*argv); |
| if (list->count > ANQPO_MAX_IGNORE_SSID) { |
| fprintf(stderr, "Too many BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (len > 32) { |
| fprintf(stderr, "SSID too long\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| list->ssid[list->count].SSID_len = len; |
| memcpy(list->ssid[list->count].SSID, *argv, len); |
| list->count++; |
| } |
| } |
| else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| |
| length = OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid) + |
| (list->count * (sizeof(wl_anqpo_ignore_ssid_list_t) - |
| OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid))); |
| list->count = htod16(list->count); |
| |
| err = wlu_iovar_set(wl, cmd->name, list, length); |
| |
| done: |
| free(buffer); |
| return err; |
| } |
| |
| static int |
| wl_anqpo_ignore_bssid_list(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err = BCME_USAGE_ERROR, malloc_size; |
| char *buffer; |
| wl_anqpo_ignore_bssid_list_t *list; |
| int length; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| malloc_size = OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid) + |
| (ANQPO_MAX_IGNORE_BSSID * (sizeof(wl_anqpo_ignore_bssid_list_t) - |
| OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid))); |
| buffer = malloc(malloc_size); |
| if (buffer == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| memset(buffer, 0, malloc_size); |
| list = (wl_anqpo_ignore_bssid_list_t *)buffer; |
| |
| /* get */ |
| if (!argv[1]) { |
| int i; |
| |
| if ((err = wlu_iovar_get(wl, cmd->name, list, malloc_size)) < 0) |
| goto done; |
| list->count = dtoh16(list->count); |
| for (i = 0; i < list->count; i++) { |
| printf("%s\n", wl_ether_etoa(&list->bssid[i])); |
| } |
| goto done; |
| } |
| |
| /* set */ |
| argv++; |
| if (!stricmp(*argv, "clear")) { |
| list->is_clear = TRUE; |
| } |
| else if (!stricmp(*argv, "set")) { |
| list->is_clear = TRUE; |
| while (*++argv) { |
| if (list->count > ANQPO_MAX_IGNORE_BSSID) { |
| fprintf(stderr, "Too many BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (!wl_ether_atoe(*argv, &list->bssid[list->count])) { |
| fprintf(stderr, "Invalid BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| list->count++; |
| } |
| } |
| else if (!stricmp(*argv, "append")) { |
| while (*++argv) { |
| if (list->count > ANQPO_MAX_IGNORE_BSSID) { |
| fprintf(stderr, "Too many BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| if (!wl_ether_atoe(*argv, &list->bssid[list->count])) { |
| fprintf(stderr, "Invalid BSSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| list->count++; |
| } |
| } |
| else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto done; |
| } |
| |
| length = OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid) + |
| (list->count * (sizeof(wl_anqpo_ignore_bssid_list_t) - |
| OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid))); |
| list->count = htod16(list->count); |
| |
| err = wlu_iovar_set(wl, cmd->name, list, length); |
| |
| done: |
| free(buffer); |
| return err; |
| } |
| |
| #if defined(linux) |
| static int |
| wl_anqpo_results(void *wl, cmd_t *cmd, char **argv) |
| { |
| int fd, err, octets; |
| struct sockaddr_ll sll; |
| struct ifreq ifr; |
| char *data; |
| uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */ |
| |
| UNUSED_PARAMETER(cmd); |
| UNUSED_PARAMETER(argv); |
| |
| memset(&ifr, 0, sizeof(ifr)); |
| strncpy(ifr.ifr_name, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1)); |
| |
| memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN); |
| event_inds_mask[WLC_E_GAS_FRAGMENT_RX / 8] |= 1 << (WLC_E_GAS_FRAGMENT_RX % 8); |
| event_inds_mask[WLC_E_GAS_COMPLETE / 8] |= 1 << (WLC_E_GAS_COMPLETE % 8); |
| if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) |
| goto exit2; |
| |
| fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM)); |
| if (fd < 0) { |
| printf("Cannot create socket %d\n", fd); |
| err = -1; |
| goto exit2; |
| } |
| |
| err = ioctl(fd, SIOCGIFINDEX, &ifr); |
| if (err < 0) { |
| printf("Cannot get index %d\n", err); |
| goto exit1; |
| } |
| |
| /* bind the socket first before starting so we won't miss any event */ |
| memset(&sll, 0, sizeof(sll)); |
| sll.sll_family = AF_PACKET; |
| sll.sll_protocol = hton16(ETHER_TYPE_BRCM); |
| sll.sll_ifindex = ifr.ifr_ifindex; |
| err = bind(fd, (struct sockaddr *)&sll, sizeof(sll)); |
| if (err < 0) { |
| printf("Cannot bind %d\n", err); |
| goto exit1; |
| } |
| |
| data = (char*)malloc(ANQPO_EVENTS_BUFFER_SIZE); |
| |
| if (data == NULL) { |
| printf("Cannot not allocate %d bytes for events receive buffer\n", |
| ANQPO_EVENTS_BUFFER_SIZE); |
| err = BCME_NOMEM; |
| goto exit1; |
| } |
| |
| /* receive result */ |
| while (1) { |
| bcm_event_t *bcm_event; |
| int event_type; |
| |
| octets = recv(fd, data, ANQPO_EVENTS_BUFFER_SIZE, 0); |
| bcm_event = (bcm_event_t *)data; |
| event_type = ntoh32(bcm_event->event.event_type); |
| |
| if (octets >= (int)sizeof(bcm_event_t)) { |
| uint32 status = ntoh32(bcm_event->event.status); |
| |
| if (event_type == WLC_E_GAS_FRAGMENT_RX) { |
| wl_event_gas_t *gas_data = |
| (wl_event_gas_t *)&data[sizeof(bcm_event_t)]; |
| |
| gas_data->channel = dtoh16(gas_data->channel); |
| gas_data->status_code = dtoh16(gas_data->status_code); |
| gas_data->data_len = dtoh16(gas_data->data_len); |
| |
| printf("WLC_E_GAS_FRAGMENT_RX: %s\n", |
| status == WLC_E_STATUS_PARTIAL ? "WLC_E_STATUS_PARTIAL" : |
| status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" : |
| status == WLC_E_STATUS_FAIL ? "WLC_E_STATUS_FAIL" : |
| "unknown"); |
| printf(" channel = %d\n", gas_data->channel); |
| printf(" peer = %s\n", |
| wl_ether_etoa(&bcm_event->event.addr)); |
| printf(" dialog token = 0x%02x (%d)\n", |
| gas_data->dialog_token, gas_data->dialog_token); |
| printf(" fragment id = 0x%02x\n", gas_data->fragment_id); |
| printf(" GAS status = %s\n", |
| gas_data->status_code == DOT11_SC_SUCCESS ? "SUCCESS" : |
| gas_data->status_code == DOT11_SC_FAILURE ? "UNSPECIFIED" : |
| gas_data->status_code == DOT11_SC_ADV_PROTO_NOT_SUPPORTED ? |
| "ADVERTISEMENT_PROTOCOL_NOT_SUPPORTED" : |
| gas_data->status_code == DOT11_SC_NO_OUTSTAND_REQ ? |
| "NO_OUTSTANDING_REQUEST" : |
| gas_data->status_code == DOT11_SC_RSP_NOT_RX_FROM_SERVER ? |
| "RESPONSE_NOT_RECEIVED_FROM_SERVER" : |
| gas_data->status_code == DOT11_SC_TIMEOUT ? |
| "TIMEOUT" : |
| gas_data->status_code == DOT11_SC_QUERY_RSP_TOO_LARGE ? |
| "QUERY_RESPONSE_TOO_LARGE" : |
| gas_data->status_code == DOT11_SC_SERVER_UNREACHABLE ? |
| "SERVER_UNREACHABLE" : |
| gas_data->status_code == DOT11_SC_TRANSMIT_FAILURE ? |
| "TRANSMISSION_FAILURE" : "unknown"); |
| printf(" GAS data length = %d\n", gas_data->data_len); |
| if (gas_data->data_len) { |
| wl_hexdump(gas_data->data, gas_data->data_len); |
| } |
| } |
| else if (event_type == WLC_E_GAS_COMPLETE) { |
| printf("WLC_E_GAS_COMPLETE: %s\n", |
| status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" : |
| "unknown"); |
| } |
| } |
| } |
| |
| free(data); |
| exit1: |
| close(fd); |
| exit2: |
| return err; |
| } |
| #endif /* linux */ |