| /* |
| * wl tdls 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_tdls.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" |
| |
| static cmd_func_t wl_tdls_endpoint; |
| static cmd_func_t wl_tdls_wfd_ie; |
| |
| static cmd_t wl_tdls_cmds[] = { |
| { "tdls_endpoint", wl_tdls_endpoint, WLC_GET_VAR, WLC_SET_VAR, |
| "Available TDLS operations to each TDLS peer.\n" |
| "\tusage: wl tdls_endpoint <disc, create, delete, PM, wake, cw> <ea> [chanspec]\n" |
| "\t [chanspec] only applies to 'cw' operaton.\n\n" |
| "\t addendum:\n" |
| "\t wl tdls_endpoint wfd_disc <ea> sends a WFD tunneled Probe Request"}, |
| { "tdls_wfd_ie", wl_tdls_wfd_ie, WLC_GET_VAR, -1, |
| "To set, get and clear additional WFD IE in setup_req and setup_resp\n" |
| "\tTo set2, get2 and clear2 additional WFD IE in tunneled probe_req and probe_resp\n" |
| "\tusage: wl tdls_wfd_ie get <own|peer_eth_addr#> [ip] [port]\n" |
| "\t wl tdls_wfd_ie get2 <own|peer_eth_addr#> [alt_mac] [port] [PC_bit]\n" |
| "\t\t peer_eth_addr#: HH:HH:HH:HH:HH:HH\n" |
| "\t\t and peer must be TDLS connected (only in case of setup)\n\n" |
| "\t wl tdls_wfd_ie <clr|clr2> own\n\n" |
| "\t wl tdls_wfd_ie set own wfd_ie_hexa_string [ip# [port# [type# [bssid#]]]]\n" |
| "\t wl tdls_wfd_ie set2 own wfd_ie_hexa_string [alt_mac# [port# [type#]]]\n" |
| "\t\t wfd_ie_hexa_string: should start with the full WFD IE header\n" |
| "\t\t e.g. 0xDDXX506F9A0A...\n" |
| "\t\t ip#: XXX.XXX.XXX.XXX\n" |
| "\t\t alt_mac#: HH:HH:HH:HH:HH:HH\n" |
| "\t\t port#: 0-65535\n" |
| "\t\t type#: 0 for source, 1 for primary sink\n" |
| "\t\t bssid#: HH:HH:HH:HH:HH:HH"}, |
| { "tdls_sta_info", wl_sta_info, WLC_GET_VAR, -1, |
| "wl tdls_sta_info <xx:xx:xx:xx:xx:xx>"}, |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_tdls_module_init(void) |
| { |
| (void)g_swap; |
| |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register tdls commands */ |
| wl_module_cmds_register(wl_tdls_cmds); |
| } |
| |
| static int |
| wl_tdls_endpoint(void *wl, cmd_t *cmd, char **argv) |
| { |
| const char *cmdname_tdls = "tdls_endpoint"; |
| tdls_iovar_t info; |
| chanspec_t chanspec; |
| |
| if (strcmp(cmd->name, cmdname_tdls)) { |
| printf("error: invalid command name.\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| memset(&info, 0, sizeof(tdls_iovar_t)); |
| |
| if (!strcmp("create", *argv)) |
| info.mode = TDLS_MANUAL_EP_CREATE; |
| else if (!strcmp("modify", *argv)) |
| info.mode = TDLS_MANUAL_EP_MODIFY; |
| else if (!strcmp("delete", *argv)) |
| info.mode = TDLS_MANUAL_EP_DELETE; |
| else if (!strcmp("PM", *argv)) |
| info.mode = TDLS_MANUAL_EP_PM; |
| else if (!strcmp("wake", *argv)) |
| info.mode = TDLS_MANUAL_EP_WAKE; |
| else if (!strcmp("disc", *argv)) |
| info.mode = TDLS_MANUAL_EP_DISCOVERY; |
| else if (!strcmp("cw", *argv)) { |
| info.mode = TDLS_MANUAL_EP_CHSW; |
| } |
| else if (!strcmp("wfd_disc", *argv)) |
| info.mode = TDLS_MANUAL_EP_WFD_TPQ; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| argv++; |
| if (!*argv) { |
| printf("error: missing ea\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (!wl_ether_atoe(*argv, &info.ea)) { |
| printf("error: could not parse MAC address %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (info.mode == TDLS_MANUAL_EP_CHSW) { |
| argv++; |
| if (!*argv) { |
| printf("error: missing target channel number\n"); |
| return BCME_USAGE_ERROR; |
| } |
| if (atoi(*argv) != 0) { |
| chanspec = wf_chspec_aton(*argv); |
| if (chanspec == 0) { |
| printf("error: bad chanspec \"%s\".\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| chanspec = wl_chspec_to_driver(chanspec); |
| if (chanspec == INVCHANSPEC) { |
| return BCME_USAGE_ERROR; |
| } |
| info.chanspec = chanspec; |
| } |
| } |
| |
| return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info)); |
| } |
| |
| |
| #define WFD_DEV 0 |
| #define WFD_DEV_LEN 6 |
| #define WFD_IP 8 |
| #define WFD_IP_LEN 5 |
| #define WFD_ALT_MAC 10 |
| #define WFD_ALT_MAC_LEN 6 |
| |
| static int |
| wl_tdls_wfd_ie(void *wl, cmd_t *cmd, char **argv) |
| { |
| const char *cmdname_tdls = "tdls_wfd_ie"; |
| tdls_wfd_ie_iovar_t info; |
| tdls_wfd_ie_iovar_t* buf_info = (tdls_wfd_ie_iovar_t*) buf; |
| int ret; |
| uint8* ptr; |
| uint8 element, subelement = 0; |
| uint16 offset; |
| uint8 buffer[TDLS_WFD_IE_SIZE - (WFA_OUI_LEN + 3)]; |
| uint16 length, element_length, current_length; |
| bcm_tlv_t * ie; |
| unsigned long value; |
| struct ether_addr ea; |
| struct ipv4_addr ipa_set; |
| |
| if (strcmp(cmd->name, cmdname_tdls)) { |
| printf("error: invalid command name.\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp(*argv, "clr")) { |
| memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| info.mode = TDLS_WFD_IE_TX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info)); |
| |
| } else if (!strcmp(*argv, "get")) { |
| memset(buf_info, 0, sizeof(*buf_info)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| buf_info->mode = TDLS_WFD_IE_TX; |
| else if (wl_ether_atoe(*argv, &buf_info->ea)) |
| buf_info->mode = TDLS_WFD_IE_RX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if ((ret = wlu_iovar_getbuf(wl, cmd->name, buf_info, |
| sizeof(*buf_info), buf, WLC_IOCTL_MAXLEN)) < 0) { |
| return ret; |
| } |
| |
| /* empty */ |
| if (!buf_info->length) |
| return ret; |
| |
| if (!*++argv) |
| wl_hexdump((uchar *)buf_info->data, buf_info->length); |
| else { |
| if (!strcmp("ip", *argv)) { |
| element = WFD_IP; |
| element_length = WFD_IP_LEN; |
| } else if (!strcmp("port", *argv)) { |
| element = WFD_DEV; |
| element_length = WFD_DEV_LEN; |
| } else { |
| printf("error: unknown element\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| /* Reassemble the WFD IE (without header) */ |
| |
| ptr = buf_info->data; |
| length = buf_info->length; |
| offset = 0; |
| current_length = 0; |
| |
| while (length - offset > WFA_OUI_LEN + 3) { |
| if ((ie = bcm_parse_tlvs(ptr + offset, |
| length - offset, DOT11_MNG_VS_ID)) != NULL) { |
| if (ie->len > WFA_OUI_LEN + 1) { |
| if ((!memcmp(ie->data, WFA_OUI, WFA_OUI_LEN)) && |
| (*(ie->data + WFA_OUI_LEN) == |
| WFA_OUI_TYPE_WFD)) { |
| /* WFD */ |
| memcpy(buffer + current_length, |
| ie->data + WFA_OUI_LEN + 1, |
| ie->len - WFA_OUI_LEN - 1); |
| current_length += ie->len - WFA_OUI_LEN - 1; |
| } |
| } |
| offset = (uint16)((uint8*)ie - ptr + ie->len + 2); |
| } |
| else |
| break; |
| } |
| |
| /* Find the elements */ |
| |
| ptr = buffer; |
| length = current_length; |
| |
| while (length > 3) { |
| current_length = (ptr[1] << 8) + ptr[2]; |
| if ((ptr[0] == element) && (current_length == element_length) && |
| (current_length <= length - 3)) { |
| |
| switch (element) { |
| case WFD_IP: |
| /* we do not care about the IP version i.e. ptr[3] */ |
| printf("%u.%u.%u.%u\n", ptr[4], ptr[5], ptr[6], ptr[7]); |
| break; |
| |
| case WFD_DEV: |
| /* just get the RTSP TCP valid port */ |
| printf("%u\n", (ptr[5] << 8) + ptr[6]); |
| break; |
| } |
| break; |
| |
| } else { |
| if (current_length + 3 < length) { |
| length -= current_length + 3; |
| ptr += current_length + 3; |
| } else |
| break; |
| } |
| } |
| } |
| |
| return ret; |
| |
| } else if (!strcmp(*argv, "set")) { |
| memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| info.mode = TDLS_WFD_IE_TX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| argv++; |
| if (!*argv) { |
| printf("error: missing IE string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (strlen((char*)*argv) - 2 > sizeof(info.data) * 2) { |
| printf("error: IE string too long; max is %u bytes\n", |
| (unsigned int)sizeof(info.data)); |
| return BCME_BADARG; |
| } |
| |
| ret = wl_pattern_atoh(*argv, (char*)info.data); |
| |
| if (ret <= 0) { |
| printf("error: could not parse IE string address %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| |
| info.length = ret; |
| |
| if (*++argv) { |
| /* IP specified */ |
| |
| /* watchdog */ |
| if (info.length != 32) { |
| printf( |
| "if one or several set fields are used, " |
| "the following the IE string must be\n" |
| "exactly 32 bytes and must have the following order:\n" |
| "\t6-byte header (0xDD1E506F9A0A)\n" |
| "\t9-byte subelement 0 (WFD device information)\n" |
| "\t9-byte subelement 1 (BSSID)\n" |
| "\t8-byte subelement 8 (IP address)\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (!wl_atoip(*argv, &ipa_set)) |
| return BCME_USAGE_ERROR; |
| |
| memcpy(&info.data[28], (uint8*) &ipa_set, sizeof(ipa_set)); |
| |
| if (*++argv) { |
| /* port specified */ |
| |
| value = strtoul(*argv, NULL, 0); |
| info.data[11] = (uint8) (0xFF & (value >> 8)); |
| info.data[12] = (uint8) (0xFF & value); |
| |
| if (*++argv) { |
| /* WFD type (Source or Primary Sink) specified */ |
| |
| element = (uint8) (0x01 & strtoul(*argv, NULL, 0)); |
| if (element) |
| info.data[10] |= 0x01; |
| else |
| info.data[10] &= ~0x01; |
| |
| if (*++argv) { |
| /* BSSID specified */ |
| |
| if (!wl_ether_atoe(*argv, &ea)) |
| return BCME_USAGE_ERROR; |
| |
| memcpy(&info.data[18], (uint8*) &ea, sizeof(ea)); |
| } |
| } |
| } |
| } |
| |
| return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info)); |
| |
| } else if (!strcmp(*argv, "clr2")) { |
| memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| info.mode = TDLS_WFD_PROBE_IE_TX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info)); |
| |
| } else if (!strcmp(*argv, "get2")) { |
| memset(buf_info, 0, sizeof(*buf_info)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| buf_info->mode = TDLS_WFD_PROBE_IE_TX; |
| else if (wl_ether_atoe(*argv, &buf_info->ea)) |
| buf_info->mode = TDLS_WFD_PROBE_IE_RX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if ((ret = wlu_iovar_getbuf(wl, cmd->name, buf_info, |
| sizeof(*buf_info), buf, WLC_IOCTL_MAXLEN)) < 0) { |
| return ret; |
| } |
| |
| /* empty */ |
| if (!buf_info->length) |
| return ret; |
| |
| if (!*++argv) |
| wl_hexdump((uchar *)buf_info->data, buf_info->length); |
| |
| else { |
| if (!strcmp("alt_mac", *argv)) { |
| element = WFD_ALT_MAC; |
| element_length = WFD_ALT_MAC_LEN; |
| } else if (!strcmp("port", *argv)) { |
| element = WFD_DEV; |
| element_length = WFD_DEV_LEN; |
| subelement = 1; |
| } else if (!strcmp("PC_bit", *argv)) { |
| element = WFD_DEV; |
| element_length = WFD_DEV_LEN; |
| subelement = 0; |
| } else { |
| printf("error: unknown element\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| /* Reassemble the WFD IE (without header) */ |
| |
| ptr = buf_info->data; |
| length = buf_info->length; |
| offset = 0; |
| current_length = 0; |
| |
| while (length - offset > WFA_OUI_LEN + 3) { |
| if ((ie = bcm_parse_tlvs(ptr + offset, |
| length - offset, DOT11_MNG_VS_ID)) != NULL) { |
| if (ie->len > WFA_OUI_LEN + 1) { |
| if ((!memcmp(ie->data, WFA_OUI, WFA_OUI_LEN)) && |
| (*(ie->data + WFA_OUI_LEN) == |
| WFA_OUI_TYPE_WFD)) { |
| /* WFD */ |
| memcpy(buffer + current_length, |
| ie->data + WFA_OUI_LEN + 1, |
| ie->len - WFA_OUI_LEN - 1); |
| current_length += ie->len - WFA_OUI_LEN - 1; |
| } |
| } |
| offset = (uint16)((uint8*)ie - ptr + ie->len + 2); |
| } |
| else |
| break; |
| } |
| |
| /* Find the elements */ |
| |
| ptr = buffer; |
| length = current_length; |
| |
| while (length > 3) { |
| current_length = (ptr[1] << 8) + ptr[2]; |
| if ((ptr[0] == element) && (current_length == element_length) && |
| (current_length <= length - 3)) { |
| |
| switch (element) { |
| case WFD_ALT_MAC: |
| printf("%02X:%02X:%02X:%02X:%02X:%02X\n", |
| ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], ptr[8]); |
| break; |
| case WFD_DEV: |
| if (subelement) |
| /* just get the RTSP TCP valid port */ |
| printf("%u\n", (ptr[5] << 8) + ptr[6]); |
| else |
| /* just get the Preferred Connection bit */ |
| printf("%u\n", ptr[4] >> 7); |
| break; |
| } |
| break; |
| |
| } else { |
| if (current_length + 3 < length) { |
| length -= current_length + 3; |
| ptr += current_length + 3; |
| } else |
| break; |
| } |
| } |
| } |
| |
| return ret; |
| |
| } else if (!strcmp(*argv, "set2")) { |
| memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t)); |
| |
| if (!*++argv) |
| return BCME_USAGE_ERROR; |
| |
| if (!strcmp("own", *argv)) |
| info.mode = TDLS_WFD_PROBE_IE_TX; |
| else { |
| printf("error: invalid mode string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| argv++; |
| if (!*argv) { |
| printf("error: missing IE string\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (strlen((char*)*argv) - 2 > sizeof(info.data) * 2) { |
| printf("error: IE string too long; max is %u bytes\n", |
| (unsigned int)sizeof(info.data)); |
| return BCME_USAGE_ERROR; |
| } |
| |
| ret = wl_pattern_atoh(*argv, (char*)info.data); |
| |
| if (ret <= 0) { |
| printf("error: could not parse IE string address %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| |
| info.length = ret; |
| |
| if (*++argv) { |
| /* alt MAC specified */ |
| |
| /* watchdog */ |
| if (info.length != 24) { |
| printf( |
| "if one or several set2 fields are used, " |
| "the following the IE string must be\n" |
| "exactly 24 bytes and must have the following order:\n" |
| "\t6-byte header (0xDD16506F9A0A)\n" |
| "\t9-byte subelement 0 (WFD device information)\n" |
| "\t9-byte subelement 10 (alternate MAC address)\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (!wl_ether_atoe(*argv, &ea)) |
| return BCME_USAGE_ERROR; |
| memcpy(&info.data[18], (uint8*) &ea, sizeof(ea)); |
| |
| if (*++argv) { |
| /* port specified */ |
| |
| value = strtoul(*argv, NULL, 0); |
| info.data[11] = (uint8) (0xFF & (value >> 8)); |
| info.data[12] = (uint8) (0xFF & value); |
| |
| if (*++argv) { |
| /* WFD type (Source or Primary Sink) specified */ |
| |
| element = (uint8) (0x01 & strtoul(*argv, NULL, 0)); |
| if (element) |
| info.data[10] |= 0x01; |
| else |
| info.data[10] &= ~0x01; |
| } |
| } |
| } |
| |
| return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info)); |
| |
| } else { |
| printf("error: unknown operation\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } |