162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file implement the Wireless Extensions core API. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 562306a36Sopenharmony_ci * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. 662306a36Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * (As all part of the Linux kernel, this file is GPL) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/wireless.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <net/cfg80211.h> 1862306a36Sopenharmony_ci#include <net/iw_handler.h> 1962306a36Sopenharmony_ci#include <net/netlink.h> 2062306a36Sopenharmony_ci#include <net/wext.h> 2162306a36Sopenharmony_ci#include <net/net_namespace.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_citypedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, 2462306a36Sopenharmony_ci unsigned int, struct iw_request_info *, 2562306a36Sopenharmony_ci iw_handler); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Meta-data about all the standard Wireless Extension request we 3062306a36Sopenharmony_ci * know about. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic const struct iw_ioctl_description standard_ioctl[] = { 3362306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { 3462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 3562306a36Sopenharmony_ci }, 3662306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNAME)] = { 3762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_CHAR, 3862306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 3962306a36Sopenharmony_ci }, 4062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWNWID)] = { 4162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 4262306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 4362306a36Sopenharmony_ci }, 4462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNWID)] = { 4562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 4662306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 4762306a36Sopenharmony_ci }, 4862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWFREQ)] = { 4962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_FREQ, 5062306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 5162306a36Sopenharmony_ci }, 5262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWFREQ)] = { 5362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_FREQ, 5462306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWMODE)] = { 5762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_UINT, 5862306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 5962306a36Sopenharmony_ci }, 6062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWMODE)] = { 6162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_UINT, 6262306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 6362306a36Sopenharmony_ci }, 6462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSENS)] = { 6562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSENS)] = { 6862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRANGE)] = { 7162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 7262306a36Sopenharmony_ci }, 7362306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRANGE)] = { 7462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 7562306a36Sopenharmony_ci .token_size = 1, 7662306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_range), 7762306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 7862306a36Sopenharmony_ci }, 7962306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPRIV)] = { 8062306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 8162306a36Sopenharmony_ci }, 8262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ 8362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 8462306a36Sopenharmony_ci .token_size = sizeof(struct iw_priv_args), 8562306a36Sopenharmony_ci .max_tokens = 16, 8662306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSTATS)] = { 8962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_NULL, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ 9262306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 9362306a36Sopenharmony_ci .token_size = 1, 9462306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_statistics), 9562306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSPY)] = { 9862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 9962306a36Sopenharmony_ci .token_size = sizeof(struct sockaddr), 10062306a36Sopenharmony_ci .max_tokens = IW_MAX_SPY, 10162306a36Sopenharmony_ci }, 10262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSPY)] = { 10362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 10462306a36Sopenharmony_ci .token_size = sizeof(struct sockaddr) + 10562306a36Sopenharmony_ci sizeof(struct iw_quality), 10662306a36Sopenharmony_ci .max_tokens = IW_MAX_SPY, 10762306a36Sopenharmony_ci }, 10862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { 10962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 11062306a36Sopenharmony_ci .token_size = sizeof(struct iw_thrspy), 11162306a36Sopenharmony_ci .min_tokens = 1, 11262306a36Sopenharmony_ci .max_tokens = 1, 11362306a36Sopenharmony_ci }, 11462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { 11562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 11662306a36Sopenharmony_ci .token_size = sizeof(struct iw_thrspy), 11762306a36Sopenharmony_ci .min_tokens = 1, 11862306a36Sopenharmony_ci .max_tokens = 1, 11962306a36Sopenharmony_ci }, 12062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWAP)] = { 12162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAP)] = { 12462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 12562306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWMLME)] = { 12862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 12962306a36Sopenharmony_ci .token_size = 1, 13062306a36Sopenharmony_ci .min_tokens = sizeof(struct iw_mlme), 13162306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_mlme), 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { 13462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 13562306a36Sopenharmony_ci .token_size = sizeof(struct sockaddr) + 13662306a36Sopenharmony_ci sizeof(struct iw_quality), 13762306a36Sopenharmony_ci .max_tokens = IW_MAX_AP, 13862306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 13962306a36Sopenharmony_ci }, 14062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWSCAN)] = { 14162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 14262306a36Sopenharmony_ci .token_size = 1, 14362306a36Sopenharmony_ci .min_tokens = 0, 14462306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_scan_req), 14562306a36Sopenharmony_ci }, 14662306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWSCAN)] = { 14762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 14862306a36Sopenharmony_ci .token_size = 1, 14962306a36Sopenharmony_ci .max_tokens = IW_SCAN_MAX_DATA, 15062306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_NOMAX, 15162306a36Sopenharmony_ci }, 15262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWESSID)] = { 15362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 15462306a36Sopenharmony_ci .token_size = 1, 15562306a36Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 15662306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWESSID)] = { 15962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 16062306a36Sopenharmony_ci .token_size = 1, 16162306a36Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 16262306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP, 16362306a36Sopenharmony_ci }, 16462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWNICKN)] = { 16562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 16662306a36Sopenharmony_ci .token_size = 1, 16762306a36Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWNICKN)] = { 17062306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 17162306a36Sopenharmony_ci .token_size = 1, 17262306a36Sopenharmony_ci .max_tokens = IW_ESSID_MAX_SIZE, 17362306a36Sopenharmony_ci }, 17462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRATE)] = { 17562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 17662306a36Sopenharmony_ci }, 17762306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRATE)] = { 17862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 17962306a36Sopenharmony_ci }, 18062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRTS)] = { 18162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRTS)] = { 18462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 18562306a36Sopenharmony_ci }, 18662306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWFRAG)] = { 18762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 18862306a36Sopenharmony_ci }, 18962306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWFRAG)] = { 19062306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 19162306a36Sopenharmony_ci }, 19262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { 19362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 19462306a36Sopenharmony_ci }, 19562306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { 19662306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 19762306a36Sopenharmony_ci }, 19862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWRETRY)] = { 19962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 20062306a36Sopenharmony_ci }, 20162306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWRETRY)] = { 20262306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWENCODE)] = { 20562306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 20662306a36Sopenharmony_ci .token_size = 1, 20762306a36Sopenharmony_ci .max_tokens = IW_ENCODING_TOKEN_MAX, 20862306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWENCODE)] = { 21162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 21262306a36Sopenharmony_ci .token_size = 1, 21362306a36Sopenharmony_ci .max_tokens = IW_ENCODING_TOKEN_MAX, 21462306a36Sopenharmony_ci .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, 21562306a36Sopenharmony_ci }, 21662306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPOWER)] = { 21762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWPOWER)] = { 22062306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWGENIE)] = { 22362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 22462306a36Sopenharmony_ci .token_size = 1, 22562306a36Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 22662306a36Sopenharmony_ci }, 22762306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWGENIE)] = { 22862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 22962306a36Sopenharmony_ci .token_size = 1, 23062306a36Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 23162306a36Sopenharmony_ci }, 23262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWAUTH)] = { 23362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWAUTH)] = { 23662306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_PARAM, 23762306a36Sopenharmony_ci }, 23862306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { 23962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 24062306a36Sopenharmony_ci .token_size = 1, 24162306a36Sopenharmony_ci .min_tokens = sizeof(struct iw_encode_ext), 24262306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_encode_ext) + 24362306a36Sopenharmony_ci IW_ENCODING_TOKEN_MAX, 24462306a36Sopenharmony_ci }, 24562306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { 24662306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 24762306a36Sopenharmony_ci .token_size = 1, 24862306a36Sopenharmony_ci .min_tokens = sizeof(struct iw_encode_ext), 24962306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_encode_ext) + 25062306a36Sopenharmony_ci IW_ENCODING_TOKEN_MAX, 25162306a36Sopenharmony_ci }, 25262306a36Sopenharmony_ci [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { 25362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 25462306a36Sopenharmony_ci .token_size = 1, 25562306a36Sopenharmony_ci .min_tokens = sizeof(struct iw_pmksa), 25662306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_pmksa), 25762306a36Sopenharmony_ci }, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_cistatic const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 26262306a36Sopenharmony_ci * Meta-data about all the additional standard Wireless Extension events 26362306a36Sopenharmony_ci * we know about. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cistatic const struct iw_ioctl_description standard_event[] = { 26662306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVTXDROP)] = { 26762306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 26862306a36Sopenharmony_ci }, 26962306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVQUAL)] = { 27062306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_QUAL, 27162306a36Sopenharmony_ci }, 27262306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVCUSTOM)] = { 27362306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 27462306a36Sopenharmony_ci .token_size = 1, 27562306a36Sopenharmony_ci .max_tokens = IW_CUSTOM_MAX, 27662306a36Sopenharmony_ci }, 27762306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVREGISTERED)] = { 27862306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 27962306a36Sopenharmony_ci }, 28062306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVEXPIRED)] = { 28162306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_ADDR, 28262306a36Sopenharmony_ci }, 28362306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVGENIE)] = { 28462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 28562306a36Sopenharmony_ci .token_size = 1, 28662306a36Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { 28962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 29062306a36Sopenharmony_ci .token_size = 1, 29162306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_michaelmicfailure), 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVASSOCREQIE)] = { 29462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 29562306a36Sopenharmony_ci .token_size = 1, 29662306a36Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 29762306a36Sopenharmony_ci }, 29862306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { 29962306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 30062306a36Sopenharmony_ci .token_size = 1, 30162306a36Sopenharmony_ci .max_tokens = IW_GENERIC_IE_MAX, 30262306a36Sopenharmony_ci }, 30362306a36Sopenharmony_ci [IW_EVENT_IDX(IWEVPMKIDCAND)] = { 30462306a36Sopenharmony_ci .header_type = IW_HEADER_TYPE_POINT, 30562306a36Sopenharmony_ci .token_size = 1, 30662306a36Sopenharmony_ci .max_tokens = sizeof(struct iw_pmkid_cand), 30762306a36Sopenharmony_ci }, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_cistatic const unsigned int standard_event_num = ARRAY_SIZE(standard_event); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* Size (in bytes) of various events */ 31262306a36Sopenharmony_cistatic const int event_type_size[] = { 31362306a36Sopenharmony_ci IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 31462306a36Sopenharmony_ci 0, 31562306a36Sopenharmony_ci IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 31662306a36Sopenharmony_ci 0, 31762306a36Sopenharmony_ci IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ 31862306a36Sopenharmony_ci IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ 31962306a36Sopenharmony_ci IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 32062306a36Sopenharmony_ci 0, 32162306a36Sopenharmony_ci IW_EV_POINT_LEN, /* Without variable payload */ 32262306a36Sopenharmony_ci IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ 32362306a36Sopenharmony_ci IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 32762306a36Sopenharmony_cistatic const int compat_event_type_size[] = { 32862306a36Sopenharmony_ci IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 32962306a36Sopenharmony_ci 0, 33062306a36Sopenharmony_ci IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 33162306a36Sopenharmony_ci 0, 33262306a36Sopenharmony_ci IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ 33362306a36Sopenharmony_ci IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ 33462306a36Sopenharmony_ci IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 33562306a36Sopenharmony_ci 0, 33662306a36Sopenharmony_ci IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ 33762306a36Sopenharmony_ci IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ 33862306a36Sopenharmony_ci IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci#endif 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* IW event code */ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_civoid wireless_nlevent_flush(void) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct sk_buff *skb; 34862306a36Sopenharmony_ci struct net *net; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci down_read(&net_rwsem); 35162306a36Sopenharmony_ci for_each_net(net) { 35262306a36Sopenharmony_ci while ((skb = skb_dequeue(&net->wext_nlevents))) 35362306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, 35462306a36Sopenharmony_ci GFP_KERNEL); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci up_read(&net_rwsem); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wireless_nlevent_flush); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int wext_netdev_notifier_call(struct notifier_block *nb, 36162306a36Sopenharmony_ci unsigned long state, void *ptr) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * When a netdev changes state in any way, flush all pending messages 36562306a36Sopenharmony_ci * to avoid them going out in a strange order, e.g. RTM_NEWLINK after 36662306a36Sopenharmony_ci * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close() 36762306a36Sopenharmony_ci * or similar - all of which could otherwise happen due to delays from 36862306a36Sopenharmony_ci * schedule_work(). 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci wireless_nlevent_flush(); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return NOTIFY_OK; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic struct notifier_block wext_netdev_notifier = { 37662306a36Sopenharmony_ci .notifier_call = wext_netdev_notifier_call, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int __net_init wext_pernet_init(struct net *net) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci skb_queue_head_init(&net->wext_nlevents); 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void __net_exit wext_pernet_exit(struct net *net) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci skb_queue_purge(&net->wext_nlevents); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic struct pernet_operations wext_pernet_ops = { 39162306a36Sopenharmony_ci .init = wext_pernet_init, 39262306a36Sopenharmony_ci .exit = wext_pernet_exit, 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int __init wireless_nlevent_init(void) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci int err = register_pernet_subsys(&wext_pernet_ops); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (err) 40062306a36Sopenharmony_ci return err; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci err = register_netdevice_notifier(&wext_netdev_notifier); 40362306a36Sopenharmony_ci if (err) 40462306a36Sopenharmony_ci unregister_pernet_subsys(&wext_pernet_ops); 40562306a36Sopenharmony_ci return err; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cisubsys_initcall(wireless_nlevent_init); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* Process events generated by the wireless layer or the driver. */ 41162306a36Sopenharmony_cistatic void wireless_nlevent_process(struct work_struct *work) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci wireless_nlevent_flush(); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, 41962306a36Sopenharmony_ci struct sk_buff *skb) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct ifinfomsg *r; 42262306a36Sopenharmony_ci struct nlmsghdr *nlh; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); 42562306a36Sopenharmony_ci if (!nlh) 42662306a36Sopenharmony_ci return NULL; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci r = nlmsg_data(nlh); 42962306a36Sopenharmony_ci r->ifi_family = AF_UNSPEC; 43062306a36Sopenharmony_ci r->__ifi_pad = 0; 43162306a36Sopenharmony_ci r->ifi_type = dev->type; 43262306a36Sopenharmony_ci r->ifi_index = dev->ifindex; 43362306a36Sopenharmony_ci r->ifi_flags = dev_get_flags(dev); 43462306a36Sopenharmony_ci r->ifi_change = 0; /* Wireless changes don't affect those flags */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (nla_put_string(skb, IFLA_IFNAME, dev->name)) 43762306a36Sopenharmony_ci goto nla_put_failure; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return nlh; 44062306a36Sopenharmony_ci nla_put_failure: 44162306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 44262306a36Sopenharmony_ci return NULL; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * Main event dispatcher. Called from other parts and drivers. 44862306a36Sopenharmony_ci * Send the event on the appropriate channels. 44962306a36Sopenharmony_ci * May be called from interrupt context. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_civoid wireless_send_event(struct net_device * dev, 45262306a36Sopenharmony_ci unsigned int cmd, 45362306a36Sopenharmony_ci union iwreq_data * wrqu, 45462306a36Sopenharmony_ci const char * extra) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci const struct iw_ioctl_description * descr = NULL; 45762306a36Sopenharmony_ci int extra_len = 0; 45862306a36Sopenharmony_ci struct iw_event *event; /* Mallocated whole event */ 45962306a36Sopenharmony_ci int event_len; /* Its size */ 46062306a36Sopenharmony_ci int hdr_len; /* Size of the event header */ 46162306a36Sopenharmony_ci int wrqu_off = 0; /* Offset in wrqu */ 46262306a36Sopenharmony_ci /* Don't "optimise" the following variable, it will crash */ 46362306a36Sopenharmony_ci unsigned int cmd_index; /* *MUST* be unsigned */ 46462306a36Sopenharmony_ci struct sk_buff *skb; 46562306a36Sopenharmony_ci struct nlmsghdr *nlh; 46662306a36Sopenharmony_ci struct nlattr *nla; 46762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 46862306a36Sopenharmony_ci struct __compat_iw_event *compat_event; 46962306a36Sopenharmony_ci struct compat_iw_point compat_wrqu; 47062306a36Sopenharmony_ci struct sk_buff *compskb; 47162306a36Sopenharmony_ci int ptr_len; 47262306a36Sopenharmony_ci#endif 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * Nothing in the kernel sends scan events with data, be safe. 47662306a36Sopenharmony_ci * This is necessary because we cannot fix up scan event data 47762306a36Sopenharmony_ci * for compat, due to being contained in 'extra', but normally 47862306a36Sopenharmony_ci * applications are required to retrieve the scan data anyway 47962306a36Sopenharmony_ci * and no data is included in the event, this codifies that 48062306a36Sopenharmony_ci * practice. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (WARN_ON(cmd == SIOCGIWSCAN && extra)) 48362306a36Sopenharmony_ci extra = NULL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Get the description of the Event */ 48662306a36Sopenharmony_ci if (cmd <= SIOCIWLAST) { 48762306a36Sopenharmony_ci cmd_index = IW_IOCTL_IDX(cmd); 48862306a36Sopenharmony_ci if (cmd_index < standard_ioctl_num) 48962306a36Sopenharmony_ci descr = &(standard_ioctl[cmd_index]); 49062306a36Sopenharmony_ci } else { 49162306a36Sopenharmony_ci cmd_index = IW_EVENT_IDX(cmd); 49262306a36Sopenharmony_ci if (cmd_index < standard_event_num) 49362306a36Sopenharmony_ci descr = &(standard_event[cmd_index]); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci /* Don't accept unknown events */ 49662306a36Sopenharmony_ci if (descr == NULL) { 49762306a36Sopenharmony_ci /* Note : we don't return an error to the driver, because 49862306a36Sopenharmony_ci * the driver would not know what to do about it. It can't 49962306a36Sopenharmony_ci * return an error to the user, because the event is not 50062306a36Sopenharmony_ci * initiated by a user request. 50162306a36Sopenharmony_ci * The best the driver could do is to log an error message. 50262306a36Sopenharmony_ci * We will do it ourselves instead... 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", 50562306a36Sopenharmony_ci cmd); 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Check extra parameters and set extra_len */ 51062306a36Sopenharmony_ci if (descr->header_type == IW_HEADER_TYPE_POINT) { 51162306a36Sopenharmony_ci /* Check if number of token fits within bounds */ 51262306a36Sopenharmony_ci if (wrqu->data.length > descr->max_tokens) { 51362306a36Sopenharmony_ci netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n", 51462306a36Sopenharmony_ci cmd, wrqu->data.length); 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci if (wrqu->data.length < descr->min_tokens) { 51862306a36Sopenharmony_ci netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n", 51962306a36Sopenharmony_ci cmd, wrqu->data.length); 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci /* Calculate extra_len - extra is NULL for restricted events */ 52362306a36Sopenharmony_ci if (extra != NULL) 52462306a36Sopenharmony_ci extra_len = wrqu->data.length * descr->token_size; 52562306a36Sopenharmony_ci /* Always at an offset in wrqu */ 52662306a36Sopenharmony_ci wrqu_off = IW_EV_POINT_OFF; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Total length of the event */ 53062306a36Sopenharmony_ci hdr_len = event_type_size[descr->header_type]; 53162306a36Sopenharmony_ci event_len = hdr_len + extra_len; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * The problem for 64/32 bit. 53562306a36Sopenharmony_ci * 53662306a36Sopenharmony_ci * On 64-bit, a regular event is laid out as follows: 53762306a36Sopenharmony_ci * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 53862306a36Sopenharmony_ci * | event.len | event.cmd | p a d d i n g | 53962306a36Sopenharmony_ci * | wrqu data ... (with the correct size) | 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci * This padding exists because we manipulate event->u, 54262306a36Sopenharmony_ci * and 'event' is not packed. 54362306a36Sopenharmony_ci * 54462306a36Sopenharmony_ci * An iw_point event is laid out like this instead: 54562306a36Sopenharmony_ci * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 54662306a36Sopenharmony_ci * | event.len | event.cmd | p a d d i n g | 54762306a36Sopenharmony_ci * | iwpnt.len | iwpnt.flg | p a d d i n g | 54862306a36Sopenharmony_ci * | extra data ... 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * The second padding exists because struct iw_point is extended, 55162306a36Sopenharmony_ci * but this depends on the platform... 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * On 32-bit, all the padding shouldn't be there. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 55762306a36Sopenharmony_ci if (!skb) 55862306a36Sopenharmony_ci return; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Send via the RtNetlink event channel */ 56162306a36Sopenharmony_ci nlh = rtnetlink_ifinfo_prep(dev, skb); 56262306a36Sopenharmony_ci if (WARN_ON(!nlh)) { 56362306a36Sopenharmony_ci kfree_skb(skb); 56462306a36Sopenharmony_ci return; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Add the wireless events in the netlink packet */ 56862306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_WIRELESS, event_len); 56962306a36Sopenharmony_ci if (!nla) { 57062306a36Sopenharmony_ci kfree_skb(skb); 57162306a36Sopenharmony_ci return; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci event = nla_data(nla); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Fill event - first clear to avoid data leaking */ 57662306a36Sopenharmony_ci memset(event, 0, hdr_len); 57762306a36Sopenharmony_ci event->len = event_len; 57862306a36Sopenharmony_ci event->cmd = cmd; 57962306a36Sopenharmony_ci memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); 58062306a36Sopenharmony_ci if (extra_len) 58162306a36Sopenharmony_ci memcpy(((char *) event) + hdr_len, extra, extra_len); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci nlmsg_end(skb, nlh); 58462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 58562306a36Sopenharmony_ci hdr_len = compat_event_type_size[descr->header_type]; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* ptr_len is remaining size in event header apart from LCP */ 58862306a36Sopenharmony_ci ptr_len = hdr_len - IW_EV_COMPAT_LCP_LEN; 58962306a36Sopenharmony_ci event_len = hdr_len + extra_len; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 59262306a36Sopenharmony_ci if (!compskb) { 59362306a36Sopenharmony_ci kfree_skb(skb); 59462306a36Sopenharmony_ci return; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Send via the RtNetlink event channel */ 59862306a36Sopenharmony_ci nlh = rtnetlink_ifinfo_prep(dev, compskb); 59962306a36Sopenharmony_ci if (WARN_ON(!nlh)) { 60062306a36Sopenharmony_ci kfree_skb(skb); 60162306a36Sopenharmony_ci kfree_skb(compskb); 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Add the wireless events in the netlink packet */ 60662306a36Sopenharmony_ci nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); 60762306a36Sopenharmony_ci if (!nla) { 60862306a36Sopenharmony_ci kfree_skb(skb); 60962306a36Sopenharmony_ci kfree_skb(compskb); 61062306a36Sopenharmony_ci return; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci compat_event = nla_data(nla); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci compat_event->len = event_len; 61562306a36Sopenharmony_ci compat_event->cmd = cmd; 61662306a36Sopenharmony_ci if (descr->header_type == IW_HEADER_TYPE_POINT) { 61762306a36Sopenharmony_ci compat_wrqu.length = wrqu->data.length; 61862306a36Sopenharmony_ci compat_wrqu.flags = wrqu->data.flags; 61962306a36Sopenharmony_ci memcpy(compat_event->ptr_bytes, 62062306a36Sopenharmony_ci ((char *)&compat_wrqu) + IW_EV_COMPAT_POINT_OFF, 62162306a36Sopenharmony_ci ptr_len); 62262306a36Sopenharmony_ci if (extra_len) 62362306a36Sopenharmony_ci memcpy(&compat_event->ptr_bytes[ptr_len], 62462306a36Sopenharmony_ci extra, extra_len); 62562306a36Sopenharmony_ci } else { 62662306a36Sopenharmony_ci /* extra_len must be zero, so no if (extra) needed */ 62762306a36Sopenharmony_ci memcpy(compat_event->ptr_bytes, wrqu, ptr_len); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci nlmsg_end(compskb, nlh); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci skb_shinfo(skb)->frag_list = compskb; 63362306a36Sopenharmony_ci#endif 63462306a36Sopenharmony_ci skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); 63562306a36Sopenharmony_ci schedule_work(&wireless_nlevent_work); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL(wireless_send_event); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 64062306a36Sopenharmony_cistatic void wireless_warn_cfg80211_wext(void) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci char name[sizeof(current->comm)]; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci pr_warn_once("warning: `%s' uses wireless extensions which will stop working for Wi-Fi 7 hardware; use nl80211\n", 64562306a36Sopenharmony_ci get_task_comm(name, current)); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci#endif 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/* IW handlers */ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistruct iw_statistics *get_wireless_stats(struct net_device *dev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 65462306a36Sopenharmony_ci if ((dev->wireless_handlers != NULL) && 65562306a36Sopenharmony_ci (dev->wireless_handlers->get_wireless_stats != NULL)) 65662306a36Sopenharmony_ci return dev->wireless_handlers->get_wireless_stats(dev); 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 66062306a36Sopenharmony_ci if (dev->ieee80211_ptr && 66162306a36Sopenharmony_ci dev->ieee80211_ptr->wiphy && 66262306a36Sopenharmony_ci dev->ieee80211_ptr->wiphy->wext && 66362306a36Sopenharmony_ci dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) { 66462306a36Sopenharmony_ci wireless_warn_cfg80211_wext(); 66562306a36Sopenharmony_ci if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) 66662306a36Sopenharmony_ci return NULL; 66762306a36Sopenharmony_ci return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci#endif 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* not found */ 67262306a36Sopenharmony_ci return NULL; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* noinline to avoid a bogus warning with -O3 */ 67662306a36Sopenharmony_cistatic noinline int iw_handler_get_iwstats(struct net_device * dev, 67762306a36Sopenharmony_ci struct iw_request_info * info, 67862306a36Sopenharmony_ci union iwreq_data * wrqu, 67962306a36Sopenharmony_ci char * extra) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci /* Get stats from the driver */ 68262306a36Sopenharmony_ci struct iw_statistics *stats; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci stats = get_wireless_stats(dev); 68562306a36Sopenharmony_ci if (stats) { 68662306a36Sopenharmony_ci /* Copy statistics to extra */ 68762306a36Sopenharmony_ci memcpy(extra, stats, sizeof(struct iw_statistics)); 68862306a36Sopenharmony_ci wrqu->data.length = sizeof(struct iw_statistics); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Check if we need to clear the updated flag */ 69162306a36Sopenharmony_ci if (wrqu->data.flags != 0) 69262306a36Sopenharmony_ci stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; 69362306a36Sopenharmony_ci return 0; 69462306a36Sopenharmony_ci } else 69562306a36Sopenharmony_ci return -EOPNOTSUPP; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic iw_handler get_handler(struct net_device *dev, unsigned int cmd) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci /* Don't "optimise" the following variable, it will crash */ 70162306a36Sopenharmony_ci unsigned int index; /* *MUST* be unsigned */ 70262306a36Sopenharmony_ci const struct iw_handler_def *handlers = NULL; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 70562306a36Sopenharmony_ci if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) { 70662306a36Sopenharmony_ci wireless_warn_cfg80211_wext(); 70762306a36Sopenharmony_ci if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) 70862306a36Sopenharmony_ci return NULL; 70962306a36Sopenharmony_ci handlers = dev->ieee80211_ptr->wiphy->wext; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci#endif 71262306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 71362306a36Sopenharmony_ci if (dev->wireless_handlers) 71462306a36Sopenharmony_ci handlers = dev->wireless_handlers; 71562306a36Sopenharmony_ci#endif 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!handlers) 71862306a36Sopenharmony_ci return NULL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Try as a standard command */ 72162306a36Sopenharmony_ci index = IW_IOCTL_IDX(cmd); 72262306a36Sopenharmony_ci if (index < handlers->num_standard) 72362306a36Sopenharmony_ci return handlers->standard[index]; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci#ifdef CONFIG_WEXT_PRIV 72662306a36Sopenharmony_ci /* Try as a private command */ 72762306a36Sopenharmony_ci index = cmd - SIOCIWFIRSTPRIV; 72862306a36Sopenharmony_ci if (index < handlers->num_private) 72962306a36Sopenharmony_ci return handlers->private[index]; 73062306a36Sopenharmony_ci#endif 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Not found */ 73362306a36Sopenharmony_ci return NULL; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, 73762306a36Sopenharmony_ci const struct iw_ioctl_description *descr, 73862306a36Sopenharmony_ci iw_handler handler, struct net_device *dev, 73962306a36Sopenharmony_ci struct iw_request_info *info) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int err, extra_size, user_length = 0, essid_compat = 0; 74262306a36Sopenharmony_ci char *extra; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Calculate space needed by arguments. Always allocate 74562306a36Sopenharmony_ci * for max space. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci extra_size = descr->max_tokens * descr->token_size; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Check need for ESSID compatibility for WE < 21 */ 75062306a36Sopenharmony_ci switch (cmd) { 75162306a36Sopenharmony_ci case SIOCSIWESSID: 75262306a36Sopenharmony_ci case SIOCGIWESSID: 75362306a36Sopenharmony_ci case SIOCSIWNICKN: 75462306a36Sopenharmony_ci case SIOCGIWNICKN: 75562306a36Sopenharmony_ci if (iwp->length == descr->max_tokens + 1) 75662306a36Sopenharmony_ci essid_compat = 1; 75762306a36Sopenharmony_ci else if (IW_IS_SET(cmd) && (iwp->length != 0)) { 75862306a36Sopenharmony_ci char essid[IW_ESSID_MAX_SIZE + 1]; 75962306a36Sopenharmony_ci unsigned int len; 76062306a36Sopenharmony_ci len = iwp->length * descr->token_size; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (len > IW_ESSID_MAX_SIZE) 76362306a36Sopenharmony_ci return -EFAULT; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci err = copy_from_user(essid, iwp->pointer, len); 76662306a36Sopenharmony_ci if (err) 76762306a36Sopenharmony_ci return -EFAULT; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (essid[iwp->length - 1] == '\0') 77062306a36Sopenharmony_ci essid_compat = 1; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci default: 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci iwp->length -= essid_compat; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Check what user space is giving us */ 78062306a36Sopenharmony_ci if (IW_IS_SET(cmd)) { 78162306a36Sopenharmony_ci /* Check NULL pointer */ 78262306a36Sopenharmony_ci if (!iwp->pointer && iwp->length != 0) 78362306a36Sopenharmony_ci return -EFAULT; 78462306a36Sopenharmony_ci /* Check if number of token fits within bounds */ 78562306a36Sopenharmony_ci if (iwp->length > descr->max_tokens) 78662306a36Sopenharmony_ci return -E2BIG; 78762306a36Sopenharmony_ci if (iwp->length < descr->min_tokens) 78862306a36Sopenharmony_ci return -EINVAL; 78962306a36Sopenharmony_ci } else { 79062306a36Sopenharmony_ci /* Check NULL pointer */ 79162306a36Sopenharmony_ci if (!iwp->pointer) 79262306a36Sopenharmony_ci return -EFAULT; 79362306a36Sopenharmony_ci /* Save user space buffer size for checking */ 79462306a36Sopenharmony_ci user_length = iwp->length; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Don't check if user_length > max to allow forward 79762306a36Sopenharmony_ci * compatibility. The test user_length < min is 79862306a36Sopenharmony_ci * implied by the test at the end. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Support for very large requests */ 80262306a36Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_NOMAX) && 80362306a36Sopenharmony_ci (user_length > descr->max_tokens)) { 80462306a36Sopenharmony_ci /* Allow userspace to GET more than max so 80562306a36Sopenharmony_ci * we can support any size GET requests. 80662306a36Sopenharmony_ci * There is still a limit : -ENOMEM. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci extra_size = user_length * descr->token_size; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Note : user_length is originally a __u16, 81162306a36Sopenharmony_ci * and token_size is controlled by us, 81262306a36Sopenharmony_ci * so extra_size won't get negative and 81362306a36Sopenharmony_ci * won't overflow... 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Sanity-check to ensure we never end up _allocating_ zero 81962306a36Sopenharmony_ci * bytes of data for extra. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci if (extra_size <= 0) 82262306a36Sopenharmony_ci return -EFAULT; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* kzalloc() ensures NULL-termination for essid_compat. */ 82562306a36Sopenharmony_ci extra = kzalloc(extra_size, GFP_KERNEL); 82662306a36Sopenharmony_ci if (!extra) 82762306a36Sopenharmony_ci return -ENOMEM; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* If it is a SET, get all the extra data in here */ 83062306a36Sopenharmony_ci if (IW_IS_SET(cmd) && (iwp->length != 0)) { 83162306a36Sopenharmony_ci if (copy_from_user(extra, iwp->pointer, 83262306a36Sopenharmony_ci iwp->length * 83362306a36Sopenharmony_ci descr->token_size)) { 83462306a36Sopenharmony_ci err = -EFAULT; 83562306a36Sopenharmony_ci goto out; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (cmd == SIOCSIWENCODEEXT) { 83962306a36Sopenharmony_ci struct iw_encode_ext *ee = (void *) extra; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (iwp->length < sizeof(*ee) + ee->key_len) { 84262306a36Sopenharmony_ci err = -EFAULT; 84362306a36Sopenharmony_ci goto out; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * If this is a GET, but not NOMAX, it means that the extra 85162306a36Sopenharmony_ci * data is not bounded by userspace, but by max_tokens. Thus 85262306a36Sopenharmony_ci * set the length to max_tokens. This matches the extra data 85362306a36Sopenharmony_ci * allocation. 85462306a36Sopenharmony_ci * The driver should fill it with the number of tokens it 85562306a36Sopenharmony_ci * provided, and it may check iwp->length rather than having 85662306a36Sopenharmony_ci * knowledge of max_tokens. If the driver doesn't change the 85762306a36Sopenharmony_ci * iwp->length, this ioctl just copies back max_token tokens 85862306a36Sopenharmony_ci * filled with zeroes. Hopefully the driver isn't claiming 85962306a36Sopenharmony_ci * them to be valid data. 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ci iwp->length = descr->max_tokens; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci err = handler(dev, info, (union iwreq_data *) iwp, extra); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci iwp->length += essid_compat; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* If we have something to return to the user */ 86962306a36Sopenharmony_ci if (!err && IW_IS_GET(cmd)) { 87062306a36Sopenharmony_ci /* Check if there is enough buffer up there */ 87162306a36Sopenharmony_ci if (user_length < iwp->length) { 87262306a36Sopenharmony_ci err = -E2BIG; 87362306a36Sopenharmony_ci goto out; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (copy_to_user(iwp->pointer, extra, 87762306a36Sopenharmony_ci iwp->length * 87862306a36Sopenharmony_ci descr->token_size)) { 87962306a36Sopenharmony_ci err = -EFAULT; 88062306a36Sopenharmony_ci goto out; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Generate an event to notify listeners of the change */ 88562306a36Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_EVENT) && 88662306a36Sopenharmony_ci ((err == 0) || (err == -EIWCOMMIT))) { 88762306a36Sopenharmony_ci union iwreq_data *data = (union iwreq_data *) iwp; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (descr->flags & IW_DESCR_FLAG_RESTRICT) 89062306a36Sopenharmony_ci /* If the event is restricted, don't 89162306a36Sopenharmony_ci * export the payload. 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_ci wireless_send_event(dev, cmd, data, NULL); 89462306a36Sopenharmony_ci else 89562306a36Sopenharmony_ci wireless_send_event(dev, cmd, data, extra); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ciout: 89962306a36Sopenharmony_ci kfree(extra); 90062306a36Sopenharmony_ci return err; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* 90462306a36Sopenharmony_ci * Call the commit handler in the driver 90562306a36Sopenharmony_ci * (if exist and if conditions are right) 90662306a36Sopenharmony_ci * 90762306a36Sopenharmony_ci * Note : our current commit strategy is currently pretty dumb, 90862306a36Sopenharmony_ci * but we will be able to improve on that... 90962306a36Sopenharmony_ci * The goal is to try to agreagate as many changes as possible 91062306a36Sopenharmony_ci * before doing the commit. Drivers that will define a commit handler 91162306a36Sopenharmony_ci * are usually those that need a reset after changing parameters, so 91262306a36Sopenharmony_ci * we want to minimise the number of reset. 91362306a36Sopenharmony_ci * A cool idea is to use a timer : at each "set" command, we re-set the 91462306a36Sopenharmony_ci * timer, when the timer eventually fires, we call the driver. 91562306a36Sopenharmony_ci * Hopefully, more on that later. 91662306a36Sopenharmony_ci * 91762306a36Sopenharmony_ci * Also, I'm waiting to see how many people will complain about the 91862306a36Sopenharmony_ci * netif_running(dev) test. I'm open on that one... 91962306a36Sopenharmony_ci * Hopefully, the driver will remember to do a commit in "open()" ;-) 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ciint call_commit_handler(struct net_device *dev) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 92462306a36Sopenharmony_ci if (netif_running(dev) && 92562306a36Sopenharmony_ci dev->wireless_handlers && 92662306a36Sopenharmony_ci dev->wireless_handlers->standard[0]) 92762306a36Sopenharmony_ci /* Call the commit handler on the driver */ 92862306a36Sopenharmony_ci return dev->wireless_handlers->standard[0](dev, NULL, 92962306a36Sopenharmony_ci NULL, NULL); 93062306a36Sopenharmony_ci else 93162306a36Sopenharmony_ci return 0; /* Command completed successfully */ 93262306a36Sopenharmony_ci#else 93362306a36Sopenharmony_ci /* cfg80211 has no commit */ 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci#endif 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci/* 93962306a36Sopenharmony_ci * Main IOCTl dispatcher. 94062306a36Sopenharmony_ci * Check the type of IOCTL and call the appropriate wrapper... 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_cistatic int wireless_process_ioctl(struct net *net, struct iwreq *iwr, 94362306a36Sopenharmony_ci unsigned int cmd, 94462306a36Sopenharmony_ci struct iw_request_info *info, 94562306a36Sopenharmony_ci wext_ioctl_func standard, 94662306a36Sopenharmony_ci wext_ioctl_func private) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct net_device *dev; 94962306a36Sopenharmony_ci iw_handler handler; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* Permissions are already checked in dev_ioctl() before calling us. 95262306a36Sopenharmony_ci * The copy_to/from_user() of ifr is also dealt with in there */ 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* Make sure the device exist */ 95562306a36Sopenharmony_ci if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL) 95662306a36Sopenharmony_ci return -ENODEV; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* A bunch of special cases, then the generic case... 95962306a36Sopenharmony_ci * Note that 'cmd' is already filtered in dev_ioctl() with 96062306a36Sopenharmony_ci * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ 96162306a36Sopenharmony_ci if (cmd == SIOCGIWSTATS) 96262306a36Sopenharmony_ci return standard(dev, iwr, cmd, info, 96362306a36Sopenharmony_ci &iw_handler_get_iwstats); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci#ifdef CONFIG_WEXT_PRIV 96662306a36Sopenharmony_ci if (cmd == SIOCGIWPRIV && dev->wireless_handlers) 96762306a36Sopenharmony_ci return standard(dev, iwr, cmd, info, 96862306a36Sopenharmony_ci iw_handler_get_private); 96962306a36Sopenharmony_ci#endif 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Basic check */ 97262306a36Sopenharmony_ci if (!netif_device_present(dev)) 97362306a36Sopenharmony_ci return -ENODEV; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* New driver API : try to find the handler */ 97662306a36Sopenharmony_ci handler = get_handler(dev, cmd); 97762306a36Sopenharmony_ci if (handler) { 97862306a36Sopenharmony_ci /* Standard and private are not the same */ 97962306a36Sopenharmony_ci if (cmd < SIOCIWFIRSTPRIV) 98062306a36Sopenharmony_ci return standard(dev, iwr, cmd, info, handler); 98162306a36Sopenharmony_ci else if (private) 98262306a36Sopenharmony_ci return private(dev, iwr, cmd, info, handler); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci return -EOPNOTSUPP; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci/* If command is `set a parameter', or `get the encoding parameters', 98862306a36Sopenharmony_ci * check if the user has the right to do it. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_cistatic int wext_permission_check(unsigned int cmd) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || 99362306a36Sopenharmony_ci cmd == SIOCGIWENCODEEXT) && 99462306a36Sopenharmony_ci !capable(CAP_NET_ADMIN)) 99562306a36Sopenharmony_ci return -EPERM; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/* entry point from dev ioctl */ 100162306a36Sopenharmony_cistatic int wext_ioctl_dispatch(struct net *net, struct iwreq *iwr, 100262306a36Sopenharmony_ci unsigned int cmd, struct iw_request_info *info, 100362306a36Sopenharmony_ci wext_ioctl_func standard, 100462306a36Sopenharmony_ci wext_ioctl_func private) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci int ret = wext_permission_check(cmd); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (ret) 100962306a36Sopenharmony_ci return ret; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci dev_load(net, iwr->ifr_name); 101262306a36Sopenharmony_ci rtnl_lock(); 101362306a36Sopenharmony_ci ret = wireless_process_ioctl(net, iwr, cmd, info, standard, private); 101462306a36Sopenharmony_ci rtnl_unlock(); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return ret; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* 102062306a36Sopenharmony_ci * Wrapper to call a standard Wireless Extension handler. 102162306a36Sopenharmony_ci * We do various checks and also take care of moving data between 102262306a36Sopenharmony_ci * user space and kernel space. 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_cistatic int ioctl_standard_call(struct net_device * dev, 102562306a36Sopenharmony_ci struct iwreq *iwr, 102662306a36Sopenharmony_ci unsigned int cmd, 102762306a36Sopenharmony_ci struct iw_request_info *info, 102862306a36Sopenharmony_ci iw_handler handler) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci const struct iw_ioctl_description * descr; 103162306a36Sopenharmony_ci int ret = -EINVAL; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Get the description of the IOCTL */ 103462306a36Sopenharmony_ci if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) 103562306a36Sopenharmony_ci return -EOPNOTSUPP; 103662306a36Sopenharmony_ci descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* Check if we have a pointer to user space data or not */ 103962306a36Sopenharmony_ci if (descr->header_type != IW_HEADER_TYPE_POINT) { 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* No extra arguments. Trivial to handle */ 104262306a36Sopenharmony_ci ret = handler(dev, info, &(iwr->u), NULL); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Generate an event to notify listeners of the change */ 104562306a36Sopenharmony_ci if ((descr->flags & IW_DESCR_FLAG_EVENT) && 104662306a36Sopenharmony_ci ((ret == 0) || (ret == -EIWCOMMIT))) 104762306a36Sopenharmony_ci wireless_send_event(dev, cmd, &(iwr->u), NULL); 104862306a36Sopenharmony_ci } else { 104962306a36Sopenharmony_ci ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, 105062306a36Sopenharmony_ci handler, dev, info); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Call commit handler if needed and defined */ 105462306a36Sopenharmony_ci if (ret == -EIWCOMMIT) 105562306a36Sopenharmony_ci ret = call_commit_handler(dev); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Here, we will generate the appropriate event if needed */ 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return ret; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ciint wext_handle_ioctl(struct net *net, unsigned int cmd, void __user *arg) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci struct iw_request_info info = { .cmd = cmd, .flags = 0 }; 106662306a36Sopenharmony_ci struct iwreq iwr; 106762306a36Sopenharmony_ci int ret; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (copy_from_user(&iwr, arg, sizeof(iwr))) 107062306a36Sopenharmony_ci return -EFAULT; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci ret = wext_ioctl_dispatch(net, &iwr, cmd, &info, 107562306a36Sopenharmony_ci ioctl_standard_call, 107662306a36Sopenharmony_ci ioctl_private_call); 107762306a36Sopenharmony_ci if (ret >= 0 && 107862306a36Sopenharmony_ci IW_IS_GET(cmd) && 107962306a36Sopenharmony_ci copy_to_user(arg, &iwr, sizeof(struct iwreq))) 108062306a36Sopenharmony_ci return -EFAULT; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci return ret; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 108662306a36Sopenharmony_cistatic int compat_standard_call(struct net_device *dev, 108762306a36Sopenharmony_ci struct iwreq *iwr, 108862306a36Sopenharmony_ci unsigned int cmd, 108962306a36Sopenharmony_ci struct iw_request_info *info, 109062306a36Sopenharmony_ci iw_handler handler) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci const struct iw_ioctl_description *descr; 109362306a36Sopenharmony_ci struct compat_iw_point *iwp_compat; 109462306a36Sopenharmony_ci struct iw_point iwp; 109562306a36Sopenharmony_ci int err; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci descr = standard_ioctl + IW_IOCTL_IDX(cmd); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (descr->header_type != IW_HEADER_TYPE_POINT) 110062306a36Sopenharmony_ci return ioctl_standard_call(dev, iwr, cmd, info, handler); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci iwp_compat = (struct compat_iw_point *) &iwr->u.data; 110362306a36Sopenharmony_ci iwp.pointer = compat_ptr(iwp_compat->pointer); 110462306a36Sopenharmony_ci iwp.length = iwp_compat->length; 110562306a36Sopenharmony_ci iwp.flags = iwp_compat->flags; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci iwp_compat->pointer = ptr_to_compat(iwp.pointer); 111062306a36Sopenharmony_ci iwp_compat->length = iwp.length; 111162306a36Sopenharmony_ci iwp_compat->flags = iwp.flags; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return err; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ciint compat_wext_handle_ioctl(struct net *net, unsigned int cmd, 111762306a36Sopenharmony_ci unsigned long arg) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 112062306a36Sopenharmony_ci struct iw_request_info info; 112162306a36Sopenharmony_ci struct iwreq iwr; 112262306a36Sopenharmony_ci char *colon; 112362306a36Sopenharmony_ci int ret; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) 112662306a36Sopenharmony_ci return -EFAULT; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci iwr.ifr_name[IFNAMSIZ-1] = 0; 112962306a36Sopenharmony_ci colon = strchr(iwr.ifr_name, ':'); 113062306a36Sopenharmony_ci if (colon) 113162306a36Sopenharmony_ci *colon = 0; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci info.cmd = cmd; 113462306a36Sopenharmony_ci info.flags = IW_REQUEST_FLAG_COMPAT; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci ret = wext_ioctl_dispatch(net, &iwr, cmd, &info, 113762306a36Sopenharmony_ci compat_standard_call, 113862306a36Sopenharmony_ci compat_private_call); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (ret >= 0 && 114162306a36Sopenharmony_ci IW_IS_GET(cmd) && 114262306a36Sopenharmony_ci copy_to_user(argp, &iwr, sizeof(struct iwreq))) 114362306a36Sopenharmony_ci return -EFAULT; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci return ret; 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci#endif 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cichar *iwe_stream_add_event(struct iw_request_info *info, char *stream, 115062306a36Sopenharmony_ci char *ends, struct iw_event *iwe, int event_len) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci event_len = iwe_stream_event_len_adjust(info, event_len); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* Check if it's possible */ 115762306a36Sopenharmony_ci if (likely((stream + event_len) < ends)) { 115862306a36Sopenharmony_ci iwe->len = event_len; 115962306a36Sopenharmony_ci /* Beware of alignement issues on 64 bits */ 116062306a36Sopenharmony_ci memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); 116162306a36Sopenharmony_ci memcpy(stream + lcp_len, &iwe->u, 116262306a36Sopenharmony_ci event_len - lcp_len); 116362306a36Sopenharmony_ci stream += event_len; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return stream; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_event); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cichar *iwe_stream_add_point(struct iw_request_info *info, char *stream, 117162306a36Sopenharmony_ci char *ends, struct iw_event *iwe, char *extra) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci int event_len = iwe_stream_point_len(info) + iwe->u.data.length; 117462306a36Sopenharmony_ci int point_len = iwe_stream_point_len(info); 117562306a36Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* Check if it's possible */ 117862306a36Sopenharmony_ci if (likely((stream + event_len) < ends)) { 117962306a36Sopenharmony_ci iwe->len = event_len; 118062306a36Sopenharmony_ci memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); 118162306a36Sopenharmony_ci memcpy(stream + lcp_len, 118262306a36Sopenharmony_ci ((char *) &iwe->u) + IW_EV_POINT_OFF, 118362306a36Sopenharmony_ci IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); 118462306a36Sopenharmony_ci if (iwe->u.data.length && extra) 118562306a36Sopenharmony_ci memcpy(stream + point_len, extra, iwe->u.data.length); 118662306a36Sopenharmony_ci stream += event_len; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return stream; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_point); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cichar *iwe_stream_add_value(struct iw_request_info *info, char *event, 119462306a36Sopenharmony_ci char *value, char *ends, struct iw_event *iwe, 119562306a36Sopenharmony_ci int event_len) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci int lcp_len = iwe_stream_lcp_len(info); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* Don't duplicate LCP */ 120062306a36Sopenharmony_ci event_len -= IW_EV_LCP_LEN; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* Check if it's possible */ 120362306a36Sopenharmony_ci if (likely((value + event_len) < ends)) { 120462306a36Sopenharmony_ci /* Add new value */ 120562306a36Sopenharmony_ci memcpy(value, &iwe->u, event_len); 120662306a36Sopenharmony_ci value += event_len; 120762306a36Sopenharmony_ci /* Patch LCP */ 120862306a36Sopenharmony_ci iwe->len = value - event; 120962306a36Sopenharmony_ci memcpy(event, (char *) iwe, lcp_len); 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return value; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ciEXPORT_SYMBOL(iwe_stream_add_value); 1215