/* * WPA Supplicant - driver interaction with Ralink Wireless Client * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * */ #include "includes.h" #include <sys/ioctl.h> #include "wireless_copy.h" #include "common.h" #include "driver.h" #include "l2_packet/l2_packet.h" #include "eloop.h" #include "ieee802_11_defs.h" #include "priv_netlink.h" #include "driver_ralink.h" static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); #define MAX_SSID_LEN 32 struct wpa_driver_ralink_data { void *ctx; int ioctl_sock; int event_sock; char ifname[IFNAMSIZ + 1]; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; size_t assoc_resp_ies_len; int no_of_pmkid; struct ndis_pmkid_entry *pmkid; int we_version_compiled; int ap_scan; int scanning_done; u8 g_driver_down; }; static int ralink_set_oid(struct wpa_driver_ralink_data *drv, unsigned short oid, char *data, int len) { char *buf; struct iwreq iwr; buf = os_zalloc(len); if (buf == NULL) return -1; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.flags = oid; iwr.u.data.flags |= OID_GET_SET_TOGGLE; if (data) os_memcpy(buf, data, len); iwr.u.data.pointer = (caddr_t) buf; iwr.u.data.length = len; if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", __func__, oid, len); os_free(buf); return -1; } os_free(buf); return 0; } static int ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) { struct iwreq iwr; UCHAR enabled = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = (UCHAR*) &enabled; iwr.u.data.flags = RT_OID_NEW_DRIVER; if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { wpa_printf(MSG_DEBUG, "%s: failed", __func__); return 0; } return (enabled == 1) ? 1 : 0; } static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) { struct wpa_driver_ralink_data *drv = priv; struct iwreq iwr; int ret = 0; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { perror("ioctl[SIOCGIWAP]"); ret = -1; } os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); return ret; } static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_ralink_data *drv = priv; #if 0 struct wpa_supplicant *wpa_s = drv->ctx; struct wpa_ssid *entry; #endif int ssid_len; u8 bssid[ETH_ALEN]; u8 ssid_str[MAX_SSID_LEN]; struct iwreq iwr; #if 0 int result = 0; #endif int ret = 0; #if 0 BOOLEAN ieee8021x_mode = FALSE; BOOLEAN ieee8021x_required_key = FALSE; #endif if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) ssid; iwr.u.essid.length = 32; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); ret = -1; } else ret = iwr.u.essid.length; if (ret <= 0) return ret; ssid_len = ret; os_memset(ssid_str, 0, MAX_SSID_LEN); os_memcpy(ssid_str, ssid, ssid_len); if (drv->ap_scan == 0) { /* Read BSSID form driver */ if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { wpa_printf(MSG_WARNING, "Could not read BSSID from " "driver."); return ret; } #if 0 entry = wpa_s->conf->ssid; while (entry) { if (!entry->disabled && ssid_len == entry->ssid_len && os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { /* match the config of driver */ result = 1; break; } entry = entry->next; } if (result) { wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " "ieee_required_keys parameters to driver"); /* set 802.1x mode and ieee_required_keys parameter */ if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) ieee8021x_required_key = TRUE; ieee8021x_mode = TRUE; } if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); } else { wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); } if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) { wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); } else { wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", entry->eapol_flags); } } #endif } return ret; } static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, const u8 *ssid, size_t ssid_len) { NDIS_802_11_SSID *buf; int ret = 0; struct iwreq iwr; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); buf = os_zalloc(sizeof(NDIS_802_11_SSID)); if (buf == NULL) return -1; os_memset(buf, 0, sizeof(buf)); buf->SsidLength = ssid_len; os_memcpy(buf->Ssid, ssid, ssid_len); os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.flags = OID_802_11_SSID; iwr.u.data.flags |= OID_GET_SET_TOGGLE; iwr.u.data.pointer = (caddr_t) buf; iwr.u.data.length = sizeof(NDIS_802_11_SSID); if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); ret = -1; } os_free(buf); return ret; } static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, const u8 *data, size_t data_len) { NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; size_t i; union wpa_event_data event; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (data_len < 8) { wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " "Event (len=%lu)", (unsigned long) data_len); return; } pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" " NumCandidates %d", (int) pmkid->Version, (int) pmkid->NumCandidates); if (pmkid->Version != 1) { wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " "List Version %d", (int) pmkid->Version); return; } if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " "underflow"); return; } os_memset(&event, 0, sizeof(event)); for (i = 0; i < pmkid->NumCandidates; i++) { PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", (unsigned long) i, MAC2STR(p->BSSID), (int) p->Flags); os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); event.pmkid_candidate.index = i; event.pmkid_candidate.preauth = p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &event); } } static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) { int len, count, i, ret; struct ndis_pmkid_entry *entry; NDIS_802_11_PMKID *p; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); count = 0; entry = drv->pmkid; while (entry) { count++; if (count >= drv->no_of_pmkid) break; entry = entry->next; } len = 8 + count * sizeof(BSSID_INFO); p = os_zalloc(len); if (p == NULL) return -1; p->Length = len; p->BSSIDInfoCount = count; entry = drv->pmkid; for (i = 0; i < count; i++) { os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); entry = entry->next; } wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (const u8 *) p, len); ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); os_free(p); return ret; } static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_ralink_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->no_of_pmkid == 0) return 0; prev = NULL; entry = drv->pmkid; while (entry) { if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) break; prev = entry; entry = entry->next; } if (entry) { /* Replace existing entry for this BSSID and move it into the * beginning of the list. */ os_memcpy(entry->pmkid, pmkid, 16); if (prev) { prev->next = entry->next; entry->next = drv->pmkid; drv->pmkid = entry; } } else { entry = os_malloc(sizeof(*entry)); if (entry) { os_memcpy(entry->bssid, bssid, ETH_ALEN); os_memcpy(entry->pmkid, pmkid, 16); entry->next = drv->pmkid; drv->pmkid = entry; } } return wpa_driver_ralink_set_pmkid(drv); } static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct wpa_driver_ralink_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->no_of_pmkid == 0) return 0; entry = drv->pmkid; prev = NULL; drv->pmkid = NULL; while (entry) { if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && os_memcmp(entry->pmkid, pmkid, 16) == 0) { if (prev) prev->next = entry->next; else drv->pmkid = entry->next; os_free(entry); break; } prev = entry; entry = entry->next; } return wpa_driver_ralink_set_pmkid(drv); } static int wpa_driver_ralink_flush_pmkid(void *priv) { struct wpa_driver_ralink_data *drv = priv; NDIS_802_11_PMKID p; struct ndis_pmkid_entry *pmkid, *prev; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->no_of_pmkid == 0) return 0; pmkid = drv->pmkid; drv->pmkid = NULL; while (pmkid) { prev = pmkid; pmkid = pmkid->next; os_free(prev); } os_memset(&p, 0, sizeof(p)); p.Length = 8; p.BSSIDInfoCount = 0; wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", (const u8 *) &p, 8); return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); } static void wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, void *ctx, char *custom) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); os_memset(&data, 0, sizeof(data)); /* Host AP driver */ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { /* receive a MICFAILURE report */ data.michael_mic_failure.unicast = os_strstr(custom, " unicast") != NULL; /* TODO: parse parameters(?) */ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { /* receive assoc. req. IEs */ char *spos; int bytes; spos = custom + 17; /*get IE's length */ /* * bytes = strlen(spos); ==> bug, bytes may less than original * size by using this way to get size. snowpin 20070312 * if (!bytes) * return; */ bytes = drv->assoc_req_ies_len; data.assoc_info.req_ies = os_malloc(bytes); if (data.assoc_info.req_ies == NULL) return; data.assoc_info.req_ies_len = bytes; os_memcpy(data.assoc_info.req_ies, spos, bytes); /* skip the '\0' byte */ spos += bytes + 1; data.assoc_info.resp_ies = NULL; data.assoc_info.resp_ies_len = 0; if (os_strncmp(spos, " RespIEs=", 9) == 0) { /* receive assoc. resp. IEs */ spos += 9; /* get IE's length */ bytes = os_strlen(spos); if (!bytes) goto done; data.assoc_info.resp_ies = os_malloc(bytes); if (data.assoc_info.resp_ies == NULL) goto done; data.assoc_info.resp_ies_len = bytes; os_memcpy(data.assoc_info.resp_ies, spos, bytes); } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); /* free allocated memory */ done: os_free(data.assoc_info.resp_ies); os_free(data.assoc_info.req_ies); } } static void wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, void *ctx, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; #if 0 BOOLEAN ieee8021x_required_key = FALSE; #endif wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); assoc_info_buf = info_pos = NULL; pos = data; end = data + len; while (pos + IW_EV_LCP_LEN <= end) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); if (iwe->len <= IW_EV_LCP_LEN) return; custom = pos + IW_EV_POINT_LEN; if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) { /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; os_memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); } else { os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } switch (iwe->cmd) { case IWEVCUSTOM: if (custom + iwe->u.data.length > end) return; buf = os_malloc(iwe->u.data.length + 1); if (buf == NULL) return; os_memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; if (drv->ap_scan == 1) { if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) || (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) || (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) || (iwe->u.data.flags == RT_ASSOCINFO_EVENT_FLAG)) { if (drv->scanning_done == 0) { os_free(buf); return; } } } if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive ASSOCIATED_EVENT !!!"); /* determine whether the dynamic-WEP is used or * not */ #if 0 if (wpa_s && wpa_s->current_ssid && wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if ((wpa_s->current_ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); ieee8021x_required_key = TRUE; } if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) { wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); } wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", wpa_s->current_ssid->eapol_flags); } #endif wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive ReqIEs !!!"); drv->assoc_req_ies = os_malloc(iwe->u.data.length); if (drv->assoc_req_ies == NULL) { os_free(buf); return; } drv->assoc_req_ies_len = iwe->u.data.length; os_memcpy(drv->assoc_req_ies, custom, iwe->u.data.length); } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive RespIEs !!!"); drv->assoc_resp_ies = os_malloc(iwe->u.data.length); if (drv->assoc_resp_ies == NULL) { os_free(drv->assoc_req_ies); drv->assoc_req_ies = NULL; os_free(buf); return; } drv->assoc_resp_ies_len = iwe->u.data.length; os_memcpy(drv->assoc_resp_ies, custom, iwe->u.data.length); } else if (iwe->u.data.flags == RT_ASSOCINFO_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive ASSOCINFO_EVENT !!!"); assoc_info_buf = os_zalloc(drv->assoc_req_ies_len + drv->assoc_resp_ies_len + 1); if (assoc_info_buf == NULL) { os_free(drv->assoc_req_ies); drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; os_free(buf); return; } if (drv->assoc_req_ies) { os_memcpy(assoc_info_buf, drv->assoc_req_ies, drv->assoc_req_ies_len); } info_pos = assoc_info_buf + drv->assoc_req_ies_len; if (drv->assoc_resp_ies) { os_memcpy(info_pos, drv->assoc_resp_ies, drv->assoc_resp_ies_len); } assoc_info_buf[drv->assoc_req_ies_len + drv->assoc_resp_ies_len] = '\0'; wpa_driver_ralink_event_wireless_custom( drv, ctx, assoc_info_buf); os_free(drv->assoc_req_ies); drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; os_free(assoc_info_buf); } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive DISASSOCIATED_EVENT !!!"); wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive PMKIDCAND_EVENT !!!"); wpa_driver_ralink_event_pmkid( drv, (const u8 *) custom, iwe->u.data.length); } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { drv->g_driver_down = 1; eloop_terminate(); } else if (iwe->u.data.flags == RT_REPORT_AP_INFO) { if (drv->ap_scan != 1) { typedef struct PACKED { UCHAR bssid[MAC_ADDR_LEN]; UCHAR ssid[MAX_LEN_OF_SSID]; INT ssid_len; UCHAR wpa_ie[40]; INT wpa_ie_len; UCHAR rsn_ie[40]; INT rsn_ie_len; INT freq; USHORT caps; } *PAPINFO; wpa_printf(MSG_DEBUG, "Custom wireless" " event: receive " "RT_REPORT_AP_INFO !!!"); //printf("iwe->u.data.length = %d\n", iwe->u.data.length); //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); #if 0 wpa_s->num_scan_results = 1; if (wpa_s->scan_results) os_free(wpa_s->scan_results); wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); if (wpa_s->scan_results) { PAPINFO pApInfo = (PAPINFO)buf; os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; if (pApInfo->wpa_ie_len > 0) { os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; } else if (pApInfo->rsn_ie_len > 0) { os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; } wpa_s->scan_results[0].caps = pApInfo->caps; wpa_s->scan_results[0].freq = pApInfo->freq; } else { wpa_printf("wpa_s->scan_" "results fail to " "os_malloc!!\n"); } #endif } } else { wpa_driver_ralink_event_wireless_custom( drv, ctx, buf); } os_free(buf); break; } pos += iwe->len; } } static void wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, void *ctx, struct nlmsghdr *h, int len) { struct ifinfomsg *ifi; int attrlen, nlmsg_len, rta_len; struct rtattr * attr; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (len < (int) sizeof(*ifi)) return; ifi = NLMSG_DATA(h); wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); attrlen = h->nlmsg_len - nlmsg_len; wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); if (attrlen < 0) return; attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); rta_len = RTA_ALIGN(sizeof(struct rtattr)); wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); while (RTA_OK(attr, attrlen)) { wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); if (attr->rta_type == IFLA_WIRELESS) { wpa_driver_ralink_event_wireless( drv, ctx, ((char *) attr) + rta_len, attr->rta_len - rta_len); } attr = RTA_NEXT(attr, attrlen); wpa_hexdump(MSG_DEBUG, "attr3: ", (u8 *) attr, sizeof(struct rtattr)); } } static void wpa_driver_ralink_event_receive(int sock, void *ctx, void *sock_ctx) { char buf[8192]; int left; struct sockaddr_nl from; socklen_t fromlen; struct nlmsghdr *h; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); fromlen = sizeof(from); left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) perror("recvfrom(netlink)"); return; } h = (struct nlmsghdr *) buf; wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); while (left >= (int) sizeof(*h)) { int len, plen; len = h->nlmsg_len; plen = len - sizeof(*h); if (len > left || plen < 0) { wpa_printf(MSG_DEBUG, "Malformed netlink message: " "len=%d left=%d plen=%d", len, left, plen); break; } switch (h->nlmsg_type) { case RTM_NEWLINK: wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, plen); break; } len = NLMSG_ALIGN(len); left -= len; h = (struct nlmsghdr *) ((char *) h + len); } if (left > 0) { wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " "message", left); } } static int ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) { struct iwreq iwr; UINT we_version_compiled = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) &we_version_compiled; iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { wpa_printf(MSG_DEBUG, "%s: failed", __func__); return -1; } drv->we_version_compiled = we_version_compiled; return 0; } static int ralink_set_iface_flags(void *priv, int dev_up) { struct wpa_driver_ralink_data *drv = priv; struct ifreq ifr; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->ioctl_sock < 0) return -1; os_memset(&ifr, 0, sizeof(ifr)); os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { perror("ioctl[SIOCGIFFLAGS]"); return -1; } if (dev_up) ifr.ifr_flags |= IFF_UP; else ifr.ifr_flags &= ~IFF_UP; if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { perror("ioctl[SIOCSIFFLAGS]"); return -1; } return 0; } static void * wpa_driver_ralink_init(void *ctx, const char *ifname) { int s; struct wpa_driver_ralink_data *drv; struct ifreq ifr; struct sockaddr_nl local; UCHAR enable_wpa_supplicant = 0; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); /* open socket to kernel */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return NULL; } /* do it */ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { perror(ifr.ifr_name); return NULL; } drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->scanning_done = 1; drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ drv->ctx = ctx; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); drv->ioctl_sock = s; drv->g_driver_down = 0; s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (s < 0) { perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); close(drv->ioctl_sock); os_free(drv); return NULL; } os_memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK; if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { perror("bind(netlink)"); close(s); close(drv->ioctl_sock); os_free(drv); return NULL; } eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); drv->event_sock = s; drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ ralink_set_iface_flags(drv, 1); /* mark up during setup */ ralink_get_we_version_compiled(drv); wpa_driver_ralink_flush_pmkid(drv); if (drv->ap_scan == 1) enable_wpa_supplicant = 1; else enable_wpa_supplicant = 2; /* trigger driver support wpa_supplicant */ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", (int) enable_wpa_supplicant); wpa_printf(MSG_ERROR, "RALINK: Driver does not support " "wpa_supplicant"); close(s); close(drv->ioctl_sock); os_free(drv); return NULL; } if (drv->ap_scan == 1) drv->scanning_done = 0; return drv; } static void wpa_driver_ralink_deinit(void *priv) { struct wpa_driver_ralink_data *drv = priv; UCHAR enable_wpa_supplicant; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); enable_wpa_supplicant = 0; if (drv->g_driver_down == 0) { /* trigger driver disable wpa_supplicant support */ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, (char *) &enable_wpa_supplicant, sizeof(BOOLEAN)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", (int) enable_wpa_supplicant); } wpa_driver_ralink_flush_pmkid(drv); sleep(1); ralink_set_iface_flags(drv, 0); } eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); eloop_unregister_read_sock(drv->event_sock); close(drv->event_sock); close(drv->ioctl_sock); os_free(drv); } static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_ralink_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); drv->scanning_done = 1; } static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) { struct wpa_driver_ralink_data *drv = priv; struct iwreq iwr; int ret = 0; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (ssid_len > IW_ESSID_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", __FUNCTION__, (unsigned long) ssid_len); return -1; } /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { perror("ioctl[SIOCSIWSCAN]"); ret = -1; } /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, drv->ctx); drv->scanning_done = 0; return ret; } static int wpa_driver_ralink_get_scan_results(void *priv, struct wpa_scan_result *results, size_t max_size) { struct wpa_driver_ralink_data *drv = priv; UCHAR *buf = NULL; NDIS_802_11_BSSID_LIST_EX *wsr; NDIS_WLAN_BSSID_EX *wbi; struct iwreq iwr; int rv = 0; size_t ap_num; u8 *pos, *end; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (drv->we_version_compiled >= 17) { buf = os_zalloc(8192); iwr.u.data.length = 8192; } else { buf = os_zalloc(4096); iwr.u.data.length = 4096; } if (buf == NULL) return -1; wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; wsr->NumberOfItems = 0; os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.data.pointer = (void *) buf; iwr.u.data.flags = OID_802_11_BSSID_LIST; if ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); os_free(buf); return -1; } os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; ++ap_num) { os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, wbi->Ssid.SsidLength); results[ap_num].ssid_len = wbi->Ssid.SsidLength; results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); /* get ie's */ wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); pos = (u8 *) wbi + sizeof(*wbi) - 1; end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) break; pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; os_memcpy(&results[ap_num].caps, pos, 2); pos += 2; while (pos + 1 < end && pos + 2 + pos[1] <= end) { u8 ielen = 2 + pos[1]; if (ielen > SSID_MAX_WPA_IE_LEN) { pos += ielen; continue; } if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { os_memcpy(results[ap_num].wpa_ie, pos, ielen); results[ap_num].wpa_ie_len = ielen; } else if (pos[0] == WLAN_EID_RSN) { os_memcpy(results[ap_num].rsn_ie, pos, ielen); results[ap_num].rsn_ie_len = ielen; } pos += ielen; } wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); } os_free(buf); return ap_num; } static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, NDIS_802_11_AUTHENTICATION_MODE mode) { NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, (char *) &auth_mode, sizeof(auth_mode)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_AUTHENTICATION_MODE (%d)", (int) auth_mode); return -1; } return 0; } static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, int key_idx, const u8 *addr, const u8 *bssid, int pairwise) { NDIS_802_11_REMOVE_KEY rkey; NDIS_802_11_KEY_INDEX _index; int res, res2; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); os_memset(&rkey, 0, sizeof(rkey)); rkey.Length = sizeof(rkey); rkey.KeyIndex = key_idx; if (pairwise) rkey.KeyIndex |= 1 << 30; os_memcpy(rkey.BSSID, bssid, ETH_ALEN); res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, sizeof(rkey)); /* AlbertY@20060210 removed it */ if (0 /* !pairwise */) { res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, (char *) &_index, sizeof(_index)); } else res2 = 0; if (res < 0 && res2 < 0) return res; return 0; } static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, int pairwise, int key_idx, int set_tx, const u8 *key, size_t key_len) { NDIS_802_11_WEP *wep; size_t len; int res; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); len = 12 + key_len; wep = os_zalloc(len); if (wep == NULL) return -1; wep->Length = len; wep->KeyIndex = key_idx; if (set_tx) wep->KeyIndex |= 0x80000000; wep->KeyLength = key_len; os_memcpy(wep->KeyMaterial, key, key_len); wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", (const u8 *) wep, len); res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); os_free(wep); return res; } static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_ralink_data *drv = priv; size_t len, i; NDIS_802_11_KEY *nkey; int res, pairwise; u8 bssid[ETH_ALEN]; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) { /* Group Key */ pairwise = 0; wpa_driver_ralink_get_bssid(drv, bssid); } else { /* Pairwise Key */ pairwise = 1; os_memcpy(bssid, addr, ETH_ALEN); } if (alg == WPA_ALG_NONE || key_len == 0) { return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, pairwise); } if (alg == WPA_ALG_WEP) { return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, set_tx, key, key_len); } len = 12 + 6 + 6 + 8 + key_len; nkey = os_zalloc(len); if (nkey == NULL) return -1; nkey->Length = len; nkey->KeyIndex = key_idx; if (set_tx) nkey->KeyIndex |= 1 << 31; if (pairwise) nkey->KeyIndex |= 1 << 30; if (seq && seq_len) nkey->KeyIndex |= 1 << 29; nkey->KeyLength = key_len; os_memcpy(nkey->BSSID, bssid, ETH_ALEN); if (seq && seq_len) { for (i = 0; i < seq_len; i++) nkey->KeyRSC |= seq[i] << (i * 8); } if (alg == WPA_ALG_TKIP && key_len == 32) { os_memcpy(nkey->KeyMaterial, key, 16); os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); } else { os_memcpy(nkey->KeyMaterial, key, key_len); } wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", (const u8 *) nkey, len); res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); os_free(nkey); return res; } static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, int reason_code) { struct wpa_driver_ralink_data *drv = priv; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_DISASSOCIATE"); } return 0; } static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, int reason_code) { struct wpa_driver_ralink_data *drv = priv; wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (ralink_get_new_driver_flag(drv) == 0) { return wpa_driver_ralink_disassociate(priv, addr, reason_code); } else { MLME_DEAUTH_REQ_STRUCT mlme; os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); mlme.Reason = reason_code; os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, (char *) &mlme, sizeof(MLME_DEAUTH_REQ_STRUCT)); } } static int wpa_driver_ralink_associate(void *priv, struct wpa_driver_associate_params *params) { struct wpa_driver_ralink_data *drv = priv; NDIS_802_11_NETWORK_INFRASTRUCTURE mode; NDIS_802_11_AUTHENTICATION_MODE auth_mode; NDIS_802_11_WEP_STATUS encr; BOOLEAN ieee8021xMode; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); if (params->mode == IEEE80211_MODE_IBSS) mode = Ndis802_11IBSS; else mode = Ndis802_11Infrastructure; if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, (char *) &mode, sizeof(mode)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ } if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { if (params->auth_alg & AUTH_ALG_SHARED_KEY) { if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; } else auth_mode = Ndis802_11AuthModeOpen; } else if (params->wpa_ie[0] == WLAN_EID_RSN) { if (params->key_mgmt_suite == KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPA2PSK; else auth_mode = Ndis802_11AuthModeWPA2; } else { if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) auth_mode = Ndis802_11AuthModeWPANone; else if (params->key_mgmt_suite == KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPA; } switch (params->pairwise_suite) { case CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; break; case CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; break; case CIPHER_WEP40: case CIPHER_WEP104: encr = Ndis802_11Encryption1Enabled; break; case CIPHER_NONE: if (params->group_suite == CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; else if (params->group_suite == CIPHER_TKIP) encr = Ndis802_11Encryption2Enabled; else encr = Ndis802_11EncryptionDisabled; break; default: encr = Ndis802_11EncryptionDisabled; break; } ralink_set_auth_mode(drv, auth_mode); /* notify driver that IEEE8021x mode is enabled */ if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) ieee8021xMode = TRUE; else ieee8021xMode = FALSE; if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021xMode, sizeof(BOOLEAN)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021xMode); } if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, (char *) &encr, sizeof(encr)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_WEP_STATUS(%d)", (int) encr); } if ((ieee8021xMode == FALSE) && (encr == Ndis802_11Encryption1Enabled)) { /* static WEP */ int enabled = 0; if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, (char *) &enabled, sizeof(enabled)) < 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " "OID_802_11_DROP_UNENCRYPTED(%d)", (int) encr); } } return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); } static int wpa_driver_ralink_set_countermeasures(void *priv, int enabled) { struct wpa_driver_ralink_data *drv = priv; if (drv->g_driver_down == 1) return -1; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, sizeof(int)); } const struct wpa_driver_ops wpa_driver_ralink_ops = { .name = "ralink", .desc = "Ralink Wireless Client driver", .get_bssid = wpa_driver_ralink_get_bssid, .get_ssid = wpa_driver_ralink_get_ssid, .set_key = wpa_driver_ralink_set_key, .init = wpa_driver_ralink_init, .deinit = wpa_driver_ralink_deinit, .set_countermeasures = wpa_driver_ralink_set_countermeasures, .scan = wpa_driver_ralink_scan, .get_scan_results = wpa_driver_ralink_get_scan_results, .deauthenticate = wpa_driver_ralink_deauthenticate, .disassociate = wpa_driver_ralink_disassociate, .associate = wpa_driver_ralink_associate, .add_pmkid = wpa_driver_ralink_add_pmkid, .remove_pmkid = wpa_driver_ralink_remove_pmkid, .flush_pmkid = wpa_driver_ralink_flush_pmkid, };