📄 driver_nl80211.c
字号:
/* * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> * * 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 <net/if_arp.h>#include <netlink/genl/genl.h>#include <netlink/genl/family.h>#include <netlink/genl/ctrl.h>#include "nl80211_copy.h"#ifdef CONFIG_CLIENT_MLME#include <netpacket/packet.h>#include <linux/if_ether.h>#include "radiotap.h"#include "radiotap_iter.h"#endif /* CONFIG_CLIENT_MLME */#include "wireless_copy.h"#include "common.h"#include "driver.h"#include "eloop.h"#include "ieee802_11_defs.h"#ifndef IFF_LOWER_UP#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */#endif#ifndef IFF_DORMANT#define IFF_DORMANT 0x20000 /* driver signals dormant */#endif#ifndef IF_OPER_DORMANT#define IF_OPER_DORMANT 5#endif#ifndef IF_OPER_UP#define IF_OPER_UP 6#endifstruct wpa_driver_nl80211_data { void *ctx; int wext_event_sock; int ioctl_sock; char ifname[IFNAMSIZ + 1]; int ifindex; int if_removed; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; size_t assoc_resp_ies_len; struct wpa_driver_capa capa; int has_capability; int we_version_compiled; /* for set_auth_alg fallback */ int use_crypt; int auth_alg_fallback; int operstate; char mlmedev[IFNAMSIZ + 1]; int scan_complete_events; struct nl_handle *nl_handle; struct nl_cache *nl_cache; struct nl_cb *nl_cb; struct genl_family *nl80211;#ifdef CONFIG_CLIENT_MLME int monitor_sock; /* socket for monitor */ int monitor_ifidx;#endif /* CONFIG_CLIENT_MLME */};static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);static int wpa_driver_nl80211_set_mode(void *priv, int mode);static int wpa_driver_nl80211_flush_pmkid(void *priv);static int wpa_driver_nl80211_get_range(void *priv);static voidwpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);/* nl80211 code */static int ack_handler(struct nl_msg *msg, void *arg){ int *err = arg; *err = 0; return NL_STOP;}static int finish_handler(struct nl_msg *msg, void *arg){ int *ret = arg; *ret = 0; return NL_SKIP;}static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg){ int *ret = arg; *ret = err->error; return NL_SKIP;}static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data){ struct nl_cb *cb; int err = -ENOMEM; cb = nl_cb_clone(drv->nl_cb); if (!cb) goto out; err = nl_send_auto_complete(drv->nl_handle, msg); if (err < 0) goto out; err = 1; nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); if (valid_handler) nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); while (err > 0) nl_recvmsgs(drv->nl_handle, cb); out: nl_cb_put(cb); nlmsg_free(msg); return err;}struct family_data { const char *group; int id;};static int family_handler(struct nl_msg *msg, void *arg){ struct family_data *res = arg; struct nlattr *tb[CTRL_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *mcgrp; int i; nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[CTRL_ATTR_MCAST_GROUPS]) return NL_SKIP; nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), nla_len(mcgrp), NULL); if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || !tb2[CTRL_ATTR_MCAST_GRP_ID] || os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), res->group, nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) continue; res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); break; }; return NL_SKIP;}static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, const char *family, const char *group){ struct nl_msg *msg; int ret = -1; struct family_data res = { group, -ENOENT }; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); ret = send_and_recv_msgs(drv, msg, family_handler, &res); msg = NULL; if (ret == 0) ret = res.id;nla_put_failure: nlmsg_free(msg); return ret;}static int wpa_driver_nl80211_send_oper_ifla( struct wpa_driver_nl80211_data *drv, int linkmode, int operstate){ struct { struct nlmsghdr hdr; struct ifinfomsg ifinfo; char opts[16]; } req; struct rtattr *rta; static int nl_seq; ssize_t ret; os_memset(&req, 0, sizeof(req)); req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.hdr.nlmsg_type = RTM_SETLINK; req.hdr.nlmsg_flags = NLM_F_REQUEST; req.hdr.nlmsg_seq = ++nl_seq; req.hdr.nlmsg_pid = 0; req.ifinfo.ifi_family = AF_UNSPEC; req.ifinfo.ifi_type = 0; req.ifinfo.ifi_index = drv->ifindex; req.ifinfo.ifi_flags = 0; req.ifinfo.ifi_change = 0; if (linkmode != -1) { rta = (struct rtattr *) ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); rta->rta_type = IFLA_LINKMODE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = linkmode; req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char)); } if (operstate != -1) { rta = (struct rtattr *) ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); rta->rta_type = IFLA_OPERSTATE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = operstate; req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char)); } wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", linkmode, operstate); ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " "%s (assume operstate is not supported)", strerror(errno)); } return ret < 0 ? -1 : 0;}static int wpa_driver_nl80211_set_auth_param( struct wpa_driver_nl80211_data *drv, int idx, u32 value){ struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.param.flags = idx & IW_AUTH_INDEX; iwr.u.param.value = value; if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { if (errno != EOPNOTSUPP) { wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " "value 0x%x) failed: %s)", idx, value, strerror(errno)); } ret = errno == EOPNOTSUPP ? -2 : -1; } return ret;}static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid){ struct wpa_driver_nl80211_data *drv = priv; struct iwreq iwr; int ret = 0; 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_nl80211_set_bssid(void *priv, const u8 *bssid){ struct wpa_driver_nl80211_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.ap_addr.sa_family = ARPHRD_ETHER; if (bssid) os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); else os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { perror("ioctl[SIOCSIWAP]"); ret = -1; } return ret;}static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid){ struct wpa_driver_nl80211_data *drv = priv; struct iwreq iwr; int ret = 0; 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 > 32) ret = 32; /* Some drivers include nul termination in the SSID, so let's * remove it here before further processing. WE-21 changes this * to explicitly require the length _not_ to include nul * termination. */ if (ret > 0 && ssid[ret - 1] == '\0' && drv->we_version_compiled < 21) ret--; } return ret;}static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, size_t ssid_len){ struct wpa_driver_nl80211_data *drv = priv; struct iwreq iwr; int ret = 0; char buf[33]; if (ssid_len > 32) return -1; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ iwr.u.essid.flags = (ssid_len != 0); os_memset(buf, 0, sizeof(buf)); os_memcpy(buf, ssid, ssid_len); iwr.u.essid.pointer = (caddr_t) buf; if (drv->we_version_compiled < 21) { /* For historic reasons, set SSID length to include one extra * character, C string nul termination, even though SSID is * really an octet string that should not be presented as a C * string. Some Linux drivers decrement the length by one and * can thus end up missing the last octet of the SSID if the * length is not incremented here. WE-21 changes this to * explicitly require the length _not_ to include nul * termination. */ if (ssid_len) ssid_len++; } iwr.u.essid.length = ssid_len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { perror("ioctl[SIOCSIWESSID]"); ret = -1; } return ret;}static int wpa_driver_nl80211_set_freq(void *priv, int freq){ struct wpa_driver_nl80211_data *drv = priv; struct iwreq iwr; int ret = 0; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.freq.m = freq * 100000; iwr.u.freq.e = 1; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { perror("ioctl[SIOCSIWFREQ]"); ret = -1; } return ret;}static voidwpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom){ union wpa_event_data data; wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", custom); os_memset(&data, 0, sizeof(data)); /* Host AP driver */ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { 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) { char *spos; int bytes; spos = custom + 17; bytes = strspn(spos, "0123456789abcdefABCDEF"); if (!bytes || (bytes & 1)) return; bytes /= 2; data.assoc_info.req_ies = os_malloc(bytes); if (data.assoc_info.req_ies == NULL) return; data.assoc_info.req_ies_len = bytes; hexstr2bin(spos, data.assoc_info.req_ies, bytes); spos += bytes * 2; data.assoc_info.resp_ies = NULL; data.assoc_info.resp_ies_len = 0; if (os_strncmp(spos, " RespIEs=", 9) == 0) { spos += 9; bytes = strspn(spos, "0123456789abcdefABCDEF"); if (!bytes || (bytes & 1)) goto done; bytes /= 2; data.assoc_info.resp_ies = os_malloc(bytes); if (data.assoc_info.resp_ies == NULL) goto done; data.assoc_info.resp_ies_len = bytes; hexstr2bin(spos, data.assoc_info.resp_ies, bytes); } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); done: os_free(data.assoc_info.resp_ies); os_free(data.assoc_info.req_ies);#ifdef CONFIG_PEERKEY } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { if (hwaddr_aton(custom + 17, data.stkstart.peer)) { wpa_printf(MSG_DEBUG, "WEXT: unrecognized " "STKSTART.request '%s'", custom + 17); return; } wpa_supplicant_event(ctx, EVENT_STKSTART, &data);#endif /* CONFIG_PEERKEY */ }}static int wpa_driver_nl80211_event_wireless_michaelmicfailure( void *ctx, const char *ev, size_t len)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -