18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file implement the Wireless Extensions core API. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 58c2ecf20Sopenharmony_ci * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. 68c2ecf20Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * (As all part of the Linux kernel, this file is GPL) 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/wireless.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 188c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 198c2ecf20Sopenharmony_ci#include <net/netlink.h> 208c2ecf20Sopenharmony_ci#include <net/wext.h> 218c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_citypedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, 248c2ecf20Sopenharmony_ci unsigned int, struct iw_request_info *, 258c2ecf20Sopenharmony_ci iw_handler); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Meta-data about all the standard Wireless Extension request we 308c2ecf20Sopenharmony_ci * know about. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic const struct iw_ioctl_description standard_ioctl[] = { 338c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { 348c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 358c2ecf20Sopenharmony_ci }, 368c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNAME)] = { 378c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_CHAR, 388c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWNWID)] = { 418c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 428c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 438c2ecf20Sopenharmony_ci }, 448c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNWID)] = { 458c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 468c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 478c2ecf20Sopenharmony_ci }, 488c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWFREQ)] = { 498c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_FREQ, 508c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 518c2ecf20Sopenharmony_ci }, 528c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWFREQ)] = { 538c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_FREQ, 548c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWMODE)] = { 578c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_UINT, 588c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWMODE)] = { 618c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_UINT, 628c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSENS)] = { 658c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 668c2ecf20Sopenharmony_ci }, 678c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSENS)] = { 688c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRANGE)] = { 718c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRANGE)] = { 748c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 758c2ecf20Sopenharmony_ci .token_size = 1, 768c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_range), 778c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPRIV)] = { 808c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ 838c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 848c2ecf20Sopenharmony_ci .token_size = sizeof(struct iw_priv_args), 858c2ecf20Sopenharmony_ci .max_tokens = 16, 868c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSTATS)] = { 898c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ 928c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 938c2ecf20Sopenharmony_ci .token_size = 1, 948c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_statistics), 958c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSPY)] = { 988c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 998c2ecf20Sopenharmony_ci .token_size = sizeof(struct sockaddr), 1008c2ecf20Sopenharmony_ci .max_tokens = IW_MAX_SPY, 1018c2ecf20Sopenharmony_ci }, 1028c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSPY)] = { 1038c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1048c2ecf20Sopenharmony_ci .token_size = sizeof(struct sockaddr) + 1058c2ecf20Sopenharmony_ci sizeof(struct iw_quality), 1068c2ecf20Sopenharmony_ci .max_tokens = IW_MAX_SPY, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { 1098c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1108c2ecf20Sopenharmony_ci .token_size = sizeof(struct iw_thrspy), 1118c2ecf20Sopenharmony_ci .min_tokens = 1, 1128c2ecf20Sopenharmony_ci .max_tokens = 1, 1138c2ecf20Sopenharmony_ci }, 1148c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { 1158c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1168c2ecf20Sopenharmony_ci .token_size = sizeof(struct iw_thrspy), 1178c2ecf20Sopenharmony_ci .min_tokens = 1, 1188c2ecf20Sopenharmony_ci .max_tokens = 1, 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWAP)] = { 1218c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 1228c2ecf20Sopenharmony_ci }, 1238c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAP)] = { 1248c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 1258c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWMLME)] = { 1288c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1298c2ecf20Sopenharmony_ci .token_size = 1, 1308c2ecf20Sopenharmony_ci .min_tokens = sizeof(struct iw_mlme), 1318c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_mlme), 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { 1348c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1358c2ecf20Sopenharmony_ci .token_size = sizeof(struct sockaddr) + 1368c2ecf20Sopenharmony_ci sizeof(struct iw_quality), 1378c2ecf20Sopenharmony_ci .max_tokens = IW_MAX_AP, 1388c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 1398c2ecf20Sopenharmony_ci }, 1408c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSCAN)] = { 1418c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1428c2ecf20Sopenharmony_ci .token_size = 1, 1438c2ecf20Sopenharmony_ci .min_tokens = 0, 1448c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_scan_req), 1458c2ecf20Sopenharmony_ci }, 1468c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSCAN)] = { 1478c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1488c2ecf20Sopenharmony_ci .token_size = 1, 1498c2ecf20Sopenharmony_ci .max_tokens = IW_SCAN_MAX_DATA, 1508c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWESSID)] = { 1538c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1548c2ecf20Sopenharmony_ci .token_size = 1, 1558c2ecf20Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 1568c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWESSID)] = { 1598c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1608c2ecf20Sopenharmony_ci .token_size = 1, 1618c2ecf20Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 1628c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 1638c2ecf20Sopenharmony_ci }, 1648c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWNICKN)] = { 1658c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1668c2ecf20Sopenharmony_ci .token_size = 1, 1678c2ecf20Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 1688c2ecf20Sopenharmony_ci }, 1698c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNICKN)] = { 1708c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 1718c2ecf20Sopenharmony_ci .token_size = 1, 1728c2ecf20Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 1738c2ecf20Sopenharmony_ci }, 1748c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRATE)] = { 1758c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1768c2ecf20Sopenharmony_ci }, 1778c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRATE)] = { 1788c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1798c2ecf20Sopenharmony_ci }, 1808c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRTS)] = { 1818c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1828c2ecf20Sopenharmony_ci }, 1838c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRTS)] = { 1848c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWFRAG)] = { 1878c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWFRAG)] = { 1908c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1918c2ecf20Sopenharmony_ci }, 1928c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { 1938c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1948c2ecf20Sopenharmony_ci }, 1958c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { 1968c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRETRY)] = { 1998c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRETRY)] = { 2028c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWENCODE)] = { 2058c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2068c2ecf20Sopenharmony_ci .token_size = 1, 2078c2ecf20Sopenharmony_ci .max_tokens = IW_ENCODING_TOKEN_MAX, 2088c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, 2098c2ecf20Sopenharmony_ci }, 2108c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWENCODE)] = { 2118c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2128c2ecf20Sopenharmony_ci .token_size = 1, 2138c2ecf20Sopenharmony_ci .max_tokens = IW_ENCODING_TOKEN_MAX, 2148c2ecf20Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPOWER)] = { 2178c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2188c2ecf20Sopenharmony_ci }, 2198c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWPOWER)] = { 2208c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2218c2ecf20Sopenharmony_ci }, 2228c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWGENIE)] = { 2238c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2248c2ecf20Sopenharmony_ci .token_size = 1, 2258c2ecf20Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWGENIE)] = { 2288c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2298c2ecf20Sopenharmony_ci .token_size = 1, 2308c2ecf20Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 2318c2ecf20Sopenharmony_ci }, 2328c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWAUTH)] = { 2338c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2348c2ecf20Sopenharmony_ci }, 2358c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAUTH)] = { 2368c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 2378c2ecf20Sopenharmony_ci }, 2388c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { 2398c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2408c2ecf20Sopenharmony_ci .token_size = 1, 2418c2ecf20Sopenharmony_ci .min_tokens = sizeof(struct iw_encode_ext), 2428c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_encode_ext) + 2438c2ecf20Sopenharmony_ci IW_ENCODING_TOKEN_MAX, 2448c2ecf20Sopenharmony_ci }, 2458c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { 2468c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2478c2ecf20Sopenharmony_ci .token_size = 1, 2488c2ecf20Sopenharmony_ci .min_tokens = sizeof(struct iw_encode_ext), 2498c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_encode_ext) + 2508c2ecf20Sopenharmony_ci IW_ENCODING_TOKEN_MAX, 2518c2ecf20Sopenharmony_ci }, 2528c2ecf20Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { 2538c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2548c2ecf20Sopenharmony_ci .token_size = 1, 2558c2ecf20Sopenharmony_ci .min_tokens = sizeof(struct iw_pmksa), 2568c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_pmksa), 2578c2ecf20Sopenharmony_ci }, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_cistatic const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * Meta-data about all the additional standard Wireless Extension events 2638c2ecf20Sopenharmony_ci * we know about. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_cistatic const struct iw_ioctl_description standard_event[] = { 2668c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVTXDROP)] = { 2678c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 2688c2ecf20Sopenharmony_ci }, 2698c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVQUAL)] = { 2708c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_QUAL, 2718c2ecf20Sopenharmony_ci }, 2728c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVCUSTOM)] = { 2738c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2748c2ecf20Sopenharmony_ci .token_size = 1, 2758c2ecf20Sopenharmony_ci .max_tokens = IW_CUSTOM_MAX, 2768c2ecf20Sopenharmony_ci }, 2778c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVREGISTERED)] = { 2788c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVEXPIRED)] = { 2818c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVGENIE)] = { 2848c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2858c2ecf20Sopenharmony_ci .token_size = 1, 2868c2ecf20Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 2878c2ecf20Sopenharmony_ci }, 2888c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { 2898c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2908c2ecf20Sopenharmony_ci .token_size = 1, 2918c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_michaelmicfailure), 2928c2ecf20Sopenharmony_ci }, 2938c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVASSOCREQIE)] = { 2948c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 2958c2ecf20Sopenharmony_ci .token_size = 1, 2968c2ecf20Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 2978c2ecf20Sopenharmony_ci }, 2988c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { 2998c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 3008c2ecf20Sopenharmony_ci .token_size = 1, 3018c2ecf20Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 3028c2ecf20Sopenharmony_ci }, 3038c2ecf20Sopenharmony_ci [IW_EVENT_IDX(IWEVPMKIDCAND)] = { 3048c2ecf20Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 3058c2ecf20Sopenharmony_ci .token_size = 1, 3068c2ecf20Sopenharmony_ci .max_tokens = sizeof(struct iw_pmkid_cand), 3078c2ecf20Sopenharmony_ci }, 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_cistatic const unsigned int standard_event_num = ARRAY_SIZE(standard_event); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* Size (in bytes) of various events */ 3128c2ecf20Sopenharmony_cistatic const int event_type_size[] = { 3138c2ecf20Sopenharmony_ci IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 3148c2ecf20Sopenharmony_ci 0, 3158c2ecf20Sopenharmony_ci IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 3168c2ecf20Sopenharmony_ci 0, 3178c2ecf20Sopenharmony_ci IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ 3188c2ecf20Sopenharmony_ci IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ 3198c2ecf20Sopenharmony_ci IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 3208c2ecf20Sopenharmony_ci 0, 3218c2ecf20Sopenharmony_ci IW_EV_POINT_LEN, /* Without variable payload */ 3228c2ecf20Sopenharmony_ci IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ 3238c2ecf20Sopenharmony_ci IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 3278c2ecf20Sopenharmony_cistatic const int compat_event_type_size[] = { 3288c2ecf20Sopenharmony_ci IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 3298c2ecf20Sopenharmony_ci 0, 3308c2ecf20Sopenharmony_ci IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 3318c2ecf20Sopenharmony_ci 0, 3328c2ecf20Sopenharmony_ci IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ 3338c2ecf20Sopenharmony_ci IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ 3348c2ecf20Sopenharmony_ci IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 3358c2ecf20Sopenharmony_ci 0, 3368c2ecf20Sopenharmony_ci IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ 3378c2ecf20Sopenharmony_ci IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ 3388c2ecf20Sopenharmony_ci IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci#endif 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* IW event code */ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid wireless_nlevent_flush(void) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct sk_buff *skb; 3488c2ecf20Sopenharmony_ci struct net *net; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci down_read(&net_rwsem); 3518c2ecf20Sopenharmony_ci for_each_net(net) { 3528c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&net->wext_nlevents))) 3538c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, 3548c2ecf20Sopenharmony_ci GFP_KERNEL); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci up_read(&net_rwsem); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wireless_nlevent_flush); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int wext_netdev_notifier_call(struct notifier_block *nb, 3618c2ecf20Sopenharmony_ci unsigned long state, void *ptr) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * When a netdev changes state in any way, flush all pending messages 3658c2ecf20Sopenharmony_ci * to avoid them going out in a strange order, e.g. RTM_NEWLINK after 3668c2ecf20Sopenharmony_ci * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close() 3678c2ecf20Sopenharmony_ci * or similar - all of which could otherwise happen due to delays from 3688c2ecf20Sopenharmony_ci * schedule_work(). 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci wireless_nlevent_flush(); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return NOTIFY_OK; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct notifier_block wext_netdev_notifier = { 3768c2ecf20Sopenharmony_ci .notifier_call = wext_netdev_notifier_call, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int __net_init wext_pernet_init(struct net *net) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci skb_queue_head_init(&net->wext_nlevents); 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void __net_exit wext_pernet_exit(struct net *net) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci skb_queue_purge(&net->wext_nlevents); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic struct pernet_operations wext_pernet_ops = { 3918c2ecf20Sopenharmony_ci .init = wext_pernet_init, 3928c2ecf20Sopenharmony_ci .exit = wext_pernet_exit, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int __init wireless_nlevent_init(void) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci int err = register_pernet_subsys(&wext_pernet_ops); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (err) 4008c2ecf20Sopenharmony_ci return err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&wext_netdev_notifier); 4038c2ecf20Sopenharmony_ci if (err) 4048c2ecf20Sopenharmony_ci unregister_pernet_subsys(&wext_pernet_ops); 4058c2ecf20Sopenharmony_ci return err; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cisubsys_initcall(wireless_nlevent_init); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* Process events generated by the wireless layer or the driver. */ 4118c2ecf20Sopenharmony_cistatic void wireless_nlevent_process(struct work_struct *work) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci wireless_nlevent_flush(); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, 4198c2ecf20Sopenharmony_ci struct sk_buff *skb) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct ifinfomsg *r; 4228c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); 4258c2ecf20Sopenharmony_ci if (!nlh) 4268c2ecf20Sopenharmony_ci return NULL; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 4298c2ecf20Sopenharmony_ci r->ifi_family = AF_UNSPEC; 4308c2ecf20Sopenharmony_ci r->__ifi_pad = 0; 4318c2ecf20Sopenharmony_ci r->ifi_type = dev->type; 4328c2ecf20Sopenharmony_ci r->ifi_index = dev->ifindex; 4338c2ecf20Sopenharmony_ci r->ifi_flags = dev_get_flags(dev); 4348c2ecf20Sopenharmony_ci r->ifi_change = 0; /* Wireless changes don't affect those flags */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (nla_put_string(skb, IFLA_IFNAME, dev->name)) 4378c2ecf20Sopenharmony_ci goto nla_put_failure; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return nlh; 4408c2ecf20Sopenharmony_ci nla_put_failure: 4418c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 4428c2ecf20Sopenharmony_ci return NULL; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/* 4478c2ecf20Sopenharmony_ci * Main event dispatcher. Called from other parts and drivers. 4488c2ecf20Sopenharmony_ci * Send the event on the appropriate channels. 4498c2ecf20Sopenharmony_ci * May be called from interrupt context. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_civoid wireless_send_event(struct net_device * dev, 4528c2ecf20Sopenharmony_ci unsigned int cmd, 4538c2ecf20Sopenharmony_ci union iwreq_data * wrqu, 4548c2ecf20Sopenharmony_ci const char * extra) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci const struct iw_ioctl_description * descr = NULL; 4578c2ecf20Sopenharmony_ci int extra_len = 0; 4588c2ecf20Sopenharmony_ci struct iw_event *event; /* Mallocated whole event */ 4598c2ecf20Sopenharmony_ci int event_len; /* Its size */ 4608c2ecf20Sopenharmony_ci int hdr_len; /* Size of the event header */ 4618c2ecf20Sopenharmony_ci int wrqu_off = 0; /* Offset in wrqu */ 4628c2ecf20Sopenharmony_ci /* Don't "optimise" the following variable, it will crash */ 4638c2ecf20Sopenharmony_ci unsigned int cmd_index; /* *MUST* be unsigned */ 4648c2ecf20Sopenharmony_ci struct sk_buff *skb; 4658c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 4668c2ecf20Sopenharmony_ci struct nlattr *nla; 4678c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 4688c2ecf20Sopenharmony_ci struct __compat_iw_event *compat_event; 4698c2ecf20Sopenharmony_ci struct compat_iw_point compat_wrqu; 4708c2ecf20Sopenharmony_ci struct sk_buff *compskb; 4718c2ecf20Sopenharmony_ci#endif 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * Nothing in the kernel sends scan events with data, be safe. 4758c2ecf20Sopenharmony_ci * This is necessary because we cannot fix up scan event data 4768c2ecf20Sopenharmony_ci * for compat, due to being contained in 'extra', but normally 4778c2ecf20Sopenharmony_ci * applications are required to retrieve the scan data anyway 4788c2ecf20Sopenharmony_ci * and no data is included in the event, this codifies that 4798c2ecf20Sopenharmony_ci * practice. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci if (WARN_ON(cmd == SIOCGIWSCAN && extra)) 4828c2ecf20Sopenharmony_ci extra = NULL; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Get the description of the Event */ 4858c2ecf20Sopenharmony_ci if (cmd <= SIOCIWLAST) { 4868c2ecf20Sopenharmony_ci cmd_index = IW_IOCTL_IDX(cmd); 4878c2ecf20Sopenharmony_ci if (cmd_index < standard_ioctl_num) 4888c2ecf20Sopenharmony_ci descr = &(standard_ioctl[cmd_index]); 4898c2ecf20Sopenharmony_ci } else { 4908c2ecf20Sopenharmony_ci cmd_index = IW_EVENT_IDX(cmd); 4918c2ecf20Sopenharmony_ci if (cmd_index < standard_event_num) 4928c2ecf20Sopenharmony_ci descr = &(standard_event[cmd_index]); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci /* Don't accept unknown events */ 4958c2ecf20Sopenharmony_ci if (descr == NULL) { 4968c2ecf20Sopenharmony_ci /* Note : we don't return an error to the driver, because 4978c2ecf20Sopenharmony_ci * the driver would not know what to do about it. It can't 4988c2ecf20Sopenharmony_ci * return an error to the user, because the event is not 4998c2ecf20Sopenharmony_ci * initiated by a user request. 5008c2ecf20Sopenharmony_ci * The best the driver could do is to log an error message. 5018c2ecf20Sopenharmony_ci * We will do it ourselves instead... 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", 5048c2ecf20Sopenharmony_ci cmd); 5058c2ecf20Sopenharmony_ci return; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Check extra parameters and set extra_len */ 5098c2ecf20Sopenharmony_ci if (descr->header_type == IW_HEADER_TYPE_POINT) { 5108c2ecf20Sopenharmony_ci /* Check if number of token fits within bounds */ 5118c2ecf20Sopenharmony_ci if (wrqu->data.length > descr->max_tokens) { 5128c2ecf20Sopenharmony_ci netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n", 5138c2ecf20Sopenharmony_ci cmd, wrqu->data.length); 5148c2ecf20Sopenharmony_ci return; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci if (wrqu->data.length < descr->min_tokens) { 5178c2ecf20Sopenharmony_ci netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n", 5188c2ecf20Sopenharmony_ci cmd, wrqu->data.length); 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci /* Calculate extra_len - extra is NULL for restricted events */ 5228c2ecf20Sopenharmony_ci if (extra != NULL) 5238c2ecf20Sopenharmony_ci extra_len = wrqu->data.length * descr->token_size; 5248c2ecf20Sopenharmony_ci /* Always at an offset in wrqu */ 5258c2ecf20Sopenharmony_ci wrqu_off = IW_EV_POINT_OFF; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Total length of the event */ 5298c2ecf20Sopenharmony_ci hdr_len = event_type_size[descr->header_type]; 5308c2ecf20Sopenharmony_ci event_len = hdr_len + extra_len; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * The problem for 64/32 bit. 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * On 64-bit, a regular event is laid out as follows: 5368c2ecf20Sopenharmony_ci * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 5378c2ecf20Sopenharmony_ci * | event.len | event.cmd | p a d d i n g | 5388c2ecf20Sopenharmony_ci * | wrqu data ... (with the correct size) | 5398c2ecf20Sopenharmony_ci * 5408c2ecf20Sopenharmony_ci * This padding exists because we manipulate event->u, 5418c2ecf20Sopenharmony_ci * and 'event' is not packed. 5428c2ecf20Sopenharmony_ci * 5438c2ecf20Sopenharmony_ci * An iw_point event is laid out like this instead: 5448c2ecf20Sopenharmony_ci * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 5458c2ecf20Sopenharmony_ci * | event.len | event.cmd | p a d d i n g | 5468c2ecf20Sopenharmony_ci * | iwpnt.len | iwpnt.flg | p a d d i n g | 5478c2ecf20Sopenharmony_ci * | extra data ... 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * The second padding exists because struct iw_point is extended, 5508c2ecf20Sopenharmony_ci * but this depends on the platform... 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * On 32-bit, all the padding shouldn't be there. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5568c2ecf20Sopenharmony_ci if (!skb) 5578c2ecf20Sopenharmony_ci return; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Send via the RtNetlink event channel */ 5608c2ecf20Sopenharmony_ci nlh = rtnetlink_ifinfo_prep(dev, skb); 5618c2ecf20Sopenharmony_ci if (WARN_ON(!nlh)) { 5628c2ecf20Sopenharmony_ci kfree_skb(skb); 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Add the wireless events in the netlink packet */ 5678c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IFLA_WIRELESS, event_len); 5688c2ecf20Sopenharmony_ci if (!nla) { 5698c2ecf20Sopenharmony_ci kfree_skb(skb); 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci event = nla_data(nla); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Fill event - first clear to avoid data leaking */ 5758c2ecf20Sopenharmony_ci memset(event, 0, hdr_len); 5768c2ecf20Sopenharmony_ci event->len = event_len; 5778c2ecf20Sopenharmony_ci event->cmd = cmd; 5788c2ecf20Sopenharmony_ci memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); 5798c2ecf20Sopenharmony_ci if (extra_len) 5808c2ecf20Sopenharmony_ci memcpy(((char *) event) + hdr_len, extra, extra_len); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 5838c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 5848c2ecf20Sopenharmony_ci hdr_len = compat_event_type_size[descr->header_type]; 5858c2ecf20Sopenharmony_ci event_len = hdr_len + extra_len; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5888c2ecf20Sopenharmony_ci if (!compskb) { 5898c2ecf20Sopenharmony_ci kfree_skb(skb); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Send via the RtNetlink event channel */ 5948c2ecf20Sopenharmony_ci nlh = rtnetlink_ifinfo_prep(dev, compskb); 5958c2ecf20Sopenharmony_ci if (WARN_ON(!nlh)) { 5968c2ecf20Sopenharmony_ci kfree_skb(skb); 5978c2ecf20Sopenharmony_ci kfree_skb(compskb); 5988c2ecf20Sopenharmony_ci return; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* Add the wireless events in the netlink packet */ 6028c2ecf20Sopenharmony_ci nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); 6038c2ecf20Sopenharmony_ci if (!nla) { 6048c2ecf20Sopenharmony_ci kfree_skb(skb); 6058c2ecf20Sopenharmony_ci kfree_skb(compskb); 6068c2ecf20Sopenharmony_ci return; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci compat_event = nla_data(nla); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci compat_event->len = event_len; 6118c2ecf20Sopenharmony_ci compat_event->cmd = cmd; 6128c2ecf20Sopenharmony_ci if (descr->header_type == IW_HEADER_TYPE_POINT) { 6138c2ecf20Sopenharmony_ci compat_wrqu.length = wrqu->data.length; 6148c2ecf20Sopenharmony_ci compat_wrqu.flags = wrqu->data.flags; 6158c2ecf20Sopenharmony_ci memcpy(&compat_event->pointer, 6168c2ecf20Sopenharmony_ci ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, 6178c2ecf20Sopenharmony_ci hdr_len - IW_EV_COMPAT_LCP_LEN); 6188c2ecf20Sopenharmony_ci if (extra_len) 6198c2ecf20Sopenharmony_ci memcpy(((char *) compat_event) + hdr_len, 6208c2ecf20Sopenharmony_ci extra, extra_len); 6218c2ecf20Sopenharmony_ci } else { 6228c2ecf20Sopenharmony_ci /* extra_len must be zero, so no if (extra) needed */ 6238c2ecf20Sopenharmony_ci memcpy(&compat_event->pointer, wrqu, 6248c2ecf20Sopenharmony_ci hdr_len - IW_EV_COMPAT_LCP_LEN); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci nlmsg_end(compskb, nlh); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci skb_shinfo(skb)->frag_list = compskb; 6308c2ecf20Sopenharmony_ci#endif 6318c2ecf20Sopenharmony_ci skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); 6328c2ecf20Sopenharmony_ci schedule_work(&wireless_nlevent_work); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wireless_send_event); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/* IW handlers */ 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistruct iw_statistics *get_wireless_stats(struct net_device *dev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 6438c2ecf20Sopenharmony_ci if ((dev->wireless_handlers != NULL) && 6448c2ecf20Sopenharmony_ci (dev->wireless_handlers->get_wireless_stats != NULL)) 6458c2ecf20Sopenharmony_ci return dev->wireless_handlers->get_wireless_stats(dev); 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 6498c2ecf20Sopenharmony_ci if (dev->ieee80211_ptr && 6508c2ecf20Sopenharmony_ci dev->ieee80211_ptr->wiphy && 6518c2ecf20Sopenharmony_ci dev->ieee80211_ptr->wiphy->wext && 6528c2ecf20Sopenharmony_ci dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) 6538c2ecf20Sopenharmony_ci return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); 6548c2ecf20Sopenharmony_ci#endif 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* not found */ 6578c2ecf20Sopenharmony_ci return NULL; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* noinline to avoid a bogus warning with -O3 */ 6618c2ecf20Sopenharmony_cistatic noinline int iw_handler_get_iwstats(struct net_device * dev, 6628c2ecf20Sopenharmony_ci struct iw_request_info * info, 6638c2ecf20Sopenharmony_ci union iwreq_data * wrqu, 6648c2ecf20Sopenharmony_ci char * extra) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci /* Get stats from the driver */ 6678c2ecf20Sopenharmony_ci struct iw_statistics *stats; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci stats = get_wireless_stats(dev); 6708c2ecf20Sopenharmony_ci if (stats) { 6718c2ecf20Sopenharmony_ci /* Copy statistics to extra */ 6728c2ecf20Sopenharmony_ci memcpy(extra, stats, sizeof(struct iw_statistics)); 6738c2ecf20Sopenharmony_ci wrqu->data.length = sizeof(struct iw_statistics); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Check if we need to clear the updated flag */ 6768c2ecf20Sopenharmony_ci if (wrqu->data.flags != 0) 6778c2ecf20Sopenharmony_ci stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci } else 6808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic iw_handler get_handler(struct net_device *dev, unsigned int cmd) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci /* Don't "optimise" the following variable, it will crash */ 6868c2ecf20Sopenharmony_ci unsigned int index; /* *MUST* be unsigned */ 6878c2ecf20Sopenharmony_ci const struct iw_handler_def *handlers = NULL; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 6908c2ecf20Sopenharmony_ci if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) 6918c2ecf20Sopenharmony_ci handlers = dev->ieee80211_ptr->wiphy->wext; 6928c2ecf20Sopenharmony_ci#endif 6938c2ecf20Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 6948c2ecf20Sopenharmony_ci if (dev->wireless_handlers) 6958c2ecf20Sopenharmony_ci handlers = dev->wireless_handlers; 6968c2ecf20Sopenharmony_ci#endif 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!handlers) 6998c2ecf20Sopenharmony_ci return NULL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Try as a standard command */ 7028c2ecf20Sopenharmony_ci index = IW_IOCTL_IDX(cmd); 7038c2ecf20Sopenharmony_ci if (index < handlers->num_standard) 7048c2ecf20Sopenharmony_ci return handlers->standard[index]; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci#ifdef CONFIG_WEXT_PRIV 7078c2ecf20Sopenharmony_ci /* Try as a private command */ 7088c2ecf20Sopenharmony_ci index = cmd - SIOCIWFIRSTPRIV; 7098c2ecf20Sopenharmony_ci if (index < handlers->num_private) 7108c2ecf20Sopenharmony_ci return handlers->private[index]; 7118c2ecf20Sopenharmony_ci#endif 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* Not found */ 7148c2ecf20Sopenharmony_ci return NULL; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, 7188c2ecf20Sopenharmony_ci const struct iw_ioctl_description *descr, 7198c2ecf20Sopenharmony_ci iw_handler handler, struct net_device *dev, 7208c2ecf20Sopenharmony_ci struct iw_request_info *info) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int err, extra_size, user_length = 0, essid_compat = 0; 7238c2ecf20Sopenharmony_ci char *extra; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Calculate space needed by arguments. Always allocate 7268c2ecf20Sopenharmony_ci * for max space. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_ci extra_size = descr->max_tokens * descr->token_size; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Check need for ESSID compatibility for WE < 21 */ 7318c2ecf20Sopenharmony_ci switch (cmd) { 7328c2ecf20Sopenharmony_ci case SIOCSIWESSID: 7338c2ecf20Sopenharmony_ci case SIOCGIWESSID: 7348c2ecf20Sopenharmony_ci case SIOCSIWNICKN: 7358c2ecf20Sopenharmony_ci case SIOCGIWNICKN: 7368c2ecf20Sopenharmony_ci if (iwp->length == descr->max_tokens + 1) 7378c2ecf20Sopenharmony_ci essid_compat = 1; 7388c2ecf20Sopenharmony_ci else if (IW_IS_SET(cmd) && (iwp->length != 0)) { 7398c2ecf20Sopenharmony_ci char essid[IW_ESSID_MAX_SIZE + 1]; 7408c2ecf20Sopenharmony_ci unsigned int len; 7418c2ecf20Sopenharmony_ci len = iwp->length * descr->token_size; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (len > IW_ESSID_MAX_SIZE) 7448c2ecf20Sopenharmony_ci return -EFAULT; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci err = copy_from_user(essid, iwp->pointer, len); 7478c2ecf20Sopenharmony_ci if (err) 7488c2ecf20Sopenharmony_ci return -EFAULT; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (essid[iwp->length - 1] == '\0') 7518c2ecf20Sopenharmony_ci essid_compat = 1; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci default: 7558c2ecf20Sopenharmony_ci break; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci iwp->length -= essid_compat; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* Check what user space is giving us */ 7618c2ecf20Sopenharmony_ci if (IW_IS_SET(cmd)) { 7628c2ecf20Sopenharmony_ci /* Check NULL pointer */ 7638c2ecf20Sopenharmony_ci if (!iwp->pointer && iwp->length != 0) 7648c2ecf20Sopenharmony_ci return -EFAULT; 7658c2ecf20Sopenharmony_ci /* Check if number of token fits within bounds */ 7668c2ecf20Sopenharmony_ci if (iwp->length > descr->max_tokens) 7678c2ecf20Sopenharmony_ci return -E2BIG; 7688c2ecf20Sopenharmony_ci if (iwp->length < descr->min_tokens) 7698c2ecf20Sopenharmony_ci return -EINVAL; 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci /* Check NULL pointer */ 7728c2ecf20Sopenharmony_ci if (!iwp->pointer) 7738c2ecf20Sopenharmony_ci return -EFAULT; 7748c2ecf20Sopenharmony_ci /* Save user space buffer size for checking */ 7758c2ecf20Sopenharmony_ci user_length = iwp->length; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Don't check if user_length > max to allow forward 7788c2ecf20Sopenharmony_ci * compatibility. The test user_length < min is 7798c2ecf20Sopenharmony_ci * implied by the test at the end. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Support for very large requests */ 7838c2ecf20Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_NOMAX) && 7848c2ecf20Sopenharmony_ci (user_length > descr->max_tokens)) { 7858c2ecf20Sopenharmony_ci /* Allow userspace to GET more than max so 7868c2ecf20Sopenharmony_ci * we can support any size GET requests. 7878c2ecf20Sopenharmony_ci * There is still a limit : -ENOMEM. 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_ci extra_size = user_length * descr->token_size; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Note : user_length is originally a __u16, 7928c2ecf20Sopenharmony_ci * and token_size is controlled by us, 7938c2ecf20Sopenharmony_ci * so extra_size won't get negative and 7948c2ecf20Sopenharmony_ci * won't overflow... 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Sanity-check to ensure we never end up _allocating_ zero 8008c2ecf20Sopenharmony_ci * bytes of data for extra. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci if (extra_size <= 0) 8038c2ecf20Sopenharmony_ci return -EFAULT; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* kzalloc() ensures NULL-termination for essid_compat. */ 8068c2ecf20Sopenharmony_ci extra = kzalloc(extra_size, GFP_KERNEL); 8078c2ecf20Sopenharmony_ci if (!extra) 8088c2ecf20Sopenharmony_ci return -ENOMEM; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* If it is a SET, get all the extra data in here */ 8118c2ecf20Sopenharmony_ci if (IW_IS_SET(cmd) && (iwp->length != 0)) { 8128c2ecf20Sopenharmony_ci if (copy_from_user(extra, iwp->pointer, 8138c2ecf20Sopenharmony_ci iwp->length * 8148c2ecf20Sopenharmony_ci descr->token_size)) { 8158c2ecf20Sopenharmony_ci err = -EFAULT; 8168c2ecf20Sopenharmony_ci goto out; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (cmd == SIOCSIWENCODEEXT) { 8208c2ecf20Sopenharmony_ci struct iw_encode_ext *ee = (void *) extra; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (iwp->length < sizeof(*ee) + ee->key_len) { 8238c2ecf20Sopenharmony_ci err = -EFAULT; 8248c2ecf20Sopenharmony_ci goto out; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { 8308c2ecf20Sopenharmony_ci /* 8318c2ecf20Sopenharmony_ci * If this is a GET, but not NOMAX, it means that the extra 8328c2ecf20Sopenharmony_ci * data is not bounded by userspace, but by max_tokens. Thus 8338c2ecf20Sopenharmony_ci * set the length to max_tokens. This matches the extra data 8348c2ecf20Sopenharmony_ci * allocation. 8358c2ecf20Sopenharmony_ci * The driver should fill it with the number of tokens it 8368c2ecf20Sopenharmony_ci * provided, and it may check iwp->length rather than having 8378c2ecf20Sopenharmony_ci * knowledge of max_tokens. If the driver doesn't change the 8388c2ecf20Sopenharmony_ci * iwp->length, this ioctl just copies back max_token tokens 8398c2ecf20Sopenharmony_ci * filled with zeroes. Hopefully the driver isn't claiming 8408c2ecf20Sopenharmony_ci * them to be valid data. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci iwp->length = descr->max_tokens; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci err = handler(dev, info, (union iwreq_data *) iwp, extra); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci iwp->length += essid_compat; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* If we have something to return to the user */ 8508c2ecf20Sopenharmony_ci if (!err && IW_IS_GET(cmd)) { 8518c2ecf20Sopenharmony_ci /* Check if there is enough buffer up there */ 8528c2ecf20Sopenharmony_ci if (user_length < iwp->length) { 8538c2ecf20Sopenharmony_ci err = -E2BIG; 8548c2ecf20Sopenharmony_ci goto out; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (copy_to_user(iwp->pointer, extra, 8588c2ecf20Sopenharmony_ci iwp->length * 8598c2ecf20Sopenharmony_ci descr->token_size)) { 8608c2ecf20Sopenharmony_ci err = -EFAULT; 8618c2ecf20Sopenharmony_ci goto out; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci /* Generate an event to notify listeners of the change */ 8668c2ecf20Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_EVENT) && 8678c2ecf20Sopenharmony_ci ((err == 0) || (err == -EIWCOMMIT))) { 8688c2ecf20Sopenharmony_ci union iwreq_data *data = (union iwreq_data *) iwp; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (descr->flags & IW_DESCR_FLAG_RESTRICT) 8718c2ecf20Sopenharmony_ci /* If the event is restricted, don't 8728c2ecf20Sopenharmony_ci * export the payload. 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_ci wireless_send_event(dev, cmd, data, NULL); 8758c2ecf20Sopenharmony_ci else 8768c2ecf20Sopenharmony_ci wireless_send_event(dev, cmd, data, extra); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ciout: 8808c2ecf20Sopenharmony_ci kfree(extra); 8818c2ecf20Sopenharmony_ci return err; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/* 8858c2ecf20Sopenharmony_ci * Call the commit handler in the driver 8868c2ecf20Sopenharmony_ci * (if exist and if conditions are right) 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * Note : our current commit strategy is currently pretty dumb, 8898c2ecf20Sopenharmony_ci * but we will be able to improve on that... 8908c2ecf20Sopenharmony_ci * The goal is to try to agreagate as many changes as possible 8918c2ecf20Sopenharmony_ci * before doing the commit. Drivers that will define a commit handler 8928c2ecf20Sopenharmony_ci * are usually those that need a reset after changing parameters, so 8938c2ecf20Sopenharmony_ci * we want to minimise the number of reset. 8948c2ecf20Sopenharmony_ci * A cool idea is to use a timer : at each "set" command, we re-set the 8958c2ecf20Sopenharmony_ci * timer, when the timer eventually fires, we call the driver. 8968c2ecf20Sopenharmony_ci * Hopefully, more on that later. 8978c2ecf20Sopenharmony_ci * 8988c2ecf20Sopenharmony_ci * Also, I'm waiting to see how many people will complain about the 8998c2ecf20Sopenharmony_ci * netif_running(dev) test. I'm open on that one... 9008c2ecf20Sopenharmony_ci * Hopefully, the driver will remember to do a commit in "open()" ;-) 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_ciint call_commit_handler(struct net_device *dev) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 9058c2ecf20Sopenharmony_ci if (netif_running(dev) && 9068c2ecf20Sopenharmony_ci dev->wireless_handlers && 9078c2ecf20Sopenharmony_ci dev->wireless_handlers->standard[0]) 9088c2ecf20Sopenharmony_ci /* Call the commit handler on the driver */ 9098c2ecf20Sopenharmony_ci return dev->wireless_handlers->standard[0](dev, NULL, 9108c2ecf20Sopenharmony_ci NULL, NULL); 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci return 0; /* Command completed successfully */ 9138c2ecf20Sopenharmony_ci#else 9148c2ecf20Sopenharmony_ci /* cfg80211 has no commit */ 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci#endif 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci/* 9208c2ecf20Sopenharmony_ci * Main IOCTl dispatcher. 9218c2ecf20Sopenharmony_ci * Check the type of IOCTL and call the appropriate wrapper... 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_cistatic int wireless_process_ioctl(struct net *net, struct iwreq *iwr, 9248c2ecf20Sopenharmony_ci unsigned int cmd, 9258c2ecf20Sopenharmony_ci struct iw_request_info *info, 9268c2ecf20Sopenharmony_ci wext_ioctl_func standard, 9278c2ecf20Sopenharmony_ci wext_ioctl_func private) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct net_device *dev; 9308c2ecf20Sopenharmony_ci iw_handler handler; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* Permissions are already checked in dev_ioctl() before calling us. 9338c2ecf20Sopenharmony_ci * The copy_to/from_user() of ifr is also dealt with in there */ 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* Make sure the device exist */ 9368c2ecf20Sopenharmony_ci if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL) 9378c2ecf20Sopenharmony_ci return -ENODEV; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* A bunch of special cases, then the generic case... 9408c2ecf20Sopenharmony_ci * Note that 'cmd' is already filtered in dev_ioctl() with 9418c2ecf20Sopenharmony_ci * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ 9428c2ecf20Sopenharmony_ci if (cmd == SIOCGIWSTATS) 9438c2ecf20Sopenharmony_ci return standard(dev, iwr, cmd, info, 9448c2ecf20Sopenharmony_ci &iw_handler_get_iwstats); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci#ifdef CONFIG_WEXT_PRIV 9478c2ecf20Sopenharmony_ci if (cmd == SIOCGIWPRIV && dev->wireless_handlers) 9488c2ecf20Sopenharmony_ci return standard(dev, iwr, cmd, info, 9498c2ecf20Sopenharmony_ci iw_handler_get_private); 9508c2ecf20Sopenharmony_ci#endif 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Basic check */ 9538c2ecf20Sopenharmony_ci if (!netif_device_present(dev)) 9548c2ecf20Sopenharmony_ci return -ENODEV; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* New driver API : try to find the handler */ 9578c2ecf20Sopenharmony_ci handler = get_handler(dev, cmd); 9588c2ecf20Sopenharmony_ci if (handler) { 9598c2ecf20Sopenharmony_ci /* Standard and private are not the same */ 9608c2ecf20Sopenharmony_ci if (cmd < SIOCIWFIRSTPRIV) 9618c2ecf20Sopenharmony_ci return standard(dev, iwr, cmd, info, handler); 9628c2ecf20Sopenharmony_ci else if (private) 9638c2ecf20Sopenharmony_ci return private(dev, iwr, cmd, info, handler); 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci/* If command is `set a parameter', or `get the encoding parameters', 9698c2ecf20Sopenharmony_ci * check if the user has the right to do it. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_cistatic int wext_permission_check(unsigned int cmd) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || 9748c2ecf20Sopenharmony_ci cmd == SIOCGIWENCODEEXT) && 9758c2ecf20Sopenharmony_ci !capable(CAP_NET_ADMIN)) 9768c2ecf20Sopenharmony_ci return -EPERM; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* entry point from dev ioctl */ 9828c2ecf20Sopenharmony_cistatic int wext_ioctl_dispatch(struct net *net, struct iwreq *iwr, 9838c2ecf20Sopenharmony_ci unsigned int cmd, struct iw_request_info *info, 9848c2ecf20Sopenharmony_ci wext_ioctl_func standard, 9858c2ecf20Sopenharmony_ci wext_ioctl_func private) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci int ret = wext_permission_check(cmd); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (ret) 9908c2ecf20Sopenharmony_ci return ret; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci dev_load(net, iwr->ifr_name); 9938c2ecf20Sopenharmony_ci rtnl_lock(); 9948c2ecf20Sopenharmony_ci ret = wireless_process_ioctl(net, iwr, cmd, info, standard, private); 9958c2ecf20Sopenharmony_ci rtnl_unlock(); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return ret; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/* 10018c2ecf20Sopenharmony_ci * Wrapper to call a standard Wireless Extension handler. 10028c2ecf20Sopenharmony_ci * We do various checks and also take care of moving data between 10038c2ecf20Sopenharmony_ci * user space and kernel space. 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_cistatic int ioctl_standard_call(struct net_device * dev, 10068c2ecf20Sopenharmony_ci struct iwreq *iwr, 10078c2ecf20Sopenharmony_ci unsigned int cmd, 10088c2ecf20Sopenharmony_ci struct iw_request_info *info, 10098c2ecf20Sopenharmony_ci iw_handler handler) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci const struct iw_ioctl_description * descr; 10128c2ecf20Sopenharmony_ci int ret = -EINVAL; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Get the description of the IOCTL */ 10158c2ecf20Sopenharmony_ci if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) 10168c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10178c2ecf20Sopenharmony_ci descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Check if we have a pointer to user space data or not */ 10208c2ecf20Sopenharmony_ci if (descr->header_type != IW_HEADER_TYPE_POINT) { 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* No extra arguments. Trivial to handle */ 10238c2ecf20Sopenharmony_ci ret = handler(dev, info, &(iwr->u), NULL); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Generate an event to notify listeners of the change */ 10268c2ecf20Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_EVENT) && 10278c2ecf20Sopenharmony_ci ((ret == 0) || (ret == -EIWCOMMIT))) 10288c2ecf20Sopenharmony_ci wireless_send_event(dev, cmd, &(iwr->u), NULL); 10298c2ecf20Sopenharmony_ci } else { 10308c2ecf20Sopenharmony_ci ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, 10318c2ecf20Sopenharmony_ci handler, dev, info); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* Call commit handler if needed and defined */ 10358c2ecf20Sopenharmony_ci if (ret == -EIWCOMMIT) 10368c2ecf20Sopenharmony_ci ret = call_commit_handler(dev); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci /* Here, we will generate the appropriate event if needed */ 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return ret; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ciint wext_handle_ioctl(struct net *net, unsigned int cmd, void __user *arg) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct iw_request_info info = { .cmd = cmd, .flags = 0 }; 10478c2ecf20Sopenharmony_ci struct iwreq iwr; 10488c2ecf20Sopenharmony_ci int ret; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (copy_from_user(&iwr, arg, sizeof(iwr))) 10518c2ecf20Sopenharmony_ci return -EFAULT; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = wext_ioctl_dispatch(net, &iwr, cmd, &info, 10568c2ecf20Sopenharmony_ci ioctl_standard_call, 10578c2ecf20Sopenharmony_ci ioctl_private_call); 10588c2ecf20Sopenharmony_ci if (ret >= 0 && 10598c2ecf20Sopenharmony_ci IW_IS_GET(cmd) && 10608c2ecf20Sopenharmony_ci copy_to_user(arg, &iwr, sizeof(struct iwreq))) 10618c2ecf20Sopenharmony_ci return -EFAULT; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 10678c2ecf20Sopenharmony_cistatic int compat_standard_call(struct net_device *dev, 10688c2ecf20Sopenharmony_ci struct iwreq *iwr, 10698c2ecf20Sopenharmony_ci unsigned int cmd, 10708c2ecf20Sopenharmony_ci struct iw_request_info *info, 10718c2ecf20Sopenharmony_ci iw_handler handler) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci const struct iw_ioctl_description *descr; 10748c2ecf20Sopenharmony_ci struct compat_iw_point *iwp_compat; 10758c2ecf20Sopenharmony_ci struct iw_point iwp; 10768c2ecf20Sopenharmony_ci int err; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci descr = standard_ioctl + IW_IOCTL_IDX(cmd); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (descr->header_type != IW_HEADER_TYPE_POINT) 10818c2ecf20Sopenharmony_ci return ioctl_standard_call(dev, iwr, cmd, info, handler); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci iwp_compat = (struct compat_iw_point *) &iwr->u.data; 10848c2ecf20Sopenharmony_ci iwp.pointer = compat_ptr(iwp_compat->pointer); 10858c2ecf20Sopenharmony_ci iwp.length = iwp_compat->length; 10868c2ecf20Sopenharmony_ci iwp.flags = iwp_compat->flags; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci iwp_compat->pointer = ptr_to_compat(iwp.pointer); 10918c2ecf20Sopenharmony_ci iwp_compat->length = iwp.length; 10928c2ecf20Sopenharmony_ci iwp_compat->flags = iwp.flags; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return err; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ciint compat_wext_handle_ioctl(struct net *net, unsigned int cmd, 10988c2ecf20Sopenharmony_ci unsigned long arg) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 11018c2ecf20Sopenharmony_ci struct iw_request_info info; 11028c2ecf20Sopenharmony_ci struct iwreq iwr; 11038c2ecf20Sopenharmony_ci char *colon; 11048c2ecf20Sopenharmony_ci int ret; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) 11078c2ecf20Sopenharmony_ci return -EFAULT; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci iwr.ifr_name[IFNAMSIZ-1] = 0; 11108c2ecf20Sopenharmony_ci colon = strchr(iwr.ifr_name, ':'); 11118c2ecf20Sopenharmony_ci if (colon) 11128c2ecf20Sopenharmony_ci *colon = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci info.cmd = cmd; 11158c2ecf20Sopenharmony_ci info.flags = IW_REQUEST_FLAG_COMPAT; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci ret = wext_ioctl_dispatch(net, &iwr, cmd, &info, 11188c2ecf20Sopenharmony_ci compat_standard_call, 11198c2ecf20Sopenharmony_ci compat_private_call); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (ret >= 0 && 11228c2ecf20Sopenharmony_ci IW_IS_GET(cmd) && 11238c2ecf20Sopenharmony_ci copy_to_user(argp, &iwr, sizeof(struct iwreq))) 11248c2ecf20Sopenharmony_ci return -EFAULT; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci return ret; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci#endif 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cichar *iwe_stream_add_event(struct iw_request_info *info, char *stream, 11318c2ecf20Sopenharmony_ci char *ends, struct iw_event *iwe, int event_len) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci event_len = iwe_stream_event_len_adjust(info, event_len); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* Check if it's possible */ 11388c2ecf20Sopenharmony_ci if (likely((stream + event_len) < ends)) { 11398c2ecf20Sopenharmony_ci iwe->len = event_len; 11408c2ecf20Sopenharmony_ci /* Beware of alignement issues on 64 bits */ 11418c2ecf20Sopenharmony_ci memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); 11428c2ecf20Sopenharmony_ci memcpy(stream + lcp_len, &iwe->u, 11438c2ecf20Sopenharmony_ci event_len - lcp_len); 11448c2ecf20Sopenharmony_ci stream += event_len; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return stream; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_event); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cichar *iwe_stream_add_point(struct iw_request_info *info, char *stream, 11528c2ecf20Sopenharmony_ci char *ends, struct iw_event *iwe, char *extra) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci int event_len = iwe_stream_point_len(info) + iwe->u.data.length; 11558c2ecf20Sopenharmony_ci int point_len = iwe_stream_point_len(info); 11568c2ecf20Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* Check if it's possible */ 11598c2ecf20Sopenharmony_ci if (likely((stream + event_len) < ends)) { 11608c2ecf20Sopenharmony_ci iwe->len = event_len; 11618c2ecf20Sopenharmony_ci memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); 11628c2ecf20Sopenharmony_ci memcpy(stream + lcp_len, 11638c2ecf20Sopenharmony_ci ((char *) &iwe->u) + IW_EV_POINT_OFF, 11648c2ecf20Sopenharmony_ci IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); 11658c2ecf20Sopenharmony_ci if (iwe->u.data.length && extra) 11668c2ecf20Sopenharmony_ci memcpy(stream + point_len, extra, iwe->u.data.length); 11678c2ecf20Sopenharmony_ci stream += event_len; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return stream; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_point); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cichar *iwe_stream_add_value(struct iw_request_info *info, char *event, 11758c2ecf20Sopenharmony_ci char *value, char *ends, struct iw_event *iwe, 11768c2ecf20Sopenharmony_ci int event_len) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Don't duplicate LCP */ 11818c2ecf20Sopenharmony_ci event_len -= IW_EV_LCP_LEN; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Check if it's possible */ 11848c2ecf20Sopenharmony_ci if (likely((value + event_len) < ends)) { 11858c2ecf20Sopenharmony_ci /* Add new value */ 11868c2ecf20Sopenharmony_ci memcpy(value, &iwe->u, event_len); 11878c2ecf20Sopenharmony_ci value += event_len; 11888c2ecf20Sopenharmony_ci /* Patch LCP */ 11898c2ecf20Sopenharmony_ci iwe->len = value - event; 11908c2ecf20Sopenharmony_ci memcpy(event, (char *) iwe, lcp_len); 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci return value; 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_value); 1196